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