Source for file Hydrate.php

Documentation is available at Hydrate.php

  1. <?php
  2. /*
  3.  *  $Id: Hydrate.php 2234 2007-08-14 18:28:35Z romanb $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information, see
  19.  * <http://www.phpdoctrine.com>.
  20.  */
  21.  
  22. /**
  23.  * Doctrine_Hydrate is a base class for Doctrine_RawSql and Doctrine_Query.
  24.  * Its purpose is to populate object graphs.
  25.  *
  26.  *
  27.  * @package     Doctrine
  28.  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  29.  * @category    Object Relational Mapping
  30.  * @link        www.phpdoctrine.com
  31.  * @since       1.0
  32.  * @version     $Revision: 2234 $
  33.  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  34.  */
  35. class Doctrine_Hydrate extends Doctrine_Object implements Serializable
  36. {
  37.     /**
  38.      * QUERY TYPE CONSTANTS
  39.      */
  40.  
  41.     /**
  42.      * constant for SELECT queries
  43.      */
  44.     const SELECT = 0;
  45.     /**
  46.      * constant for DELETE queries
  47.      */
  48.     const DELETE = 1;
  49.     /**
  50.      * constant for UPDATE queries
  51.      */
  52.     const UPDATE = 2;
  53.     /**
  54.      * constant for INSERT queries
  55.      */
  56.     const INSERT = 3;
  57.     /**
  58.      * constant for CREATE queries
  59.      */
  60.     const CREATE = 4;
  61.     /**
  62.      * Constant for the array hydration mode.
  63.      */
  64.     const HYDRATE_ARRAY = 3;
  65.     /**
  66.      * Constant for the record (object) hydration mode.
  67.      */
  68.     const HYDRATE_RECORD = 2;
  69.  
  70.     /**
  71.      * @var array $params                       query input parameters
  72.      */
  73.     protected $_params      = array();
  74.     /**
  75.      * @var Doctrine_Connection $conn           Doctrine_Connection object
  76.      */
  77.     protected $_conn;
  78.     /**
  79.      * @var Doctrine_View $_view                Doctrine_View object, when set this object will use the
  80.      *                                           the query given by the view object for object population
  81.      */
  82.     protected $_view;
  83.     /**
  84.      * @var array $_aliasMap                    two dimensional array containing the map for query aliases
  85.      *       Main keys are component aliases
  86.      *
  87.      *           table               table object associated with given alias
  88.      *
  89.      *           relation            the relation object owned by the parent
  90.      *
  91.      *           parent              the alias of the parent
  92.      *
  93.      *           agg                 the aggregates of this component
  94.      */
  95.     protected $_aliasMap         = array();
  96.     /**
  97.      *
  98.      */
  99.     protected $pendingAggregates = array();
  100.     /**
  101.      * @var array $aggregateMap             an array containing all aggregate aliases, keys as dql aliases
  102.      *                                       and values as sql aliases
  103.      */
  104.     protected $aggregateMap      = array();
  105.     /**
  106.      * @var array $_options                 an array of options
  107.      */
  108.     protected $_options    = array(
  109.                             'fetchMode'      => Doctrine::FETCH_RECORD,
  110.                             'parserCache'    => false,
  111.                             'resultSetCache' => false,
  112.                             );
  113.     /**
  114.      * @var string $_sql            cached SQL query
  115.      */
  116.     protected $_sql;
  117.     /**
  118.      * @var array $parts            SQL query string parts
  119.      */
  120.     protected $parts = array(
  121.         'select'    => array(),
  122.         'distinct'  => false,
  123.         'forUpdate' => false,
  124.         'from'      => array(),
  125.         'set'       => array(),
  126.         'join'      => array(),
  127.         'where'     => array(),
  128.         'groupby'   => array(),
  129.         'having'    => array(),
  130.         'orderby'   => array(),
  131.         'limit'     => false,
  132.         'offset'    => false,
  133.         );
  134.     /**
  135.      * @var integer $type                   the query type
  136.      *
  137.      * @see Doctrine_Query::* constants
  138.      */
  139.     protected $type            = self::SELECT;
  140.     /**
  141.      * @var array 
  142.      */
  143.     protected $_cache;
  144.     /**
  145.      * The current hydration mode.
  146.      */
  147.     protected $_hydrationMode = self::HYDRATE_RECORD;
  148.     /**
  149.      * @var boolean $_expireCache           a boolean value that indicates whether or not to force cache expiration
  150.      */
  151.     protected $_expireCache     = false;
  152.  
  153.     protected $_timeToLive;
  154.  
  155.     protected $_tableAliases    = array();
  156.     /**
  157.      * @var array $_tableAliasSeeds         A simple array keys representing table aliases and values
  158.      *                                       as table alias seeds. The seeds are used for generating short table
  159.      *                                       aliases.
  160.      */
  161.     protected $_tableAliasSeeds = array();
  162.     /**
  163.      * constructor
  164.      *
  165.      * @param Doctrine_Connection|null$connection 
  166.      */
  167.     public function __construct($connection null)
  168.     {
  169.         if ($connection instanceof Doctrine_Connection)) {
  170.             $connection Doctrine_Manager::getInstance()->getCurrentConnection();
  171.         }
  172.         $this->_conn = $connection;
  173.     }
  174.     /**
  175.      * getRootAlias
  176.      * returns the alias of the the root component
  177.      *
  178.      * @return array 
  179.      */
  180.     public function getRootAlias()
  181.     {
  182.         if (!$this->_aliasMap{
  183.           $this->getSql();
  184.         }
  185.         
  186.         reset($this->_aliasMap);
  187.  
  188.         return key($this->_aliasMap);
  189.     }
  190.     /**
  191.      * getRootDeclaration
  192.      * returns the root declaration
  193.      *
  194.      * @return array 
  195.      */
  196.     public function getRootDeclaration()
  197.     {
  198.         $map reset($this->_aliasMap);
  199.  
  200.         return $map;
  201.     }
  202.     /**
  203.      * getRoot
  204.      * returns the root component for this object
  205.      *
  206.      * @return Doctrine_Table       root components table
  207.      */
  208.     public function getRoot()
  209.     {
  210.         $map reset($this->_aliasMap);
  211.  
  212.         if isset($map['table'])) {
  213.             throw new Doctrine_Hydrate_Exception('Root component not initialized.');
  214.         }
  215.  
  216.         return $map['table'];
  217.     }
  218.     /**
  219.      * getSql
  220.      * return the sql associated with this object
  221.      *
  222.      * @return string   sql query string
  223.      */
  224.     public function getSql()
  225.     {
  226.         return $this->getQuery();
  227.     }
  228.     /**
  229.      * useCache
  230.      *
  231.      * @param Doctrine_Cache_Interface|bool$driver      cache driver
  232.      * @param integer $timeToLive                        how long the cache entry is valid
  233.      * @return Doctrine_Hydrate         this object
  234.      */
  235.     public function useCache($driver true$timeToLive null)
  236.     {
  237.         if ($driver !== null{
  238.             if ($driver !== true{
  239.                 if ($driver instanceof Doctrine_Cache_Interface)) {
  240.                     $msg 'First argument should be instance of Doctrine_Cache_Interface or null.';
  241.  
  242.                     throw new Doctrine_Hydrate_Exception($msg);
  243.                 }
  244.             }
  245.         }
  246.         $this->_cache = $driver;
  247.  
  248.         return $this->setCacheLifeSpan($timeToLive);
  249.     }
  250.     /**
  251.      * expireCache
  252.      *
  253.      * @param boolean $expire       whether or not to force cache expiration
  254.      * @return Doctrine_Hydrate     this object
  255.      */
  256.     public function expireCache($expire true)
  257.     {
  258.         $this->_expireCache = true;
  259.  
  260.         return $this;
  261.     }
  262.     /**
  263.      * setCacheLifeSpan
  264.      *
  265.      * @param integer $timeToLive   how long the cache entry is valid
  266.      * @return Doctrine_Hydrate     this object
  267.      */
  268.     public function setCacheLifeSpan($timeToLive)
  269.     {
  270.         if ($timeToLive !== null{
  271.             $timeToLive = (int) $timeToLive;
  272.         }
  273.         $this->_timeToLive = $timeToLive;
  274.  
  275.         return $this;
  276.     }
  277.     /**
  278.      * getCacheDriver
  279.      * returns the cache driver associated with this object
  280.      *
  281.      * @return Doctrine_Cache_Interface|boolean|null   cache driver
  282.      */
  283.     public function getCacheDriver()
  284.     {
  285.         if ($this->_cache instanceof Doctrine_Cache_Interface{
  286.             return $this->_cache;
  287.         else {
  288.             return $this->_conn->getCacheDriver();
  289.         }
  290.     }
  291.     /**
  292.      * Sets the fetchmode.
  293.      *
  294.      * @param integer $fetchmode  One of the Doctrine_Hydrate::HYDRATE_* constants.
  295.      */
  296.     public function setHydrationMode($hydrationMode)
  297.     {
  298.         $this->_hydrationMode = $hydrationMode;
  299.         return $this;
  300.     }
  301.     /**
  302.      * serialize
  303.      * this method is automatically called when this Doctrine_Hydrate is serialized
  304.      *
  305.      * @return array    an array of serialized properties
  306.      */
  307.     public function serialize()
  308.     {
  309.         $vars get_object_vars($this);
  310.  
  311.     }
  312.     /**
  313.      * unseralize
  314.      * this method is automatically called everytime a Doctrine_Hydrate object is unserialized
  315.      *
  316.      * @param string $serialized                Doctrine_Record as serialized string
  317.      * @return void 
  318.      */
  319.     public function unserialize($serialized)
  320.     {
  321.  
  322.     }
  323.     /**
  324.      * generateNewTableAlias
  325.      * generates a new alias from given table alias
  326.      *
  327.      * @param string $tableAlias    table alias from which to generate the new alias from
  328.      * @return string               the created table alias
  329.      */
  330.     public function generateNewTableAlias($tableAlias)
  331.     {
  332.         if (isset($this->_tableAliases[$tableAlias])) {
  333.             // generate a new alias
  334.             $name substr($tableAlias01);
  335.             $i    ((int) substr($tableAlias1));
  336.  
  337.             if ($i == 0{
  338.                 $i 1;
  339.             }
  340.  
  341.             $newIndex  ($this->_tableAliasSeeds[$name$i);
  342.  
  343.             return $name $newIndex;
  344.         }
  345.  
  346.         return $tableAlias;
  347.     }
  348.     /**
  349.      * hasTableAlias
  350.      * whether or not this object has given tableAlias
  351.      *
  352.      * @param string $tableAlias    the table alias to be checked
  353.      * @return boolean              true if this object has given alias, otherwise false
  354.      */
  355.     public function hasTableAlias($tableAlias)
  356.     {
  357.         return (isset($this->_tableAliases[$tableAlias]));
  358.     }
  359.     /**
  360.      * getComponentAlias
  361.      * get component alias associated with given table alias
  362.      *
  363.      * @param string $tableAlias    the table alias that identifies the component alias
  364.      * @return string               component alias
  365.      */
  366.     public function getComponentAlias($tableAlias)
  367.     {
  368.         if isset($this->_tableAliases[$tableAlias])) {
  369.             throw new Doctrine_Hydrate_Exception('Unknown table alias ' $tableAlias);
  370.         }
  371.         return $this->_tableAliases[$tableAlias];
  372.     }
  373.     /**
  374.      * getTableAliasSeed
  375.      * returns the alias seed for given table alias
  376.      *
  377.      * @param string $tableAlias    table alias that identifies the alias seed
  378.      * @return integer              table alias seed
  379.      */
  380.     public function getTableAliasSeed($tableAlias)
  381.     {
  382.         if isset($this->_tableAliasSeeds[$tableAlias])) {
  383.             return 0;
  384.         }
  385.         return $this->_tableAliasSeeds[$tableAlias];
  386.     }
  387.     /**
  388.      * generateTableAlias
  389.      * generates a table alias from given table name and associates
  390.      * it with given component alias
  391.      *
  392.      * @param string $componentAlias    the component alias to be associated with generated table alias
  393.      * @param string $tableName         the table name from which to generate the table alias
  394.      * @return string                   the generated table alias
  395.      */
  396.     public function generateTableAlias($componentAlias$tableName)
  397.     {
  398.         $char   strtolower(substr($tableName01));
  399.  
  400.         $alias  $char;
  401.  
  402.         if isset($this->_tableAliasSeeds[$alias])) {
  403.             $this->_tableAliasSeeds[$alias1;
  404.         }
  405.  
  406.         while (isset($this->_tableAliases[$alias])) {
  407.             if isset($this->_tableAliasSeeds[$alias])) {
  408.                 $this->_tableAliasSeeds[$alias1;
  409.             }
  410.             $alias $char . ++$this->_tableAliasSeeds[$alias];
  411.         }
  412.  
  413.         $this->_tableAliases[$alias$componentAlias;
  414.  
  415.         return $alias;
  416.     }
  417.     /**
  418.      * getTableAliases
  419.      * returns all table aliases
  420.      *
  421.      * @return array        table aliases as an array
  422.      */
  423.     public function getTableAliases()
  424.     {
  425.         return $this->_tableAliases;
  426.     }
  427.     /** 
  428.      * addTableAlias
  429.      * adds an alias for table and associates it with given component alias
  430.      *
  431.      * @param string $componentAlias    the alias for the query component associated with given tableAlias
  432.      * @param string $tableAlias        the table alias to be added
  433.      * @return Doctrine_Hydrate 
  434.      */
  435.     public function addTableAlias($tableAlias$componentAlias)
  436.     {
  437.         $this->_tableAliases[$tableAlias$componentAlias;
  438.  
  439.         return $this;
  440.     }
  441.     /**
  442.      * getTableAlias
  443.      * some database such as Oracle need the identifier lengths to be < ~30 chars
  444.      * hence Doctrine creates as short identifier aliases as possible
  445.      *
  446.      * this method is used for the creation of short table aliases, its also
  447.      * smart enough to check if an alias already exists for given component (componentAlias)
  448.      *
  449.      * @param string $componentAlias    the alias for the query component to search table alias for
  450.      * @param string $tableName         the table name from which the table alias is being created
  451.      * @return string                   the generated / fetched short alias
  452.      */
  453.     public function getTableAlias($componentAlias$tableName null)
  454.     {
  455.         $alias array_search($componentAlias$this->_tableAliases);
  456.  
  457.         if ($alias !== false{
  458.             return $alias;
  459.         }
  460.  
  461.         if ($tableName === null{
  462.             throw new Doctrine_Hydrate_Exception("Couldn't get short alias for " $componentAlias);
  463.         }
  464.  
  465.         return $this->generateTableAlias($componentAlias$tableName);
  466.     }
  467.     /**
  468.      * addQueryPart
  469.      * adds a query part in the query part array
  470.      *
  471.      * @param string $name          the name of the query part to be added
  472.      * @param string $part          query part string
  473.      * @throws Doctrine_Hydrate_Exception   if trying to add unknown query part
  474.      * @return Doctrine_Hydrate     this object
  475.      */
  476.     public function addQueryPart($name$part)
  477.     {
  478.         if isset($this->parts[$name])) {
  479.             throw new Doctrine_Hydrate_Exception('Unknown query part ' $name);
  480.         }
  481.         if (is_array($part)) {
  482.             $this->parts[$namearray_merge($this->parts[$name]$part);
  483.         else {
  484.             $this->parts[$name][$part;
  485.         }
  486.         return $this;
  487.     }
  488.     /**
  489.      * setQueryPart
  490.      * sets a query part in the query part array
  491.      *
  492.      * @param string $name          the name of the query part to be set
  493.      * @param string $part          query part string
  494.      * @throws Doctrine_Hydrate_Exception   if trying to set unknown query part
  495.      * @return Doctrine_Hydrate     this object
  496.      */
  497.     public function getQueryPart($part)
  498.     {
  499.         if isset($this->parts[$part])) {
  500.             throw new Doctrine_Hydrate_Exception('Unknown query part ' $part);
  501.         }
  502.  
  503.         return $this->parts[$part];
  504.     }
  505.     /**
  506.      * removeQueryPart
  507.      * removes a query part from the query part array
  508.      *
  509.      * @param string $name          the name of the query part to be removed
  510.      * @throws Doctrine_Hydrate_Exception   if trying to remove unknown query part
  511.      * @return Doctrine_Hydrate     this object
  512.      */
  513.     public function removeQueryPart($name)
  514.     {
  515.         if (isset($this->parts[$name])) {
  516.             if ($name == 'limit' || $name == 'offset'{
  517.                 $this->parts[$namefalse;
  518.             else {
  519.                 $this->parts[$namearray();
  520.             }
  521.         else {
  522.             throw new Doctrine_Hydrate_Exception('Unknown query part ' $name);
  523.         }
  524.         return $this;
  525.     }
  526.     /**
  527.      * setQueryPart
  528.      * sets a query part in the query part array
  529.      *
  530.      * @param string $name          the name of the query part to be set
  531.      * @param string $part          query part string
  532.      * @throws Doctrine_Hydrate_Exception   if trying to set unknown query part
  533.      * @return Doctrine_Hydrate     this object
  534.      */
  535.     public function setQueryPart($name$part)
  536.     {
  537.         if isset($this->parts[$name])) {
  538.             throw new Doctrine_Hydrate_Exception('Unknown query part ' $name);
  539.         }
  540.  
  541.         if ($name !== 'limit' && $name !== 'offset'{
  542.             if (is_array($part)) {
  543.                 $this->parts[$name$part;
  544.             else {
  545.                 $this->parts[$namearray($part);
  546.             }
  547.         else {
  548.             $this->parts[$name$part;
  549.         }
  550.  
  551.         return $this;
  552.     }
  553.     /**
  554.      * hasAliasDeclaration
  555.      * whether or not this object has a declaration for given component alias
  556.      *
  557.      * @param string $componentAlias    the component alias the retrieve the declaration from
  558.      * @return boolean 
  559.      */
  560.     public function hasAliasDeclaration($componentAlias)
  561.     {
  562.         return isset($this->_aliasMap[$componentAlias]);
  563.     }
  564.     /**
  565.      * getAliasDeclaration
  566.      * get the declaration for given component alias
  567.      *
  568.      * @param string $componentAlias    the component alias the retrieve the declaration from
  569.      * @return array                    the alias declaration
  570.      */
  571.     public function getAliasDeclaration($componentAlias)
  572.     {
  573.         if isset($this->_aliasMap[$componentAlias])) {
  574.             throw new Doctrine_Hydrate_Exception('Unknown component alias ' $componentAlias);
  575.         }
  576.  
  577.         return $this->_aliasMap[$componentAlias];
  578.     }
  579.     /**
  580.      * copyAliases
  581.      * copy aliases from another Hydrate object
  582.      *
  583.      * this method is needed by DQL subqueries which need the aliases
  584.      * of the parent query
  585.      *
  586.      * @param Doctrine_Hydrate $query   the query object from which the
  587.      *                                   aliases are copied from
  588.      * @return Doctrine_Hydrate         this object
  589.      */
  590.     public function copyAliases(Doctrine_Hydrate $query)
  591.     {
  592.         $this->_tableAliases = $query->_tableAliases;
  593.         $this->_aliasMap     = $query->_aliasMap;
  594.         $this->_tableAliasSeeds = $query->_tableAliasSeeds;
  595.         return $this;
  596.     }
  597.     /**
  598.      * createSubquery
  599.      * creates a subquery
  600.      *
  601.      * @return Doctrine_Hydrate 
  602.      */
  603.     public function createSubquery()
  604.     {
  605.         $class get_class($this);
  606.         $obj   new $class();
  607.  
  608.         // copy the aliases to the subquery
  609.         $obj->copyAliases($this);
  610.  
  611.         // this prevents the 'id' being selected, re ticket #307
  612.         $obj->isSubquery(true);
  613.  
  614.         return $obj;
  615.     }
  616.     /**
  617.      * limitSubqueryUsed
  618.      * whether or not limit subquery was used
  619.      *
  620.      * @return boolean 
  621.      */
  622.     public function isLimitSubqueryUsed()
  623.     {
  624.         return false;
  625.     }
  626.     /**
  627.      * clear
  628.      * resets all the variables
  629.      *
  630.      * @return void 
  631.      */
  632.     protected function clear()
  633.     {
  634.         $this->parts = array(
  635.                     'select'    => array(),
  636.                     'distinct'  => false,
  637.                     'forUpdate' => false,
  638.                     'from'      => array(),
  639.                     'set'       => array(),
  640.                     'join'      => array(),
  641.                     'where'     => array(),
  642.                     'groupby'   => array(),
  643.                     'having'    => array(),
  644.                     'orderby'   => array(),
  645.                     'limit'     => false,
  646.                     'offset'    => false,
  647.                     );
  648.         $this->inheritanceApplied false;
  649.     }
  650.     /**
  651.      * getConnection
  652.      *
  653.      * @return Doctrine_Connection 
  654.      */
  655.     public function getConnection()
  656.     {
  657.         return $this->_conn;
  658.     }
  659.     /**
  660.      * setView
  661.      * sets a database view this query object uses
  662.      * this method should only be called internally by doctrine
  663.      *
  664.      * @param Doctrine_View $view       database view
  665.      * @return void 
  666.      */
  667.     public function setView(Doctrine_View $view)
  668.     {
  669.         $this->_view = $view;
  670.     }
  671.     /**
  672.      * getView
  673.      * returns the view associated with this query object (if any)
  674.      *
  675.      * @return Doctrine_View        the view associated with this query object
  676.      */
  677.     public function getView()
  678.     {
  679.         return $this->_view;
  680.     }
  681.     /**
  682.      * getParams
  683.      *
  684.      * @return array 
  685.      */
  686.     public function getParams()
  687.     {
  688.         return $this->_params;
  689.     }
  690.     /**
  691.      * setParams
  692.      *
  693.      * @param array $params 
  694.      */
  695.     public function setParams(array $params array()) {
  696.         $this->_params = $params;
  697.     }
  698.     public function convertEnums($params)
  699.     {
  700.         return $params;
  701.     }
  702.     /**
  703.      * setAliasMap
  704.      * sets the whole component alias map
  705.      *
  706.      * @param array $map            alias map
  707.      * @return Doctrine_Hydrate     this object
  708.      */
  709.     public function setAliasMap(array $map)
  710.     {
  711.         $this->_aliasMap $map;
  712.  
  713.         return $this;
  714.     }
  715.     /**
  716. /**
  717.      * getAliasMap
  718.      * returns the component alias map
  719.      *
  720.      * @return array    component alias map
  721.      */
  722.     public function getAliasMap()
  723.     {
  724.         return $this->_aliasMap;
  725.     }
  726.     /**
  727.      * getCachedForm
  728.      * returns the cached form of this query for given resultSet
  729.      *
  730.      * @param array $resultSet 
  731.      * @return string           serialized string representation of this query
  732.      */
  733.     public function getCachedForm(array $resultSet)
  734.     {
  735.         $map '';
  736.  
  737.         foreach ($this->getAliasMap(as $k => $v{
  738.             if isset($v['parent'])) {
  739.                 $map[$k][$v['table']->getComponentName();
  740.             else {
  741.                 $map[$k][$v['parent''.' $v['relation']->getAlias();
  742.             }
  743.             if (isset($v['agg'])) {
  744.                 $map[$k][$v['agg'];
  745.             }
  746.         }
  747.  
  748.         return serialize(array($resultSet$map$this->getTableAliases()));
  749.     }
  750.     public function _execute($params)
  751.     {
  752.         $params $this->_conn->convertBooleans(array_merge($this->_params$params));
  753.  
  754.         if $this->_view{
  755.             $query $this->getQuery($params);
  756.         else {
  757.             $query $this->_view->getSelectSql();
  758.         }
  759.  
  760.         $params $this->convertEnums($params);
  761.  
  762.         if ($this->isLimitSubqueryUsed(&&
  763.             $this->_conn->getAttribute(Doctrine::ATTR_DRIVER_NAME!== 'mysql'{
  764.  
  765.             $params array_merge($params$params);
  766.         }
  767.  
  768.         if ($this->type !== self::SELECT{
  769.             return $this->_conn->exec($query$params);
  770.         }
  771.  
  772.         $stmt $this->_conn->execute($query$params);
  773.         return $stmt;
  774.     }
  775.     /**
  776.      * execute
  777.      * executes the query and populates the data set
  778.      *
  779.      * @param string $params 
  780.      * @return Doctrine_Collection            the root collection
  781.      */
  782.     public function execute($params array()$hydrationMode null)
  783.     {
  784.         if ($this->_cache{
  785.             $cacheDriver $this->getCacheDriver();
  786.  
  787.             $dql  $this->getDql();
  788.             // calculate hash for dql query
  789.             $hash md5($dql var_export($paramstrue));
  790.  
  791.             $cached ($this->_expireCachenull $cacheDriver->fetch($hash);
  792.  
  793.  
  794.             if ($cached === null{
  795.                 // cache miss
  796.                 $stmt $this->_execute($params);
  797.                 $array $this->parseData2($stmtself::HYDRATE_ARRAY);
  798.  
  799.                 $cached $this->getCachedForm($array);
  800.  
  801.                 $cacheDriver->save($hash$cached$this->_timeToLive);
  802.             else {
  803.                 $cached unserialize($cached);
  804.                 $this->_tableAliases $cached[2];
  805.                 $array $cached[0];
  806.  
  807.                 $map   array();
  808.                 foreach ($cached[1as $k => $v{
  809.                     $e explode('.'$v[0]);
  810.                     if (count($e=== 1{
  811.                         $map[$k]['table'$this->_conn->getTable($e[0]);
  812.                     else {
  813.                         $map[$k]['parent']   $e[0];
  814.                         $map[$k]['relation'$map[$e[0]]['table']->getRelation($e[1]);
  815.                         $map[$k]['table']    $map[$k]['relation']->getTable();
  816.                     }
  817.                     if (isset($v[1])) {
  818.                         $map[$k]['agg'$v[1];
  819.                     }
  820.                 }
  821.                 $this->_aliasMap $map;
  822.             }
  823.         else {
  824.             $stmt $this->_execute($params);
  825.  
  826.             if (is_integer($stmt)) {
  827.                 return $stmt;
  828.             }
  829.  
  830.             $array $this->parseData2($stmt$hydrationMode);
  831.         }
  832.         return $array;
  833.     }
  834.  
  835.     /**
  836.      * getType
  837.      *
  838.      * returns the type of this query object
  839.      * by default the type is Doctrine_Hydrate::SELECT but if update() or delete()
  840.      * are being called the type is Doctrine_Hydrate::UPDATE and Doctrine_Hydrate::DELETE,
  841.      * respectively
  842.      *
  843.      * @see Doctrine_Hydrate::SELECT
  844.      * @see Doctrine_Hydrate::UPDATE
  845.      * @see Doctrine_Hydrate::DELETE
  846.      *
  847.      * @return integer      return the query type
  848.      */
  849.     public function getType()
  850.     {
  851.         return $this->type;
  852.     }
  853.     /**
  854.      * applyInheritance
  855.      * applies column aggregation inheritance to DQL / SQL query
  856.      *
  857.      * @return string 
  858.      */
  859.     public function applyInheritance()
  860.     {
  861.         // get the inheritance maps
  862.         $array array();
  863.  
  864.         foreach ($this->_aliasMap as $componentAlias => $data{
  865.             $tableAlias $this->getTableAlias($componentAlias);
  866.             $array[$tableAlias][$data['table']->inheritanceMap;
  867.         }
  868.  
  869.         // apply inheritance maps
  870.         $str '';
  871.         $c array();
  872.  
  873.         $index 0;
  874.         foreach ($array as $tableAlias => $maps{
  875.             $a array();
  876.  
  877.             // don't use table aliases if the query isn't a select query
  878.             if ($this->type !== Doctrine_Query::SELECT{
  879.                 $tableAlias '';
  880.             else {
  881.                 $tableAlias .= '.';
  882.             }
  883.  
  884.             foreach ($maps as $map{
  885.                 $b array();
  886.                 foreach ($map as $field => $value{
  887.                     $identifier $this->_conn->quoteIdentifier($tableAlias $field);
  888.  
  889.                     if ($index 0{
  890.                         $b['(' $identifier ' = ' $this->_conn->quote($value)
  891.                              . ' OR ' $identifier ' IS NULL)';
  892.                     else {
  893.                         $b[$identifier ' = ' $this->_conn->quote($value);
  894.                     }
  895.                 }
  896.  
  897.                 if empty($b)) {
  898.                     $a[implode(' AND '$b);
  899.                 }
  900.             }
  901.  
  902.             if empty($a)) {
  903.                 $c[implode(' AND '$a);
  904.             }
  905.             $index++;
  906.         }
  907.  
  908.         $str .= implode(' AND '$c);
  909.  
  910.         return $str;
  911.     }
  912.     /**
  913.      * fetchArray
  914.      * Convenience method to execute using array fetching as hydration mode.
  915.      *
  916.      * @param string $params 
  917.      * @return array 
  918.      */
  919.     public function fetchArray($params array()) {
  920.         return $this->execute($paramsself::HYDRATE_ARRAY);
  921.     }
  922.     /**
  923.      * fetchOne
  924.      * Convenience method to execute the query and return the first item
  925.      * of the collection.
  926.      *
  927.      * @param string $params Parameters
  928.      * @param int $hydrationMode Hydration mode
  929.      * @return mixed Array or Doctrine_Collection or false if no result.
  930.      */
  931.     public function fetchOne($params array()$hydrationMode null)
  932.     {
  933.         if (is_null($hydrationMode)) {
  934.             $hydrationMode $this->_hydrationMode;
  935.         }
  936.  
  937.         $collection $this->execute($params$hydrationMode);
  938.  
  939.         switch ($hydrationMode{
  940.             case self::HYDRATE_RECORD:
  941.                 if (count($collection0{
  942.                     return $collection->getFirst();
  943.                 }
  944.             case self::HYDRATE_ARRAY:
  945.                 if (!empty($collection[0])) {
  946.                     return $collection[0];
  947.                 }
  948.         }
  949.  
  950.         return false;
  951.     }
  952.     /**
  953.      * parseData
  954.      * parses the data returned by statement object
  955.      *
  956.      * This is method defines the core of Doctrine object population algorithm
  957.      * hence this method strives to be as fast as possible
  958.      *
  959.      * The key idea is the loop over the rowset only once doing all the needed operations
  960.      * within this massive loop.
  961.      *
  962.      * @param mixed $stmt 
  963.      * @return array 
  964.      */
  965.     public function parseData2($stmt$hydrationMode)
  966.     {
  967.  
  968.         $cache array();
  969.         $rootMap   reset($this->_aliasMap);
  970.         $rootAlias key($this->_aliasMap);
  971.         $componentName $rootMap['table']->getComponentName();
  972.         $index 0;
  973.         $incr  true;
  974.         $lastAlias '';
  975.         $currData  array();
  976.  
  977.         if ($hydrationMode === null{
  978.             $hydrationMode $this->_hydrationMode;
  979.         }
  980.  
  981.         if ($hydrationMode === self::HYDRATE_ARRAY{
  982.             $driver new Doctrine_Hydrate_Array();
  983.         else {
  984.             $driver new Doctrine_Hydrate_Record();
  985.         }
  986.  
  987.         $array $driver->getElementCollection($componentName);
  988.         $identifiable array();
  989.  
  990.         if ($stmt === false || $stmt === 0{
  991.             return $array;
  992.         }
  993.  
  994.         while ($data $stmt->fetch(Doctrine::FETCH_ASSOC)) {
  995.             $parse true;
  996.  
  997.             foreach ($data as $key => $value{
  998.  
  999.                 // The following little cache solution ensures that field aliases are
  1000.                 // parsed only once. This increases speed on large result sets by an order
  1001.                 // of magnitude.
  1002.                 if isset($cache[$key])) {
  1003.                     $e explode('__'$key);
  1004.                     $cache[$key]['field'$field strtolower(array_pop($e));
  1005.                     $cache[$key]['alias'$this->_tableAliases[strtolower(implode('__'$e))];
  1006.                 }
  1007.  
  1008.  
  1009.                 $map   $this->_aliasMap[$cache[$key]['alias']];
  1010.                 $table $map['table'];
  1011.                 $alias $cache[$key]['alias'];
  1012.                 $field $cache[$key]['field'];
  1013.  
  1014.                 if (isset($this->_aliasMap[$alias]['agg'][$field])) {
  1015.                     $field $this->_aliasMap[$alias]['agg'][$field];
  1016.                 }
  1017.  
  1018.  
  1019.                 $componentName  $map['table']->getComponentName();
  1020.                 if (isset($map['relation'])) {
  1021.                     $componentAlias $map['relation']->getAlias();
  1022.                 else {
  1023.                     $componentAlias $map['table']->getComponentName();
  1024.                 }
  1025.  
  1026.  
  1027.                 if isset($currData[$alias])) {
  1028.                     $currData[$aliasarray();
  1029.                 }
  1030.  
  1031.                 if isset($prev[$alias])) {
  1032.                     $prev[$aliasarray();
  1033.                 }
  1034.  
  1035.  
  1036.                 $skip false;
  1037.                 if (($alias !== $lastAlias || $parse&& empty($currData[$alias])) {
  1038.  
  1039.                     // component changed
  1040.                     $element $driver->getElement($currData[$alias]$componentName);
  1041.  
  1042.                     $oneToOne false;
  1043.  
  1044.                     if ($alias === $rootAlias{
  1045.                         // dealing with root component
  1046.  
  1047.                         $index $driver->search($element$array);
  1048.                         if ($index === false{
  1049.                             $array[$element;
  1050.                         }
  1051.  
  1052.                         $coll =$array;
  1053.                     else {
  1054.                         $parent   $map['parent'];
  1055.                         $relation $map['relation'];
  1056.  
  1057.                         if (!isset($prev[$parent])) {
  1058.                             break;
  1059.                         }
  1060.  
  1061.                         // check the type of the relation
  1062.                         if $relation->isOneToOne()) {
  1063.                             // initialize the collection
  1064.  
  1065.                             if ($driver->initRelated($prev[$parent]$componentAlias)) {
  1066.  
  1067.                                 // append element
  1068.                                 if (isset($identifiable[$alias])) {
  1069.                                     $index $driver->search($element$prev[$parent][$componentAlias]);
  1070.  
  1071.                                     if ($index === false{
  1072.                                         $prev[$parent][$componentAlias][$element;
  1073.                                     }
  1074.                                 }
  1075.                                 // register collection for later snapshots
  1076.                                 $driver->registerCollection($prev[$parent][$componentAlias]);
  1077.                             }
  1078.                         else {
  1079.                             if isset($identifiable[$alias])) {
  1080.                                 $prev[$parent][$componentAlias$driver->getNullPointer();
  1081.                             else {
  1082.                                 $prev[$parent][$componentAlias$element;
  1083.                             }
  1084.                             $oneToOne true;
  1085.                         }
  1086.                         $coll =$prev[$parent][$componentAlias];
  1087.                     }
  1088.  
  1089.                     $this->_setLastElement($prev$coll$index$alias$oneToOne);
  1090.  
  1091.                     $currData[$aliasarray();
  1092.                     $identifiable[$aliasnull;
  1093.                 }
  1094.  
  1095.  
  1096.  
  1097.                 $currData[$alias][$field$table->prepareValue($field$value);
  1098.                 $index false;
  1099.                 if ($value !== null{
  1100.                     $identifiable[$aliastrue;
  1101.                 }
  1102.                 $lastAlias $alias;
  1103.                 $parse false;
  1104.  
  1105.             }
  1106.         }
  1107.  
  1108.         foreach ($currData as $alias => $data{
  1109.             $table $this->_aliasMap[$alias]['table'];
  1110.             $componentName $table->getComponentName();
  1111.             // component changed       
  1112.  
  1113.             $element $driver->getElement($currData[$alias]$componentName);
  1114.  
  1115.             $oneToOne false;
  1116.  
  1117.             if ($alias === $rootAlias{
  1118.                 // dealing with root component
  1119.                 $index $driver->search($element$array);
  1120.                 if ($index === false{
  1121.                     $array[$element;
  1122.                 }
  1123.                 $coll =$array;
  1124.             else {
  1125.                 $parent   $this->_aliasMap[$alias]['parent'];
  1126.                 $relation $this->_aliasMap[$alias]['relation'];
  1127.                 $componentAlias $relation->getAlias();
  1128.  
  1129.                 if (!isset($prev[$parent])) {
  1130.                     break;
  1131.                 }
  1132.  
  1133.                 // check the type of the relation
  1134.                 if $relation->isOneToOne()) {
  1135.                     // initialize the collection
  1136.  
  1137.                     if ($driver->initRelated($prev[$parent]$componentAlias)) {
  1138.  
  1139.                         // append element
  1140.                         if (isset($identifiable[$alias])) {
  1141.                             $index $driver->search($element$prev[$parent][$componentAlias]);
  1142.  
  1143.                             if ($index === false{
  1144.                                 $prev[$parent][$componentAlias][$element;
  1145.                             }
  1146.                         }
  1147.                         // register collection for later snapshots
  1148.                         $driver->registerCollection($prev[$parent][$componentAlias]);
  1149.                     }
  1150.                 else {
  1151.                     if isset($identifiable[$alias])) {
  1152.                         $prev[$parent][$componentAlias$driver->getNullPointer();
  1153.                     else {
  1154.  
  1155.                         $prev[$parent][$componentAlias$element;
  1156.                     }
  1157.                     $oneToOne true;
  1158.                 }
  1159.                 $coll =$prev[$parent][$componentAlias];
  1160.             }
  1161.  
  1162.             $this->_setLastElement($prev$coll$index$alias$oneToOne);
  1163.  
  1164.             $index false;
  1165.             $currData[$aliasarray();
  1166.             unset($identifiable[$alias]);
  1167.         }
  1168.  
  1169.         $driver->flush();
  1170.  
  1171.         $stmt->closeCursor();
  1172.         return $array;
  1173.     }
  1174.     /**
  1175.      * _setLastElement
  1176.      *
  1177.      * sets the last element of given data array / collection
  1178.      * as previous element
  1179.      *
  1180.      * @param boolean|integer$index 
  1181.      * @return void 
  1182.      */
  1183.     public function _setLastElement(&$prev&$coll$index$alias$oneToOne)
  1184.     {
  1185.         if ($coll === self::$_null{
  1186.             return false;
  1187.         }
  1188.         if ($index !== false{
  1189.             $prev[$alias=$coll[$index];
  1190.         else {
  1191.             // first check the count (we do not want to get the last element
  1192.             // of an empty collection/array)
  1193.             if (count($coll0{
  1194.                 if (is_array($coll)) {
  1195.                     if ($oneToOne{
  1196.                         $prev[$alias=$coll;
  1197.                     else {
  1198.                         end($coll);
  1199.                         $prev[$alias=$coll[key($coll)];
  1200.                     }
  1201.                 else {
  1202.                     $prev[$alias$coll->getLast();
  1203.                 }
  1204.             else {
  1205.                 if (isset($prev[$alias])) {
  1206.                     unset($prev[$alias]);
  1207.                 }
  1208.             }
  1209.         }
  1210.     }
  1211.     /**
  1212.      * @return string                   returns a string representation of this object
  1213.      */
  1214.     public function __toString()
  1215.     {
  1216.         return Doctrine_Lib::formatSql($this->getQuery());
  1217.     }
  1218. }