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