ProtoDAO.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2007-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 ProtoDAO extends GenericDAO
00016     {
00017         public function getJoinPrefix($field, $prefix = null)
00018         {
00019             return $this->getJoinName($field, $prefix).'__';
00020         }
00021         
00022         public function getJoinName($field, $prefix = null)
00023         {
00024             return dechex(crc32($prefix.$this->getTable())).'_'.$field;
00025         }
00026         
00027         public function fetchCollections(
00028             array $collections, array $list
00029         )
00030         {
00031             Assert::isNotEmptyArray($list);
00032             
00033             $ids = ArrayUtils::getIdsArray($list);
00034             
00035             $mainId = DBField::create(
00036                 $this->getIdName(),
00037                 $this->getTable()
00038             );
00039             
00040             foreach ($collections as $path => $info) {
00041                 $lazy = $info['lazy'];
00042                 
00043                 $query =
00044                     OSQL::select()->get($mainId)->
00045                     from($this->getTable());
00046                 
00047                 $proto = reset($list)->proto();
00048                 
00049                 $this->processPath($proto, $path, $query, $this->getTable());
00050                 
00051                 if ($criteria = $info['criteria']) {
00052                     $query = $criteria->setDao($this)->fillSelectQuery($query);
00053                 }
00054                 
00055                 $query->andWhere(
00056                     Expression::in($mainId, $ids)
00057                 );
00058                 
00059                 $propertyPath = $info['propertyPath'];
00060                 
00061                 $property   = $propertyPath->getFinalProperty();
00062                 $proto      = $propertyPath->getFinalProto();
00063                 $dao        = $propertyPath->getFinalDao();
00064                 
00065                 $selfName = $this->getObjectName();
00066                 $self = new $selfName;
00067                 $getter = 'get'.ucfirst($property->getName());
00068                 
00069                 Assert::isTrue(
00070                     $property->getRelationId() == MetaRelation::ONE_TO_MANY
00071                     || $property->getRelationId() == MetaRelation::MANY_TO_MANY
00072                 );
00073                 
00074                 $table = $dao->getJoinName($property->getColumnName());
00075                 
00076                 $id = $this->getIdName();
00077                 $collection = array();
00078                 
00079                 if ($lazy) {
00080                     if ($property->getRelationId() == MetaRelation::MANY_TO_MANY) {
00081                         $childId = $self->$getter()->getChildIdField();
00082                     } else {
00083                         $childId = $dao->getIdName();
00084                     }
00085                     
00086                     $alias = 'cid'; // childId, collectionId, whatever
00087                     
00088                     $field = DBField::create(
00089                         $childId,
00090                         $self->$getter()->getHelperTable()
00091                     );
00092                     
00093                     $query->get($field, $alias);
00094                     
00095                     if (!$property->isRequired())
00096                         $query->andWhere(Expression::notNull($field));
00097                     
00098                     try {
00099                         $rows = $dao->getCustomList($query);
00100                         
00101                         foreach ($rows as $row)
00102                             if (!empty($row[$alias]))
00103                                 $collection[$row[$id]][] = $row[$alias];
00104                         
00105                     } catch (ObjectNotFoundException $e) {/*_*/}
00106                 } else {
00107                     $prefix = $table.'_';
00108                     
00109                     foreach ($dao->getFields() as $field) {
00110                         $query->get(
00111                             DBField::create($field, $table),
00112                             $prefix.$field
00113                         );
00114                     }
00115                     
00116                     if (!$property->isRequired()) {
00117                         $query->andWhere(
00118                             Expression::notNull(
00119                                 DBField::create($dao->getIdName(), $table)
00120                             )
00121                         );
00122                     }
00123                     
00124                     try {
00125                         // otherwise we don't know which object
00126                         // belongs to which collection
00127                         $rows = $dao->getCustomList($query);
00128                         
00129                         foreach ($rows as $row) {
00130                             $collection[$row[$id]][] =
00131                                 $dao->makeObject($row, $prefix);
00132                         }
00133                     } catch (ObjectNotFoundException $e) {/*_*/}
00134                 }
00135                 
00136                 $suffix = ucfirst($property->getName());
00137                 $fillMethod = 'fill'.$suffix;
00138                 $getMethod = 'get'.$suffix;
00139                 
00140                 Assert::isTrue(
00141                     method_exists(reset($list), $fillMethod),
00142                     'can not find filler'
00143                 );
00144                 
00145                 Assert::isTrue(
00146                     method_exists(reset($list), $getMethod),
00147                     'can not find getter'
00148                 );
00149                 
00150                 foreach ($list as $object) {
00151                     if (!empty($collection[$object->getId()]))
00152                         $object->$fillMethod($collection[$object->getId()], $lazy);
00153                     else
00154                         $object->$getMethod()->mergeList(array());
00155                 }
00156             }
00157             
00158             return $list;
00159         }
00160         
00161         protected function setQueryFields(InsertOrUpdateQuery $query, $object)
00162         {
00163             $this->checkObjectType($object);
00164             
00165             return $this->getProtoClass()->fillQuery($query, $object);
00166         }
00167         
00168         private function processPath(
00169             AbstractProtoClass $proto,
00170             $probablyPath,
00171             JoinCapableQuery $query,
00172             $table,
00173             $parentRequired = true,
00174             $prefix = null
00175         )
00176         {
00177             $path = explode('.', $probablyPath);
00178             
00179             try {
00180                 $property = $proto->getPropertyByName($path[0]);
00181             } catch (MissingElementException $e) {
00182                 // oh, it's a value, not a property
00183                 return new DBValue($probablyPath);
00184             }
00185             
00186             unset($path[0]);
00187             
00188             Assert::isTrue(
00189                 $property->getRelationId() != null
00190                 && !$property->isGenericType()
00191             );
00192             
00193             Assert::classExists($property->getClassName());
00194             
00195             // checking whether we're playing with value object
00196             if (!method_exists($property->getClassName(), 'dao')) {
00197                 if (
00198                     method_exists($property->getClassName(), 'proto')
00199                     && count($path) > 1
00200                 ) {
00201                     return
00202                         $this->processPath(
00203                             $property->getProto(),
00204                             implode('.', $path),
00205                             $query,
00206                             $table
00207                         );
00208                 } else {
00209                     return
00210                         $this->guessAtom(
00211                             implode('.', $path),
00212                             $query,
00213                             $table,
00214                             $prefix
00215                         );
00216                 }
00217             } else {
00218                 $propertyDao = call_user_func(
00219                     array($property->getClassName(), 'dao')
00220                 );
00221             
00222                 Assert::isNotNull(
00223                     $propertyDao,
00224                     'can not find target dao for "'.$property->getName()
00225                     .'" property at "'.get_class($proto).'"'
00226                 );
00227             }
00228             
00229             $alias = $propertyDao->getJoinName(
00230                 $property->getColumnName(),
00231                 $prefix
00232             );
00233             
00234             if (
00235                 $property->getRelationId() == MetaRelation::ONE_TO_MANY
00236                 || $property->getRelationId() == MetaRelation::MANY_TO_MANY
00237             ) {
00238                 $remoteName = $property->getClassName();
00239                 $selfName = $this->getObjectName();
00240                 $self = new $selfName;
00241                 $getter = $property->getGetter();
00242                 $dao = call_user_func(array($remoteName, 'dao'));
00243                 
00244                 if ($property->getRelationId() == MetaRelation::MANY_TO_MANY) {
00245                     $helperTable = $self->$getter()->getHelperTable();
00246                     $helperAlias = $helperTable;
00247                     
00248                     if (!$query->hasJoinedTable($helperAlias)) {
00249                         $logic =
00250                             Expression::eq(
00251                                 DBField::create(
00252                                     $this->getIdName(),
00253                                     $table
00254                                 ),
00255                                 
00256                                 DBField::create(
00257                                     $self->$getter()->getParentIdField(),
00258                                     $helperAlias
00259                                 )
00260                             );
00261                         
00262                         if ($property->isRequired())
00263                             $query->join($helperTable, $logic, $helperAlias);
00264                         else
00265                             $query->leftJoin($helperTable, $logic, $helperAlias);
00266                     }
00267                     
00268                     $logic =
00269                         Expression::eq(
00270                             DBField::create(
00271                                 $propertyDao->getIdName(),
00272                                 $alias
00273                             ),
00274                             
00275                             DBField::create(
00276                                 $self->$getter()->getChildIdField(),
00277                                 $helperAlias
00278                             )
00279                         );
00280                 } else {
00281                     $logic =
00282                         Expression::eq(
00283                             DBField::create(
00284                                 $self->$getter()->getParentIdField(),
00285                                 $alias
00286                             ),
00287                             
00288                             DBField::create(
00289                                 $this->getIdName(),
00290                                 $table
00291                             )
00292                         );
00293                 }
00294                 
00295                 if (!$query->hasJoinedTable($alias)) {
00296                     if ($property->isRequired() && $parentRequired)
00297                         $query->join($dao->getTable(), $logic, $alias);
00298                     else
00299                         $query->leftJoin($dao->getTable(), $logic, $alias);
00300                 }
00301             } else { // OneToOne, lazy OneToOne
00302                 
00303                 // prevents useless joins
00304                 if (
00305                     isset($path[1])
00306                     && (count($path) == 1)
00307                     && ($path[1] == $propertyDao->getIdName())
00308                 )
00309                     return
00310                         new DBField(
00311                             $property->getColumnName(),
00312                             $table
00313                         );
00314 
00315                 if (!$query->hasJoinedTable($alias)) {
00316                     $logic =
00317                         Expression::eq(
00318                             DBField::create(
00319                                 $property->getColumnName(),
00320                                 $table
00321                             ),
00322                             
00323                             DBField::create(
00324                                 $propertyDao->getIdName(),
00325                                 $alias
00326                             )
00327                         );
00328                     
00329                     if ($property->isRequired() && $parentRequired)
00330                         $query->join($propertyDao->getTable(), $logic, $alias);
00331                     else
00332                         $query->leftJoin($propertyDao->getTable(), $logic, $alias);
00333                 }
00334             }
00335             
00336             if ($path) {
00337                 return $propertyDao->guessAtom(
00338                     implode('.', $path),
00339                     $query,
00340                     $alias,
00341                     $property->isRequired() && $parentRequired,
00342                     $propertyDao->getJoinPrefix($property->getColumnName(), $prefix)
00343                 );
00344             }
00345             
00346             // ok, we're done
00347         }
00348         
00349         public function guessAtom(
00350             $atom,
00351             JoinCapableQuery $query,
00352             $table = null,
00353             $parentRequired = true,
00354             $prefix = null
00355         )
00356         {
00357             if ($table === null)
00358                 $table = $this->getTable();
00359 
00360             if (is_string($atom)) {
00361                 if (strpos($atom, '.') !== false) {
00362                     return
00363                         $this->processPath(
00364                             call_user_func(
00365                                 array($this->getObjectName(), 'proto')
00366                             ),
00367                             $atom,
00368                             $query,
00369                             $table,
00370                             $parentRequired,
00371                             $prefix
00372                         );
00373                 } elseif (
00374                     array_key_exists(
00375                         $atom,
00376                         $mapping = $this->getMapping()
00377                     )
00378                 ) {
00379                     return new DBField($mapping[$atom], $table);
00380                 } elseif (
00381                     ($query instanceof SelectQuery)
00382                     && $query->hasAliasInside($atom)
00383                 ) {
00384                     return new DBField($atom);
00385                 }
00386             } elseif ($atom instanceof MappableObject)
00387                 return $atom->toMapped($this, $query);
00388             elseif (
00389                 ($atom instanceof DBValue)
00390                 || ($atom instanceof DBField)
00391             ) {
00392                 return $atom;
00393             }
00394             
00395             return new DBValue($atom);
00396         }
00397     }
00398 ?>