AbstractProtoClass.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2006-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 
00015     abstract class AbstractProtoClass extends Singleton
00016     {
00017         private $depth = 0;
00018         private $storage = array();
00019         private $skipList = array();
00020         
00021         abstract protected function makePropertyList();
00022         
00026         public function beginPrefetch()
00027         {
00028             $this->storage[++$this->depth] = array();
00029             $this->skipList[$this->depth] = array();
00030             
00031             return $this;
00032         }
00033         
00037         public function skipObjectPrefetching(Identifiable $object)
00038         {
00039             if ($this->depth) {
00040                 if (!isset($this->skipList[$this->depth][$object->getId()]))
00041                     $this->skipList[$this->depth][$object->getId()] = 1;
00042                 else
00043                     ++$this->skipList[$this->depth][$object->getId()];
00044             }
00045             
00046             return $this;
00047         }
00048         
00049         public function endPrefetch(array $objectList)
00050         {
00051             if (!$this->depth)
00052                 throw new WrongStateException('prefetch mode is already off');
00053             
00054             foreach ($this->storage[$this->depth] as $setter => $innerList) {
00055                 Assert::isEqual(
00056                     count($objectList),
00057                     count($innerList) + array_sum($this->skipList[$this->depth])
00058                 );
00059                 
00060                 $ids = array();
00061                 
00062                 foreach ($innerList as $inner)
00063                     if ($inner)
00064                         $ids[] = $inner->getId();
00065                 
00066                 // finding first available inner object
00067                 foreach ($innerList as $inner)
00068                     if ($inner)
00069                         break;
00070                 
00071                 if (!$inner)
00072                     continue;
00073                 
00074                 // put yet unmapped objects into dao's identityMap
00075                 $inner->dao()->getListByIds($ids);
00076                 
00077                 $skippedMap = $this->skipList[$this->depth];
00078                 
00079                 $i = $j = 0;
00080                 
00081                 foreach ($objectList as $object) {
00082                     $objectId = $object->getId();
00083                     
00084                     if (isset($skippedMap[$objectId])) {
00085                         if ($skippedMap[$objectId] == 1)
00086                             unset($skippedMap[$objectId]);
00087                         else
00088                             --$skippedMap[$objectId];
00089                         ++$j;
00090                         continue;
00091                     }
00092                     
00093                     if ($innerList[$i]) {
00094                         try {
00095                             // avoid dao "caching" here
00096                             // because of possible breakage
00097                             // in overriden properties
00098                             $object->$setter(
00099                                 $innerList[$i]->dao()->getById(
00100                                     $innerList[$i]->getId()
00101                                 )
00102                             );
00103                         } catch (ObjectNotFoundException $e) {
00104                             throw new WrongStateException(
00105                                 'possible corruption found: '.$e->getMessage()
00106                             );
00107                         }
00108                     }
00109                     
00110                     ++$i;
00111                 }
00112                 
00113                 Assert::isEqual(
00114                     $i,
00115                     count($objectList) - $j
00116                 );
00117             }
00118             
00119             unset($this->skipList[$this->depth], $this->storage[$this->depth--]);
00120             
00121             return $objectList;
00122         }
00123         
00124         public static function makeOnlyObject($className, $array, $prefix = null)
00125         {
00126             return self::assemblyObject(new $className, $array, $prefix);
00127         }
00128         
00129         public static function completeObject(Prototyped $object)
00130         {
00131             return self::fetchEncapsulants($object);
00132         }
00133         
00134         final public function getPropertyList()
00135         {
00136             static $lists = array();
00137             
00138             $className = get_class($this);
00139             
00140             if (!isset($lists[$className])) {
00141                 $lists[$className] = $this->makePropertyList();
00142             }
00143             
00144             return $lists[$className];
00145         }
00146         
00147         final public function getExpandedPropertyList($prefix = null)
00148         {
00149             static $lists = array();
00150             
00151             $className = get_class($this);
00152             
00153             if (!isset($lists[$className])) {
00154                 foreach ($this->makePropertyList() as $property) {
00155                     if ($property instanceof InnerMetaProperty) {
00156                         $lists[$className] =
00157                             array_merge(
00158                                 $lists[$className],
00159                                 $property->getProto()->getExpandedPropertyList(
00160                                     $property->getName().':'
00161                                 )
00162                             );
00163                     } else {
00164                         $lists[
00165                             $className
00166                         ][
00167                             $prefix.$property->getName()
00168                         ]
00169                             = $property;
00170                     }
00171                 }
00172             }
00173             
00174             return $lists[$className];
00175         }
00176         
00181         public function getPropertyByName($name)
00182         {
00183             if ($property = $this->safePropertyGet($name))
00184                 return $property;
00185             
00186             throw new MissingElementException(
00187                 "unknown property requested by name '{$name}'"
00188             );
00189         }
00190         
00191         public function isPropertyExists($name)
00192         {
00193             return $this->safePropertyGet($name) !== null;
00194         }
00195         
00199         public function makeForm($prefix = null)
00200         {
00201             $form = Form::create();
00202             
00203             foreach ($this->getPropertyList() as $property) {
00204                 $property->fillForm($form, $prefix);
00205             }
00206             
00207             return $form;
00208         }
00209         
00213         public function fillQuery(
00214             InsertOrUpdateQuery $query, Prototyped $object
00215         )
00216         {
00217             foreach ($this->getPropertyList() as $property) {
00218                 $property->fillQuery($query, $object);
00219             }
00220             
00221             return $query;
00222         }
00223         
00224         public function getMapping()
00225         {
00226             static $mappings = array();
00227             
00228             $className = get_class($this);
00229             
00230             if (!isset($mappings[$className])) {
00231                 $mapping = array();
00232                 foreach ($this->getPropertyList() as $property) {
00233                     $mapping = $property->fillMapping($mapping);
00234                 }
00235                 $mappings[$className] = $mapping;
00236             }
00237             
00238             return $mappings[$className];
00239         }
00240         
00241         public function importPrimitive(
00242             $path,
00243             Form $form,
00244             BasePrimitive $prm,
00245             /* Prototyped */ $object,
00246             $ignoreNull = true
00247         )
00248         {
00249             if (strpos($path, ':') !== false) {
00250                 return $this->forwardPrimitive(
00251                     $path, $form, $prm, $object, $ignoreNull
00252                 );
00253             } else {
00254                 $property = $this->getPropertyByName($path);
00255                 $getter = $property->getGetter();
00256                 
00257                 if (
00258                     !$property->isFormless()
00259                     && ($property->getFetchStrategyId() == FetchStrategy::LAZY)
00260                     && !$object->{$getter.'Id'}()
00261                 ) {
00262                     return $object;
00263                 }
00264                 
00265                 $value = $object->$getter();
00266                 
00267                 if (!$ignoreNull || ($value !== null)) {
00268                     $form->importValue($prm->getName(), $value);
00269                 }
00270             }
00271             
00272             return $object;
00273         }
00274         
00275         public function exportPrimitive(
00276             $path,
00277             BasePrimitive $prm,
00278             /* Prototyped */ $object,
00279             $ignoreNull = true
00280         )
00281         {
00282             if (strpos($path, ':') !== false) {
00283                 return $this->forwardPrimitive(
00284                     $path, null, $prm, $object, $ignoreNull
00285                 );
00286             } else {
00287                 $property = $this->getPropertyByName($path);
00288                 $setter = $property->getSetter();
00289                 $value = $prm->getValue();
00290                 
00291                 if (
00292                     !$ignoreNull || ($value !== null)
00293                 ) {
00294                     if ($property->isIdentifier()) {
00295                         $value = $value->getId();
00296                     }
00297                     
00298                     $dropper = $property->getDropper();
00299                     
00300                     if (
00301                         ($value === null)
00302                             && method_exists($object, $dropper)
00303                             && (
00304                                 !$property->getRelationId()
00305                                 || (
00306                                     $property->getRelationId()
00307                                     == MetaRelation::ONE_TO_ONE
00308                                 )
00309                             )
00310                     ) {
00311                         $object->$dropper();
00312                         
00313                         return $object;
00314                     } elseif (
00315                         (
00316                             $property->getRelationId()
00317                             == MetaRelation::ONE_TO_MANY
00318                         ) || (
00319                             $property->getRelationId()
00320                             == MetaRelation::MANY_TO_MANY
00321                         )
00322                     ) {
00323                         if ($value === null)
00324                             $value = array();
00325                         
00326                         $getter = $property->getGetter();
00327                         $object->$getter()->setList($value);
00328                         
00329                         return $object;
00330                     }
00331                     
00332                     $object->$setter($value);
00333                 }
00334             }
00335             
00336             return $object;
00337         }
00338         
00339         private static function fetchEncapsulants(Prototyped $object)
00340         {
00341             $proto = $object->proto();
00342             
00343             foreach ($proto->getPropertyList() as $property) {
00344                 if (
00345                     $property->getRelationId() == MetaRelation::ONE_TO_ONE
00346                     && ($property->getFetchStrategyId() != FetchStrategy::LAZY)
00347                 ) {
00348                     $getter = $property->getGetter();
00349                     $setter = $property->getSetter();
00350                     
00351                     if (($inner = $object->$getter()) instanceof DAOConnected) {
00352                         if ($proto->depth)
00353                             $proto->storage[$proto->depth][$setter][] = $inner;
00354                         else
00355                             $object->$setter(
00356                                 $inner->dao()->getById(
00357                                     $inner->getId()
00358                                 )
00359                             );
00360                     } elseif (
00361                         $proto->depth
00362                         // emulating 'instanceof DAOConnected'
00363                         && method_exists($property->getClassName(), 'dao')
00364                     )
00365                         $proto->storage[$proto->depth][$setter][] = null;
00366                 }
00367             }
00368             
00369             return $object;
00370         }
00371         
00372         private static function assemblyObject(
00373             Prototyped $object, $array, $prefix = null
00374         )
00375         {
00376             if ($object instanceof DAOConnected)
00377                 $dao = $object->dao();
00378             else
00379                 $dao = null;
00380             
00381             $proto = $object->proto();
00382             
00383             foreach ($proto->getPropertyList() as $property) {
00384                 $setter = $property->getSetter();
00385                 
00386                 if ($property instanceof InnerMetaProperty) {
00387                     $object->$setter(
00388                         $property->toValue($dao, $array, $prefix)
00389                     );
00390                 } elseif ($property->isBuildable($array, $prefix)) {
00391                     if ($property->getRelationId() == MetaRelation::ONE_TO_ONE) {
00392                         if (
00393                             $property->getFetchStrategyId()
00394                             == FetchStrategy::LAZY
00395                         ) {
00396                             $columnName = $prefix.$property->getColumnName();
00397                             
00398                             $object->
00399                                 {$setter.'Id'}($array[$columnName]);
00400                             
00401                             continue;
00402                         }
00403                     }
00404                     
00405                     $object->$setter($property->toValue($dao, $array, $prefix));
00406                 }
00407             }
00408             
00409             return $object;
00410         }
00411         
00412         private function forwardPrimitive(
00413             $path,
00414             Form $form = null,
00415             BasePrimitive $prm,
00416             /* Prototyped */ $object,
00417             $ignoreNull = true
00418         )
00419         {
00420             list($propertyName, $path) = explode(':', $path, 2);
00421             
00422             $property = $this->getPropertyByName($propertyName);
00423             
00424             Assert::isTrue($property instanceof InnerMetaProperty);
00425             
00426             $getter = $property->getGetter();
00427             
00428             if ($form)
00429                 return $property->getProto()->importPrimitive(
00430                     $path, $form, $prm, $object->$getter(), $ignoreNull
00431                 );
00432             else
00433                 return $property->getProto()->exportPrimitive(
00434                     $path, $prm, $object->$getter(), $ignoreNull
00435                 );
00436         }
00437         
00438         private function safePropertyGet($name)
00439         {
00440             $list = $this->getPropertyList();
00441             
00442             if (isset($list[$name]))
00443                 return $list[$name];
00444             
00445             return null;
00446         }
00447     }
00448 ?>