Source for file RawSql.php

Documentation is available at RawSql.php

  1. <?php
  2. /*
  3.  *  $Id: RawSql.php 1847 2007-06-26 10:05:26Z zYne $
  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. Doctrine::autoload('Doctrine_Query_Abstract');
  22. /**
  23.  * Doctrine_RawSql
  24.  *
  25.  * @package     Doctrine
  26.  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  27.  * @category    Object Relational Mapping
  28.  * @link        www.phpdoctrine.com
  29.  * @since       1.0
  30.  * @version     $Revision: 1847 $
  31.  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  32.  */
  33. {
  34.     /**
  35.      * @var array $fields 
  36.      */
  37.     private $fields = array();
  38.     /**
  39.      * parseQueryPart
  40.      * parses given query part
  41.      *
  42.      * @param string $queryPartName     the name of the query part
  43.      * @param string $queryPart         query part to be parsed
  44.      * @param boolean $append           whether or not to append the query part to its stack
  45.      *                                   if false is given, this method will overwrite
  46.      *                                   the given query part stack with $queryPart
  47.      * @return Doctrine_Query           this object
  48.      */
  49.     public function parseQueryPart($queryPartName$queryPart$append false
  50.     {
  51.         if ($queryPartName == 'select'{
  52.             preg_match_all('/{([^}{]*)}/U'$queryPart$m);
  53.  
  54.             $this->fields = $m[1];
  55.             $this->parts['select'array();
  56.         else {
  57.             if $append{
  58.                 $this->parts[$queryPartNamearray($queryPart);
  59.             else {
  60.                 $this->parts[$queryPartName][$queryPart;
  61.             }
  62.         }
  63.         return $this;
  64.     }
  65.     /**
  66.      * parseQuery
  67.      * parses an sql query and adds the parts to internal array
  68.      *
  69.      * @param string $query         query to be parsed
  70.      * @return Doctrine_RawSql      this object
  71.      */
  72.     public function parseQuery($query)
  73.     {
  74.         preg_match_all('/{([^}{]*)}/U'$query$m);
  75.  
  76.         $this->fields = $m[1];
  77.         $this->clear();
  78.  
  79.         $e Doctrine_Tokenizer::sqlExplode($query,' ');
  80.  
  81.         foreach ($e as $k => $part{
  82.             $low strtolower($part);
  83.             switch (strtolower($part)) {
  84.                 case 'select':
  85.                 case 'from':
  86.                 case 'where':
  87.                 case 'limit':
  88.                 case 'offset':
  89.                 case 'having':
  90.                     $p $low;
  91.                     if isset($parts[$low])) {
  92.                         $parts[$lowarray();
  93.                     }
  94.                     break;
  95.                 case 'order':
  96.                 case 'group':
  97.                     $i ($k 1);
  98.                     if (isset($e[$i]&& strtolower($e[$i]=== 'by'{
  99.                         $p $low;
  100.                         $p .= 'by';
  101.                         $parts[$low 'by'array();
  102.  
  103.                     else {
  104.                         $parts[$p][$part;
  105.                     }
  106.                     break;
  107.                 case 'by':
  108.                     continue;
  109.                 default:
  110.                     if isset($parts[$p][0])) {
  111.                         $parts[$p][0$part;
  112.                     else {
  113.                         $parts[$p][0.= ' '.$part;
  114.                     }
  115.             }
  116.         }
  117.  
  118.         $this->parts = $parts;
  119.         $this->parts['select'array();
  120.  
  121.         return $this;
  122.     }
  123.     /**
  124.      * getQuery
  125.      * builds the sql query from the given query parts
  126.      *
  127.      * @return string       the built sql query
  128.      */
  129.     public function getQuery()
  130.     {
  131.         $select array();
  132.  
  133.         foreach ($this->fields as $field{
  134.             $e explode('.'$field);
  135.             if isset($e[1])) {
  136.                 throw new Doctrine_RawSql_Exception('All selected fields in Sql query must be in format tableAlias.fieldName');
  137.             }
  138.             // try to auto-add component
  139.             if $this->hasTableAlias($e[0])) {
  140.                 try {
  141.                     $this->addComponent($e[0]ucwords($e[0]));
  142.                 catch(Doctrine_Exception $exception{
  143.                     throw new Doctrine_RawSql_Exception('The associated component for table alias ' $e[0' couldn\'t be found.');
  144.                 }
  145.             }
  146.  
  147.             $componentAlias $this->getComponentAlias($e[0]);
  148.             
  149.             if ($e[1== '*'{
  150.                 foreach ($this->_aliasMap[$componentAlias]['table']->getColumnNames(as $name{
  151.                     $field $e[0'.' $name;
  152.  
  153.                     $select[$componentAlias][$field$field ' AS ' $e[0'__' $name;
  154.                 }
  155.             else {
  156.                 $field $e[0'.' $e[1];
  157.                 $select[$componentAlias][$field$field ' AS ' $e[0'__' $e[1];
  158.             }
  159.         }
  160.  
  161.         // force-add all primary key fields
  162.  
  163.         foreach ($this->getTableAliases(as $tableAlias => $componentAlias{
  164.             $map $this->_aliasMap[$componentAlias];
  165.  
  166.             foreach ($map['table']->getPrimaryKeys(as $key{
  167.                 $field $tableAlias '.' $key;
  168.  
  169.                 if isset($this->parts['select'][$field])) {
  170.                     $select[$componentAlias][$field$field ' AS ' $tableAlias '__' $key;
  171.                 }
  172.             }
  173.         }
  174.         
  175.         // first add the fields of the root component
  176.         reset($this->_aliasMap);
  177.         $componentAlias key($this->_aliasMap);
  178.  
  179.         $q 'SELECT ' implode(', '$select[$componentAlias]);
  180.         unset($select[$componentAlias]);
  181.  
  182.         foreach ($select as $component => $fields{
  183.             if empty($fields)) {
  184.                 $q .= ', ' implode(', '$fields);
  185.             }
  186.         }
  187.  
  188.         $string $this->applyInheritance();
  189.         if empty($string)) {
  190.             $this->parts['where'][$string;
  191.         }
  192.         $copy $this->parts;
  193.         unset($copy['select']);
  194.  
  195.         $q .= empty($this->parts['from']))?    ' FROM '     implode(' '$this->parts['from']'';
  196.         $q .= empty($this->parts['where']))?   ' WHERE '    implode(' AND '$this->parts['where']'';
  197.         $q .= empty($this->parts['groupby']))' GROUP BY ' implode(', '$this->parts['groupby']'';
  198.         $q .= empty($this->parts['having']))?  ' HAVING '   implode(' AND '$this->parts['having']'';
  199.         $q .= empty($this->parts['orderby']))' ORDER BY ' implode(', '$this->parts['orderby']'';
  200.         $q .= empty($this->parts['limit']))?   ' LIMIT ' implode(' '$this->parts['limit']'';
  201.         $q .= empty($this->parts['offset']))?  ' OFFSET ' implode(' '$this->parts['offset']'';
  202.  
  203.         if empty($string)) {
  204.             array_pop($this->parts['where']);
  205.         }
  206.         return $q;
  207.     }
  208.     /**
  209.      * getFields
  210.      * returns the fields associated with this parser
  211.      *
  212.      * @return array    all the fields associated with this parser
  213.      */
  214.     public function getFields()
  215.     {
  216.         return $this->fields;
  217.     }
  218.     /**
  219.      * addComponent
  220.      *
  221.      * @param string $tableAlias 
  222.      * @param string $componentName 
  223.      * @return Doctrine_RawSql 
  224.      */
  225.     public function addComponent($tableAlias$path)
  226.     {
  227.         $tmp           explode(' '$path);
  228.         $originalAlias (count($tmp1end($tmpnull;
  229.  
  230.         $e explode('.'$tmp[0]);
  231.  
  232.         $fullPath $tmp[0];
  233.         $fullLength strlen($fullPath);
  234.  
  235.         $table null;
  236.  
  237.         $currPath '';
  238.  
  239.         if (isset($this->_aliasMap[$e[0]])) {
  240.             $table $this->_aliasMap[$e[0]]['table'];
  241.  
  242.             $currPath $parent array_shift($e);
  243.         }
  244.  
  245.         foreach ($e as $k => $component{
  246.             // get length of the previous path
  247.             $length strlen($currPath);
  248.  
  249.             // build the current component path
  250.             $currPath ($currPath$currPath '.' $component $component;
  251.  
  252.             $delimeter substr($fullPath$length1);
  253.  
  254.             // if an alias is not given use the current path as an alias identifier
  255.             if (strlen($currPath=== $fullLength && isset($originalAlias)) {
  256.                 $componentAlias $originalAlias;
  257.             else {
  258.                 $componentAlias $currPath;
  259.             }
  260.             if isset($table)) {
  261.                 $conn Doctrine_Manager::getInstance()
  262.                         ->getConnectionForComponent($component);
  263.                         
  264.                 $table $conn->getTable($component);
  265.                 $this->_aliasMap[$componentAliasarray('table' => $table);
  266.             else {
  267.                 $relation $table->getRelation($component);
  268.  
  269.                 $this->_aliasMap[$componentAliasarray('table'    => $relation->getTable(),
  270.                                                           'parent'   => $parent,
  271.                                                           'relation' => $relation);
  272.             }
  273.             $this->addTableAlias($tableAlias$componentAlias);
  274.  
  275.             $parent $currPath;
  276.         }
  277.  
  278.         return $this;
  279.     }
  280.  
  281. }