GenericDAO.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2005-2008 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 
00017     abstract class GenericDAO extends Singleton implements BaseDAO
00018     {
00019         private $identityMap    = array();
00020         
00021         abstract public function getTable();
00022         abstract public function getObjectName();
00023         
00024         public function makeObject($array, $prefix = null)
00025         {
00026             if (
00027                 isset(
00028                     $this->identityMap[
00029                         $array[$idName = $prefix.$this->getIdName()]
00030                     ]
00031                 )
00032             ) {
00033                 $this->getProtoClass()->skipObjectPrefetching(
00034                     $this->identityMap[$array[$idName]]
00035                 );
00036                 
00037                 return $this->identityMap[$array[$idName]];
00038             }
00039             
00040             return
00041                 $this->completeObject(
00042                     $this->makeOnlyObject($array, $prefix)
00043                 );
00044         }
00045         
00046         public function makeOnlyObject($array, $prefix = null)
00047         {
00048             // adding incomplete object to identity map
00049             // solves case with circular-dependent objects
00050             return $this->addObjectToMap(
00051                 $this->getProtoClass()->makeOnlyObject(
00052                     $this->getObjectName(), $array, $prefix
00053                 )
00054             );
00055         }
00056         
00057         public function completeObject(Identifiable $object)
00058         {
00059             return $this->getProtoClass()->completeObject(
00060                 // same purpose as in makeOnlyObject,
00061                 // but for objects retrieved from cache
00062                 $this->addObjectToMap($object)
00063             );
00064         }
00065         
00072         public function getLinkName()
00073         {
00074             return null;
00075         }
00076         
00077         public function getIdName()
00078         {
00079             return 'id';
00080         }
00081         
00082         public function getSequence()
00083         {
00084             return $this->getTable().'_id';
00085         }
00086         
00090         public function getProtoClass()
00091         {
00092             static $protos = array();
00093             
00094             if (!isset($protos[$className = $this->getObjectName()]))
00095                 $protos[$className] = call_user_func(array($className, 'proto'));
00096             
00097             return $protos[$className];
00098         }
00099         
00100         public function getMapping()
00101         {
00102             return $this->getProtoClass()->getMapping();
00103         }
00104         
00105         public function getFields()
00106         {
00107             static $fields = array();
00108             
00109             $className = $this->getObjectName();
00110             
00111             if (!isset($fields[$className])) {
00112                 $fields[$className] = array_values($this->getMapping());
00113             }
00114             
00115             return $fields[$className];
00116         }
00117         
00121         public function makeSelectHead()
00122         {
00123             static $selectHead = array();
00124             
00125             if (!isset($selectHead[$className = $this->getObjectName()])) {
00126                 $table = $this->getTable();
00127                 
00128                 $object =
00129                     OSQL::select()->
00130                     from($table);
00131                 
00132                 foreach ($this->getFields() as $field)
00133                     $object->get(new DBField($field, $table));
00134                 
00135                 $selectHead[$className] = $object;
00136             }
00137             
00138             return clone $selectHead[$className];
00139         }
00140         
00144         public function makeTotalCountQuery()
00145         {
00146             return
00147                 OSQL::select()->
00148                 get(
00149                     SQLFunction::create('count', DBValue::create('*'))
00150                 )->
00151                 from($this->getTable());
00152         }
00153         
00155 
00156         public function getById($id, $expires = Cache::EXPIRES_MEDIUM)
00157         {
00158             Assert::isScalar($id);
00159             Assert::isNotEmpty($id);
00160             
00161             if (isset($this->identityMap[$id]))
00162                 return $this->identityMap[$id];
00163             
00164             return $this->addObjectToMap(
00165                 Cache::worker($this)->getById($id, $expires)
00166             );
00167         }
00168         
00169         public function getByLogic(
00170             LogicalObject $logic, $expires = Cache::DO_NOT_CACHE
00171         )
00172         {
00173             return $this->addObjectToMap(
00174                 Cache::worker($this)->getByLogic($logic, $expires)
00175             );
00176         }
00177         
00178         public function getByQuery(
00179             SelectQuery $query, $expires = Cache::DO_NOT_CACHE
00180         )
00181         {
00182             return $this->addObjectToMap(
00183                 Cache::worker($this)->getByQuery($query, $expires)
00184             );
00185         }
00186         
00187         public function getCustom(
00188             SelectQuery $query, $expires = Cache::DO_NOT_CACHE
00189         )
00190         {
00191             return Cache::worker($this)->getCustom($query, $expires);
00192         }
00193         
00194         public function getListByIds(
00195             array $ids, $expires = Cache::EXPIRES_MEDIUM
00196         )
00197         {
00198             $mapped = $remain = array();
00199             
00200             foreach ($ids as $id) {
00201                 if (isset($this->identityMap[$id])) {
00202                     $mapped[] = $this->identityMap[$id];
00203                 } else {
00204                     $remain[] = $id;
00205                 }
00206             }
00207             
00208             if ($remain) {
00209                 $list = $this->addObjectListToMap(
00210                     Cache::worker($this)->getListByIds($remain, $expires)
00211                 );
00212                 
00213                 $mapped = array_merge($mapped, $list);
00214             }
00215             
00216             return ArrayUtils::regularizeList($ids, $mapped);
00217         }
00218         
00219         public function getListByQuery(
00220             SelectQuery $query, $expires = Cache::DO_NOT_CACHE
00221         )
00222         {
00223             return $this->addObjectListToMap(
00224                 Cache::worker($this)->getListByQuery($query, $expires)
00225             );
00226         }
00227         
00228         public function getListByLogic(
00229             LogicalObject $logic, $expires = Cache::DO_NOT_CACHE
00230         )
00231         {
00232             return $this->addObjectListToMap(
00233                 Cache::worker($this)->getListByLogic($logic, $expires)
00234             );
00235         }
00236         
00237         public function getPlainList($expires = Cache::EXPIRES_MEDIUM)
00238         {
00239             return $this->addObjectListToMap(
00240                 Cache::worker($this)->getPlainList($expires)
00241             );
00242         }
00243         
00244         public function getTotalCount($expires = Cache::DO_NOT_CACHE)
00245         {
00246             return Cache::worker($this)->getTotalCount($expires);
00247         }
00248         
00249         public function getCustomList(
00250             SelectQuery $query, $expires = Cache::DO_NOT_CACHE
00251         )
00252         {
00253             return Cache::worker($this)->getCustomList($query, $expires);
00254         }
00255         
00256         public function getCustomRowList(
00257             SelectQuery $query, $expires = Cache::DO_NOT_CACHE
00258         )
00259         {
00260             return Cache::worker($this)->getCustomRowList($query, $expires);
00261         }
00262         
00263         public function getQueryResult(
00264             SelectQuery $query, $expires = Cache::DO_NOT_CACHE
00265         )
00266         {
00267             return Cache::worker($this)->getQueryResult($query, $expires);
00268         }
00269         
00270         public function drop(Identifiable $object)
00271         {
00272             $this->checkObjectType($object);
00273             
00274             return $this->dropById($object->getId());
00275         }
00276         
00277         public function dropById($id)
00278         {
00279             unset($this->identityMap[$id]);
00280             
00281             $count = Cache::worker($this)->dropById($id);
00282             
00283             if (1 != $count)
00284                 throw new WrongStateException('no object were dropped');
00285             
00286             return $count;
00287         }
00288         
00289         public function dropByIds(array $ids)
00290         {
00291             foreach ($ids as $id)
00292                 unset($this->identityMap[$id]);
00293             
00294             $count = Cache::worker($this)->dropByIds($ids);
00295             
00296             if ($count != count($ids))
00297                 throw new WrongStateException('not all objects were dropped');
00298             
00299             return $count;
00300         }
00301         
00302         public function uncacheById($id)
00303         {
00304             unset($this->identityMap[$id]);
00305             
00306             return Cache::worker($this)->uncacheById($id);
00307         }
00308         
00309         public function uncacheByIds($ids)
00310         {
00311             foreach ($ids as $id)
00312                 unset($this->identityMap[$id]);
00313             
00314             return Cache::worker($this)->uncacheByIds($ids);
00315         }
00316         
00317         public function uncacheLists()
00318         {
00319             $this->dropIdentityMap();
00320             
00321             return Cache::worker($this)->uncacheLists();
00322         }
00324         
00328         public function dropIdentityMap()
00329         {
00330             $this->identityMap = array();
00331             
00332             return $this;
00333         }
00334 
00335         public function dropObjectIdentityMapById($id)
00336         {
00337             unset($this->identityMap[$id]);
00338 
00339             return $this;
00340         }
00341         
00342         protected function inject(
00343             InsertOrUpdateQuery $query,
00344             Identifiable $object
00345         )
00346         {
00347             $this->checkObjectType($object);
00348             
00349             return $this->doInject(
00350                 $this->setQueryFields(
00351                     $query->setTable($this->getTable()), $object
00352                 ),
00353                 $object
00354             );
00355         }
00356         
00357         protected function doInject(
00358             InsertOrUpdateQuery $query,
00359             Identifiable $object
00360         )
00361         {
00362             $db = DBPool::getByDao($this);
00363             
00364             if (!$db->isQueueActive()) {
00365                 $count = $db->queryCount($query);
00366                 
00367                 $this->uncacheById($object->getId());
00368                 
00369                 if ($count !== 1)
00370                     throw new WrongStateException(
00371                         $count.' rows affected: racy or insane inject happened: '
00372                         .$query->toDialectString($db->getDialect())
00373                     );
00374             } else {
00375                 $db->queryNull($query);
00376                 
00377                 $this->uncacheById($object->getId());
00378             }
00379             
00380             // clean out Identifier, if any
00381             return $this->addObjectToMap($object->setId($object->getId()));
00382         }
00383         
00384         /* void */ protected function checkObjectType(Identifiable $object)
00385         {
00386             Assert::isSame(
00387                 get_class($object),
00388                 $this->getObjectName(),
00389                 'strange object given, i can not inject it'
00390             );
00391         }
00392         
00393         private function addObjectToMap(Identifiable $object)
00394         {
00395             return $this->identityMap[$object->getId()] = $object;
00396         }
00397         
00398         private function addObjectListToMap($list)
00399         {
00400             foreach ($list as $object)
00401                 $this->identityMap[$object->getId()] = $object;
00402             
00403             return $list;
00404         }
00405     }
00406 ?>