UnifiedContainer.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2005-2009 by Konstantin V. Arkhipov                     *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU Lesser General Public License as        *
00007  *   published by the Free Software Foundation; either version 3 of the    *
00008  *   License, or (at your option) any later version.                       *
00009  *                                                                         *
00010  ***************************************************************************/
00011 
00012 /*
00013     UnifiedContainer:
00014 
00015         child's and parent's field names:
00016             abstract public function getChildIdField()
00017             abstract public function getParentIdField()
00018 
00019         all we need from outer world:
00020             public function __construct(
00021                 Identifiable $parent, UnifiedContainer $dao, $lazy = true
00022             )
00023 
00024         if you want to apply Criteria's "filter":
00025             public function setCriteria(Criteria $criteria)
00026 
00027         first you should fetch whatever you want:
00028             public function fetch()
00029 
00030         then you can get it:
00031             public function getList()
00032         
00033         set you modified list:
00034             public function setList($list)
00035 
00036         finally, sync fetched data and stored one:
00037             public function save()
00038 
00039     OneToManyLinked <- UnifiedContainer:
00040 
00041         indicates whether child can be free (parent_id nullable):
00042             protected function isUnlinkable()
00043 
00044     ManyToManyLinked <- UnifiedContainer:
00045 
00046         helper's table name:
00047             abstract public function getHelperTable()
00048 
00049         id field name at parent's primary table:
00050             protected function getParentTableIdField()
00051 */
00052 
00060     abstract class UnifiedContainer
00061     {
00062         protected $worker   = null;
00063         protected $parent   = null;
00064         
00065         protected $dao      = null;
00066         
00067         protected $lazy     = true;
00068         protected $fetched  = false;
00069 
00070         protected $list     = array();
00071         protected $clones   = array();
00072         
00073         // sleep state
00074         protected $workerClass  = null;
00075         protected $daoClass     = null;
00076 
00077         protected $comparator   = null;
00078         
00079         abstract public function getParentIdField();
00080         abstract public function getChildIdField();
00081         
00082         public function __construct(
00083             Identifiable $parent, GenericDAO $dao, $lazy = true
00084         )
00085         {
00086             Assert::isBoolean($lazy);
00087             
00088             $this->parent   = $parent;
00089             $this->lazy     = $lazy;
00090             $this->dao      = $dao;
00091             
00092             Assert::isInstance($dao->getObjectName(), 'Identifiable');
00093 
00094             $this->comparator = SerializedObjectComparator::me();
00095         }
00096         
00097         public function __sleep()
00098         {
00099             $this->daoClass = get_class($this->dao);
00100             $this->workerClass = get_class($this->worker);
00101             return array('workerClass', 'daoClass', 'parent', 'lazy');
00102         }
00103         
00104         public function __wakeup()
00105         {
00106             $this->dao = Singleton::getInstance($this->daoClass);
00107             $this->worker = new $this->workerClass($this);
00108         }
00109         
00110         public function getParentObject()
00111         {
00112             return $this->parent;
00113         }
00114         
00118         public function getDao()
00119         {
00120             return $this->dao;
00121         }
00122         
00123         public function isLazy()
00124         {
00125             return $this->lazy;
00126         }
00127         
00128         public function isFetched()
00129         {
00130             return $this->fetched;
00131         }
00132         
00137         public function setCriteria(Criteria $criteria)
00138         {
00139             Assert::isTrue(
00140                 $criteria->getDao() === null
00141                 || (
00142                     $criteria->getDao() === $this->dao
00143                 ),
00144                 "criteria's dao doesn't match container's one"
00145             );
00146             
00147             if (!$criteria->getDao())
00148                 $criteria->setDao($this->dao);
00149             
00150             $this->worker->setCriteria($criteria);
00151             
00152             return $this;
00153         }
00154         
00158         public function getCriteria()
00159         {
00160             return $this->worker->getCriteria();
00161         }
00162         
00163         public function setObjectComparator(Comparator $comparator)
00164         {
00165             $this->comparator = $comparator;
00166             
00167             return $this;
00168         }
00169         
00174         public function setList($list)
00175         {
00176             Assert::isArray($list);
00177             
00178             $this->list = $list;
00179             
00180             return $this;
00181         }
00182         
00186         public function mergeList(array $list)
00187         {
00188             if ($this->lazy) {
00189                 foreach ($list as $id)
00190                     $this->list[$id] = $id;
00191             } else {
00192                 $this->list = array_merge($this->list, $list);
00193             }
00194             
00195             $this->fetched = true;
00196             
00197             return $this;
00198         }
00199 
00200         public function getList()
00201         {
00202             if (!$this->list && !$this->isFetched())
00203                 $this->fetch();
00204             
00205             return $this->list;
00206         }
00207         
00208         public function getCount()
00209         {
00210             if (!$this->isFetched() && $this->parent->getId()) {
00211                 $row = $this->dao->getCustom($this->worker->makeCountQuery());
00212                 
00213                 return current($row);
00214             }
00215             
00216             return count($this->list);
00217         }
00218         
00223         public function fetch()
00224         {
00225             if (!$this->parent->getId())
00226                 throw new WrongStateException(
00227                     'save parent object first'
00228                 );
00229             
00230             try {
00231                 $this->fetchList();
00232             } catch (ObjectNotFoundException $e) {
00233                 // yummy
00234             }
00235             
00236             $this->fetched = true;
00237             
00238             return $this;
00239         }
00240         
00245         public function save()
00246         {
00247             Assert::isArray(
00248                 $this->list,
00249                 "that's not an array :-/"
00250             );
00251 
00252             if (!$this->fetched)
00253                 throw new WrongStateException(
00254                     'do not want to save non-fetched collection'
00255                 );
00256             
00257             $list   = $this->list;
00258             $clones = $this->clones;
00259             
00260             $ids = $insert = $delete = $update = array();
00261             
00262             if ($this->lazy) {
00263                 foreach ($list as $id) {
00264                     if (!isset($clones[$id]))
00265                         $insert[] = $ids[$id] = $id;
00266                     else
00267                         $ids[$id] = $id;
00268                 }
00269                 
00270                 foreach ($clones as $id) {
00271                     if (!isset($ids[$id]))
00272                         $delete[] = $id;
00273                 }
00274             } else {
00275                 foreach ($list as $object) {
00276                     $id = $object->getId();
00277                     
00278                     if (null === $id) {
00279                         $insert[] = $object;
00280                     } elseif (
00281                         isset($clones[$id])
00282                         // there is no another way yet to compare objects without
00283                         // risk of falling into fatal error:
00284                         // "nesting level too deep?"
00285                         && ($this->comparator->compare($object, $clones[$id]) <> 0)
00286                     ) {
00287                         $update[] = $object;
00288                     } elseif (!isset($clones[$id])) {
00289                         $insert[] = $object;
00290                     }
00291                     
00292                     if (null !== $id)
00293                         $ids[$id] = $object;
00294                 }
00295                 
00296                 foreach ($clones as $id => $object) {
00297                     if (!isset($ids[$id]))
00298                         $delete[] = $object;
00299                 }
00300             }
00301 
00302             $db = DBPool::getByDao($this->getDao());
00303             
00304             if (!$db->inTransaction()) {
00305                 $outerQueue = $db->isQueueActive();
00306                 
00307                 if (!$outerQueue)
00308                     $db->queueStart();
00309                 
00310                 $db->begin();
00311                 
00312                 try {
00313                     $this->worker->sync($insert, $update, $delete);
00314                     
00315                     $db->commit();
00316                     
00317                     if (!$outerQueue)
00318                         $db->queueFlush();
00319                 } catch (DatabaseException $e) {
00320                     if (!$outerQueue)
00321                         $db->queueDrop()->queueStop();
00322                     
00323                     $db->rollback();
00324                     
00325                     throw $e;
00326                 }
00327             } else {
00328                 $this->worker->sync($insert, $update, $delete);
00329             }
00330             
00331             $this->clones = array();
00332             $this->syncClones();
00333             $this->dao->uncacheLists();
00334 
00335             return $this;
00336         }
00337 
00341         public function clean()
00342         {
00343             $this->list = $this->clones = array();
00344             
00345             $this->fetched = false;
00346             
00347             return $this;
00348         }
00349         
00353         public function dropList()
00354         {
00355             $this->worker->dropList();
00356             
00357             $this->clean();
00358             
00359             return $this;
00360         }
00361         
00362         /* void */ public static function destroy(UnifiedContainer $container)
00363         {
00364             unset($container->worker, $container);
00365         }
00366         
00367         protected function fetchList()
00368         {
00369             $query = $this->worker->makeFetchQuery();
00370             
00371             if ($this->lazy) {
00372                 $list = $this->dao->getCustomRowList($query);
00373                 
00374                 // special case for handling result from db
00375                 if (
00376                     isset($list[0])
00377                     && is_array($list[0])
00378                 ) {
00379                     $newList = array();
00380                     
00381                     foreach ($list as $key => $value)
00382                         $newList[] = $value[$this->getChildIdField()];
00383                     
00384                     $list = $newList;
00385                 }
00386             } else
00387                 $list = $this->dao->getListByQuery($query);
00388             
00389             $this->list = array();
00390             
00391             return $this->importList($list);
00392         }
00393         
00397         private function importList(array $list)
00398         {
00399             $this->mergeList($list);
00400 
00401             $this->syncClones();
00402             
00403             return $this;
00404         }
00405     
00409         private function syncClones()
00410         {
00411             if ($this->lazy) {
00412                 foreach ($this->list as $id) {
00413                     $this->clones[$id] = $id;
00414                 }
00415             } else {
00416                 foreach ($this->list as $object) {
00417                     // don't track unsaved objects
00418                     if ($id = $object->getId())
00419                         $this->clones[$id] = clone $object;
00420                 }
00421             }
00422             
00423             return $this;
00424         }
00425     }
00426 ?>