[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/AkActiveRecord/AkAssociations/ -> AkHasMany.php (source)

   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(!empty($options['include'])){
 171                      $finder_options['include'] = $options['include'];
 172                  }
 173                  if($FoundAssociates = $Associated->find('all',$finder_options)){
 174                      array_map(array(&$this,'_setAssociatedMemberId'),$FoundAssociates);
 175                      $this->Owner->{$this->association_id} =& $FoundAssociates;
 176                  }
 177              }
 178              if(empty($this->Owner->{$this->association_id})){
 179                  $this->Owner->{$this->association_id} = array();
 180              }
 181  
 182              $this->Owner->{$options['handler_name']}->_loaded = true;
 183          }
 184          return $this->Owner->{$this->association_id};
 185      }
 186  
 187  
 188      /**
 189       * add($object), add(array($object, $object2)) - adds one or more objects to the collection by setting 
 190       * their foreign keys to the collection?s primary key. Items are saved automatically when parent has been saved.
 191       */
 192      function add(&$Associated)
 193      {
 194          if(is_array($Associated)){
 195              $succes = true;
 196              $succes = $this->Owner->notifyObservers('beforeAdd') ? $succes : false;
 197              $options = $this->getOptions($this->association_id);
 198              foreach (array_keys($Associated) as $k){
 199                  if($succes && !empty($options['before_add']) && method_exists($this->Owner, $options['before_add']) && $this->Owner->{$options['before_add']}($Associated[$k]) === false ){
 200                      $succes = false;
 201                  }
 202                  if($succes && !$this->_hasAssociatedMember($Associated[$k])){
 203                      $this->Owner->{$this->association_id}[] =& $Associated[$k];
 204                      $this->_setAssociatedMemberId($Associated[$k]);
 205                      if($this->_relateAssociatedWithOwner($Associated[$k])){
 206                          $succes = $Associated[$k]->save() ? $succes : false;
 207                          if($succes && !empty($options['after_add']) && method_exists($this->Owner, $options['after_add']) && $this->Owner->{$options['after_add']}($Associated[$k]) === false ){
 208                              $succes = false;
 209                          }
 210                      }
 211                  }
 212              }
 213              $succes = $this->Owner->notifyObservers('afterAdd') ? $succes : false;
 214              return $succes;
 215          }else{
 216              $associates = array();
 217              $associates[] =& $Associated;
 218              return $this->add($associates);
 219          }
 220      }
 221  
 222      function push(&$record)
 223      {
 224          return $this->add($record);
 225      }
 226  
 227      function concat(&$record)
 228      {
 229          return $this->add($record);
 230      }
 231  
 232      /**
 233      * Remove all records from this association
 234      */
 235      function deleteAll($Skip = null)
 236      {
 237          $this->load();
 238          return $this->delete($this->Owner->{$this->association_id}, $Skip);
 239      }
 240  
 241      function reset()
 242      {
 243          $options = $this->getOptions($this->association_id);
 244          $this->Owner->{$options['handler_name']}->_loaded = false;
 245      }
 246  
 247      function set(&$objects)
 248      {
 249          $this->deleteAll($objects);
 250          $this->add($objects);
 251      }
 252  
 253      function setIds()
 254      {
 255          $ids = func_get_args();
 256          $ids = is_array($ids[0]) ? $ids[0] : $ids;
 257          
 258          $AssociatedModel =& $this->getAssociatedModelInstance();
 259          if(!empty($ids)){
 260              $NewAssociates =& $AssociatedModel->find($ids);
 261              $this->set($NewAssociates);
 262          }
 263      }
 264  
 265      function setByIds()
 266      {
 267          $ids = func_get_args();
 268          call_user_func_array(array($this,'setIds'), $ids);
 269      }
 270  
 271      function addId($id)
 272      {
 273          $AssociatedModel =& $this->getAssociatedModelInstance();
 274          if($NewAssociated = $AssociatedModel->find($id)){
 275              return $this->add($NewAssociated);
 276          }
 277          return false;
 278      }
 279  
 280  
 281      function delete(&$Associated, $Skip = null)
 282      {
 283          $success = true;
 284          if(!is_array($Associated)){
 285              $associated_elements = array();
 286              $associated_elements[] =& $Associated;
 287              return $this->delete($associated_elements, $Skip);
 288          }else{
 289              $options = $this->getOptions($this->association_id);
 290  
 291              $ids_to_skip = array();
 292              $Skip = empty($Skip) ? null : (is_array($Skip) ? $Skip : array($Skip));
 293              if(!empty($Skip)){
 294                  foreach (array_keys($Skip) as $k){
 295                      $ids_to_skip[] = $Skip[$k]->getId();
 296                  }
 297              }
 298  
 299              $ids_to_nullify = array();
 300              $ids_to_delete = array();
 301              $items_to_remove_from_collection = array();
 302              $AssociatedModel =& $this->getAssociatedModelInstance();
 303  
 304              $owner_type = $this->_findOwnerTypeForAssociation($AssociatedModel, $this->Owner);
 305  
 306              foreach (array_keys($Associated) as $k){
 307                  $items_to_remove_from_collection[] = $Associated[$k]->getId();
 308                  if(!in_array($Associated[$k]->getId() , $ids_to_skip)){
 309                      switch ($options['dependent']) {
 310                          case 'destroy':
 311                              $success = $Associated[$k]->destroy() ? $success : false;
 312                          break;
 313                          case 'delete_all':
 314                              $ids_to_delete[] = $Associated[$k]->getId();
 315                          break;
 316                          case 'nullify':
 317                              $id_to_nullify = $Associated[$k]->quotedId();
 318                              if(!empty($id_to_nullify)){
 319                                  $ids_to_nullify[] = $id_to_nullify;
 320                              }
 321                          default:
 322                          break;
 323                      }
 324                  }
 325              }
 326  
 327              $ids_to_nullify = empty($ids_to_nullify) ? false : array_diff($ids_to_nullify,array(''));
 328              if(!empty($ids_to_nullify)){
 329                  $success = $AssociatedModel->updateAll(
 330                  ' '.$options['foreign_key'].' = NULL ',
 331                  ' '.$options['foreign_key'].' = '.$this->Owner->quotedId().' AND '.$AssociatedModel->getPrimaryKey().' IN ('.join(', ',$ids_to_nullify).')'
 332                  ) ? $success : false;
 333              }elseif(!empty($ids_to_delete)){
 334                  $success = $AssociatedModel->delete($ids_to_delete) ? $success : false;
 335              }
 336  
 337              $this->removeFromCollection($items_to_remove_from_collection);
 338          }
 339  
 340          return $success;
 341      }
 342  
 343  
 344  
 345      /**
 346      * Remove records from the collection. Use delete() in order to trigger database dependencies
 347      */
 348      function removeFromCollection(&$records)
 349      {
 350          if(!is_array($records)){
 351              $records_array = array();
 352              $records_array[] =& $records;
 353              $this->delete($records_array);
 354          }else{
 355              $this->Owner->notifyObservers('beforeRemove');
 356              $options = $this->getOptions($this->association_id);
 357              foreach (array_keys($records) as $k){
 358  
 359                  if(!empty($options['before_remove']) && method_exists($this->Owner, $options['before_remove']) && $this->Owner->{$options['before_remove']}($records[$k]) === false ){
 360                      continue;
 361                  }
 362                  
 363                  if(isset($records[$k]->__activeRecordObject)){
 364                      $record_id = $records[$k]->getId();
 365                  }else{
 366                      $record_id = $records[$k];
 367                  }
 368  
 369                  foreach (array_keys($this->Owner->{$this->association_id}) as $kk){
 370                      if(
 371                      (
 372                      !empty($this->Owner->{$this->association_id}[$kk]->__hasManyMemberId) &&
 373                      !empty($records[$k]->__hasManyMemberId) &&
 374                      $records[$k]->__hasManyMemberId == $this->Owner->{$this->association_id}[$kk]->__hasManyMemberId
 375                      ) || (
 376                      !empty($this->Owner->{$this->association_id}[$kk]->__activeRecordObject) &&
 377                      $record_id == $this->Owner->{$this->association_id}[$kk]->getId()
 378                      )
 379                      ){
 380                          unset($this->Owner->{$this->association_id}[$kk]);
 381                      }
 382                  }
 383                  
 384                  $this->_unsetAssociatedMemberId($records[$k]);
 385                  
 386                  if(!empty($options['after_remove']) && method_exists($this->Owner, $options['after_remove'])){
 387                      $this->Owner->{$options['after_remove']}($records[$k]);
 388                  }
 389                  
 390              }
 391              $this->Owner->notifyObservers('afterRemove');
 392          }
 393      }
 394  
 395  
 396  
 397  
 398      function _setAssociatedMemberId(&$Member)
 399      {
 400          if(empty($Member->__hasManyMemberId)) {
 401              $Member->__hasManyMemberId = Ak::randomString();
 402          }
 403          $object_id = method_exists($Member,'getId') ? $Member->getId() : null;
 404          if(!empty($object_id)){
 405              $this->associated_ids[$object_id] = $Member->__hasManyMemberId;
 406          }
 407      }
 408  
 409      function _unsetAssociatedMemberId(&$Member)
 410      {
 411          $id = $this->_getAssociatedMemberId($Member);
 412          unset($this->associated_ids[$id]);
 413          unset($Member->__hasManyMemberId);
 414      }
 415  
 416      function _getAssociatedMemberId(&$Member)
 417      {
 418          if(!empty($Member->__hasManyMemberId)) {
 419              return array_search($Member->__hasManyMemberId, $this->associated_ids);
 420          }
 421          return false;
 422      }
 423  
 424      function _hasAssociatedMember(&$Member)
 425      {
 426          return !empty($Member->__hasManyMemberId);
 427      }
 428  
 429  
 430      function _relateAssociatedWithOwner(&$Associated)
 431      {
 432          if(!$this->Owner->isNewRecord()){
 433              if(method_exists($Associated, 'getModelName')){
 434                  $foreign_key = $this->getOption($this->association_id, 'foreign_key');
 435                  if($this->getOption($this->association_id, 'class_name') != $Associated->getModelName() || $foreign_key == $Associated->get($foreign_key)){
 436                      return false;
 437                  }
 438                  $Associated->set($foreign_key, $this->Owner->getId());
 439                  return true;
 440              }
 441          }
 442          return false;
 443      }
 444  
 445      function &_build($association_id, &$AssociatedObject, $reference_associated = true)
 446      {
 447          if($reference_associated){
 448              $this->Owner->$association_id =& $AssociatedObject;
 449          }else{
 450              $this->Owner->$association_id = $AssociatedObject;
 451          }
 452          $this->Owner->$association_id->_AssociationHandler =& $this;
 453          $this->Owner->$association_id->_associatedAs = $this->getType();
 454          $this->Owner->$association_id->_associationId = $association_id;
 455          $this->Owner->_associations[$association_id] =& $this->Owner->$association_id;
 456          return $this->Owner->$association_id;
 457      }
 458  
 459  
 460  
 461  
 462      function constructSql($set_owner_table_has_included = true)
 463      {
 464          $options = $this->getOptions($this->association_id);
 465          $Associated =& $this->getAssociatedModelInstance();
 466          $owner_id = $this->Owner->quotedId();
 467          $table_name = (!empty($options['include']) || $set_owner_table_has_included) ? '__owner' : $Associated->getTableName();
 468  
 469          if(empty($options['finder_sql'])){
 470              $options['finder_sql'] = ' '.$table_name.'.'.$options['foreign_key'].' = '.(empty($owner_id) ? 'null' : $owner_id).' ';
 471              $options['finder_sql'] .= !empty($options['conditions']) ? ' AND '.$options['conditions'].' ' : '';
 472          }
 473          if(empty($options['counter_sql']) && !empty($options['finder_sql'])){
 474              $options['counter_sql'] = $options['finder_sql'];
 475          }elseif(empty($options['counter_sql'])){
 476              $options['counter_sql'] = ' '.$table_name.'.'.$options['foreign_key'].' = '.(empty($owner_id) ? 'null' : $owner_id).' ';
 477              $options['counter_sql'] .= !empty($options['conditions']) ? ' AND '.$options['conditions'].' ' : '';
 478          }
 479  
 480          if(!empty($options['counter_sql']) && strtoupper(substr($options['counter_sql'],0,6)) != 'SELECT'){
 481              $options['counter_sql'] = 'SELECT COUNT(*) FROM '.$table_name.' WHERE '.$options['counter_sql'];
 482          }
 483  
 484          $this->setOptions($this->association_id, $options);
 485      }
 486  
 487  
 488  
 489      function count($force_count = false)
 490      {
 491          $count = 0;
 492          $options = $this->getOptions($this->association_id);
 493          if($force_count || (empty($this->Owner->{$options['handler_name']}->_loaded) && !$this->Owner->isNewRecord())){
 494              $this->constructSql(false);
 495              $options = $this->getOptions($this->association_id);
 496              $Associated =& $this->getAssociatedModelInstance();
 497  
 498              if($this->_hasCachedCounter()){
 499                  $count = $Associated->getAttribute($this->_getCachedCounterAttributeName());
 500              }elseif(!empty($options['counter_sql'])){
 501                  $count = $Associated->countBySql($options['counter_sql']);
 502              }else{
 503                  $count = (strtoupper(substr($options['finder_sql'],0,6)) != 'SELECT') ?
 504                  $Associated->count($options['foreign_key'].'='.$this->Owner->quotedId()) :
 505                  $Associated->countBySql($options['finder_sql']);
 506              }
 507          }else{
 508              $count = count($this->Owner->{$this->association_id});
 509          }
 510  
 511          if($count == 0){
 512              $this->Owner->{$this->association_id} = array();
 513              $this->Owner->{$options['handler_name']}->_loaded = true;
 514          }
 515  
 516          return $count;
 517      }
 518  
 519      function size()
 520      {
 521          return $this->count();
 522      }
 523  
 524  
 525      function &build($attributes = array(), $set_as_new_record = true)
 526      {
 527          $options = $this->getOptions($this->association_id);
 528          Ak::import($options['class_name']);
 529          $record =& new $options['class_name']($attributes);
 530          $record->_newRecord = $set_as_new_record;
 531          $this->Owner->{$this->association_id}[] =& $record;
 532          $this->_setAssociatedMemberId($record);
 533          $this->_relateAssociatedWithOwner($record);
 534          return $record;
 535      }
 536  
 537      function &create($attributes = array())
 538      {
 539          $record =& $this->build($attributes);
 540          if(!$this->Owner->isNewRecord()){
 541              $record->save();
 542          }
 543          return $record;
 544      }
 545  
 546  
 547      function getAssociatedFinderSqlOptions($association_id, $options = array())
 548      {
 549          $options = $this->getOptions($this->association_id);
 550          $Associated =& $this->getAssociatedModelInstance();
 551          $table_name = $Associated->getTableName();
 552          $owner_id = $this->Owner->quotedId();
 553  
 554          $finder_options = array();
 555  
 556          foreach ($options as $option=>$value) {
 557              if(!empty($value)){
 558                  $finder_options[$option] = trim($Associated->_addTableAliasesToAssociatedSql('_'.$this->association_id, $value));
 559              }
 560          }
 561  
 562          $finder_options['joins'] = $this->constructSqlForInclusion();
 563          $finder_options['selection'] = '';
 564  
 565          foreach (array_keys($Associated->getColumns()) as $column_name){
 566              $finder_options['selection'] .= '_'.$this->association_id.'.'.$column_name.' AS _'.$this->association_id.'_'.$column_name.', ';
 567          }
 568  
 569          $finder_options['selection'] = trim($finder_options['selection'], ', ');
 570  
 571          $finder_options['conditions'] = empty($options['conditions']) ? '' : 
 572  
 573          $Associated->_addTableAliasesToAssociatedSql('_'.$this->association_id, $options['conditions']).' ';
 574          
 575          return $finder_options;
 576      }
 577  
 578      function constructSqlForInclusion()
 579      {
 580          $Associated =& $this->getAssociatedModelInstance();
 581          $options = $this->getOptions($this->association_id);
 582          return ' LEFT OUTER JOIN '.
 583          $Associated->getTableName().' AS _'.$this->association_id.
 584          ' ON '.
 585          '__owner.'.$this->Owner->getPrimaryKey().
 586          ' = '.
 587          '_'.$this->association_id.'.'.$options['foreign_key'].' ';
 588      }
 589  
 590  
 591      function _hasCachedCounter()
 592      {
 593          $Associated =& $this->getAssociatedModelInstance();
 594          return $Associated->isAttributePresent($this->_getCachedCounterAttributeName());
 595      }
 596  
 597      function _getCachedCounterAttributeName()
 598      {
 599          return $this->association_id.'_count';
 600      }
 601  
 602  
 603      function &getAssociatedModelInstance()
 604      {
 605          static $ModelInstances;
 606              $class_name = $this->getOption($this->association_id, 'class_name');
 607          if(empty($ModelInstances[$class_name])){  
 608              Ak::import($class_name);
 609              $ModelInstances[$class_name] =& new $class_name();
 610          }
 611          return $ModelInstances[$class_name];
 612      }
 613  
 614  
 615      function &find()
 616      {
 617          $result = false;
 618          if(!$this->Owner->isNewRecord()){
 619  
 620              $args = func_get_args();
 621              $num_args = func_num_args();
 622  
 623              if(!empty($args[$num_args-1]) && is_array($args[$num_args-1])){
 624                  $options_in_args = true;
 625                  $options = $args[$num_args-1];
 626              }else{
 627                  $options_in_args = false;
 628                  $options = array();
 629              }
 630  
 631              $this->constructSql(!empty($options['include']));
 632              $has_many_options = $this->getOptions($this->association_id);
 633              $Associated =& $this->getAssociatedModelInstance();
 634              if (empty($options['conditions'])) {
 635                  $options['conditions'] = @$has_many_options['finder_sql'];
 636              } elseif(!empty($has_many_options['finder_sql']) && is_array($options['conditions']) && !strstr($options['conditions'][0], $has_many_options['finder_sql'])) {
 637                  $options['conditions'][0] .= ' AND '. $has_many_options['finder_sql'];
 638              } elseif (!empty($has_many_options['finder_sql']) && !strstr($options['conditions'], $has_many_options['finder_sql'])) {
 639                  $options['conditions'] .= ' AND '. $has_many_options['finder_sql'];
 640              }
 641  
 642              $options['order'] = empty($options['order']) ? @$has_many_options['order'] : $options['order'];
 643              $options['include'] = empty($options['include']) ? @$has_many_options['include'] : $options['include'];
 644  
 645              if($options_in_args){
 646                  $args[$num_args-1] = $options;
 647              }else{
 648                  $args = empty($args) ? array('all') : $args;
 649                  array_push($args, $options);
 650              }
 651  
 652              $result =& Ak::call_user_func_array(array(&$Associated,'find'), $args);
 653          }
 654  
 655          return $result;
 656      }
 657  
 658  
 659      function isEmpty()
 660      {
 661          return $this->count() === 0;
 662      }
 663  
 664      function getSize()
 665      {
 666          return $this->count();
 667      }
 668  
 669      function clear()
 670      {
 671          return $this->deleteAll();
 672      }
 673  
 674      /**
 675      * Triggers
 676      */
 677      function afterCreate(&$object)
 678      {
 679          return $this->_afterCallback($object);
 680      }
 681  
 682      function afterUpdate(&$object)
 683      {
 684          return $this->_afterCallback($object);
 685      }
 686  
 687  
 688      function beforeDestroy(&$object)
 689      {
 690          $success = true;
 691  
 692          foreach ((array)$object->_associationIds as $k => $v){
 693              if(isset($object->$k) && is_array($object->$k) && isset($object->$v) && method_exists($object->$v, 'getType') && $object->$v->getType() == 'hasMany'){
 694  
 695                  $ids_to_delete = array();
 696                  $ids_to_nullify = array();
 697                  $items_to_remove_from_collection = array();
 698  
 699                  $object->$v->load();
 700                  foreach(array_keys($object->$k) as $key){
 701  
 702                      $items_to_remove_from_collection[] =& $object->{$k}[$key];
 703  
 704                      switch ($object->$v->options[$k]['dependent']) {
 705  
 706                          case 'destroy':
 707                          $success = $object->{$k}[$key]->destroy() ? $success : false;
 708                          break;
 709  
 710                          case 'delete_all':
 711                          $ids_to_delete[] = $object->{$k}[$key]->getId();
 712                          break;
 713  
 714                          case 'nullify':
 715                          $id_to_nullify = $object->{$k}[$key]->quotedId();
 716                          if(!empty($id_to_nullify)){
 717                              $ids_to_nullify[] = $id_to_nullify;
 718                          }
 719                          break;
 720  
 721                          default:
 722                          break;
 723                      }
 724                  }
 725                  
 726                  $ids_to_nullify = empty($ids_to_nullify) ? false : array_diff($ids_to_nullify,array(''));
 727                  if(!empty($ids_to_nullify)){
 728                      $success = $object->{$k}[$key]->updateAll(
 729                      ' '.$object->$v->options[$k]['foreign_key'].' = NULL ',
 730                      ' '.$object->$v->options[$k]['foreign_key'].' = '.$object->quotedId().' AND '.$object->{$k}[$key]->getPrimaryKey().' IN ('.join(', ', $ids_to_nullify).')'
 731                      ) ? $success : false;
 732                  }elseif(!empty($ids_to_delete)){
 733                      $success = $object->{$k}[$key]->delete($ids_to_delete) ? $success : false;
 734                  }
 735                  $object->$v->removeFromCollection($items_to_remove_from_collection);
 736              }
 737          }
 738  
 739          return $success;
 740      }
 741  
 742      function _afterCallback(&$object)
 743      {
 744  
 745          $success = true;
 746  
 747          $object_id = $object->getId();
 748          foreach (array_keys($object->hasMany->models) as $association_id){
 749              $CollectionHandler =& $object->hasMany->models[$association_id];
 750              $foreign_key = $CollectionHandler->getOption($association_id, 'foreign_key');
 751              $class_name = strtolower($CollectionHandler->getOption($association_id, 'class_name'));
 752              if(!empty($object->$association_id) && is_array($object->$association_id)){
 753                  foreach (array_keys($object->$association_id) as $k){
 754                      if(!empty($object->{$association_id}[$k]) && strtolower(get_class($object->{$association_id}[$k])) == strtolower($class_name)){
 755                          $AssociatedItem =& $object->{$association_id}[$k];
 756                          $AssociatedItem->set($foreign_key, $object_id);
 757                          $success = !$AssociatedItem->save() ? false : $success;
 758                      }
 759                  }
 760              }
 761          }
 762          return $success;
 763      }
 764  
 765  }
 766  
 767  
 768  ?>


Generated: Mon Oct 27 12:43:49 2008 Cross-referenced by PHPXref 0.6