# Criteria.class.php

Go to the documentation of this file.

    00001 <?php
    00002 /****************************************************************************
    00003  *   Copyright (C) 2006-2008 by Konstantin V. Arkhipov, Anton E. Lebedevich *
    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     final class Criteria extends QueryIdentification
    00018     {
    00019         private $dao        = null;
    00020         private $daoClass   = null;
    00021         private $logic      = null;
    00022         private $order      = null;
    00023         private $strategy   = null;
    00024         private $projection = null;
    00025
    00026         private $distinct   = false;
    00027
    00028         private $limit  = null;
    00029         private $offset = null;
    00030
    00031         private $collections = array();
    00032
    00033         // dao-like behaviour: will throw ObjectNotFoundException when 'false'
    00034         private $silent = true;
    00035
    00039         public static function create(/* ProtoDAO */ $dao = null)
    00040         {
    00041             return new self($dao);
    00042         }
    00043
    00044         public function __construct(/* ProtoDAO */ $dao = null)
    00045         {
    00046             if ($dao)
    00047                 Assert::isTrue($dao instanceof ProtoDAO);
    00048
    00049             $this->dao = $dao;
    00050             $this->logic = Expression::andBlock();
    00051             $this->order = new OrderChain();
    00052             $this->strategy = FetchStrategy::join();
    00053             $this->projection = Projection::chain();
    00054         }
    00055
    00056         public function __clone()
    00057         {
    00058             $this->logic = clone $this->logic;
    00059             $this->order = clone $this->order;
    00060             $this->strategy = clone $this->strategy;
    00061             $this->projection = clone $this->projection;
    00062         }
    00063
    00064         public function __sleep()
    00065         {
    00066             $this->daoClass = get_class($this->dao);
    00067
    00068             $vars = get_object_vars($this);
    00069             unset($vars['dao']);
    00070             return array_keys($vars);
    00071         }
    00072
    00073         public function __wakeup()
    00074         {
    00075             $this->dao = Singleton::getInstance($this->daoClass);
    00076         }
    00077
    00081         public function getDao()
    00082         {
    00083             return $this->dao;
    00084         }
    00085
    00089         public function setDao(ProtoDAO $dao)
    00090         {
    00091             $this->dao = $dao;
    00092
    00093             return $this;
    00094         }
    00095
    00099         public function getLogic()
    00100         {
    00101             return $this->logic;
    00102         }
    00103
    00107         public function add(LogicalObject $logic)
    00108         {
    00109             $this->logic->expAnd($logic);
    00110
    00111             return $this;
    00112         }
    00113
    00117         public function getOrder()
    00118         {
    00119             return $this->order;
    00120         }
    00121
    00125         public function addOrder(/* MapableObject */ $order)
    00126         {
    00127             if (!$order instanceof MappableObject)
    00128                 $order = new OrderBy($order);
    00129
    00130             $this->order->add($order);
    00131
    00132             return $this;
    00133         }
    00134
    00138         public function prependOrder(/* MapableObject */ $order)
    00139         {
    00140             if (!$order instanceof MappableObject)
    00141                 $order = new OrderBy($order);
    00142
    00143             $this->order->prepend($order);
    00144
    00145             return $this;
    00146         }
    00147
    00151         public function dropOrder()
    00152         {
    00153             $this->order = new OrderChain();
    00154
    00155             return $this;
    00156         }
    00157
    00158         public function getLimit()
    00159         {
    00160             return $this->limit;
    00161         }
    00162
    00166         public function setLimit($limit)
    00167         {
    00168             $this->limit = $limit;
    00169
    00170             return $this;
    00171         }
    00172
    00173         public function getOffset()
    00174         {
    00175             return $this->offset;
    00176         }
    00177
    00181         public function setOffset($offset)
    00182         {
    00183             $this->offset = $offset;
    00184
    00185             return $this;
    00186         }
    00187
    00191         public function getFetchStrategy()
    00192         {
    00193             return $this->strategy;
    00194         }
    00195
    00199         public function setFetchStrategy(FetchStrategy $strategy)
    00200         {
    00201             $this->strategy = $strategy;
    00202
    00203             return $this;
    00204         }
    00205
    00209         public function setProjection(ObjectProjection $chain)
    00210         {
    00211             if ($chain instanceof ProjectionChain)
    00212                 $this->projection = $chain;
    00213             else
    00214                 $this->projection = Projection::chain()->add($chain);
    00215
    00216             return $this;
    00217         }
    00218
    00222         public function addProjection(ObjectProjection $projection)
    00223         {
    00224             if (
    00225                 !$projection instanceof ProjectionChain
    00226                 || !$projection->isEmpty()
    00227             )
    00228                 $this->projection->add($projection);
    00229
    00230             return $this;
    00231         }
    00232
    00236         public function getProjection()
    00237         {
    00238             return $this->projection;
    00239         }
    00240
    00244         public function dropProjection()
    00245         {
    00246             $this->projection = Projection::chain();
    00247
    00248             return $this;
    00249         }
    00250
    00254         public function setDistinct($orly = true)
    00255         {
    00256             $this->distinct = ($orly === true);
    00257
    00258             return $this;
    00259         }
    00260
    00261         public function isDistinct()
    00262         {
    00263             return $this->distinct;
    00264         }
    00265
    00266         public function isSilent()
    00267         {
    00268             return $this->silent;
    00269         }
    00270
    00274         public function setSilent($silent)
    00275         {
    00276             Assert::isBoolean($silent);
    00277
    00278             $this->silent = $silent;
    00279
    00280             return $this;
    00281         }
    00282
    00286         public function fetchCollection(
    00287             $path, // to collection
    00288             $lazy = false, // fetching mode
    00289             /* Criteria */ $criteria = null
    00290         )
    00291         {
    00292             Assert::isBoolean($lazy);
    00293             Assert::isTrue(
    00294                 ($criteria === null)
    00295                 || ($criteria instanceof Criteria)
    00296             );
    00297
    00298             $this->collections[$path]['lazy'] = $lazy;
    00299             $this->collections[$path]['criteria'] = $criteria;
    00300             $this->collections[$path]['propertyPath']
    00301                 = new PropertyPath($this->dao->getObjectName(), $path);
    00302
    00303             return $this;
    00304         }
    00305
    00306         public function get()
    00307         {
    00308             try {
    00309                 $list = array($this->dao->getByQuery($this->toSelectQuery()));
    00310             } catch (ObjectNotFoundException $e) {
    00311                 if (!$this->isSilent())
    00312                     throw $e;
    00313
    00314                 return null;
    00315             }
    00316
    00317             if (!$this->collections || !$list)
    00318                 return reset($list);
    00319
    00320             $list = $this->dao->fetchCollections($this->collections, $list);
    00321
    00322             return reset($list);
    00323         }
    00324
    00325         public function getList()
    00326         {
    00327             try {
    00328                 $list = $this->dao->getListByQuery($this->toSelectQuery());
    00329             } catch (ObjectNotFoundException $e) {
    00330                 if (!$this->isSilent())
    00331                     throw $e;
    00332
    00333                 return array();
    00334             }
    00335
    00336             if (!$this->collections || !$list)
    00337                 return $list;
    00338
    00339             return $this->dao->fetchCollections($this->collections, $list);
    00340         }
    00341
    00345         public function getResult()
    00346         {
    00347             $result = $this->dao->getQueryResult($this->toSelectQuery());
    00348
    00349             if (!$this->collections || !$result->getCount())
    00350                 return $result;
    00351
    00352             return $result->setList(
    00353                 $this->dao->fetchCollections(
    00354                     $this->collections,
    00355                     $result->getList()
    00356                 )
    00357             );
    00358         }
    00359
    00360         public function getCustom($index = null)
    00361         {
    00362             try {
    00363                 $result = $this->dao->getCustom($this->toSelectQuery());
    00364
    00365                 if ($index) {
    00366                     if (array_key_exists($index, $result))
    00367                         return $result[$index];
    00368
    00369                     throw new MissingElementException(
    00370                         'No such key: "'.$index.'" in result set.'
    00371                     );
    00372                 }
    00373
    00374                 return $result;
    00375             } catch (ObjectNotFoundException $e) {
    00376                 if (!$this->isSilent())
    00377                     throw $e;
    00378
    00379                 return null;
    00380             }
    00381         }
    00382
    00383         public function getCustomList()
    00384         {
    00385             try {
    00386                 return $this->dao->getCustomList($this->toSelectQuery());
    00387             } catch (ObjectNotFoundException $e) {
    00388                 if (!$this->isSilent())
    00389                     throw $e;
    00390
    00391                 return array();
    00392             }
    00393         }
    00394
    00395         public function getPropertyList()
    00396         {
    00397             try {
    00398                 return $this->dao->getCustomRowList($this->toSelectQuery());
    00399             } catch (ObjectNotFoundException $e) {
    00400                 if (!$this->isSilent())
    00401                     throw $e;
    00402
    00403                 return array();
    00404             }
    00405         }
    00406
    00407         public function toString()
    00408         {
    00409             return $this->toDialectString(
    00410                 $this->dao
    00411                     ? DBPool::getByDao($this->dao)->getDialect()
    00412                     : ImaginaryDialect::me()
    00413             );
    00414         }
    00415
    00416         public function toDialectString(Dialect $dialect)
    00417         {
    00418             return $this->toSelectQuery()->toDialectString($dialect);
    00419         }
    00420
    00424         public function toSelectQuery()
    00425         {
    00426             Assert::isNotNull($this->dao, 'DAO not set');
    00427
    00428             if (!$this->projection->isEmpty()) {
    00429                 $query =
    00430                     $this->getProjection()->process(
    00431                         $this,
    00432                         $this->dao->makeSelectHead()->
    00433                             dropFields()
    00434                     );
    00435             } else
    00436                 $query = $this->dao->makeSelectHead();
    00437
    00438             if ($this->distinct)
    00439                 $query->distinct();
    00440
    00441             return $this->fillSelectQuery($query);
    00442         }
    00443
    00447         public function fillSelectQuery(SelectQuery $query)
    00448         {
    00449             $query->
    00450                 limit($this->limit, $this->offset);
    00451
    00452             if ($this->distinct)
    00453                 $query->distinct();
    00454
    00455             if ($this->logic->getSize()) {
    00456                 $query->
    00457                     andWhere(
    00458                         $this->logic->toMapped($this->dao, $query)
    00459                     );
    00460             }
    00461
    00462             if ($this->order) {
    00463                 $query->setOrderChain($this->order->toMapped($this->dao, $query));
    00464             }
    00465
    00466             if (
    00467                 $this->projection->isEmpty()
    00468                 && (
    00469                     $this->strategy->getId() <> FetchStrategy::CASCADE
    00470                 )
    00471             ) {
    00472                 $this->joinProperties($query, $this->dao, $this->dao->getTable(), true);
    00473             }
    00474
    00475             return $query;
    00476         }
    00477
    00481         public function dropProjectionByType(/* array */ $dropTypes)
    00482         {
    00483             Assert::isInstance($this->projection, 'ProjectionChain');
    00484
    00485             $this->projection->dropByType($dropTypes);
    00486
    00487             return $this;
    00488         }
    00489
    00490         private function joinProperties(
    00491             SelectQuery $query,
    00492             ProtoDAO $parentDao,
    00493             $parentTable,
    00494             $parentRequired,
    00495             $prefix = null
    00496         )
    00497         {
    00498             $proto = call_user_func(array($parentDao->getObjectName(), 'proto'));
    00499
    00500             foreach ($proto->getPropertyList() as $property) {
    00501                 if (
    00502                     ($property instanceof LightMetaProperty)
    00503                     && $property->getRelationId() == MetaRelation::ONE_TO_ONE
    00504                     && !$property->isGenericType()
    00505                     && (
    00506                         (
    00507                             !$property->getFetchStrategyId()
    00508                             && (
    00509                                 $this->getFetchStrategy()->getId()
    00510                                 == FetchStrategy::JOIN
    00511                             )
    00512                         ) || (
    00513                             $property->getFetchStrategyId()
    00514                             == FetchStrategy::JOIN
    00515                         )
    00516                     )
    00517                 ) {
    00518                     if (
    00519                         is_subclass_of(
    00520                             $property->getClassName(),
    00521                             'Enumeration'
    00522                         )
    00523                     ) {
    00524                         // field already added by makeSelectHead
    00525                         continue;
    00526                     } elseif ($property->isInner()) {
    00527                         $proto = call_user_func(
    00528                             array($property->getClassName(), 'proto')
    00529                         );
    00530
    00531                         foreach ($proto->getPropertyList() as $innerProperty)
    00532                             $query->get(
    00533                                 new DBField(
    00534                                     $innerProperty->getColumnName(),
    00535                                     $parentTable
    00536                                 )
    00537                             );
    00538
    00539                         continue;
    00540                     }
    00541
    00542                     $propertyDao = call_user_func(
    00543                         array($property->getClassName(), 'dao')
    00544                     );
    00545
    00546                     // add's custom dao's injection possibility
    00547                     if (!$propertyDao instanceof ProtoDAO)
    00548                         continue;
    00549
    00550                     $tableAlias = $propertyDao->getJoinName(
    00551                         $property->getColumnName(),
    00552                         $prefix
    00553                     );
    00554
    00555                     $fields = $propertyDao->getFields();
    00556
    00557                     if (!$query->hasJoinedTable($tableAlias)) {
    00558                         $logic =
    00559                             Expression::eq(
    00560                                 DBField::create(
    00561                                     $property->getColumnName(),
    00562                                     $parentTable
    00563                                 ),
    00564
    00565                                 DBField::create(
    00566                                     $propertyDao->getIdName(),
    00567                                     $tableAlias
    00568                                 )
    00569                             );
    00570
    00571                         if ($property->isRequired() && $parentRequired)
    00572                             $query->join($propertyDao->getTable(), $logic, $tableAlias);
    00573                         else
    00574                             $query->leftJoin($propertyDao->getTable(), $logic, $tableAlias);
    00575                     }
    00576
    00577                     foreach ($fields as $field) {
    00578                         $query->get(
    00579                             new DBField($field, $tableAlias),
    00580                             $propertyDao->getJoinPrefix($property->getColumnName(), $prefix)
    00581                                 .$field
    00582                         );
    00583                     }
    00584
    00585                     $this->joinProperties(
    00586                         $query,
    00587                         $propertyDao,
    00588                         $tableAlias,
    00589                         $property->isRequired() && $parentRequired,
    00590                         $propertyDao->getJoinPrefix($property->getColumnName(), $prefix)
    00591                     );
    00592                 }
    00593             }
    00594         }
    00595
    00599         private function getProto()
    00600         {
    00601             return
    00602                 call_user_func(
    00603                     array($this->dao->getObjectName(), 'proto')
    00604                 );
    00605         }
    00606     }
    00607 ?>