Changeset 272 for trunk/trax/vendor

Show
Ignore:
Timestamp:
01/18/07 08:49:09 (5 years ago)
Author:
john
Message:

added builtin validation methods, bug fixes, and changes submitted by grabmail

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/trax/vendor/trax/active_record.php

    r263 r272  
    7373     *  {@link http://pear.php.net/manual/en/package.database.mdb2.php PEAR MDB2} 
    7474     */ 
    75     private static $db = null; 
     75    protected static $db = null; 
    7676 
    7777    /** 
     
    177177 
    178178    /** 
    179      *  Force reconnect to database 
     179     *  Force reconnect to database every page load 
    180180     * 
    181181     *  @var boolean 
    182182     */ 
    183     public $force_reconnect = false; # should we force a connection everytime 
     183    public $force_reconnect = false; 
    184184 
    185185    /** 
     
    187187     *  each object index is off of this field 
    188188     * 
    189      *  @var boolean 
     189     *  @var string 
    190190     */     
    191191    public $index_on = "id";  
     
    267267     *  @var string[] 
    268268     */ 
    269     protected $auto_update_timestamps = array("updated_at","updated_on"); 
     269    public $auto_update_timestamps = array("updated_at","updated_on"); 
    270270 
    271271    /** 
     
    279279     *  @var string[] 
    280280     */ 
    281     protected $auto_create_timestamps = array("created_at","created_on"); 
     281    public $auto_create_timestamps = array("created_at","created_on"); 
    282282 
    283283    /** 
     
    288288     *  @var string  
    289289     */ 
    290      protected $date_format = "Y-m-d"; 
     290     public $date_format = "Y-m-d"; 
    291291 
    292292    /** 
     
    297297     *  @var string  
    298298     */     
    299      protected $time_format = "H:i:s"; 
     299     public $time_format = "H:i:s"; 
    300300        
    301301    /** 
     
    367367     */ 
    368368    public $errors = array(); 
     369 
     370    /** 
     371     * An array with all the default error messages. 
     372     */ 
     373    public $default_error_messages = array( 
     374        'inclusion' => "is not included in the list", 
     375        'exclusion' => "is reserved", 
     376        'invalid' => "is invalid", 
     377        'confirmation' => "doesn't match confirmation", 
     378        'accepted ' => "must be accepted", 
     379        'empty' => "can't be empty", 
     380        'blank' => "can't be blank", 
     381        'too_long' => "is too long (max is %d characters)", 
     382        'too_short' => "is too short (min is %d characters)", 
     383        'wrong_length' => "is the wrong length (should be %d characters)", 
     384        'taken' => "has already been taken", 
     385        'not_a_number' => "is not a number", 
     386        'not_an_integer' => "is not an integer" 
     387    ); 
     388 
     389    /** 
     390     * An array of all the builtin validation function calls. 
     391     */     
     392    protected $builtin_validation_functions = array( 
     393        'validates_acceptance_of', 
     394        'validates_confirmation_of', 
     395        'validates_exclusion_of',         
     396        'validates_format_of', 
     397        'validates_inclusion_of',         
     398        'validates_length_of', 
     399        'validates_numericality_of',         
     400        'validates_presence_of',         
     401        'validates_uniqueness_of' 
     402    ); 
     403     
     404    /** 
     405     * An array of all the builtin validation function to validate on a save/create/update. 
     406     */       
     407    public $builtins_to_validate = array(); 
    369408 
    370409    /** 
     
    433472 
    434473        # If $attributes array is passed in update the class with its contents 
    435         $this->update_attributes($attributes); 
     474        if(!is_null($attributes)) { 
     475            $this->update_attributes($attributes); 
     476        } 
     477         
     478        # If callback is defined in model run it. 
     479        # this could hurt performance... 
     480        if(method_exists($this, 'after_initialize')) { 
     481            $this->after_initialize();     
     482        }         
    436483    } 
    437484 
     
    497544        //echo "setting: $key = $value<br>"; 
    498545        if($key == "table_name") { 
    499             $this->set_content_columns($value); 
    500             # this elseif checks if first its an object if its parent is ActiveRecord 
     546            $this->set_content_columns($value);            
     547          # this elseif checks if first its an object if its parent is ActiveRecord 
    501548        } elseif(is_object($value) && get_parent_class($value) == __CLASS__ && $this->auto_save_associations) { 
    502549            if($association_type = $this->get_association_type($key)) { 
     
    672719                $this_primary_key_value = $this->$this_primary_key; 
    673720            } else { 
    674                 $this_primary_key_value = 0; 
     721                #$this_primary_key_value = 0; 
     722                # no primary key value so just return empty array same as find_all() 
     723                return array(); 
    675724            } 
    676725 
     
    753802                $conditions = "{$foreign_key} = {$foreign_key_value}"; 
    754803            } else { 
    755                 $conditions = "{$foreign_key} = 0"; 
     804                #$conditions = "{$foreign_key} = 0"; 
     805                # no primary key value so just return empty array same as find_all() 
     806                return array();                 
    756807            }             
    757808            $conditions .= $additional_conditions;  
     
    811862            $conditions = "{$foreign_key} = {$foreign_key_value}"; 
    812863        } else { 
    813             $conditions = "{$foreign_key} = 0"; 
     864            #$conditions = "{$foreign_key} = 0"; 
     865            return null; 
    814866        } 
    815867 
     
    817869         
    818870        # Get the list of other_class_name objects 
    819         $result = $other_class_object->find_first($conditions, $order); 
    820          
    821         # There should only be one result, an object, if so return it 
    822         return (is_object($result) ? $result : null); 
     871        return $other_class_object->find_first($conditions, $order); 
    823872    } 
    824873 
     
    874923            $conditions = "{$other_primary_key} = {$other_primary_key_value}"; 
    875924        } else { 
    876             $conditions = "{$other_primary_key} = 0"; 
     925            #$conditions = "{$other_primary_key} = 0"; 
     926            return null; 
    877927        } 
    878928        $conditions .= $additional_conditions; 
    879929         
    880930        # Get the list of other_class_name objects 
    881         $result = $other_class_object->find_first($conditions, $order); 
    882          
    883         # There should only be one result, an object, if so return it 
    884         return (is_object($result) ? $result : null); 
     931        return $other_class_object->find_first($conditions, $order); 
    885932    } 
    886933 
     
    912959        $aggregate_type = strtoupper(substr($aggregate_type, 0, -4)); 
    913960        ($parameters[0]) ? $field = $parameters[0] : $field = "*"; 
    914         $sql = "SELECT $aggregate_type($field) AS agg_result FROM $this->table_name "; 
     961        $sql = "SELECT {$aggregate_type}({$field}) AS agg_result FROM {$this->table_prefix}{$this->table_name} "; 
    915962         
    916963        # Use any passed-in parameters 
     
    951998        $tables = array($first_table, $second_table); 
    952999        @sort($tables); 
    953         return @implode("_", $tables); 
     1000        return $this->table_prefix.@implode("_", $tables); 
    9541001    } 
    9551002 
     
    10271074        if($this->column_attribute_exists($column) && ($conditions = $this->get_primary_key_conditions())) { 
    10281075            # Run the query to grab a specific columns value. 
    1029             $sql = "SELECT {$column} FROM {$this->table_name} WHERE {$conditions}"; 
     1076            $sql = "SELECT {$column} FROM {$this->table_prefix}{$this->table_name} WHERE {$conditions}"; 
    10301077            $this->log_query($sql); 
    10311078            $result = self::$db->queryOne($sql); 
     
    11781225    } 
    11791226 
    1180     /** 
    1181      *  Return rows selected by $conditions 
    1182      * 
    1183      *  If no rows match, an empty array is returned. 
    1184      *  @param string  SQL to use in the query.  If 
    1185      *    $conditions contains "SELECT", then $order, $limit and 
    1186      *    $joins are ignored and the query is completely specified by 
    1187      *    $conditions.  If $conditions is omitted or does not contain 
    1188      *    "SELECT", "SELECT * FROM" will be used.  If $conditions is 
    1189      *    specified and does not contain "SELECT", the query will 
    1190      *    include "WHERE $conditions".  If $conditions is null, the 
    1191      *    entire table is returned. 
    1192      *  @param string  Argument to "ORDER BY" in query. 
    1193      *    If specified, the query will include 
    1194      *    "ORDER BY $order". If omitted, no ordering will be 
    1195      *    applied.   
    1196      *  @param integer[] Page, rows per page??? 
    1197      *  @param string ??? 
    1198      *  @todo Document the $limit and $joins parameters 
     1227    /** 
     1228     *  Builds a sql statement. 
     1229     *   
    11991230     *  @uses $rows_per_page_default 
    12001231     *  @uses $rows_per_page 
    12011232     *  @uses $offset 
    12021233     *  @uses $page 
    1203      *  @uses is_error() 
    1204      *  @uses $new_record 
    1205      *  @uses query() 
    1206      *  @return object[] Array of objects of the same class as this 
    1207      *    object, one object for each row returned by the query. 
    1208      *    If the column 'id' was in the results, it is used as the key 
    1209      *    for that object in the array. 
    1210      *  @throws {@link ActiveRecordError} 
    1211      */ 
    1212     function find_all($conditions = null, $order = null, $limit = null, $joins = null) { 
    1213         //error_log("find_all(".(is_null($conditions)?'null':$conditions) 
    1214         //          .', ' . (is_null($order)?'null':$order) 
    1215         //          .', ' . (is_null($limit)?'null':var_export($limit,true)) 
    1216         //          .', ' . (is_null($joins)?'null':$joins).')'); 
    1217  
    1218         $offset = null; 
     1234     * 
     1235     */ 
     1236    function build_sql($conditions = null, $order = null, $limit = null, $joins = null) { 
     1237         
     1238        $offset = null; 
    12191239        $per_page = null; 
    12201240        $select = null; 
     
    12441264            if(is_null($select)) { 
    12451265                $select = "*"; 
    1246             } 
     1266            }  
    12471267 
    12481268            # SQL will be built from specifications in argument 
    1249             $sql  = "SELECT {$select} FROM {$this->table_name} ";          
     1269            $sql  = "SELECT {$select} FROM {$this->table_prefix}{$this->table_name} ";          
    12501270             
    12511271            # If join specified, include it 
     
    12771297                    $this->rows_per_page = $this->rows_per_page_default; 
    12781298                } 
    1279                                  
    1280                 $this->page = $_REQUEST['page']; 
     1299                 
     1300                # Only use request's page if you are calling from find_all_with_pagination() and if it is int 
     1301                if(strval(intval($_REQUEST['page'])) == $_REQUEST['page']) { 
     1302                    $this->page = $_REQUEST['page']; 
     1303                } 
     1304                 
    12811305                if($this->page <= 0) { 
    12821306                    $this->page = 1; 
     
    12841308                                 
    12851309                # Set the LIMIT string segment for the SQL 
    1286                 if(!$offset) { 
     1310                if(is_null($offset)) { 
    12871311                    $offset = ($this->page - 1) * $this->rows_per_page; 
    12881312                } 
     
    12901314                $sql .= "LIMIT {$this->rows_per_page} OFFSET {$offset}"; 
    12911315                # $sql .= "LIMIT $offset, $this->rows_per_page"; 
    1292          
    1293                 # Set number of total pages in result set   
    1294                 if($count = $this->count_all($this->primary_keys[0], $conditions, $joins)) { 
    1295                     $this->pagination_count = $count; 
    1296                     $this->pages = ( 
    1297                        ($count % $this->rows_per_page) == 0) 
    1298                         ? $count / $this->rows_per_page 
    1299                         : floor($count / $this->rows_per_page) + 1; 
    1300                 }  
    1301             } 
    1302         } 
     1316                 
     1317                # Set number of total pages in result set 
     1318                if($count = $this->count_all($this->primary_keys[0], $conditions, $joins)) { 
     1319                    $this->pagination_count = $count; 
     1320                    $this->pages = (($count % $this->rows_per_page) == 0) 
     1321                        ? $count / $this->rows_per_page 
     1322                        : floor($count / $this->rows_per_page) + 1;  
     1323                } 
     1324            } 
     1325        } 
     1326         
     1327        return $sql; 
     1328    } 
     1329 
     1330    /** 
     1331     *  Return rows selected by $conditions 
     1332     * 
     1333     *  If no rows match, an empty array is returned. 
     1334     *  @param string SQL to use in the query.  If 
     1335     *    $conditions contains "SELECT", then $order, $limit and 
     1336     *    $joins are ignored and the query is completely specified by 
     1337     *    $conditions.  If $conditions is omitted or does not contain 
     1338     *    "SELECT", "SELECT * FROM" will be used.  If $conditions is 
     1339     *    specified and does not contain "SELECT", the query will 
     1340     *    include "WHERE $conditions".  If $conditions is null, the 
     1341     *    entire table is returned. 
     1342     *  @param string Argument to "ORDER BY" in query. 
     1343     *    If specified, the query will include 
     1344     *    "ORDER BY $order". If omitted, no ordering will be 
     1345     *    applied.   
     1346     *  @param integer[] Page, rows per page??? 
     1347     *  @param string ??? 
     1348     *  @todo Document the $limit and $joins parameters 
     1349     *  @uses is_error() 
     1350     *  @uses $new_record 
     1351     *  @uses query() 
     1352     *  @return object[] Array of objects of the same class as this 
     1353     *    object, one object for each row returned by the query. 
     1354     *    If the column 'id' was in the results, it is used as the key 
     1355     *    for that object in the array. 
     1356     *  @throws {@link ActiveRecordError} 
     1357     */ 
     1358    function find_all($conditions = null, $order = null, $limit = null, $joins = null) { 
     1359        //error_log("find_all(".(is_null($conditions)?'null':$conditions) 
     1360        //          .', ' . (is_null($order)?'null':$order) 
     1361        //          .', ' . (is_null($limit)?'null':var_export($limit,true)) 
     1362        //          .', ' . (is_null($joins)?'null':$joins).')'); 
     1363 
     1364        # Placed the sql building code in a separate function 
     1365        $sql = $this->build_sql($conditions, $order, $limit, $joins); 
    13031366 
    13041367        # echo "ActiveRecord::find_all() - sql: $sql\n<br>"; 
     
    13231386            $objects[$objects_key] = $object; 
    13241387            unset($object); 
     1388            # If callback is defined in model run it. 
     1389            # this will probably hurt performance... 
     1390            if(method_exists($this, 'after_find')) { 
     1391                $this->after_find();     
     1392            } 
    13251393        } 
    13261394        return $objects; 
     
    14231491     *  @throws {@link ActiveRecordError} 
    14241492     */ 
    1425     function find_first($conditions = null, $order = null, $limit = null, $joins = null) { 
     1493    function find_first($conditions = null, $order = null, $limit = 1, $joins = null) { 
    14261494        if(is_array($conditions)) { 
    14271495            $options = $conditions;     
     
    14331501        if(!is_null($joins)) $options['joins'] = $joins; 
    14341502 
    1435         $result = $this->find_all($options); 
    1436         return @current($result); 
     1503        $result = @current($this->find_all($options)); 
     1504        return (is_object($result) ? $result : null);         
    14371505    } 
    14381506 
     
    15151583     */ 
    15161584    function update_all($updates, $conditions = null) { 
    1517         $sql = "UPDATE $this->table_name SET $updates WHERE $conditions"; 
     1585        $sql = "UPDATE {$this->table_prefix}{$this->table_name} SET {$updates} WHERE {$conditions}"; 
    15181586        $result = $this->query($sql); 
    15191587        if ($this->is_error($result)) { 
     
    16211689        self::$db->loadModule('Extended', null, true);                 
    16221690        # $primary_key_value may either be a quoted integer or php null 
    1623         $primary_key_value = self::$db->getBeforeID($this->table_name, $this->primary_keys[0]); 
     1691        $primary_key_value = self::$db->getBeforeID("{$this->table_prefix}{$this->table_name}", $this->primary_keys[0]); 
    16241692        if($this->is_error($primary_key_value)) { 
    16251693            $this->raise($primary_key_value->getMessage()); 
     
    16291697        $fields = @implode(', ', array_keys($attributes)); 
    16301698        $values = @implode(', ', array_values($attributes)); 
    1631         $sql = "INSERT INTO {$this->table_name} ($fields) VALUES ($values)"; 
     1699        $sql = "INSERT INTO {$this->table_prefix}{$this->table_name} ({$fields}) VALUES ({$values})"; 
    16321700        //echo "add_record: SQL: $sql<br>"; 
    16331701        //error_log("add_record: SQL: $sql"); 
     
    16401708            $primary_key = $this->primary_keys[0]; 
    16411709            # $id is now equivalent to the value in the id field that was inserted 
    1642             $primary_key_value = self::$db->getAfterID($primary_key_value, $this->table_name, $this->primary_keys[0]); 
     1710            $primary_key_value = self::$db->getAfterID($primary_key_value, "{$this->table_prefix}{$this->table_name}", $this->primary_keys[0]); 
    16431711            if($this->is_error($primary_key_value)) { 
    16441712                $this->raise($primary_key_value->getMessage()); 
     
    16811749        $updates = $this->get_updates_sql(); 
    16821750        $conditions = $this->get_primary_key_conditions(); 
    1683         $sql = "UPDATE {$this->table_name} SET {$updates} WHERE {$conditions}"; 
     1751        $sql = "UPDATE {$this->table_prefix}{$this->table_name} SET {$updates} WHERE {$conditions}"; 
    16841752        //echo "update_record:$sql<br>"; 
    16851753        //error_log("update_record: SQL: $sql"); 
     
    19201988 
    19211989        # Delete the record(s) 
    1922         if($this->is_error($rs = $this->query("DELETE FROM $this->table_name WHERE $conditions"))) { 
     1990        if($this->is_error($rs = $this->query("DELETE FROM {$this->table_prefix}{$this->table_name} WHERE {$conditions}"))) { 
    19231991            $this->raise($rs->getMessage()); 
    19241992        } 
     
    20842152    function update_attributes($attributes) { 
    20852153        //error_log('update_attributes()'); 
    2086  
    20872154        if(is_array($attributes)) { 
     2155            $datetime_fields = array(); 
    20882156            //  Test each attribute to be updated 
    20892157            //  and process according to its type 
     
    20912159                # datetime / date parts check 
    20922160                if(preg_match('/^\w+\(.*i\)$/i', $field)) { 
    2093      
    20942161                    //  The name of this attribute ends in "(?i)" 
    20952162                    //  indicating that it's part of a date or time 
    2096      
    2097                     //  Accumulate all the pieces of a date and time in 
    2098                     //  array $datetime_key.  The keys in the array are 
    2099                     //  the names of date/time attributes with the final 
    2100                     //  "(?i)" stripped off. 
    2101                     $datetime_key = substr($field, 0, strpos($field, "(")); 
    2102                     if( !isset($old_datetime_key) 
    2103                         || ($datetime_key != $old_datetime_key)) { 
    2104      
    2105                         //  This value of $datetime_key hasn't been seen 
    2106                         //  before, so remember it. 
    2107                         $old_datetime_key = $datetime_key;                      
    2108      
    2109                         //  $datetime_value accumulates the pieces of the 
    2110                         //  date/time attribute $datetime_key 
    2111                         $datetime_value = "";    
    2112                     }  
    2113      
    2114                     //  Concatentate pieces of the attribute's value 
    2115                     //  FIXME: this only works if the array elements 
    2116                     //  are sorted by key.  Is this guaranteed? 
    2117                     if(strstr($field, "2i") || strstr($field, "3i")) { 
    2118                         $datetime_value .= "-".$value;     
    2119                     } elseif(strstr($field, "4i")) { 
    2120                         $datetime_value .= " ".$value;         
    2121                     } elseif(strstr($field, "5i")) { 
    2122                         $datetime_value .= ":".$value;         
    2123                     } else { 
    2124                         $datetime_value .= $value;     
    2125                     }   
    2126                     $datetime_fields[$old_datetime_key] = $datetime_value;      
     2163                    $datetime_field = substr($field, 0, strpos($field, "(")); 
     2164                    if(!in_array($datetime_field, $datetime_fields)) { 
     2165                        $datetime_fields[] = $datetime_field; 
     2166                    }                                              
    21272167                    # this elseif checks if first its an object if its parent is ActiveRecord             
    21282168                } elseif(is_object($value) && get_parent_class($value) == __CLASS__ && $this->auto_save_associations) { 
     
    21412181                    } 
    21422182                } else { 
    2143      
    21442183                    //  Just a simple attribute, copy it 
    21452184                    $this->$field = $value; 
     
    21492188            //  If any date/time fields were found, assign the 
    21502189            //  accumulated values to corresponding attributes 
    2151             if(isset($datetime_fields) 
    2152                && is_array($datetime_fields)) { 
    2153                 foreach($datetime_fields as $field => $value) { 
    2154                     //error_log("$field = $value"); 
    2155                     $this->$field = $value;     
     2190            if(count($datetime_fields)) { 
     2191                foreach($datetime_fields as $datetime_field) { 
     2192                    $datetime_format = ''; 
     2193                    $datetime_value = ''; 
     2194                    if($attributes[$datetime_field."(1i)"] 
     2195                        && $attributes[$datetime_field."(2i)"] 
     2196                        && $attributes[$datetime_field."(3i)"]) { 
     2197                        $datetime_value = $attributes[$datetime_field."(1i)"] 
     2198                        . "-" . $attributes[$datetime_field."(2i)"] 
     2199                        . "-" . $attributes[$datetime_field."(3i)"]; 
     2200                        $datetime_format = $this->date_format; 
     2201                    } 
     2202                    $datetime_value .= " "; 
     2203                    if($attributes[$datetime_field."(4i)"] 
     2204                        && $attributes[$datetime_field."(5i)"]) { 
     2205                        $datetime_value .= $attributes[$datetime_field."(4i)"] 
     2206                        . ":" . $attributes[$datetime_field."(5i)"]; 
     2207                        $datetime_format .= " ".$this->time_format;                         
     2208                    }     
     2209                    if($datetime_value = trim($datetime_value)) { 
     2210                        $datetime_value = date($datetime_format, strtotime($datetime_value)); 
     2211                        //error_log("($field) $datetime_field = $datetime_value"); 
     2212                        $this->$datetime_field = $datetime_value;     
     2213                    } 
    21562214                }     
    21572215            } 
     
    22262284        } 
    22272285        $return = array(); 
    2228         foreach($attributes as $key => $value) { 
    2229             $value = $this->check_datetime($key, $value); 
    2230             $column = $this->column_for_attribute($key); 
    2231             $type = $this->attribute_is_string($key, $column) ? "Text" : "Integer"; 
    2232             $value = self::$db->quote($value, $type); 
    2233             if($value == 'NULL' && stristr($column['flags'], "not_null")) { 
    2234                 $value = "''";     
    2235             } 
    2236             $return[$key] = $value; 
     2286        foreach($attributes as $name => $value) {            
     2287            $return[$name] = $this->quote_attribute($name, $value); 
    22372288        } 
    22382289        return $return; 
    22392290    } 
     2291 
     2292    /** 
     2293     *  Quotes a single attribute for use in an sql statement. 
     2294     * 
     2295     */     
     2296    function quote_attribute($attribute, $value = null) { 
     2297        $value = is_null($value) ? $this->$attribute : $value; 
     2298        $value = $this->check_datetime($attribute, $value);         
     2299        $column = $this->column_for_attribute($attribute); 
     2300        if(isset($column['mdb2type'])) { 
     2301            $type = $column['mdb2type']; 
     2302        } else { 
     2303            $type = $this->attribute_is_string($attribute, $column) ?  
     2304                "text" : is_float($attribute) ? "float" : "integer";  
     2305        }             
     2306        $value = self::$db->quote($value, $type);     
     2307        if($value === 'NULL' && stristr($column['flags'], "not_null")) { 
     2308            $value = "''";     
     2309        }  
     2310        return $value;                
     2311    } 
     2312 
     2313    /** 
     2314     *  Escapes a string for use in an sql statement. 
     2315     * 
     2316     */     
     2317    function escape($string) { 
     2318        return(self::$db->escape($string)); 
     2319    }     
    22402320 
    22412321    /** 
     
    22702350     *  Example: if $primary_keys = array("id", "ssn") and column "id" 
    22712351     *  has value "5" and column "ssn" has value "123-45-6789" then 
    2272      *  the string "id = '5' AND ssn = '123-45-6789'" would be returned. 
     2352     *  the string "id = 5 AND ssn = '123-45-6789'" would be returned. 
    22732353     *  @uses $primary_keys 
    22742354     *  @uses quoted_attributes() 
    22752355     *  @return string Column name = 'value' [ AND name = 'value']... 
    22762356     */ 
    2277     function get_primary_key_conditions() { 
     2357    function get_primary_key_conditions($operator = "=") { 
    22782358        $conditions = null; 
    22792359        $attributes = $this->quoted_attributes(); 
     
    22832363            foreach($attributes as $key => $value) { 
    22842364                if(in_array($key, $this->primary_keys) && isset($value) && $value != "''") { 
    2285                     $conditions[] = "$key = $value";     
     2365                    $conditions[] = "{$key} {$operator} {$value}";     
    22862366                } 
    22872367            } 
     
    23382418     */     
    23392419    private function get_class_name() { 
    2340         if(!is_null($this->class_name)) { 
    2341             $class_name = $this->class_name; 
    2342         } else { 
    2343             $class_name = get_class($this);         
    2344         }  
    2345         return $class_name;                
     2420        return !is_null($this->class_name) ? $this->class_name : get_class($this);                 
    23462421    } 
    23472422 
     
    23612436     */ 
    23622437    function set_content_columns($table_name) { 
     2438        if(!is_null($this->table_prefix)) { 
     2439            $table_name = $this->table_prefix.$table_name; 
     2440        } 
    23632441        if(isset(self::$table_info[$table_name])) { 
    23642442            $this->content_columns = self::$table_info[$table_name];   
     
    23732451                foreach($this->content_columns as $column) { 
    23742452                    $this->content_columns[$i++]['human_name'] = Inflector::humanize($column['name']); 
    2375                 } 
     2453                }                 
    23762454                self::$table_info[$table_name] = $this->content_columns; 
    23772455            } 
     
    23902468        // fetch the last inserted id via autoincrement or current value of a sequence 
    23912469        if(self::$db->supports('auto_increment') === true) { 
    2392             $id = self::$db->lastInsertID($this->table_name, $this->primary_keys[0]);    
     2470            $id = self::$db->lastInsertID("{$this->table_prefix}{$this->table_name}", $this->primary_keys[0]);    
    23932471            if($this->is_error($id)) { 
    23942472                $this->raise($id->getMessage()); 
     
    24272505        $connection =& self::$active_connections[$this->connection_name]; 
    24282506        if(!is_object($connection) || $this->force_reconnect) { 
     2507            $connection_settings = array(); 
     2508            $connection_options = array(); 
    24292509            if(array_key_exists($this->connection_name, self::$database_settings)) { 
    24302510                 # Use a different custom sections settings ? 
     
    24652545 
    24662546    /** 
    2467      *  Test whether argument is a PEAR Error object or a MDB2 Error object. 
    2468      * 
    2469      *  @param object $obj Object to test 
    2470      *  @return boolean  Whether object is one of these two errors 
    2471      */ 
    2472     function is_error($obj) { 
    2473         if((PEAR::isError($obj)) || (MDB2::isError($obj))) { 
    2474             return true; 
    2475         } else { 
    2476             return false; 
    2477         } 
    2478     } 
    2479  
    2480     /** 
    2481      *  Throw an exception describing an error in this object 
    2482      * 
    2483      *  @throws {@link ActiveRecordError} 
    2484      */ 
    2485     function raise($message) { 
    2486         $error_message  = "Model Class: ".$this->get_class_name()."<br>"; 
    2487         $error_message .= "Error Message: ".$message; 
    2488         throw new ActiveRecordError($error_message, "ActiveRecord Error", "500"); 
    2489     } 
    2490  
    2491     /** 
    2492      *  Add or overwrite description of an error to the list of errors 
    2493      *  @param string $error Error message text 
    2494      *  @param string $key Key to associate with the error (in the 
    2495      *    simple case, column name).  If omitted, numeric keys will be 
    2496      *    assigned starting with 0.  If specified and the key already 
    2497      *    exists in $errors, the old error message will be overwritten 
    2498      *    with the value of $error. 
    2499      *  @uses $errors 
    2500      */ 
    2501     function add_error($error, $key = null) { 
    2502         if(!is_null($key)) { 
    2503             $this->errors[$key] = $error; 
    2504         } else { 
    2505             $this->errors[] = $error; 
    2506         } 
    2507     } 
    2508  
    2509     /** 
    2510      *  Return description of non-fatal errors 
    2511      * 
    2512      *  @uses $errors 
    2513      *  @param boolean $return_string 
    2514      *    <ul> 
    2515      *      <li>true => Concatenate all error descriptions into a string 
    2516      *        using $seperator between elements and return the 
    2517      *        string</li> 
    2518      *      <li>false => Return the error descriptions as an array</li> 
    2519      *    </ul> 
    2520      *  @param string $seperator  String to concatenate between error 
    2521      *    descriptions if $return_string == true 
    2522      *  @return mixed Error description(s), if any 
    2523      */ 
    2524     function get_errors($return_string = false, $seperator = "<br>") { 
    2525         if($return_string && count($this->errors) > 0) { 
    2526             return implode($seperator, $this->errors); 
    2527         } else { 
    2528             return $this->errors; 
    2529         } 
    2530     } 
    2531  
    2532     /** 
    2533      *  Return errors as a string. 
    2534      * 
    2535      *  Concatenate all error descriptions into a stringusing 
    2536      *  $seperator between elements and return the string. 
    2537      *  @param string $seperator  String to concatenate between error 
    2538      *    descriptions 
    2539      *  @return string Concatenated error description(s), if any 
    2540      */ 
    2541     function get_errors_as_string($seperator = "<br>") { 
    2542         return $this->get_errors(true, $seperator); 
    2543     } 
    2544  
    2545     /** 
    25462547     *  Determine if passed in attribute (table column) is a string 
    25472548     *  @param string $attribute Name of the table column 
     
    25892590     *  @uses validate(); 
    25902591     *  @uses validate_model_attributes(); 
     2592     *  @uses validate_builtin(); 
    25912593     *  @uses validate_on_create();  
    25922594     *  @return boolean  
     
    26062608            $this->validate(); 
    26072609            $this->validate_model_attributes(); 
     2610            $this->validate_builtin();             
    26082611            $this->after_validation(); 
    26092612            $this->validate_on_create();  
     
    26142617            $this->validate(); 
    26152618            $this->validate_model_attributes(); 
     2619            $this->validate_builtin(); 
    26162620            $this->after_validation(); 
    26172621            $this->validate_on_update(); 
     2622            $this->validate_on_update_builtin(); 
    26182623            $this->after_validation_on_update(); 
    26192624        } 
     
    26762681        } 
    26772682        return $validated_ok; 
    2678     } 
    2679  
     2683    }   
     2684     
    26802685    /** 
    26812686     *  Overwrite this method for validation checks on all saves and 
     
    27812786     */ 
    27822787    function after_delete() {} 
     2788 
     2789 
     2790    /** 
     2791     *  Validates any builtin validates_* functions defined as  
     2792     *  class variables in child model class. 
     2793     * 
     2794     *  eg.  
     2795     *  public $validates_presence_of = array( 
     2796     *      'first_name' => array( 
     2797     *          'message' => "is not optional.", 
     2798     *          'on' => 'update' 
     2799     *      ), 
     2800     *      'last_name' => null, 
     2801     *      'password' => array( 
     2802     *          'on' => 'create' 
     2803     *      ) 
     2804     *  ); 
     2805     * 
     2806     *  @uses $builtin_validation_functions 
     2807     */ 
     2808    function validate_builtin() { 
     2809        foreach($this->builtin_validation_functions as $method_name) { 
     2810            $validation_name = $this->$method_name; 
     2811            if(method_exists($this, $method_name) && is_array($validation_name)) { 
     2812                foreach($validation_name as $attribute_name => $options) { 
     2813                    if(!is_array($options)) { 
     2814                        $options = array();    
     2815                    } 
     2816                    $parameters = array(); 
     2817                    $on = array_key_exists('on', $options) ?  
     2818                        $options['on'] : 'save'; 
     2819                    $message = array_key_exists('message', $options) ?  
     2820                        $options['message'] : null;                   
     2821                    switch($method_name) { 
     2822                        case 'validates_acceptance_of': 
     2823                            $accept = array_key_exists('accept', $options) ? $options['accept'] : 1; 
     2824                            $parameters = array($attribute_name, $message, $accept); 
     2825                            break; 
     2826                        case 'validates_confirmation_of': 
     2827                            $parameters = array($attribute_name, $message);                             
     2828                            break; 
     2829                        case 'validates_exclusion_of':  
     2830                            $in = array_key_exists('in', $options) ? $options['in'] : array(); 
     2831                            $parameters = array($attribute_name, $in, $message);   
     2832                            break;      
     2833                        case 'validates_format_of': 
     2834                            $with = array_key_exists('with', $options) ? $options['with'] : ''; 
     2835                            $parameters = array($attribute_name, $with, $message); 
     2836                            break; 
     2837                        case 'validates_inclusion_of':    
     2838                            $in = array_key_exists('in', $options) ? $options['in'] : array(); 
     2839                            $parameters = array($attribute_name, $in, $message);    
     2840                            break;   
     2841                        case 'validates_length_of': 
     2842                            $parameters = array($attribute_name, $options); 
     2843                            break; 
     2844                        case 'validates_numericality_of':   
     2845                            $only_integer = array_key_exists('only_integer', $options) ?  
     2846                                $options['only_integer'] : false; 
     2847                            $allow_null = array_key_exists('allow_null', $options) ?  
     2848                                $options['allow_null'] : false;                           
     2849                            $parameters = array($attribute_name, $message, $only_integer, $allow_null); 
     2850                            break;       
     2851                        case 'validates_presence_of':    
     2852                            $parameters = array($attribute_name, $message);   
     2853                            break;    
     2854                        case 'validates_uniqueness_of':   
     2855                            $parameters = array($attribute_name, $message); 
     2856                            break;                        
     2857                    } 
     2858                    if(count($parameters)) {  
     2859                        $call = false; 
     2860                        if($on == 'create' && $this->new_record) { 
     2861                            $call = true; 
     2862                        } elseif($on == 'update' && !$this->new_record) { 
     2863                            $call = true; 
     2864                        } elseif($on == 'save') { 
     2865                            $call = true;          
     2866                        }        
     2867                        if($call) { 
     2868                            # error_log("calling $method_name(".implode(",",$parameters).")"); 
     2869                            call_user_func_array(array($this, $method_name), $parameters);         
     2870                        }                               
     2871                    } 
     2872                } 
     2873            }              
     2874        }         
     2875    } 
     2876 
     2877    /** 
     2878     * Validates that a checkbox is clicked. 
     2879     * eg. validates_acceptance_of('eula') 
     2880     * 
     2881     * @param string|array $attribute_names 
     2882     * @param string $message 
     2883     * @param string $accept 
     2884     */ 
     2885    function validates_acceptance_of($attribute_names, $message = null, $accept = 1) { 
     2886        $message = $this->get_error_message_for_validation($message, 'acceptance');      
     2887        foreach((array) $attribute_names as $attribute_name) {                   
     2888            if($this->$attribute_name != $accept) { 
     2889                $attribute_human = Inflector::humanize($attribute_name); 
     2890                $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     2891            } 
     2892        } 
     2893    } 
     2894 
     2895    /** 
     2896     * Validates that a field has the same value as its corresponding confirmation field. 
     2897     * eg. validates_confirmation_of('password') 
     2898     * 
     2899     * @param string|array $attribute_names 
     2900     * @param string $message 
     2901     */ 
     2902    function validates_confirmation_of($attribute_names, $message = null) { 
     2903        $message = $this->get_error_message_for_validation($message, 'confirmation'); 
     2904        foreach((array) $attribute_names as $attribute_name) {           
     2905            $attribute_confirmation = $attribute_name . '_confirmation'; 
     2906            if($this->$attribute_confirmation != $this->$attribute_name) { 
     2907                $attribute_human = Inflector::humanize($attribute_name); 
     2908                $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     2909            } 
     2910        } 
     2911    } 
     2912 
     2913    /** 
     2914     * Validates that specified attributes are NOT in an array of elements. 
     2915     * eg. validates_exclusion_of('age, 'in' => array(13, 19)) 
     2916     * 
     2917     * @param string|array $attribute_names 
     2918     * @param mixed $in array(1,2,3,4,5) or string 1..5 
     2919     * @param string $message 
     2920     */ 
     2921    function validates_exclusion_of($attribute_names, $in = array(), $message = null) { 
     2922        $message = $this->get_error_message_for_validation($message, 'exclusion'); 
     2923        foreach((array) $attribute_names as $attribute_name) {                   
     2924            if(is_string($in)) { 
     2925                list($minimum, $maximum) = explode('..', $in); 
     2926                if($this->$attribute_name >= $minimum && $this->$attribute_name <= $maximum) { 
     2927                    $attribute_human = Inflector::humanize($attribute_name); 
     2928                    $this->add_error("{$attribute_human} {$message}", $attribute_name);         
     2929                } 
     2930            } elseif(is_array($in)) { 
     2931                if(in_array($this->$attribute_name, $in)) { 
     2932                    $attribute_human = Inflector::humanize($attribute_name); 
     2933                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     2934                } 
     2935            }    
     2936        } 
     2937    } 
     2938 
     2939    /** 
     2940     * Validates that specified attributes matches a regular expression 
     2941     * eg. validates_format_of('email', '/^(+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i') 
     2942     * 
     2943     * @param string|array $attribute_names 
     2944     * @param string $regex 
     2945     * @param string $message 
     2946     */ 
     2947    function validates_format_of($attribute_names, $regex, $message = null) { 
     2948        $message = $this->get_error_message_for_validation($message, 'invalid');         
     2949        foreach((array) $attribute_names as $attribute_name) {                               
     2950            $value = $this->$attribute_name;         
     2951            # Was there an error? 
     2952            if(!preg_match($regex, $value)) { 
     2953                $attribute_human = Inflector::humanize($attribute_name); 
     2954                $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     2955            } 
     2956        } 
     2957    } 
     2958 
     2959    /** 
     2960     * Validates that specified attributes are in an array of elements. 
     2961     * eg. validates_inclusion_of('gender', array('m', 'f')) 
     2962     * 
     2963     * @param string|array $attribute_names 
     2964     * @param mixed $in array(1,2,3,4,5) or string 1..5 
     2965     * @param string $message 
     2966     */ 
     2967    function validates_inclusion_of($attribute_names, $in = array(), $message = null) { 
     2968        $message = $this->get_error_message_for_validation($message, 'inclusion'); 
     2969        foreach((array) $attribute_names as $attribute_name) {                   
     2970            if(is_string($in)) { 
     2971                list($minimum, $maximum) = explode('..', $in); 
     2972                if(!($this->$attribute_name >= $minimum && $this->$attribute_name <= $maximum)) { 
     2973                    $attribute_human = Inflector::humanize($attribute_name); 
     2974                    $this->add_error("{$attribute_human} {$message}", $attribute_name);         
     2975                } 
     2976            } elseif(is_array($in)) { 
     2977                if(!in_array($this->$attribute_name, $in)) { 
     2978                    $attribute_human = Inflector::humanize($attribute_name); 
     2979                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     2980                } 
     2981            }  
     2982        } 
     2983    } 
     2984 
     2985    /** 
     2986     * Validates that specified attributes are of some length 
     2987     * eg. validates_length_of('password', array('minimum' => 8)) 
     2988     * 
     2989     * @param string|array $attribute_names 
     2990     * @param array $options 
     2991     */  
     2992    function validates_length_of($attribute_names, $options = array( 
     2993        'too_short' => null, 'too_long' => null, 'wrong_length' => null, 'message' => null)) {                       
     2994        # Convert 'in' to 'minimum' and 'maximum' 
     2995        if(isset($options['in'])) { 
     2996            list($options['minimum'], $options['maximum']) = explode('..', $options['in']); 
     2997        } 
     2998        # If 'message' is set see if we need to override other messages 
     2999        if(isset($options['message'])) { 
     3000            if(!isset($options['too_short'])) $options['too_short'] = $options['message']; 
     3001            if(!isset($options['too_long'])) $options['too_long'] = $options['message']; 
     3002            if(!isset($options['wrong_length'])) $options['wrong_length'] = $options['message'];         
     3003        } 
     3004                 
     3005        foreach((array) $attribute_names as $attribute_name) {           
     3006            # Attribute string length 
     3007            $len = strlen($this->$attribute_name); 
     3008            $attribute_human = Inflector::humanize($attribute_name); 
     3009             
     3010            # If you have set the min length option 
     3011            if(isset($options['minimum'])) { 
     3012                $message = $this->get_error_message_for_validation($options['too_short'], 'too_short', $options['minimum']); 
     3013                if($len < $options['minimum']) { 
     3014                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3015                } 
     3016            } 
     3017             
     3018            # If you have set the max length option 
     3019            if(isset($options['maximum'])) { 
     3020                $message = $this->get_error_message_for_validation($options['too_long'], 'too_long', $options['maximum']); 
     3021                if($len > $options['maximum']) { 
     3022                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3023                } 
     3024            } 
     3025             
     3026            # If you have set an exact length option 
     3027            if(isset($options['is'])) { 
     3028                $message = $this->get_error_message_for_validation($options['wrong_length'], 'wrong_length', $options['is']); 
     3029                if($len != $options['is']) { 
     3030                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3031                } 
     3032            } 
     3033        } 
     3034    } 
     3035 
     3036    /** 
     3037     * Validates that specified attributes are numbers 
     3038     * eg. validates_numericality_of('value') 
     3039     * 
     3040     * @param string|array $attribute_names 
     3041     * @param string $message 
     3042     */ 
     3043    function validates_numericality_of($attribute_names, $message = null, $only_integer = false, $allow_null = false) {       
     3044        foreach((array) $attribute_names as $attribute_name) {   
     3045            $value = $this->$attribute_name;                 
     3046            # Skip validation if you allow null 
     3047            if($allow_null && is_null($value)) { 
     3048                break; 
     3049            }            
     3050            if($only_integer) { 
     3051                $message = $this->get_error_message_for_validation($message, 'not_an_integer'); 
     3052                if(!is_integer($value)) { 
     3053                    $attribute_human = Inflector::humanize($attribute_name); 
     3054                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3055                } 
     3056            } else { 
     3057                $message = $this->get_error_message_for_validation($message, 'not_a_number'); 
     3058                if(!is_numeric($value)) { 
     3059                    $attribute_human = Inflector::humanize($attribute_name); 
     3060                    $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3061                } 
     3062            } 
     3063        } 
     3064    } 
     3065         
     3066    /** 
     3067     * Validates that specified attributes are not blank 
     3068     * eg. validates_presence_of(array('firstname', 'lastname')) 
     3069     * 
     3070     * @param string|array $attribute_names 
     3071     * @param string $message 
     3072     */ 
     3073    function validates_presence_of($attribute_names, $message = null) { 
     3074        $message = $this->get_error_message_for_validation($message, 'empty');       
     3075        foreach((array) $attribute_names as $attribute_name) {               
     3076            if($this->$attribute_name === '' || is_null($this->$attribute_name)) { 
     3077                $attribute_human = Inflector::humanize($attribute_name); 
     3078                $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3079            } 
     3080        } 
     3081    } 
     3082     
     3083    /** 
     3084     * Validates that specified attributes are unique in the model database table 
     3085     * eg. validates_uniqueness_of('username') 
     3086     * 
     3087     * @param string|array $attribute_names 
     3088     * @param string $message 
     3089     */ 
     3090    function validates_uniqueness_of($attribute_names, $message = null) { 
     3091        $message = $this->get_error_message_for_validation($message, 'taken'); 
     3092        foreach((array) $attribute_names as $attribute_name) {                       
     3093            $quoted_value = $this->quote_attribute($attribute_name); 
     3094            # Conditions for new and existing record 
     3095            if($this->new_record) { 
     3096                $conditions = sprintf("%s = %s", $attribute_name, $quoted_value); 
     3097            } else { 
     3098                $conditions = sprintf("%s = %s AND %s", $attribute_name,  
     3099                    $quoted_value, $this->get_primary_key_conditions("!=")); 
     3100            }    
     3101            if($this->find_first($conditions)) { 
     3102                $attribute_human = Inflector::humanize($attribute_name); 
     3103                $this->add_error("{$attribute_human} {$message}", $attribute_name); 
     3104            } 
     3105        } 
     3106    } 
     3107         
     3108    /** 
     3109     * Return the error message for a validation function 
     3110     * 
     3111     * @param string $message 
     3112     * @param string $key 
     3113     * @param string $value 
     3114     * @return string 
     3115     */ 
     3116    private function get_error_message_for_validation($message, $key, $value = null) { 
     3117        if(is_null($message)) { 
     3118            # Return default error message 
     3119            return sprintf($this->default_error_messages[$key], $value); 
     3120        } else {  
     3121            # Return your custom error message 
     3122            return $message; 
     3123        } 
     3124    } 
     3125 
     3126    /** 
     3127     *  Test whether argument is a PEAR Error object or a MDB2 Error object. 
     3128     * 
     3129     *  @param object $obj Object to test 
     3130     *  @return boolean  Whether object is one of these two errors 
     3131     */ 
     3132    function is_error($obj) { 
     3133        if((PEAR::isError($obj)) || (MDB2::isError($obj))) { 
     3134            return true; 
     3135        } else { 
     3136            return false; 
     3137        } 
     3138    } 
     3139 
     3140    /** 
     3141     *  Throw an exception describing an error in this object 
     3142     * 
     3143     *  @throws {@link ActiveRecordError} 
     3144     */ 
     3145    function raise($message) { 
     3146        $error_message  = "Model Class: ".$this->get_class_name()."<br>"; 
     3147        $error_message .= "Error Message: ".$message; 
     3148        throw new ActiveRecordError($error_message, "ActiveRecord Error", "500"); 
     3149    } 
     3150 
     3151    /** 
     3152     *  Add or overwrite description of an error to the list of errors 
     3153     *  @param string $error Error message text 
     3154     *  @param string $key Key to associate with the error (in the 
     3155     *    simple case, column name).  If omitted, numeric keys will be 
     3156     *    assigned starting with 0.  If specified and the key already 
     3157     *    exists in $errors, the old error message will be overwritten 
     3158     *    with the value of $error. 
     3159     *  @uses $errors 
     3160     */ 
     3161    function add_error($error, $key = null) { 
     3162        if(!is_null($key)) { 
     3163            $this->errors[$key] = $error; 
     3164        } else { 
     3165            $this->errors[] = $error; 
     3166        } 
     3167    } 
     3168 
     3169    /** 
     3170     *  Return description of non-fatal errors 
     3171     * 
     3172     *  @uses $errors 
     3173     *  @param boolean $return_string 
     3174     *    <ul> 
     3175     *      <li>true => Concatenate all error descriptions into a string 
     3176     *        using $seperator between elements and return the 
     3177     *        string</li> 
     3178     *      <li>false => Return the error descriptions as an array</li> 
     3179     *    </ul> 
     3180     *  @param string $seperator  String to concatenate between error 
     3181     *    descriptions if $return_string == true 
     3182     *  @return mixed Error description(s), if any 
     3183     */ 
     3184    function get_errors($return_string = false, $seperator = "<br>") { 
     3185        if($return_string && count($this->errors) > 0) { 
     3186            return implode($seperator, $this->errors); 
     3187        } else { 
     3188            return $this->errors; 
     3189        } 
     3190    } 
     3191 
     3192    /** 
     3193     *  Return errors as a string. 
     3194     * 
     3195     *  Concatenate all error descriptions into a stringusing 
     3196     *  $seperator between elements and return the string. 
     3197     *  @param string $seperator  String to concatenate between error 
     3198     *    descriptions 
     3199     *  @return string Concatenated error description(s), if any 
     3200     */ 
     3201    function get_errors_as_string($seperator = "<br>") { 
     3202        return $this->get_errors(true, $seperator); 
     3203    }   
    27833204 
    27843205    /** 
     
    27953216 
    27963217} 
     3218 
     3219 
    27973220 
    27983221// -- set Emacs parameters --