# 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 ?>