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