Source for file AkHasMany.php

Documentation is available at AkHasMany.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. // +----------------------------------------------------------------------+
  5. // | Akelos Framework - http://www.akelos.org                             |
  6. // +----------------------------------------------------------------------+
  7. // | Copyright (c) 2002-2006, Akelos Media, S.L.  & Bermi Ferrer Martinez |
  8. // | Released under the GNU Lesser General Public License, see LICENSE.txt|
  9. // +----------------------------------------------------------------------+
  10.  
  11. /**
  12.  * @package ActiveRecord
  13.  * @subpackage Associations
  14.  * @author Bermi Ferrer <bermi a.t akelos c.om>
  15.  * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
  16.  * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
  17.  */
  18. require_once(AK_LIB_DIR.DS.'AkActiveRecord'.DS.'AkAssociation.php');
  19.  
  20. /**
  21.  * Adds the following methods for retrieval and query of collections of associated objects. 
  22.  * collection is replaced with the singular form of current association, 
  23.  * so var $has_many = 'clients' would hold an array of objects on $this->clients
  24.  * and a collection handling interface instance on $this->client (singular form)
  25.  * 
  26.  * * collection->load($force_reload = false) - returns an array of all the associated objects. An empty array is returned if none are found.
  27.  * * collection->add($object, ?) - adds one or more objects to the collection by setting their foreign keys to the collection's primary key. 
  28.  * (collection->push and $collection->concat are aliases to this method).
  29.  * * collection->delete($object, ?) - removes one or more objects from the collection by setting their foreign keys to NULL. This will also destroy the objects if they?re declared as belongs_to and dependent on this model.
  30.  * * collection->set($objects) - replaces the collections content by deleting and adding objects as appropriate.
  31.  * * collection->setByIds($ids) - replace the collection by the objects identified by the primary keys in ids
  32.  * * collection->clear() - removes every object from the collection. This destroys the associated objects if they are 'dependent', deletes them directly from the database if they are 'dependent' => 'delete_all', and sets their foreign keys to NULL otherwise.
  33.  * * collection->isEmpty() - returns true if there are no associated objects.
  34.  * * collection->getSize() - returns the number of associated objects.
  35.  * * collection->find() - finds an associated object according to the same rules as ActiveRecord->find.
  36.  * * collection->count() - returns the number of elements associated.  (collection->size() is an alias to this method)
  37.  * * collection->build($attributes = array()) - returns a new object of the collection type that has been instantiated with attributes and linked to this object through a foreign key but has not yet been saved. *Note:* This only works if an associated object already exists, not if it?s null
  38.  * * collection->create($attributes = array()) - returns a new object of the collection type that has been instantiated with attributes and linked to this object through a foreign key and that has already been saved (if it passed the validation). *Note:* This only works if an associated object already exists, not if it?s null
  39.  *
  40.  * Example: A Firm class declares has_many clients, which will add:
  41.  *
  42.  *  * Firm->client->load() (similar to $Clients->find('all', array('conditions' => 'firm_id = '.$id)) )
  43.  *  * Firm->client->add()
  44.  *  * Firm->client->delete()
  45.  *  * Firm->client->assign()
  46.  *  * Firm->client->assignByIds()
  47.  *  * Firm->client->clear()
  48.  *  * Firm->client->isEmpty() (similar to count($Firm->clients) == 0)
  49.  *  * Firm->client->getSize() (similar to Client.count "firm_id = #{id}")
  50.  *  * Firm->client->find() (similar to $Client->find($id, array('conditions' => 'firm_id = '.$id)) )
  51.  *  * Firm->client->build() (similar to new Client(array('firm_id' => $id)) )
  52.  *  * Firm->client->create() (similar to $c = new Client(array('firm_id' => $id)); $c->save(); return $c )
  53.  *
  54.  * The declaration can also include an options array to specialize the behavior of the association.
  55.  * 
  56.  * Options are:
  57.  * 
  58.  *  * 'class_name' - specify the class name of the association. Use it only if that name can't be inferred from the association name. So "$has_many = 'products'" will by default be linked to the Product class, but if the real class name is SpecialProduct, you?ll have to specify it with this option.
  59.  *  * 'conditions' - specify the conditions that the associated objects must meet in order to be included as a "WHERE" sql fragment, such as "price > 5 AND name LIKE ?B%?".
  60.  *  * 'order' - specify the order in which the associated objects are returned as a "ORDER BY" sql fragment, such as "last_name, first_name DESC"
  61.  *  * 'group' - specify the attribute by which the associated objects are returned as a "GROUP BY" sql fragment, such as "category"
  62.  *  * 'foreign_key' - specify the foreign key used for the association. By default this is guessed to be the name of this class in lower-case and "_id" suffixed. So a Person class that makes a has_many association will use "person_id" as the default foreign_key.
  63.  *  * 'dependent' - if set to 'destroy' all the associated objects are destroyed alongside this object by calling their destroy method. If set to 'delete_all' all associated objects are deleted without calling their destroy method. If set to 'nullify' all associated objects? foreign keys are set to NULL without calling their save callbacks.
  64.  *  * 'finder_sql' - specify a complete SQL statement to fetch the association. This is a good way to go for complex associations that depend on multiple tables. Note: When this option is used, findInCollection is not added.
  65.  *  * 'counter_sql' - specify a complete SQL statement to fetch the size of the association. If +'finder_sql'+ is specified but +'counter_sql'+, +'counter_sql'+ will be generated by replacing SELECT ? FROM with SELECT COUNT(*) FROM.
  66.  *  * 'include' - specify second-order associations that should be eager loaded when the collection is loaded.
  67.  *  * 'group' An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
  68.  *  * 'limit' An integer determining the limit on the number of rows that should be returned.
  69.  *  * 'offset' An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
  70.  *  * 'select' By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not include the joined columns.
  71.  *
  72.  * Option examples:
  73.  *
  74.  * $has_many = array(
  75.  *                  'comments'  => array('order' => 'posted_on', 'include' => 'author', 'dependent' => 'nullify'),
  76.  *                  'people'    => array('conditions' => 'deleted = 0', 'order' => 'name'),
  77.  *                  'tracks'    => array('order' => 'position', 'dependent' => 'destroy'),
  78.  *                  'members'   => array('class_name' => 'Person', 'conditions' => 'role = "merber"'));
  79.  */
  80. class AkHasMany extends AkAssociation
  81. {
  82.     var $associated_ids = array();
  83.     var $association_id;
  84.  
  85.     function &addAssociated($association_id$options = array())
  86.     {
  87.  
  88.         $default_options = array(
  89.         'class_name' => empty($options['class_name']AkInflector::classify($association_id$options['class_name'],
  90.         'conditions' => false,
  91.         'order' => false,
  92.         'include_conditions_when_included' => true,
  93.         'include_order_when_included' => true,
  94.         'group' => false,
  95.         'foreign_key' => false,
  96.         'dependent' => 'nullify',
  97.         'finder_sql' => false,
  98.         'counter_sql' => false,
  99.         'include' => false,
  100.         'instantiate' => false,
  101.         'group' => false,
  102.         'limit' => false,
  103.         'offset' => false,
  104.         'handler_name' => strtolower(AkInflector::underscore(AkInflector::singularize($association_id))),
  105.         'select' => false
  106.         );
  107.  
  108.         $options array_merge($default_options$options);
  109.  
  110.         $options['foreign_key'= empty($options['foreign_key']AkInflector::underscore($this->Owner->getModelName()).'_id' $options['foreign_key'];
  111.  
  112.         $Collection =$this->_setCollectionHandler($association_id$options['handler_name']);
  113.         $Collection->setOptions($association_id$options);
  114.  
  115.  
  116.         $this->addModel($association_id,  $Collection);
  117.  
  118.         if($options['instantiate']){
  119.             $associated =$Collection->load();
  120.         }
  121.  
  122.         $this->setAssociatedId($association_id$options['handler_name']);
  123.         $Collection->association_id = $association_id;
  124.  
  125.         return $Collection;
  126.     }
  127.  
  128.     function getType()
  129.     {
  130.         return 'hasMany';
  131.     }
  132.  
  133.     function &_setCollectionHandler($association_id$handler_name)
  134.     {
  135.         if(isset($this->Owner->$association_id)){
  136.             if(!is_array($this->Owner->$association_id)){
  137.                 trigger_error(Ak::t('%model_name::%association_id is not a collection array on current %association_id hasMany association',array('%model_name'=>$this->Owner->getModelName()'%association_id'=>$association_id))E_USER_NOTICE);
  138.             }
  139.             $associated =$this->Owner->$association_id;
  140.         }else{
  141.             $associated = array();
  142.             $this->Owner->$association_id =$associated;
  143.         }
  144.  
  145.         if(isset($this->Owner->$handler_name)){
  146.             trigger_error(Ak::t('Could not load %association_id on %model_name because "%model_name->%handler_name" attribute '.
  147.             'is already defined and can\'t be used as an association placeholder',
  148.             array('%model_name'=>$this->Owner->getModelName(),'%association_id'=>$association_id'%handler_name'=>$handler_name)),
  149.             E_USER_ERROR);
  150.             return false;
  151.         }else{
  152.             $this->Owner->$handler_name =new AkHasMany($this->Owner);
  153.         }
  154.         return $this->Owner->$handler_name;
  155.     }
  156.  
  157.  
  158.     function &load($force_reload = false)
  159.     {
  160.         $options $this->getOptions($this->association_id);
  161.         if($force_reload || empty($this->Owner->{$options['handler_name']}->_loaded)){
  162.             if(!$this->Owner->isNewRecord()){
  163.                 $this->constructSql(false);
  164.                 $options $this->getOptions($this->association_id);
  165.                 $Associated =$this->getAssociatedModelInstance();
  166.                 $finder_options = array('conditions'=>$options['finder_sql']);
  167.                 if(!empty($options['order'])){
  168.                     $finder_options['order'$options['order'];
  169.                 }
  170.                 if($FoundAssociates $Associated->find('all',$finder_options)){
  171.                     array_map(array(&$this,'_setAssociatedMemberId'),$FoundAssociates);
  172.                     $this->Owner->{$this->association_id=$FoundAssociates;
  173.                 }
  174.             }
  175.             if(empty($this->Owner->{$this->association_id})){
  176.                 $this->Owner->{$this->association_id= array();
  177.             }
  178.  
  179.             $this->Owner->{$options['handler_name']}->_loaded = true;
  180.         }
  181.         return $this->Owner->{$this->association_id};
  182.     }
  183.  
  184.  
  185.     /**
  186.      * add($object), add(array($object, $object2)) - adds one or more objects to the collection by setting 
  187.      * their foreign keys to the collection?s primary key. Items are saved automatically when parent has been saved.
  188.      */
  189.     function add(&$Associated)
  190.     {
  191.         if(is_array($Associated)){
  192.             $succes = true;
  193.             $succes $this->Owner->notifyObservers('beforeAdd'$succes : false;
  194.             $options $this->getOptions($this->association_id);
  195.             foreach (array_keys($Associatedas $k){
  196.                 if($succes && !empty($options['before_add']&& method_exists($this->Owner$options['before_add']&& $this->Owner->{$options['before_add']}($Associated[$k]=== false ){
  197.                     $succes = false;
  198.                 }
  199.                 if($succes && !$this->_hasAssociatedMember($Associated[$k])){
  200.                     $this->Owner->{$this->association_id}[=$Associated[$k];
  201.                     $this->_setAssociatedMemberId($Associated[$k]);
  202.                     if($this->_relateAssociatedWithOwner($Associated[$k])){
  203.                         $succes $Associated[$k]->save($succes : false;
  204.                         if($succes && !empty($options['after_add']&& method_exists($this->Owner$options['after_add']&& $this->Owner->{$options['after_add']}($Associated[$k]=== false ){
  205.                             $succes = false;
  206.                         }
  207.                     }
  208.                 }
  209.             }
  210.             $succes $this->Owner->notifyObservers('afterAdd'$succes : false;
  211.             return $succes;
  212.         }else{
  213.             $associates = array();
  214.             $associates[=$Associated;
  215.             return $this->add($associates);
  216.         }
  217.     }
  218.  
  219.     function push(&$record)
  220.     {
  221.         return $this->add($record);
  222.     }
  223.  
  224.     function concat(&$record)
  225.     {
  226.         return $this->add($record);
  227.     }
  228.  
  229.     /**
  230.     * Remove all records from this association
  231.     */
  232.     function deleteAll($Skip = null)
  233.     {
  234.         $this->load();
  235.         return $this->delete($this->Owner->{$this->association_id}$Skip);
  236.     }
  237.  
  238.     function reset()
  239.     {
  240.         $options $this->getOptions($this->association_id);
  241.         $this->Owner->{$options['handler_name']}->_loaded = false;
  242.     }
  243.  
  244.     function set(&$objects)
  245.     {
  246.         $this->deleteAll($objects);
  247.         $this->add($objects);
  248.     }
  249.  
  250.     function setIds()
  251.     {
  252.         $ids func_get_args();
  253.         $ids is_array($ids[0]$ids[0$ids;
  254.         
  255.         $AssociatedModel =$this->getAssociatedModelInstance();
  256.         if(!empty($ids)){
  257.             $NewAssociates =$AssociatedModel->find($ids);
  258.             $this->set($NewAssociates);
  259.         }
  260.     }
  261.  
  262.     function setByIds()
  263.     {
  264.         $ids func_get_args();
  265.         call_user_func_array(array($this,'setIds')$ids);
  266.     }
  267.  
  268.     function addId($id)
  269.     {
  270.         $AssociatedModel =$this->getAssociatedModelInstance();
  271.         if($NewAssociated $AssociatedModel->find($id)){
  272.             return $this->add($NewAssociated);
  273.         }
  274.         return false;
  275.     }
  276.  
  277.  
  278.     function delete(&$Associated$Skip = null)
  279.     {
  280.         $success = true;
  281.         if(!is_array($Associated)){
  282.             $associated_elements = array();
  283.             $associated_elements[=$Associated;
  284.             return $this->delete($associated_elements$Skip);
  285.         }else{
  286.             $options $this->getOptions($this->association_id);
  287.  
  288.             $ids_to_skip = array();
  289.             $Skip = empty($Skip? null : (is_array($Skip$Skip : array($Skip));
  290.             if(!empty($Skip)){
  291.                 foreach (array_keys($Skipas $k){
  292.                     $ids_to_skip[$Skip[$k]->getId();
  293.                 }
  294.             }
  295.  
  296.             $ids_to_nullify = array();
  297.             $ids_to_delete = array();
  298.             $items_to_remove_from_collection = array();
  299.             $AssociatedModel =$this->getAssociatedModelInstance();
  300.  
  301.             $owner_type $this->_findOwnerTypeForAssociation($AssociatedModel$this->Owner);
  302.  
  303.             foreach (array_keys($Associatedas $k){
  304.                 $items_to_remove_from_collection[$Associated[$k]->getId();
  305.                 if(!in_array($Associated[$k]->getId($ids_to_skip)){
  306.                     switch ($options['dependent']{
  307.                         case 'destroy':
  308.                             $success $Associated[$k]->destroy($success : false;
  309.                         break;
  310.                         case 'delete_all':
  311.                             $ids_to_delete[$Associated[$k]->getId();
  312.                         break;
  313.                         case 'nullify':
  314.                             $id_to_nullify $Associated[$k]->quotedId();
  315.                             if(!empty($id_to_nullify)){
  316.                                 $ids_to_nullify[$id_to_nullify;
  317.                             }
  318.                         default:
  319.                         break;
  320.                     }
  321.                 }
  322.             }
  323.  
  324.             $ids_to_nullify = empty($ids_to_nullify? false : array_diff($ids_to_nullify,array(''));
  325.             if(!empty($ids_to_nullify)){
  326.                 $success $AssociatedModel->updateAll(
  327.                 ' '.$options['foreign_key'].' = NULL ',
  328.                 ' '.$options['foreign_key'].' = '.$this->Owner->quotedId().' AND '.$AssociatedModel->getPrimaryKey().' IN ('.join(', ',$ids_to_nullify).')'
  329.                 $success : false;
  330.             }elseif(!empty($ids_to_delete)){
  331.                 $success $AssociatedModel->delete($ids_to_delete$success : false;
  332.             }
  333.  
  334.             $this->removeFromCollection($items_to_remove_from_collection);
  335.         }
  336.  
  337.         return $success;
  338.     }
  339.  
  340.  
  341.  
  342.     /**
  343.     * Remove records from the collection. Use delete() in order to trigger database dependencies
  344.     */
  345.     function removeFromCollection(&$records)
  346.     {
  347.         if(!is_array($records)){
  348.             $records_array = array();
  349.             $records_array[=$records;
  350.             $this->delete($records_array);
  351.         }else{
  352.             $this->Owner->notifyObservers('beforeRemove');
  353.             $options $this->getOptions($this->association_id);
  354.             foreach (array_keys($recordsas $k){
  355.  
  356.                 if(!empty($options['before_remove']&& method_exists($this->Owner$options['before_remove']&& $this->Owner->{$options['before_remove']}($records[$k]=== false ){
  357.                     continue;
  358.                 }
  359.                 
  360.                 if(isset($records[$k]->__activeRecordObject)){
  361.                     $record_id $records[$k]->getId();
  362.                 }else{
  363.                     $record_id $records[$k];
  364.                 }
  365.  
  366.                 foreach (array_keys($this->Owner->{$this->association_id}as $kk){
  367.                     if(
  368.                     (
  369.                     !empty($this->Owner->{$this->association_id}[$kk]->__hasManyMemberId&&
  370.                     !empty($records[$k]->__hasManyMemberId&&
  371.                     $records[$k]->__hasManyMemberId == $this->Owner->{$this->association_id}[$kk]->__hasManyMemberId
  372.                     || (
  373.                     !empty($this->Owner->{$this->association_id}[$kk]->__activeRecordObject&&
  374.                     $record_id == $this->Owner->{$this->association_id}[$kk]->getId()
  375.                     )
  376.                     ){
  377.                         unset($this->Owner->{$this->association_id}[$kk]);
  378.                     }
  379.                 }
  380.                 
  381.                 $this->_unsetAssociatedMemberId($records[$k]);
  382.                 
  383.                 if(!empty($options['after_remove']&& method_exists($this->Owner$options['after_remove'])){
  384.                     $this->Owner->{$options['after_remove']}($records[$k]);
  385.                 }
  386.                 
  387.             }
  388.             $this->Owner->notifyObservers('afterRemove');
  389.         }
  390.     }
  391.  
  392.  
  393.  
  394.  
  395.     function _setAssociatedMemberId(&$Member)
  396.     {
  397.         if(empty($Member->__hasManyMemberId)) {
  398.             $Member->__hasManyMemberId = Ak::randomString();
  399.         }
  400.         $object_id method_exists($Member,'getId'$Member->getId(: null;
  401.         if(!empty($object_id)){
  402.             $this->associated_ids[$object_id$Member->__hasManyMemberId;
  403.         }
  404.     }
  405.  
  406.     function _unsetAssociatedMemberId(&$Member)
  407.     {
  408.         $id $this->_getAssociatedMemberId($Member);
  409.         unset($this->associated_ids[$id]);
  410.         unset($Member->__hasManyMemberId);
  411.     }
  412.  
  413.     function _getAssociatedMemberId(&$Member)
  414.     {
  415.         if(!empty($Member->__hasManyMemberId)) {
  416.             return array_search($Member->__hasManyMemberId$this->associated_ids);
  417.         }
  418.         return false;
  419.     }
  420.  
  421.     function _hasAssociatedMember(&$Member)
  422.     {
  423.         return !empty($Member->__hasManyMemberId);
  424.     }
  425.  
  426.  
  427.     function _relateAssociatedWithOwner(&$Associated)
  428.     {
  429.         if(!$this->Owner->isNewRecord()){
  430.             if(method_exists($Associated'getModelName')){
  431.                 $foreign_key $this->getOption($this->association_id'foreign_key');
  432.                 if($this->getOption($this->association_id'class_name'!= $Associated->getModelName(|| $foreign_key == $Associated->get($foreign_key)){
  433.                     return false;
  434.                 }
  435.                 $Associated->set($foreign_key$this->Owner->getId());
  436.                 return true;
  437.             }
  438.         }
  439.         return false;
  440.     }
  441.  
  442.     function &_build($association_id&$AssociatedObject$reference_associated = true)
  443.     {
  444.         if($reference_associated){
  445.             $this->Owner->$association_id =$AssociatedObject;
  446.         }else{
  447.             $this->Owner->$association_id $AssociatedObject;
  448.         }
  449.         $this->Owner->$association_id->_AssociationHandler =$this;
  450.         $this->Owner->$association_id->_associatedAs = $this->getType();
  451.         $this->Owner->$association_id->_associationId = $association_id;
  452.         $this->Owner->_associations[$association_id=$this->Owner->$association_id;
  453.         return $this->Owner->$association_id;
  454.     }
  455.  
  456.  
  457.  
  458.  
  459.     function constructSql($set_owner_table_has_included = true)
  460.     {
  461.         $options $this->getOptions($this->association_id);
  462.         $Associated =$this->getAssociatedModelInstance();
  463.         $owner_id $this->Owner->quotedId();
  464.         $table_name (!empty($options['include']|| $set_owner_table_has_included'__owner' $Associated->getTableName();
  465.  
  466.         if(empty($options['finder_sql'])){
  467.             $options['finder_sql'' '.$table_name.'.'.$options['foreign_key'].' = '.(empty($owner_id'null' $owner_id).' ';
  468.             $options['finder_sql'.= !empty($options['conditions']' AND '.$options['conditions'].' ' '';
  469.         }
  470.         if(empty($options['counter_sql']&& !empty($options['finder_sql'])){
  471.             $options['counter_sql'$options['finder_sql'];
  472.         }elseif(empty($options['counter_sql'])){
  473.             $options['counter_sql'' '.$table_name.'.'.$options['foreign_key'].' = '.(empty($owner_id'null' $owner_id).' ';
  474.             $options['counter_sql'.= !empty($options['conditions']' AND '.$options['conditions'].' ' '';
  475.         }
  476.  
  477.         if(!empty($options['counter_sql']&& strtoupper(substr($options['counter_sql'],0,6)) != 'SELECT'){
  478.             $options['counter_sql''SELECT COUNT(*) FROM '.$table_name.' WHERE '.$options['counter_sql'];
  479.         }
  480.  
  481.         $this->setOptions($this->association_id$options);
  482.     }
  483.  
  484.  
  485.  
  486.     function count($force_count = false)
  487.     {
  488.         $count = 0;
  489.         $options $this->getOptions($this->association_id);
  490.         if($force_count || (empty($this->Owner->{$options['handler_name']}->_loaded&& !$this->Owner->isNewRecord())){
  491.             $this->constructSql(false);
  492.             $options $this->getOptions($this->association_id);
  493.             $Associated =$this->getAssociatedModelInstance();
  494.  
  495.             if($this->_hasCachedCounter()){
  496.                 $count $Associated->getAttribute($this->_getCachedCounterAttributeName());
  497.             }elseif(!empty($options['counter_sql'])){
  498.                 $count $Associated->countBySql($options['counter_sql']);
  499.             }else{
  500.                 $count (strtoupper(substr($options['finder_sql'],0,6)) != 'SELECT'?
  501.                 $Associated->count($options['foreign_key'].'='.$this->Owner->quotedId()) :
  502.                 $Associated->countBySql($options['finder_sql']);
  503.             }
  504.         }else{
  505.             $count count($this->Owner->{$this->association_id});
  506.         }
  507.  
  508.         if($count == 0){
  509.             $this->Owner->{$this->association_id= array();
  510.             $this->Owner->{$options['handler_name']}->_loaded = true;
  511.         }
  512.  
  513.         return $count;
  514.     }
  515.  
  516.     function size()
  517.     {
  518.         return $this->count();
  519.     }
  520.  
  521.  
  522.     function &build($attributes = array()$set_as_new_record = true)
  523.     {
  524.         $options $this->getOptions($this->association_id);
  525.         Ak::import($options['class_name']);
  526.         $record =new $options['class_name']($attributes);
  527.         $record->_newRecord = $set_as_new_record;
  528.         $this->Owner->{$this->association_id}[=$record;
  529.         $this->_setAssociatedMemberId($record);
  530.         $this->_relateAssociatedWithOwner($record);
  531.         return $record;
  532.     }
  533.  
  534.     function &create($attributes = array())
  535.     {
  536.         $record =$this->build($attributes);
  537.         if(!$this->Owner->isNewRecord()){
  538.             $record->save();
  539.         }
  540.         return $record;
  541.     }
  542.  
  543.  
  544.     function getAssociatedFinderSqlOptions($association_id$options = array())
  545.     {
  546.         $options $this->getOptions($this->association_id);
  547.         $Associated =$this->getAssociatedModelInstance();
  548.         $table_name $Associated->getTableName();
  549.         $owner_id $this->Owner->quotedId();
  550.  
  551.         $finder_options = array();
  552.  
  553.         foreach ($options as $option=>$value{
  554.             if(!empty($value)){
  555.                 $finder_options[$optiontrim($Associated->_addTableAliasesToAssociatedSql('_'.$this->association_id$value));
  556.             }
  557.         }
  558.  
  559.         $finder_options['joins'$this->constructSqlForInclusion();
  560.         $finder_options['selection''';
  561.  
  562.         foreach (array_keys($Associated->getColumns()) as $column_name){
  563.             $finder_options['selection'.= '_'.$this->association_id.'.'.$column_name.' AS _'.$this->association_id.'_'.$column_name.', ';
  564.         }
  565.  
  566.         $finder_options['selection'trim($finder_options['selection']', ');
  567.  
  568.         $finder_options['conditions'= empty($options['conditions']'' 
  569.  
  570.         $Associated->_addTableAliasesToAssociatedSql('_'.$this->association_id$options['conditions']).' ';
  571.         
  572.         return $finder_options;
  573.     }
  574.  
  575.     function constructSqlForInclusion()
  576.     {
  577.         $Associated =$this->getAssociatedModelInstance();
  578.         $options $this->getOptions($this->association_id);
  579.         return ' LEFT OUTER JOIN '.
  580.         $Associated->getTableName().' AS _'.$this->association_id.
  581.         ' ON '.
  582.         '__owner.'.$this->Owner->getPrimaryKey().
  583.         ' = '.
  584.         '_'.$this->association_id.'.'.$options['foreign_key'].' ';
  585.     }
  586.  
  587.  
  588.     function _hasCachedCounter()
  589.     {
  590.         $Associated =$this->getAssociatedModelInstance();
  591.         return $Associated->isAttributePresent($this->_getCachedCounterAttributeName());
  592.     }
  593.  
  594.     function _getCachedCounterAttributeName()
  595.     {
  596.         return $this->association_id.'_count';
  597.     }
  598.  
  599.  
  600.     function &getAssociatedModelInstance()
  601.     {
  602.         static $ModelInstances;
  603.             $class_name $this->getOption($this->association_id'class_name');
  604.         if(empty($ModelInstances[$class_name])){  
  605.             Ak::import($class_name);
  606.             $ModelInstances[$class_name=new $class_name();
  607.         }
  608.         return $ModelInstances[$class_name];
  609.     }
  610.  
  611.  
  612.     function &find()
  613.     {
  614.         $result = false;
  615.         if(!$this->Owner->isNewRecord()){
  616.  
  617.             $args func_get_args();
  618.             $num_args func_num_args();
  619.  
  620.             if(!empty($args[$num_args-1]&& is_array($args[$num_args-1])){
  621.                 $options_in_args = true;
  622.                 $options $args[$num_args-1];
  623.             }else{
  624.                 $options_in_args = false;
  625.                 $options = array();
  626.             }
  627.  
  628.             $this->constructSql(!empty($options['include']));
  629.             $has_many_options $this->getOptions($this->association_id);
  630.             $Associated =$this->getAssociatedModelInstance();
  631.             if (empty($options['conditions'])) {
  632.                 $options['conditions'@$has_many_options['finder_sql'];
  633.             elseif(!empty($has_many_options['finder_sql']&& is_array($options['conditions']&& !strstr($options['conditions'][0]$has_many_options['finder_sql'])) {
  634.                 $options['conditions'][0.= ' AND '$has_many_options['finder_sql'];
  635.             elseif (!empty($has_many_options['finder_sql']&& !strstr($options['conditions']$has_many_options['finder_sql'])) {
  636.                 $options['conditions'.= ' AND '$has_many_options['finder_sql'];
  637.             }
  638.  
  639.             $options['order'= empty($options['order']@$has_many_options['order'$options['order'];
  640.             $options['include'= empty($options['include']@$has_many_options['include'$options['include'];
  641.  
  642.             if($options_in_args){
  643.                 $args[$num_args-1$options;
  644.             }else{
  645.                 $args = empty($args? array('all'$args;
  646.                 array_push($args$options);
  647.             }
  648.  
  649.             $result =Ak::call_user_func_array(array(&$Associated,'find')$args);
  650.         }
  651.  
  652.         return $result;
  653.     }
  654.  
  655.  
  656.     function isEmpty()
  657.     {
  658.         return $this->count(=== 0;
  659.     }
  660.  
  661.     function getSize()
  662.     {
  663.         return $this->count();
  664.     }
  665.  
  666.     function clear()
  667.     {
  668.         return $this->deleteAll();
  669.     }
  670.  
  671.     /**
  672.     * Triggers
  673.     */
  674.     function afterCreate(&$object)
  675.     {
  676.         return $this->_afterCallback($object);
  677.     }
  678.  
  679.     function afterUpdate(&$object)
  680.     {
  681.         return $this->_afterCallback($object);
  682.     }
  683.  
  684.  
  685.     function beforeDestroy(&$object)
  686.     {
  687.         $success = true;
  688.  
  689.         foreach ((array)$object->_associationIds as $k => $v){
  690.             if(isset($object->$k&& is_array($object->$k&& isset($object->$v&& method_exists($object->$v'getType'&& $object->$v->getType(== 'hasMany'){
  691.  
  692.                 $ids_to_delete = array();
  693.                 $ids_to_nullify = array();
  694.                 $items_to_remove_from_collection = array();
  695.  
  696.                 $object->$v->load();
  697.                 foreach(array_keys($object->$kas $key){
  698.  
  699.                     $items_to_remove_from_collection[=$object->{$k}[$key];
  700.  
  701.                     switch ($object->$v->options[$k]['dependent']{
  702.  
  703.                         case 'destroy':
  704.                         $success $object->{$k}[$key]->destroy($success : false;
  705.                         break;
  706.  
  707.                         case 'delete_all':
  708.                         $ids_to_delete[$object->{$k}[$key]->getId();
  709.                         break;
  710.  
  711.                         case 'nullify':
  712.                         $id_to_nullify $object->{$k}[$key]->quotedId();
  713.                         if(!empty($id_to_nullify)){
  714.                             $ids_to_nullify[$id_to_nullify;
  715.                         }
  716.                         break;
  717.  
  718.                         default:
  719.                         break;
  720.                     }
  721.                 }
  722.                 
  723.                 $ids_to_nullify = empty($ids_to_nullify? false : array_diff($ids_to_nullify,array(''));
  724.                 if(!empty($ids_to_nullify)){
  725.                     $success $object->{$k}[$key]->updateAll(
  726.                     ' '.$object->$v->options[$k]['foreign_key'].' = NULL ',
  727.                     ' '.$object->$v->options[$k]['foreign_key'].' = '.$object->quotedId().' AND '.$object->{$k}[$key]->getPrimaryKey().' IN ('.join(', '$ids_to_nullify).')'
  728.                     $success : false;
  729.                 }elseif(!empty($ids_to_delete)){
  730.                     $success $object->{$k}[$key]->delete($ids_to_delete$success : false;
  731.                 }
  732.                 $object->$v->removeFromCollection($items_to_remove_from_collection);
  733.             }
  734.         }
  735.  
  736.         return $success;
  737.     }
  738.  
  739.     function _afterCallback(&$object)
  740.     {
  741.  
  742.         $success = true;
  743.  
  744.         $object_id $object->getId();
  745.         foreach (array_keys($object->hasMany->modelsas $association_id){
  746.             $CollectionHandler =$object->hasMany->models[$association_id];
  747.             $foreign_key $CollectionHandler->getOption($association_id'foreign_key');
  748.             $class_name strtolower($CollectionHandler->getOption($association_id'class_name'));
  749.             if(!empty($object->$association_id&& is_array($object->$association_id)){
  750.                 foreach (array_keys($object->$association_idas $k){
  751.                     if(!empty($object->{$association_id}[$k]&& strtolower(get_class($object->{$association_id}[$k])) == strtolower($class_name)){
  752.                         $AssociatedItem =$object->{$association_id}[$k];
  753.                         $AssociatedItem->set($foreign_key$object_id);
  754.                         $success !$AssociatedItem->save(? false : $success;
  755.                     }
  756.                 }
  757.             }
  758.         }
  759.         return $success;
  760.     }
  761.  
  762. }
  763.  
  764.  
  765. ?>

Documentation generated on Tue, 17 Jun 2008 14:25:34 +0200 by phpDocumentor 1.3.2