Changeset 281

Show
Ignore:
Timestamp:
07/12/07 11:01:18 (1 year ago)
Author:
bermiferrer
Message:

Classifying Active Record source by functionality as required by the upcoming API documentation generator.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/README.txt

    r178 r281  
    1111 
    1212You can find more information at the Akelos Framework website at 
    13 http://akelos.org  
     13http://www.akelos.org  
    1414 
    1515 
  • trunk/config/locales/en.php

    r104 r281  
    3333$dictionary['Unable to fetch current model name'] = 'Unable to fetch current model name'; 
    3434$dictionary['Unable to set "%table_name" table for the model "%model".  There is no "%table_name" available into current database layout. Set AK_ACTIVE_CONTROLLER_VALIDATE_TABLE_NAMES constant to false in order to avoid table name validation'] = 'Unable to set "%table_name" table for the model "%model".  There is no "%table_name" available into current database layout. Set AK_ACTIVE_CONTROLLER_VALIDATE_TABLE_NAMES constant to false in order to avoid table name validation'; 
    35 $dictionary['You are calling recursivelly AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'; 
    36 $dictionary['You are calling recursivelly AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'; 
     35$dictionary['You are calling recursively AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'; 
     36$dictionary['You are calling recursively AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'; 
    3737$dictionary['Error'] = 'Error'; 
    3838$dictionary['There was an error while setting the composed field "%field_name", the following mapping column/s "%columns" do not exist'] = 'There was an error while setting the composed field "%field_name", the following mapping column/s "%columns" do not exist'; 
  • trunk/config/locales/es.php

    r104 r281  
    3131$dictionary['Unable to fetch current model name'] = 'No se ha podido averiguar el nombre del modelo actual'; 
    3232$dictionary['Unable to set "%table_name" table for the model "%model".  There is no "%table_name" available into current database layout. Set AK_ACTIVE_CONTROLLER_VALIDATE_TABLE_NAMES constant to false in order to avoid table name validation'] = 'No se ha podido relacionar la tabla "%table_name" con el modelo "%model". No se ha encontrado ninguna tabla llamada "%table_name" entre las tablas de la base de datos. Defina la constante AK_ACTIVE_CONTROLLER_VALIDATE_TABLE_NAMES como falsa en config/config.php para desactivar la auto-averiguación de los nombres de tablas'; 
    33 $dictionary['You are calling recursivelly AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'; 
    34 $dictionary['You are calling recursivelly AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'; 
     33$dictionary['You are calling recursively AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::setAttribute by placing parent::setAttribute() or  parent::set() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::setAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_SET_RECURSION and set it to false'; 
     34$dictionary['You are calling recursively AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'] = 'You are calling recursivelly AkActiveRecord::getAttribute by placing parent::getAttribute() or  parent::get() on your model "%method" method. In order to avoid this, set the 3rd paramenter of parent::getAttribute to FALSE. If this was the behaviour you expected, please define the constant AK_ACTIVE_RECORD_PROTECT_GET_RECURSION and set it to false'; 
    3535$dictionary['Error'] = 'Error'; 
    3636$dictionary['There was an error while setting the composed field "%field_name", the following mapping column/s "%columns" do not exist'] = 'There was an error while setting the composed field "%field_name", the following mapping column/s "%columns" do not exist'; 
  • trunk/lib/AkActiveRecord.php

    r278 r281  
    1212 * @package AkelosFramework 
    1313 * @subpackage AkActiveRecord 
     14 * @component Active Record 
    1415 * @author Bermi Ferrer <bermi a.t akelos c.om> 2004 - 2007 
    1516 * @author Kaste 2007 
     
    118119* == Saving arrays, hashes, and other non-mappable objects in text columns == 
    119120*  
    120 * Active Record can serialize any object in text columns. To do so, you must specify this with by setting the attribute serialize whith  
     121* Active Record can serialize any object in text columns. To do so, you must specify this with by setting the attribute serialize with  
    121122* a comma separated list of columns or an array.  
    122123* This makes it possible to store arrays, hashes, and other non-mappeable objects without doing any additional work. Example: 
     
    295296    } 
    296297 
    297  
    298  
    299     /** 
    300     * If this macro is used, only those attributed named in it will be accessible  
    301     * for mass-assignment, such as new ModelName($attributes) and $this->attributes($attributes).  
    302     * This is the more conservative choice for mass-assignment protection.  
    303     * If you'd rather start from an all-open default and restrict attributes as needed,  
    304     * have a look at AkActiveRecord::setProtectedAttributes(). 
    305     */ 
    306     function setAccessibleAttributes() 
    307     { 
    308         $args = func_get_args(); 
    309         $this->_accessibleAttributes = array_unique(array_merge((array)$this->_accessibleAttributes, $args)); 
    310     } 
    311  
    312     /** 
    313      * Attributes named in this macro are protected from mass-assignment, such as  
    314      * new ModelName($attributes) and $this->attributes(attributes). Their assignment  
    315      * will simply be ignored. Instead, you can use the direct writer methods to do assignment.  
    316      * This is meant to protect sensitive attributes to be overwritten by URL/form hackers.  
    317      *  
    318      * Example: 
    319      * <code> 
    320      *   class Customer extends AkActiveRecord 
    321      *    { 
    322      *      function Customer() 
    323      *      { 
    324      *          $this->setProtectedAttributes('credit_rating'); 
    325      *      } 
    326      *    } 
    327      *   
    328      *    $Customer = new Customer('name' => 'David', 'credit_rating' => 'Excellent'); 
    329      *    $Customer->credit_rating // => null 
    330      *    $Customer->attributes(array('description' => 'Jolly fellow', 'credit_rating' => 'Superb')); 
    331      *    $Customer->credit_rating // => null 
    332      *   
    333      *    $Customer->credit_rating = 'Average' 
    334      *    $Customer->credit_rating // => 'Average' 
    335      *  </code> 
    336      */     
    337     function setProtectedAttributes() 
    338     { 
    339         $args = func_get_args(); 
    340         $this->_protectedAttributes = array_unique(array_merge((array)$this->_protectedAttributes, $args)); 
    341     } 
    342  
    343  
    344     /** 
    345     * Returns true if a connection that?s accessible to this class have already been opened. 
    346     */  
    347     function isConnected() 
    348     { 
    349         return isset($this->_db); 
    350     } 
    351  
    352     /** 
    353     * Returns the connection currently associated with the class. This can also be used to  
    354     * "borrow" the connection to do database work unrelated to any of the specific Active Records. 
    355     */ 
    356     function &getConnection() 
    357     { 
    358         return $this->_db; 
    359     } 
    360  
    361     /** 
    362     * Set the connection for the class. 
    363     */ 
    364     function setConnection($dns = null, $connection_id = null) 
    365     { 
    366         $this->_db =& Ak::db($dns, $connection_id); 
    367     } 
    368  
    369     /** 
    370     * Returns an array of columns objects where the primary id, all columns ending in "_id" or "_count",  
    371     * and columns used for single table inheritance has been removed. 
    372     */ 
    373     function getContentColumns() 
    374     { 
    375         $inheritance_column = $this->getInheritanceColumn(); 
    376         $columns = $this->getColumns(); 
    377         foreach ($columns as $name=>$details){ 
    378             if((substr($name,-3) == '_id' || substr($name,-6) == '_count') || 
    379             !empty($details['primaryKey']) || ($inheritance_column !== false && $inheritance_column == $name)){ 
    380                 unset($columns[$name]); 
    381             } 
    382         } 
    383         return $columns; 
    384     } 
    385  
    386  
    387  
     298    /** 
     299    * New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved 
     300    * (pass an array with key names matching the associated table column names).  
     301    * In both instances, valid attribute keys are determined by the column names of the associated table; hence you can't  
     302    * have attributes that aren't part of the table columns. 
     303    */ 
     304    function newRecord($attributes) 
     305    { 
     306        $this->_newRecord = true; 
     307 
     308        if(AK_ACTIVE_RECORD_SKIP_SETTING_ACTIVE_RECORD_DEFAULTS && empty($attributes)){ 
     309            return; 
     310        } 
     311 
     312        if(isset($attributes) && !is_array($attributes)){ 
     313            $attributes = func_get_args(); 
     314        } 
     315        $this->setAttributes($this->attributesFromColumnDefinition(),true); 
     316        $this->setAttributes($attributes); 
     317    } 
     318 
     319 
     320    /** 
     321    * Returns a clone of the record that hasn't been assigned an id yet and is treated as a new record. 
     322    */ 
     323    function cloneRecord() 
     324    { 
     325        $model_name = $this->getModelName(); 
     326        $attributes = $this->getAttributesBeforeTypeCast(); 
     327        if(isset($attributes[$this->getPrimaryKey()])){ 
     328            unset($attributes[$this->getPrimaryKey()]); 
     329        } 
     330        return new $model_name($attributes); 
     331    } 
     332 
     333 
     334    /** 
     335    * Returns true if this object hasn't been saved yet that is, a record for the object doesn't exist yet. 
     336    */ 
     337    function isNewRecord() 
     338    { 
     339        if(!isset($this->_newRecord) && !isset($this->{$this->getPrimaryKey()})){ 
     340            $this->_newRecord = true; 
     341        } 
     342        return $this->_newRecord; 
     343    } 
     344 
     345 
     346 
     347    /** 
     348    * Reloads the attributes of this object from the database. 
     349    */    
     350    function reload() 
     351    { 
     352        /** 
     353        * @todo clear cache 
     354        */         
     355        if($object = $this->find($this->getId())){ 
     356            $this->setAttributes($object->getAttributes(), true); 
     357            return true; 
     358        }else { 
     359            return false; 
     360        } 
     361    } 
     362 
     363 
     364 
     365    /**  
     366                         Creating records 
     367    ==================================================================== 
     368    */ 
    388369    /** 
    389370    * Creates an object, instantly saves it as a record (if the validation permits it), and returns it.  
     
    407388    } 
    408389 
    409  
     390    function createOrUpdate($validate = true) 
     391    { 
     392        if($validate && !$this->isValid() || !($this->isNewRecord() ? $this->afterValidationOnCreate() : $this->afterValidationOnUpdate())){ 
     393            $this->transactionFail(); 
     394            return false; 
     395        } 
     396        return $this->isNewRecord() ? $this->_create() : $this->_update(); 
     397    } 
     398     
     399 
     400    /** 
     401    * Creates a new record with values matching those of the instance attributes. 
     402    * Must be called as a result of a call to createOrUpdate. 
     403    */ 
     404    function _create() 
     405    { 
     406        if($this->isFrozen()){ 
     407            $this->transactionFail(); 
     408            return false; 
     409        } 
     410 
     411        if($this->beforeCreate()){ 
     412 
     413            $this->notifyObservers('beforeCreate'); 
     414 
     415            if($this->_recordTimestamps){ 
     416                if ($this->hasColumn('created_at')){ 
     417                    $this->setAttribute('created_at', Ak::getDate()); 
     418                } 
     419                if ($this->hasColumn('created_on')){ 
     420                    $this->setAttribute('created_on', Ak::getDate(null, 'Y-m-d')); 
     421                } 
     422 
     423                if(isset($this->expires_on)){ 
     424                    if(isset($this->expires_at) && $this->hasColumn('expires_at')){ 
     425                        $this->setAttribute('expires_at',Ak::getDate(strtotime($this->expires_at) + (defined('AK_TIME_DIFFERENCE') ? AK_TIME_DIFFERENCE*60 : 0))); 
     426                    }elseif(isset($this->expires_on) && $this->hasColumn('expires_on')){ 
     427                        $this->setAttribute('expires_on',Ak::getDate(strtotime($this->expires_on) + (defined('AK_TIME_DIFFERENCE') ? AK_TIME_DIFFERENCE*60 : 0), 'Y-m-d')); 
     428                    } 
     429                } 
     430            } 
     431 
     432            $attributes = $this->getColumnsForAtrributes($this->getAttributes()); 
     433 
     434            if($this->isLockingEnabled()){ 
     435                $attributes['lock_version'] = 1; 
     436                $this->setAttribute('lock_version',1); 
     437            } 
     438 
     439            $pk = $this->getPrimaryKey(); 
     440            $table = $this->getTableName(); 
     441 
     442            foreach ($attributes as $column=>$value){ 
     443                $attributes[$column] = $this->castAttributeForDatabase($column,$value); 
     444            } 
     445 
     446            /** 
     447            * @todo sanitize attributes 
     448            * 'beforeValidationOnCreate', 'afterValidationOnCreate' 
     449            */ 
     450            if(!isset($this->_generateSequence) || (isset($this->_generateSequence) && $this->_generateSequence !== false)){ 
     451                if((empty($attributes[$pk]) || (!empty($attributes[$pk]) && (integer)$attributes[$pk] > 0 ))){ 
     452                    if($this->_getDatabaseType() != 'mysql'){ 
     453                        $table_details = $this->_databaseTableInternals('seq_'.$table); 
     454                        if(!isset($table_details['ID'])){ 
     455                            $this->_db->CreateSequence('seq_'.$table); 
     456                        } 
     457                        $attributes[$pk] = $this->_db->GenID('seq_'.$table); 
     458                    } 
     459                } 
     460            } 
     461 
     462            $__attributes = $attributes; 
     463            $attributes = array_diff($attributes, array('',"''")); 
     464 
     465            $sql = 'INSERT INTO '.$table.' '. 
     466            '('.join(', ',array_keys($attributes)).') '. 
     467            'VALUES ('.join(',',array_values($attributes)).')'; 
     468 
     469            if(!$this->_executeSql($sql, false)){ 
     470                AK_DEBUG ? trigger_error($this->_db->ErrorMsg(), E_USER_NOTICE) : null; 
     471            } 
     472 
     473            $id = !empty($attributes[$pk]) ? $attributes[$pk] : $this->_db->Insert_ID($table, $pk); 
     474            $this->setId($id); 
     475 
     476            if(!$this->transactionHasFailed()){ 
     477                $this->_newRecord = false; 
     478                if(!$this->afterCreate()){ 
     479                    $this->transactionFail(); 
     480                }else{ 
     481                    $this->notifyObservers('afterCreate'); 
     482                } 
     483            } 
     484        }else{ 
     485            $this->transactionFail(); 
     486        } 
     487        return $this; 
     488    } 
     489 
     490    /*/Creating records*/ 
     491 
     492 
     493    /**  
     494                         Saving records 
     495    ==================================================================== 
     496    */ 
     497    /** 
     498    * - No record exists: Creates a new record with values matching those of the object attributes. 
     499    * - A record does exist: Updates the record with values matching those of the object attributes. 
     500    */ 
     501    function save($validate = true) 
     502    { 
     503        if($this->isFrozen()){ 
     504            return false; 
     505        } 
     506        $result = false; 
     507        $this->transactionStart(); 
     508        if($this->beforeSave() && $this->notifyObservers('beforeSave')){ 
     509            $result = $this->createOrUpdate($validate); 
     510            if(!$this->transactionHasFailed()){ 
     511                if(!$this->afterSave()){ 
     512                    $this->transactionFail(); 
     513                }else{ 
     514                    if(!$this->notifyObservers('afterSave')){ 
     515                        $this->transactionFail(); 
     516                    } 
     517                } 
     518            } 
     519        }else{ 
     520            $this->transactionFail(); 
     521        } 
     522 
     523        $result = $this->transactionHasFailed() ? false : $result; 
     524        $this->transactionComplete(); 
     525 
     526        return $result; 
     527    } 
     528     
     529    /*/Saving records*/ 
     530     
     531    /**  
     532                            Counting Records 
     533    ==================================================================== 
     534    See also: Counting Attributes. 
     535    */ 
     536     
    410537    /** 
    411538      * Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. 
     
    430557        return @(integer)$rs->fields[0]; 
    431558    } 
    432  
    433     /** 
    434     * Increments the specified counter by one. So $DiscussionBoard->incrementCounter("post_count",  
    435     * $discussion_board_id); would increment the "post_count" counter on the board responding to  
    436     * $discussion_board_id. This is used for caching aggregate values, so that they doesn't need to  
    437     * be computed every time. Especially important for looping over a collection where each element  
    438     * require a number of aggregate values. Like the $DiscussionBoard that needs to list both the number of posts and comments. 
    439     */ 
    440     function incrementCounter($counter_name, $id, $difference = 1) 
    441     { 
    442         $new_value = $this->getAttribute($counter_name) + $difference; 
    443         if($this->updateAll($counter_name.' = '.$new_value, $this->getPrimaryKey().' = '.$this->castAttributeForDatabase($this->getPrimaryKey(), $id)) === 0){ 
    444             return false; 
    445         } 
    446         return $new_value; 
    447     } 
    448  
    449     /** 
    450     * Works like AkActiveRecord::incrementCounter, but decrements instead. 
    451     */ 
    452     function decrementCounter($counter_name, $id, $difference = 1) 
    453     { 
    454         $new_value = $this->getAttribute($counter_name) - $difference; 
    455  
    456         if(!$this->updateAll($counter_name.' = '.$new_value, $this->getPrimaryKey().' = '.$this->castAttributeForDatabase($this->getPrimaryKey(), $id)) === 0){ 
    457             return false; 
    458         } 
    459         return $new_value; 
    460     } 
    461  
    462  
    463  
     559    /*/Counting Records*/ 
     560     
     561    /**  
     562                          Updating records 
     563    ==================================================================== 
     564    See also: Callbacks. 
     565    */ 
     566     
    464567    /** 
    465568    * Finds the record from the passed id, instantly saves it with the passed attributes (if the validation permits it),  
     
    507610    } 
    508611 
    509  
    510  
    511  
    512612    /** 
    513613    * Updates all records with the SET-part of an SQL update statement in updates and returns an  
     
    515615    * <code>$Billing->updateAll("category = 'authorized', approved = 1", "author = 'David'");</code> 
    516616    *  
    517     * Important note: Condifitons are not sanitized yet so beware of accepting  
     617    * Important note: Conditions are not sanitized yet so beware of accepting  
    518618    * variable conditions when using this function 
    519619    */ 
     
    534634 
    535635    /** 
     636    * Updates the associated record with values matching those of the instance attributes. 
     637    * Must be called as a result of a call to createOrUpdate. 
     638    */ 
     639    function _update() 
     640    { 
     641        if($this->isFrozen()){ 
     642            $this->transactionFail(); 
     643            return false; 
     644        } 
     645        if($this->beforeUpdate()){ 
     646            $this->notifyObservers('beforeUpdate'); 
     647 
     648            if($this->_recordTimestamps){ 
     649                if ($this->hasColumn('updated_at')){ 
     650                    $this->setAttribute('updated_at', Ak::getDate()); 
     651                } 
     652                if ($this->hasColumn('updated_on')){ 
     653                    $this->setAttribute('updated_on', Ak::getDate(null, 'Y-m-d')); 
     654                } 
     655            } 
     656 
     657 
     658            $lock_check = ''; 
     659            if ($this->isLockingEnabled()){ 
     660                $previous_value = $this->lock_version; 
     661                $this->setAttribute('lock_version', $previous_value + 1); 
     662                $lock_check = ' AND lock_version = '.$previous_value; 
     663            } 
     664 
     665            $quoted_attributes = $this->getAvailableAttributesQuoted(); 
     666 
     667            if(!empty($quoted_attributes)){ 
     668                $sql = 'UPDATE '.$this->getTableName().' '. 
     669                'SET '.join(', ', $quoted_attributes) .' '. 
     670                'WHERE '.$this->getPrimaryKey().'='.$this->quotedId().$lock_check; 
     671            } 
     672 
     673            if(!$this->_executeSql($sql, false)){ 
     674                $this->transactionFail(); 
     675                AK_DEBUG ? trigger_error($this->_db->ErrorMsg(), E_USER_NOTICE) : null; 
     676            } 
     677 
     678            if ($this->isLockingEnabled()){ 
     679                if($this->_db->Affected_Rows() != 1){ 
     680                    $this->setAttribute('lock_version', $previous_value); 
     681                    $this->transactionFail(); 
     682                    trigger_error(Ak::t('Attempted to update a stale object'), E_USER_NOTICE); 
     683                    return false; 
     684                } 
     685            } 
     686 
     687            if(!$this->transactionHasFailed()){ 
     688                if($this->afterUpdate()){ 
     689                    $this->notifyObservers('afterUpdate'); 
     690                }else { 
     691                    $this->transactionFail(); 
     692                } 
     693            } 
     694 
     695        }else{ 
     696            $this->transactionFail(); 
     697        } 
     698        return $this; 
     699    } 
     700     
     701    /*/Updating records*/ 
     702     
     703     
     704     
     705    /**  
     706                          Deleting records 
     707    ==================================================================== 
     708    See also: Callbacks. 
     709    */ 
     710     
     711    /** 
    536712    * Deletes the record with the given id without instantiating an object first. If an array of  
    537713    * ids is provided, all of them are deleted. 
     
    545721        return $this->deleteAll($this->getPrimaryKey().' IN ('.(is_array($id) ? join(', ',$id) : $id).')'); 
    546722    } 
    547  
    548  
    549     /** 
    550     * Returns an array of names for the attributes available on this object sorted alphabetically. 
    551     */ 
    552     function getAttributeNames() 
    553     { 
    554         if(!isset($this->_activeRecordHasBeenInstantiated)){ 
    555             return Ak::handleStaticCall(); 
    556         } 
    557         $attributes = array_keys($this->getAvailableAttributes()); 
    558         $names = array_combine($attributes,array_map(array(&$this,'getAttributeCaption'), $attributes)); 
    559         natsort($names); 
    560         return $names; 
    561     } 
    562  
    563  
    564     /** 
    565     * Returns true if the specified attribute has been set by the user or by a database load and is neither null nor empty? 
    566     */ 
    567     function isAttributePresent($attribute) 
    568     { 
    569         $value = $this->getAttribute($attribute); 
    570         return !empty($value); 
    571     } 
    572  
    573723 
    574724 
     
    579729    * <code>$Post->destroyAll("person_id = 5 AND (category = 'Something' OR category = 'Else')");</code> 
    580730    *  
    581     * Important note: Condifitons are not sanitized yet so beware of accepting  
     731    * Important note: Conditions are not sanitized yet so beware of accepting  
    582732    * variable conditions when using this function 
    583733    */ 
     
    666816 
    667817 
    668  
    669818    /** 
    670819    * Destroys the objects for all the records that matches the condition by instantiating  
     
    690839        } 
    691840    } 
    692  
    693  
    694  
    695  
    696  
    697     /** 
    698     * Establishes the connection to the database. Accepts an array as input where the 'adapter'  
    699     * key must be specified with the name of a database adapter (in lower-case) example for regular  
    700     * databases (MySQL, Postgresql, etc): 
    701     *  
    702     *   $AkActiveRecord->establishConnection( 
    703     *       array( 
    704     *       'adapter'  => "mysql", 
    705     *       'host'     => "localhost", 
    706     *       'username' => "myuser", 
    707     *       'password' => "mypass", 
    708     *       'database' => "somedatabase" 
    709     *       )); 
    710     * 
    711     *    Example for SQLite database: 
    712     * 
    713     *     $AkActiveRecord->establishConnection( 
    714     *       array( 
    715     *       'adapter' => "sqlite", 
    716     *       'dbfile'  => "path/to/dbfile" 
    717     *       ) 
    718     *     ) 
    719     */ 
    720     function &establishConnection($spec = null) 
    721     { 
    722         if(isset($spec)){ 
    723             $dns = is_string($spec) ? $spec : ''; 
    724             if(!empty($spec['adapter'])){ 
    725                 $dsn = $spec['adapter'] == 'sqlite' ? 
    726                 'sqlite://'.urlencode($spec['dbfile']).'/' : 
    727                 $spec['adapter'].'://'.@$spec['username'].':'.@$spec['password'].'@'.@$spec['host'].'/'.@$spec['database']; 
    728             } 
    729             $dsn .= isset($spec['persist']) && $spec['persist'] === false ? '' : '?persist'; 
    730             return $this->setConnection($dns); 
    731         }else{ 
    732             return false; 
    733         } 
    734     } 
    735  
    736  
    737     /** 
    738     * Just freeze the attributes hash, such that associations are still accessible even on destroyed records. 
    739     *  
    740     * @todo implement freeze correctly for its intended use 
    741     */ 
    742     function freeze() 
    743     { 
    744         $this->_freeze = true; 
    745     } 
    746  
    747     function isFrozen() 
    748     { 
    749         return !empty($this->_freeze); 
    750     } 
    751  
     841     
     842    /*/Deleting records*/ 
     843 
     844 
     845 
     846     
     847    /**  
     848                          Finding records 
     849    ==================================================================== 
     850    */ 
    752851 
    753852    /** 
     
    760859        return $this->find('first',array('conditions' => array($this->getPrimaryKey().' = '.$id))) !== false; 
    761860    } 
    762  
    763  
    764  
    765861 
    766862    /** 
     
    10091105 
    10101106 
    1011     function &objectCache() 
    1012     { 
    1013         static $cache; 
    1014         $args =& func_get_args(); 
    1015         if(count($args) == 2){ 
    1016             if(!isset($cache[$args[0]])){ 
    1017                 $cache[$args[0]] =& $args[1]; 
    1018             } 
    1019         }elseif(!isset($cache[$args[0]])){ 
    1020             return false; 
    1021         } 
    1022         return $cache[$args[0]]; 
    1023     } 
    1024  
    1025     /** 
    1026     * Gets an array from a string. 
    1027     * 
    1028     * Acts like Php explode() function but uses any of this as valid separators ' AND ',' and ',' + ',' ',',',';' 
    1029     */ 
    1030     function getArrayFromAkString($string) 
    1031     { 
    1032         if(is_array($string)){ 
    1033             return $string; 
    1034         } 
    1035         $string = str_replace(array(' AND ',' and ',' + ',' ',',',';'),array('|','|','|','','|','|'),trim($string)); 
    1036         return strstr($string,'|') ? explode('|', $string) : array($string); 
    1037     } 
    1038  
    1039     // Gets the column name for use with single table inheritance ? can be overridden in subclasses. 
    1040     function getInheritanceColumn() 
    1041     { 
    1042         return empty($this->_inheritanceColumn) ? ($this->hasColumn('type') ? 'type' : false ) : $this->_inheritanceColumn; 
    1043     } 
    1044  
    1045     // Defines the column name for use with single table inheritance ? can be overridden in subclasses. 
    1046     function setInheritanceColumn($column_name) 
    1047     { 
    1048         if(!$this->hasColumn($column_name)){ 
    1049             trigger_error(Ak::t('Could not set "%column_name" as the inheritance column as this column is not available on the database.',array('%column_name'=>$column_name)), E_USER_NOTICE); 
    1050             return false; 
    1051         }elseif($this->getColumnType($column_name) != 'string'){ 
    1052             trigger_error(Ak::t('Could not set %column_name as the inheritance column as this column type is "%column_type" instead of "string".',array('%column_name'=>$column_name,'%column_type'=>$this->getColumnType($column_name))), E_USER_NOTICE); 
    1053             return false; 
    1054         }else{ 
    1055             $this->_inheritanceColumn = $column_name; 
    1056             return true; 
    1057         } 
    1058     } 
    1059  
    1060     function getColumnsWithRegexBoundaries() 
    1061     { 
    1062         $columns = array_keys($this->getColumns()); 
    1063         foreach ($columns as $k=>$column){ 
    1064             $columns[$k] = '/([^\.])\b('.$column.')\b/'; 
    1065         } 
    1066         return $columns; 
    1067     } 
    1068  
    1069  
    1070  
    1071     //SELECT t.code as codigo , c.description  as descripcion FROM technical_listings as t LEFT OUTER JOIN categories as c ON c.id = t.category_id 
    1072  
    1073  
    1074  
    10751107    /** 
    10761108    * Works like find_all, but requires a complete SQL string. Examples: 
     
    11171149    } 
    11181150 
    1119  
    1120  
    1121     /** 
    1122     * This function pretends to emulate ror finders until AkActiveRecord::addMethod becomes stable on future PHP versions. 
     1151    /** 
     1152    * This function pretends to emulate RoR finders until AkActiveRecord::addMethod becomes stable on future PHP versions. 
    11231153    * @todo use PHP5 __call method for handling the magic finder methods like findFirstByUnsenameAndPassword('bermi','pass') 
    11241154    */ 
     
    11661196    } 
    11671197 
     1198    /** 
     1199    * This method allows you to use finders in a more flexible way like: 
     1200    *  
     1201    *   findBy('username AND password', $username, $password); 
     1202    *   findBy('age > ? AND name:contains', 18, 'Joe'); 
     1203    *   findBy('is_active = true AND session_id', session_id()); 
     1204    *    
     1205    */ 
    11681206    function &findBy() 
    11691207    { 
     
    12941332 
    12951333 
    1296     /** 
    1297     * Finder methods must instantiate through this method to work with the single-table inheritance model and 
    1298     * eager loading associations. 
    1299     * that makes it possible to create objects of different types from the same table. 
    1300     */ 
    1301     function &instantiate($record, $set_as_new = true) 
    1302     { 
    1303         $inheritance_column = $this->getInheritanceColumn(); 
    1304         if(!empty($record[$inheritance_column])){ 
    1305             $inheritance_column = $record[$inheritance_column]; 
    1306             $inheritance_model_name = AkInflector::camelize($inheritance_column); 
    1307             @require_once(AkInflector::toModelFilename($inheritance_model_name)); 
    1308             if(!class_exists($inheritance_model_name)){ 
    1309                 trigger_error($this->t("The single-table inheritance mechanism failed to locate the subclass: '%class_name'. ". 
    1310                 "This error is raised because the column '%column' is reserved for storing the class in case of inheritance. ". 
    1311                 "Please rename this column if you didn't intend it to be used for storing the inheritance class ". 
    1312                 "or overwrite #{self.to_s}.inheritance_column to use another column for that information.", 
    1313                 array('%class_name'=>$inheritance_model_name, '%column'=>$this->getInheritanceColumn())),E_USER_ERROR); 
    1314             } 
    1315         } 
    1316  
    1317         $model_name = isset($inheritance_model_name) ? $inheritance_model_name : $this->getModelName(); 
    1318         $object =& new $model_name('attributes', $record); 
    1319  
    1320         $object->_newRecord = $set_as_new; 
    1321  
    1322         (AK_CLI && AK_ENVIRONMENT == 'development') ? $object ->toString() : null; 
    1323  
    1324         return $object; 
    1325     } 
    1326  
    1327  
    1328  
    13291334    function constructFinderSql($options, $select_from_prefix = 'default') 
    13301335    { 
     
    14091414    } 
    14101415 
    1411  
    1412     function descendsFromActiveRecord(&$object) 
    1413     { 
    1414         if(substr(strtolower(get_parent_class($object)),-12) == 'activerecord'){ 
    1415             return true; 
    1416         } 
    1417         if(!method_exists($object, 'getInheritanceColumn')){ 
    1418             return false; 
    1419         } 
    1420         $inheritance_column = $object->getInheritanceColumn(); 
    1421         return !empty($inheritance_column); 
    1422     } 
    1423  
    1424  
    1425     function typeCondition() 
    1426     { 
    1427         $inheritance_column = $this->getInheritanceColumn(); 
    1428         $type_condition = array(); 
    1429         $table_name = $this->getTableName(); 
    1430         $available_types = array_merge(array($this->getModelName()),$this->getSubclasses()); 
    1431         foreach ($available_types as $subclass){ 
    1432             $type_condition[] = ' '.$table_name.'.'.$inheritance_column.' = \''.AkInflector::demodulize($subclass).'\' '; 
    1433         } 
    1434         return empty($type_condition) ? '' : '('.join('OR',$type_condition).') '; 
    1435     } 
    1436  
    1437     function getSubclasses() 
    1438     { 
    1439         $current_class = get_class($this); 
    1440         $subclasses = array(); 
    1441         $classes = get_declared_classes(); 
    1442  
    1443         while ($class = array_shift($classes)) { 
    1444             $parent_class = get_parent_class($class); 
    1445             if($parent_class == $current_class || in_array($parent_class,$subclasses)){ 
    1446                 $subclasses[] = $class; 
    1447             }elseif(!empty($parent_class)){ 
    1448                 $classes[] = $parent_class; 
    1449             } 
    1450         } 
    1451         $subclasses = array_unique(array_map(array(&$this,'_getModelName'),$subclasses)); 
    1452         return $subclasses; 
    1453     } 
    1454  
    14551416    function _quoteColumnName($column_name) 
    14561417    { 
     
    14591420 
    14601421 
    1461     /** 
    1462     * Parses an special formated array as a list of keys and values 
    1463     *  
    1464     * This function generates an array with values and keys from an array with numeric keys. 
    1465     *  
    1466     * This allows to parse an array to a function in the following manner. 
    1467     * create('first_name->', 'Bermi', 'last_name->', 'Ferrer'); 
    1468     * //Previous code will be the same that 
    1469     * create(array('first_name'=>'Bermi', 'last_name'=> 'Ferrer')); 
    1470     * 
    1471     * Use this syntax only for quick testings, not for production environments. If the number of arguments varies, the result might be unpredictable. 
    1472     * 
    1473     * This function syntax is disabled by default. You need to define('AK_ENABLE_AKELOS_ARGS', true) 
    1474     * if you need this functionality. 
    1475     * 
    1476     * @deprecated 
    1477     */ 
    1478     function parseAkelosArgs(&$args) 
    1479     { 
    1480         if(!AK_ENABLE_AKELOS_ARGS){ 
    1481             return ; 
    1482         } 
    1483         $k = array_keys($args); 
    1484         if(isset($k[1]) && substr($args[$k[0]],-1) == '>'){ 
    1485             $size = sizeOf($k); 
    1486             $params = array(); 
    1487             for($i = 0; $i < $size; $i++ ) { 
    1488                 $v = $args[$k[$i]]; 
    1489                 if(!isset($key) && is_string($args[$k[$i]]) && substr($v,-1) == '>'){ 
    1490                     $key = rtrim($v, '=-> '); 
    1491                 }elseif(isset($key)) { 
    1492                     $params[$key] = $v; 
    1493                     unset($key); 
    1494                 }else{ 
    1495                     $params[$k[$i]] = $v; 
    1496                 } 
    1497             } 
    1498             if(!empty($params)){ 
    1499                 $args = $params; 
    1500             } 
    1501         } 
    1502         $this->_castDateParametersFromDateHelper_($args); 
    1503     } 
    1504  
    1505     /** 
    1506     * Joins date arguments into a single attribute. Like the array generated by the date_helper, so 
    1507     * array('published_on(1i)' => 2002, 'published_on(2i)' => 'January', 'published_on(3i)' => 24) 
    1508     * Will be converted to array('published_on'=>'2002-01-24') 
    1509     */ 
    1510     function _castDateParametersFromDateHelper_(&$params) 
    1511     { 
    1512         if(empty($params)){ 
    1513             return; 
    1514         } 
    1515         $date_attributes = array(); 
    1516         foreach ($params as $k=>$v) { 
    1517             if(preg_match('/^([A-Za-z0-9_]+)\(([1-5]{1})i\)$/',$k,$match)){ 
    1518                 $date_attributes[$match[1]][$match[2]] = $v; 
    1519                 unset($params[$k]); 
    1520             } 
    1521         } 
    1522         foreach ($date_attributes as $attribute=>$date){ 
    1523             $params[$attribute] = Ak::getDate(Ak::getTimestamp(trim(@$date[1].'-'.@$date[2].'-'.@$date[3].' '.@$date[4].':'.@$date[5],' :-'))); 
    1524         } 
    1525     } 
    15261422 
    15271423 
     
    15671463 
    15681464 
    1569  
    1570     /** 
    1571     * New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved 
    1572     * (pass an array with key names matching the associated table column names).  
    1573     * In both instances, valid attribute keys are determined by the column names of the associated table ? hence you can't  
    1574     * have attributes that aren't part of the table columns. 
    1575     */ 
    1576     function newRecord($attributes) 
    1577     { 
    1578         $this->_newRecord = true; 
    1579  
    1580         if(AK_ACTIVE_RECORD_SKIP_SETTING_ACTIVE_RECORD_DEFAULTS && empty($attributes)){ 
    1581             return; 
    1582         } 
    1583  
    1584         if(isset($attributes) && !is_array($attributes)){ 
    1585             $attributes = func_get_args(); 
    1586         } 
    1587         $this->setAttributes($this->attributesFromColumnDefinition(),true); 
    1588         $this->setAttributes($attributes); 
    1589     } 
    1590  
    1591  
     1465    /** 
     1466    * Finder methods must instantiate through this method to work with the single-table inheritance model and 
     1467    * eager loading associations. 
     1468    * that makes it possible to create objects of different types from the same table. 
     1469    */ 
     1470    function &instantiate($record, $set_as_new = true) 
     1471    { 
     1472        $inheritance_column = $this->getInheritanceColumn(); 
     1473        if(!empty($record[$inheritance_column])){ 
     1474            $inheritance_column = $record[$inheritance_column]; 
     1475            $inheritance_model_name = AkInflector::camelize($inheritance_column); 
     1476            @require_once(AkInflector::toModelFilename($inheritance_model_name)); 
     1477            if(!class_exists($inheritance_model_name)){ 
     1478                trigger_error($this->t("The single-table inheritance mechanism failed to locate the subclass: '%class_name'. ". 
     1479                "This error is raised because the column '%column' is reserved for storing the class in case of inheritance. ". 
     1480                "Please rename this column if you didn't intend it to be used for storing the inheritance class ". 
     1481                "or overwrite #{self.to_s}.inheritance_column to use another column for that information.", 
     1482                array('%class_name'=>$inheritance_model_name, '%column'=>$this->getInheritanceColumn())),E_USER_ERROR); 
     1483            } 
     1484        } 
     1485 
     1486        $model_name = isset($inheritance_model_name) ? $inheritance_model_name : $this->getModelName(); 
     1487        $object =& new $model_name('attributes', $record); 
     1488 
     1489        $object->_newRecord = $set_as_new; 
     1490 
     1491        (AK_CLI && AK_ENVIRONMENT == 'development') ? $object ->toString() : null; 
     1492 
     1493        return $object; 
     1494    } 
     1495     
     1496    /*/Finding records*/ 
     1497     
     1498 
     1499 
     1500     /**  
     1501                           Table inheritance 
     1502     ==================================================================== 
     1503     */ 
     1504     function descendsFromActiveRecord(&$object) 
     1505     { 
     1506         if(substr(strtolower(get_parent_class($object)),-12) == 'activerecord'){ 
     1507             return true; 
     1508         } 
     1509         if(!method_exists($object, 'getInheritanceColumn')){ 
     1510             return false; 
     1511         } 
     1512