2006-05-30 12:42:10 +04:00
< ? php
2006-07-27 21:51:19 +04:00
/*
* $Id $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL . For more information , see
* < http :// www . phpdoctrine . com >.
*/
2006-09-04 02:37:54 +04:00
2006-05-30 12:42:10 +04:00
/**
* Doctrine_Query
*
* @ package Doctrine ORM
* @ url www . phpdoctrine . com
* @ license LGPL
*/
2006-08-26 01:20:44 +04:00
class Doctrine_Query extends Doctrine_Hydrate implements Countable {
2006-08-16 13:17:43 +04:00
/**
* @ param array $subqueryAliases the table aliases needed in some LIMIT subqueries
*/
2006-08-22 00:36:11 +04:00
private $subqueryAliases = array ();
2006-08-16 13:17:43 +04:00
/**
* @ param boolean $needsSubquery
*/
2006-08-22 00:36:11 +04:00
private $needsSubquery = false ;
/**
* @ param boolean $limitSubqueryUsed
*/
private $limitSubqueryUsed = false ;
2006-09-15 01:10:02 +04:00
private $tableStack ;
private $relationStack = array ();
2006-09-17 22:31:28 +04:00
2006-09-17 22:49:45 +04:00
private $isDistinct = false ;
2006-09-05 00:01:02 +04:00
/**
* create
* returns a new Doctrine_Query object
*
* @ return Doctrine_Query
*/
public static function create () {
return new Doctrine_Query ();
}
2006-09-15 01:10:02 +04:00
public function getTableStack () {
return $this -> tableStack ;
}
public function getRelationStack () {
return $this -> relationStack ;
}
2006-09-17 22:31:28 +04:00
public function isDistinct ( $distinct = null ) {
if ( isset ( $distinct ))
$this -> isDistinct = ( bool ) $distinct ;
return $this -> isDistinct ;
}
2006-07-31 01:37:07 +04:00
/**
* count
*
* @ return integer
*/
public function count ( Doctrine_Table $table , $params = array ()) {
$this -> remove ( 'select' );
$join = $this -> join ;
$where = $this -> where ;
$having = $this -> having ;
2006-09-17 22:49:45 +04:00
$q = " SELECT COUNT(DISTINCT " . $table -> getTableName () . '.' . $table -> getIdentifier () . " ) FROM " . $table -> getTableName () . " " ;
2006-07-31 01:37:07 +04:00
foreach ( $join as $j ) {
2006-09-17 22:49:45 +04:00
$q .= implode ( " " , $j );
2006-07-31 01:37:07 +04:00
}
2006-08-01 22:02:53 +04:00
$string = $this -> applyInheritance ();
2006-07-31 01:37:07 +04:00
if ( ! empty ( $where )) {
$q .= " WHERE " . implode ( " AND " , $where );
if ( ! empty ( $string ))
$q .= " AND ( " . $string . " ) " ;
} else {
if ( ! empty ( $string ))
$q .= " WHERE ( " . $string . " ) " ;
}
if ( ! empty ( $having ))
$q .= " HAVING " . implode ( ' AND ' , $having );
2006-05-30 12:42:10 +04:00
2006-08-22 03:19:15 +04:00
$a = $this -> getConnection () -> execute ( $q , $params ) -> fetch ( PDO :: FETCH_NUM );
2006-07-31 01:37:07 +04:00
return $a [ 0 ];
}
2006-05-30 12:42:10 +04:00
/**
* loadFields
* loads fields for a given table and
* constructs a little bit of sql for every field
*
* fields of the tables become : [ tablename ] . [ fieldname ] as [ tablename ] __ [ fieldname ]
*
* @ access private
2006-06-26 22:55:42 +04:00
* @ param object Doctrine_Table $table a Doctrine_Table object
* @ param integer $fetchmode fetchmode the table is using eg . Doctrine :: FETCH_LAZY
* @ param array $names fields to be loaded ( only used in lazy property loading )
2006-05-30 12:42:10 +04:00
* @ return void
*/
2006-07-22 03:22:15 +04:00
protected function loadFields ( Doctrine_Table $table , $fetchmode , array $names , $cpath ) {
2006-05-30 12:42:10 +04:00
$name = $table -> getComponentName ();
switch ( $fetchmode ) :
case Doctrine :: FETCH_OFFSET :
$this -> limit = $table -> getAttribute ( Doctrine :: ATTR_COLL_LIMIT );
case Doctrine :: FETCH_IMMEDIATE :
if ( ! empty ( $names ))
2006-06-26 03:42:19 +04:00
$names = array_unique ( array_merge ( $table -> getPrimaryKeys (), $names ));
else
$names = $table -> getColumnNames ();
2006-05-30 12:42:10 +04:00
break ;
case Doctrine :: FETCH_LAZY_OFFSET :
$this -> limit = $table -> getAttribute ( Doctrine :: ATTR_COLL_LIMIT );
case Doctrine :: FETCH_LAZY :
case Doctrine :: FETCH_BATCH :
2006-06-26 03:42:19 +04:00
$names = array_unique ( array_merge ( $table -> getPrimaryKeys (), $names ));
2006-05-30 12:42:10 +04:00
break ;
default :
throw new Doctrine_Exception ( " Unknown fetchmode. " );
endswitch ;
2006-06-08 17:17:15 +04:00
2006-06-26 03:42:19 +04:00
$component = $table -> getComponentName ();
$tablename = $this -> tableAliases [ $cpath ];
$this -> fetchModes [ $tablename ] = $fetchmode ;
2006-05-30 12:42:10 +04:00
$count = count ( $this -> tables );
2006-06-09 02:11:36 +04:00
2006-05-30 12:42:10 +04:00
foreach ( $names as $name ) {
if ( $count == 0 ) {
2006-07-22 03:22:15 +04:00
$this -> parts [ " select " ][] = $tablename . " . " . $name ;
2006-05-30 12:42:10 +04:00
} else {
2006-07-22 03:22:15 +04:00
$this -> parts [ " select " ][] = $tablename . " . " . $name . " AS " . $tablename . " __ " . $name ;
2006-05-30 12:42:10 +04:00
}
}
}
2006-07-05 02:36:22 +04:00
/**
* addFrom
*
* @ param strint $from
*/
public function addFrom ( $from ) {
$class = " Doctrine_Query_From " ;
$parser = new $class ( $this );
$parser -> parse ( $from );
}
/**
* addWhere
*
* @ param string $where
*/
public function addWhere ( $where ) {
$class = " Doctrine_Query_Where " ;
$parser = new $class ( $this );
$this -> parts [ 'where' ][] = $parser -> parse ( $where );
}
/**
2006-05-30 12:42:10 +04:00
* sets a query part
*
* @ param string $name
* @ param array $args
* @ return void
*/
public function __call ( $name , $args ) {
$name = strtolower ( $name );
2006-07-05 02:36:22 +04:00
2006-05-30 12:42:10 +04:00
if ( isset ( $this -> parts [ $name ])) {
$method = " parse " . ucwords ( $name );
switch ( $name ) :
2006-06-30 03:04:39 +04:00
case " from " :
$this -> parts [ 'from' ] = array ();
2006-07-22 03:22:15 +04:00
$this -> parts [ 'select' ] = array ();
2006-06-30 03:04:39 +04:00
$this -> parts [ 'join' ] = array ();
$this -> joins = array ();
$this -> tables = array ();
$this -> fetchModes = array ();
$this -> tableIndexes = array ();
$this -> tableAliases = array ();
$class = " Doctrine_Query_ " . ucwords ( $name );
$parser = new $class ( $this );
$parser -> parse ( $args [ 0 ]);
break ;
2006-05-30 12:42:10 +04:00
case " where " :
2006-06-30 03:04:39 +04:00
case " having " :
case " orderby " :
case " groupby " :
$class = " Doctrine_Query_ " . ucwords ( $name );
$parser = new $class ( $this );
$this -> parts [ $name ] = array ( $parser -> parse ( $args [ 0 ]));
2006-05-30 12:42:10 +04:00
break ;
case " limit " :
case " offset " :
if ( $args [ 0 ] == null )
$args [ 0 ] = false ;
$this -> parts [ $name ] = $args [ 0 ];
break ;
default :
$this -> parts [ $name ] = array ();
$this -> $method ( $args [ 0 ]);
endswitch ;
2006-06-14 23:17:38 +04:00
} else
throw new Doctrine_Query_Exception ( " Unknown overload method " );
2006-05-30 12:42:10 +04:00
return $this ;
}
/**
* returns a query part
*
* @ param $name query part name
* @ return mixed
*/
public function get ( $name ) {
if ( ! isset ( $this -> parts [ $name ]))
return false ;
return $this -> parts [ $name ];
}
/**
* sets a query part
*
* @ param $name query part name
* @ param $value query part value
* @ return boolean
*/
public function set ( $name , $value ) {
if ( isset ( $this -> parts [ $name ])) {
$method = " parse " . ucwords ( $name );
switch ( $name ) :
case " where " :
2006-06-14 23:17:38 +04:00
case " having " :
2006-05-30 12:42:10 +04:00
$this -> parts [ $name ] = array ( $this -> $method ( $value ));
break ;
case " limit " :
case " offset " :
if ( $value == null )
$value = false ;
$this -> parts [ $name ] = $value ;
break ;
case " from " :
2006-07-22 03:22:15 +04:00
$this -> parts [ 'select' ] = array ();
2006-06-12 12:44:08 +04:00
$this -> parts [ 'join' ] = array ();
2006-05-30 12:42:10 +04:00
$this -> joins = array ();
$this -> tables = array ();
$this -> fetchModes = array ();
2006-06-26 03:42:19 +04:00
$this -> tableIndexes = array ();
$this -> tableAliases = array ();
2006-05-30 12:42:10 +04:00
default :
$this -> parts [ $name ] = array ();
$this -> $method ( $value );
endswitch ;
return true ;
}
return false ;
}
2006-08-22 00:36:11 +04:00
/**
* @ return boolean
*/
public function isLimitSubqueryUsed () {
2006-08-22 01:04:20 +04:00
return $this -> limitSubqueryUsed ;
2006-08-22 00:36:11 +04:00
}
2006-05-30 12:42:10 +04:00
/**
* returns the built sql query
*
* @ return string
*/
2006-08-17 13:42:18 +04:00
public function getQuery () {
2006-07-22 03:22:15 +04:00
if ( empty ( $this -> parts [ " select " ]) || empty ( $this -> parts [ " from " ]))
2006-05-30 12:42:10 +04:00
return false ;
2006-08-22 00:36:11 +04:00
2006-08-16 03:25:53 +04:00
$needsSubQuery = false ;
$subquery = '' ;
2006-08-30 00:09:31 +04:00
$k = array_keys ( $this -> tables );
$table = $this -> tables [ $k [ 0 ]];
2006-08-16 03:25:53 +04:00
2006-08-30 00:09:31 +04:00
if ( ! empty ( $this -> parts [ 'limit' ]) && $this -> needsSubquery && $table -> getAttribute ( Doctrine :: ATTR_QUERY_LIMIT ) == Doctrine :: LIMIT_RECORDS ) {
2006-08-16 03:25:53 +04:00
$needsSubQuery = true ;
2006-08-22 00:36:11 +04:00
$this -> limitSubqueryUsed = true ;
}
2006-05-30 12:42:10 +04:00
// build the basic query
2006-09-17 22:31:28 +04:00
$str = '' ;
if ( $this -> isDistinct ())
$str = 'DISTINCT ' ;
$q = " SELECT " . $str . implode ( " , " , $this -> parts [ " select " ]) .
2006-05-30 12:42:10 +04:00
" FROM " ;
2006-08-16 03:25:53 +04:00
2006-05-30 12:42:10 +04:00
foreach ( $this -> parts [ " from " ] as $tname => $bool ) {
$a [] = $tname ;
}
$q .= implode ( " , " , $a );
2006-08-16 03:25:53 +04:00
if ( $needsSubQuery )
2006-08-16 13:17:43 +04:00
$subquery = 'SELECT DISTINCT ' . $table -> getTableName () . " . " . $table -> getIdentifier () .
2006-08-16 03:25:53 +04:00
' FROM ' . $table -> getTableName ();
2006-05-30 12:42:10 +04:00
if ( ! empty ( $this -> parts [ 'join' ])) {
foreach ( $this -> parts [ 'join' ] as $part ) {
$q .= " " . implode ( ' ' , $part );
}
2006-08-16 03:25:53 +04:00
if ( $needsSubQuery ) {
foreach ( $this -> parts [ 'join' ] as $parts ) {
foreach ( $parts as $part ) {
2006-08-16 13:17:43 +04:00
if ( substr ( $part , 0 , 9 ) === 'LEFT JOIN' ) {
$e = explode ( ' ' , $part );
if ( ! in_array ( $e [ 2 ], $this -> subqueryAliases ))
continue ;
}
$subquery .= " " . $part ;
2006-08-16 03:25:53 +04:00
}
}
}
2006-05-30 12:42:10 +04:00
}
2006-06-26 22:55:42 +04:00
$string = $this -> applyInheritance ();
2006-08-16 03:25:53 +04:00
if ( ! empty ( $string ))
$this -> parts [ 'where' ][] = '(' . $string . ')' ;
if ( $needsSubQuery ) {
2006-08-17 13:42:18 +04:00
// all conditions must be preserved in subquery
2006-08-16 03:25:53 +04:00
$subquery .= ( ! empty ( $this -> parts [ 'where' ])) ? " WHERE " . implode ( " AND " , $this -> parts [ " where " ]) : '' ;
$subquery .= ( ! empty ( $this -> parts [ 'groupby' ])) ? " GROUP BY " . implode ( " , " , $this -> parts [ " groupby " ]) : '' ;
$subquery .= ( ! empty ( $this -> parts [ 'having' ])) ? " HAVING " . implode ( " " , $this -> parts [ " having " ]) : '' ;
2006-06-26 22:55:42 +04:00
}
2006-08-30 00:09:31 +04:00
$modifyLimit = true ;
2006-08-16 13:17:43 +04:00
if ( ! empty ( $this -> parts [ " limit " ]) || ! empty ( $this -> parts [ " offset " ])) {
2006-08-30 00:09:31 +04:00
2006-08-16 13:17:43 +04:00
if ( $needsSubQuery ) {
2006-08-22 03:19:15 +04:00
$subquery = $this -> connection -> modifyLimitQuery ( $subquery , $this -> parts [ " limit " ], $this -> parts [ " offset " ]);
2006-09-09 03:20:51 +04:00
$dbh = $this -> connection -> getDBH ();
// mysql doesn't support LIMIT in subqueries
if ( $dbh -> getAttribute ( PDO :: ATTR_DRIVER_NAME ) == 'mysql' ) { }
//$dbh->query();
2006-08-16 13:17:43 +04:00
$field = $table -> getTableName () . '.' . $table -> getIdentifier ();
array_unshift ( $this -> parts [ 'where' ], $field . ' IN (' . $subquery . ')' );
2006-08-30 00:09:31 +04:00
$modifyLimit = false ;
2006-08-31 13:04:14 +04:00
}
2006-08-16 03:25:53 +04:00
}
2006-05-30 12:42:10 +04:00
2006-08-16 03:25:53 +04:00
$q .= ( ! empty ( $this -> parts [ 'where' ])) ? " WHERE " . implode ( " AND " , $this -> parts [ " where " ]) : '' ;
2006-06-30 03:04:39 +04:00
$q .= ( ! empty ( $this -> parts [ 'groupby' ])) ? " GROUP BY " . implode ( " , " , $this -> parts [ " groupby " ]) : '' ;
$q .= ( ! empty ( $this -> parts [ 'having' ])) ? " HAVING " . implode ( " " , $this -> parts [ " having " ]) : '' ;
$q .= ( ! empty ( $this -> parts [ 'orderby' ])) ? " ORDER BY " . implode ( " " , $this -> parts [ " orderby " ]) : '' ;
2006-08-16 13:17:43 +04:00
if ( $modifyLimit )
2006-08-22 03:19:15 +04:00
$q = $this -> connection -> modifyLimitQuery ( $q , $this -> parts [ " limit " ], $this -> parts [ " offset " ]);
2006-05-30 12:42:10 +04:00
2006-08-16 03:25:53 +04:00
// return to the previous state
if ( ! empty ( $string ))
array_pop ( $this -> parts [ 'where' ]);
if ( $needsSubQuery )
array_shift ( $this -> parts [ 'where' ]);
2006-05-30 12:42:10 +04:00
return $q ;
}
/**
* query the database with DQL ( Doctrine Query Language )
*
* @ param string $query DQL query
* @ param array $params parameters
*/
public function query ( $query , $params = array ()) {
$this -> parseQuery ( $query );
if ( $this -> aggregate ) {
$keys = array_keys ( $this -> tables );
$query = $this -> getQuery ();
2006-08-22 03:19:15 +04:00
$stmt = $this -> tables [ $keys [ 0 ]] -> getConnection () -> select ( $query , $this -> parts [ " limit " ], $this -> parts [ " offset " ]);
2006-05-30 12:42:10 +04:00
$data = $stmt -> fetch ( PDO :: FETCH_ASSOC );
if ( count ( $data ) == 1 ) {
return current ( $data );
} else {
return $data ;
}
} else {
return $this -> execute ( $params );
}
}
/**
* DQL PARSER
2006-06-26 03:42:19 +04:00
* parses a DQL query
* first splits the query in parts and then uses individual
* parsers for each part
2006-05-30 12:42:10 +04:00
*
* @ param string $query DQL query
2006-09-29 00:03:29 +04:00
* @ return Doctrine_Query
2006-05-30 12:42:10 +04:00
*/
2006-07-22 03:22:15 +04:00
public function parseQuery ( $query ) {
2006-05-30 12:42:10 +04:00
$this -> clear ();
2006-08-23 14:11:40 +04:00
$e = self :: sqlExplode ( $query , " " , " ( " , " ) " );
2006-07-05 02:36:22 +04:00
2006-05-30 12:42:10 +04:00
$parts = array ();
foreach ( $e as $k => $part ) :
switch ( strtolower ( $part )) :
case " select " :
case " from " :
case " where " :
case " limit " :
case " offset " :
2006-06-30 03:04:39 +04:00
case " having " :
2006-05-30 12:42:10 +04:00
$p = $part ;
$parts [ $part ] = array ();
break ;
case " order " :
2006-06-30 03:04:39 +04:00
case " group " :
$i = ( $k + 1 );
if ( isset ( $e [ $i ]) && strtolower ( $e [ $i ]) === " by " ) {
$p = $part ;
2006-05-30 12:42:10 +04:00
$parts [ $part ] = array ();
2006-06-30 03:04:39 +04:00
} else
$parts [ $p ][] = $part ;
2006-05-30 12:42:10 +04:00
break ;
case " by " :
continue ;
default :
$parts [ $p ][] = $part ;
endswitch ;
endforeach ;
foreach ( $parts as $k => $part ) {
$part = implode ( " " , $part );
switch ( strtoupper ( $k )) :
case " SELECT " :
$this -> parseSelect ( $part );
break ;
case " FROM " :
2006-06-30 03:04:39 +04:00
$class = " Doctrine_Query_ " . ucwords ( strtolower ( $k ));
$parser = new $class ( $this );
$parser -> parse ( $part );
2006-05-30 12:42:10 +04:00
break ;
2006-06-30 03:04:39 +04:00
case " GROUP " :
case " ORDER " :
$k .= " by " ;
2006-05-30 12:42:10 +04:00
case " WHERE " :
2006-06-30 03:04:39 +04:00
case " HAVING " :
$class = " Doctrine_Query_ " . ucwords ( strtolower ( $k ));
$parser = new $class ( $this );
$name = strtolower ( $k );
$this -> parts [ $name ][] = $parser -> parse ( $part );
2006-05-30 12:42:10 +04:00
break ;
case " LIMIT " :
$this -> parts [ " limit " ] = trim ( $part );
break ;
case " OFFSET " :
2006-08-16 03:25:53 +04:00
$this -> parts [ " offset " ] = trim ( $part );
2006-05-30 12:42:10 +04:00
break ;
endswitch ;
}
2006-09-29 00:03:29 +04:00
return $this ;
2006-05-30 12:42:10 +04:00
}
/**
* DQL ORDER BY PARSER
* parses the order by part of the query string
*
* @ param string $str
* @ return void
*/
2006-06-30 03:04:39 +04:00
final public function parseOrderBy ( $str ) {
$parser = new Doctrine_Query_Part_Orderby ( $this );
return $parser -> parse ( $str );
2006-05-30 12:42:10 +04:00
}
/**
* returns Doctrine :: FETCH_ * constant
*
* @ param string $mode
* @ return integer
*/
2006-06-30 03:04:39 +04:00
final public function parseFetchMode ( $mode ) {
2006-05-30 12:42:10 +04:00
switch ( strtolower ( $mode )) :
case " i " :
case " immediate " :
$fetchmode = Doctrine :: FETCH_IMMEDIATE ;
break ;
case " b " :
case " batch " :
$fetchmode = Doctrine :: FETCH_BATCH ;
break ;
case " l " :
case " lazy " :
$fetchmode = Doctrine :: FETCH_LAZY ;
break ;
case " o " :
case " offset " :
$fetchmode = Doctrine :: FETCH_OFFSET ;
break ;
case " lo " :
case " lazyoffset " :
$fetchmode = Doctrine :: FETCH_LAZYOFFSET ;
default :
2006-08-21 23:48:24 +04:00
throw new Doctrine_Query_Exception ( " Unknown fetchmode ' $mode '. The availible fetchmodes are 'i', 'b' and 'l'. " );
2006-05-30 12:42:10 +04:00
endswitch ;
return $fetchmode ;
}
/**
* trims brackets
*
* @ param string $str
* @ param string $e1 the first bracket , usually '('
* @ param string $e2 the second bracket , usually ')'
*/
public static function bracketTrim ( $str , $e1 , $e2 ) {
if ( substr ( $str , 0 , 1 ) == $e1 && substr ( $str , - 1 ) == $e2 )
return substr ( $str , 1 , - 1 );
else
return $str ;
}
/**
* bracketExplode
* usage :
* $str = ( age < 20 AND age > 18 ) AND email LIKE 'John@example.com'
* now exploding $str with parameters $d = ' AND ' , $e1 = '(' and $e2 = ')'
* would return an array :
* array ( " (age < 20 AND age > 18) " , " email LIKE 'John@example.com' " )
*
* @ param string $str
* @ param string $d the delimeter which explodes the string
* @ param string $e1 the first bracket , usually '('
* @ param string $e2 the second bracket , usually ')'
*
*/
2006-08-20 23:21:52 +04:00
public static function bracketExplode ( $str , $d , $e1 = '(' , $e2 = ')' ) {
2006-09-15 14:48:59 +04:00
if ( is_array ( $d )) {
$a = preg_split ( '/(' . implode ( '|' , $d ) . ')/' , $str );
$d = stripslashes ( $d [ 0 ]);
} else
2006-09-15 14:04:22 +04:00
$a = explode ( " $d " , $str );
2006-09-15 14:48:59 +04:00
2006-05-30 12:42:10 +04:00
$i = 0 ;
$term = array ();
2006-09-15 14:04:22 +04:00
foreach ( $a as $key => $val ) {
2006-05-30 12:42:10 +04:00
if ( empty ( $term [ $i ])) {
$term [ $i ] = trim ( $val );
$s1 = substr_count ( $term [ $i ], " $e1 " );
$s2 = substr_count ( $term [ $i ], " $e2 " );
if ( $s1 == $s2 ) $i ++ ;
} else {
$term [ $i ] .= " $d " . trim ( $val );
$c1 = substr_count ( $term [ $i ], " $e1 " );
$c2 = substr_count ( $term [ $i ], " $e2 " );
if ( $c1 == $c2 ) $i ++ ;
}
}
return $term ;
}
2006-08-25 22:17:20 +04:00
/**
* sqlExplode
*
* explodes a string into array using custom brackets and
* quote delimeters
*
* @ param string $str
* @ param string $d the delimeter which explodes the string
* @ param string $e1 the first bracket , usually '('
* @ param string $e2 the second bracket , usually ')'
*
* @ return array
*/
2006-08-23 14:11:40 +04:00
public static function sqlExplode ( $str , $d = " " , $e1 = '(' , $e2 = ')' ) {
$str = explode ( " $d " , $str );
$i = 0 ;
$term = array ();
foreach ( $str as $key => $val ) {
if ( empty ( $term [ $i ])) {
$term [ $i ] = trim ( $val );
$s1 = substr_count ( $term [ $i ], " $e1 " );
$s2 = substr_count ( $term [ $i ], " $e2 " );
if ( substr ( $term [ $i ], 0 , 1 ) == " ( " ) {
if ( $s1 == $s2 )
$i ++ ;
} else {
if ( ! ( substr_count ( $term [ $i ], " ' " ) & 1 ) &&
! ( substr_count ( $term [ $i ], " \" " ) & 1 ) &&
! ( substr_count ( $term [ $i ], " <EFBFBD> " ) & 1 )
) $i ++ ;
}
} else {
$term [ $i ] .= " $d " . trim ( $val );
$c1 = substr_count ( $term [ $i ], " $e1 " );
$c2 = substr_count ( $term [ $i ], " $e2 " );
if ( substr ( $term [ $i ], 0 , 1 ) == " ( " ) {
if ( $c1 == $c2 )
$i ++ ;
} else {
if ( ! ( substr_count ( $term [ $i ], " ' " ) & 1 ) &&
! ( substr_count ( $term [ $i ], " \" " ) & 1 ) &&
! ( substr_count ( $term [ $i ], " <EFBFBD> " ) & 1 )
) $i ++ ;
}
}
}
return $term ;
}
2006-05-30 12:42:10 +04:00
/**
2006-06-26 03:42:19 +04:00
* generateAlias
*
* @ param string $tableName
* @ return string
*/
final public function generateAlias ( $tableName ) {
2006-06-27 01:18:19 +04:00
if ( isset ( $this -> tableIndexes [ $tableName ])) {
2006-06-26 03:42:19 +04:00
return $tableName .++ $this -> tableIndexes [ $tableName ];
2006-06-27 01:18:19 +04:00
} else {
2006-06-26 03:42:19 +04:00
$this -> tableIndexes [ $tableName ] = 1 ;
return $tableName ;
}
}
/**
* loads a component
*
2006-05-30 12:42:10 +04:00
* @ param string $path the path of the loadable component
* @ param integer $fetchmode optional fetchmode , if not set the components default fetchmode will be used
2006-08-21 23:48:24 +04:00
* @ throws Doctrine_Query_Exception
2006-06-26 03:42:19 +04:00
* @ return Doctrine_Table
2006-05-30 12:42:10 +04:00
*/
final public function load ( $path , $loadFields = true ) {
$e = preg_split ( " /[.:]/ " , $path );
$index = 0 ;
2006-06-26 03:42:19 +04:00
$currPath = '' ;
2006-09-15 01:10:02 +04:00
$this -> tableStack = array ();
2006-05-30 12:42:10 +04:00
foreach ( $e as $key => $fullname ) {
try {
2006-06-26 03:42:19 +04:00
$copy = $e ;
$e2 = preg_split ( " /[-(]/ " , $fullname );
$name = $e2 [ 0 ];
$currPath .= " . " . $name ;
2006-05-30 12:42:10 +04:00
if ( $key == 0 ) {
2006-06-26 03:42:19 +04:00
$currPath = substr ( $currPath , 1 );
2006-05-30 12:42:10 +04:00
2006-08-22 03:19:15 +04:00
$table = $this -> connection -> getTable ( $name );
2006-05-30 12:42:10 +04:00
$tname = $table -> getTableName ();
2006-08-16 13:17:43 +04:00
2006-06-27 01:18:19 +04:00
if ( ! isset ( $this -> tableAliases [ $currPath ]))
$this -> tableIndexes [ $tname ] = 1 ;
2006-06-26 03:42:19 +04:00
2006-05-30 12:42:10 +04:00
$this -> parts [ " from " ][ $tname ] = true ;
2006-06-26 03:42:19 +04:00
$this -> tableAliases [ $currPath ] = $tname ;
$tableName = $tname ;
2006-05-30 12:42:10 +04:00
} else {
$index += strlen ( $e [( $key - 1 )]) + 1 ;
// the mark here is either '.' or ':'
$mark = substr ( $path ,( $index - 1 ), 1 );
2006-06-26 03:42:19 +04:00
if ( isset ( $this -> tableAliases [ $prevPath ])) {
$tname = $this -> tableAliases [ $prevPath ];
2006-06-12 12:44:08 +04:00
} else
$tname = $table -> getTableName ();
2006-05-30 12:42:10 +04:00
2006-08-31 13:04:14 +04:00
$fk = $table -> getRelation ( $name );
2006-06-26 03:42:19 +04:00
$name = $fk -> getTable () -> getComponentName ();
$original = $fk -> getTable () -> getTableName ();
2006-08-16 13:17:43 +04:00
2006-06-26 03:42:19 +04:00
if ( isset ( $this -> tableAliases [ $currPath ])) {
$tname2 = $this -> tableAliases [ $currPath ];
} else
$tname2 = $this -> generateAlias ( $original );
2006-05-30 12:42:10 +04:00
2006-06-26 03:42:19 +04:00
if ( $original !== $tname2 )
$aliasString = $original . " AS " . $tname2 ;
else
$aliasString = $original ;
2006-05-30 12:42:10 +04:00
2006-06-08 14:20:30 +04:00
switch ( $mark ) :
case " : " :
$join = 'INNER JOIN ' ;
break ;
case " . " :
$join = 'LEFT JOIN ' ;
break ;
default :
throw new Doctrine_Exception ( " Unknown operator ' $mark ' " );
endswitch ;
2006-08-16 13:17:43 +04:00
if ( $fk -> getType () == Doctrine_Relation :: MANY_AGGREGATE ||
$fk -> getType () == Doctrine_Relation :: MANY_COMPOSITE ) {
2006-08-22 02:04:13 +04:00
if ( ! $loadFields ) {
2006-08-16 13:17:43 +04:00
$this -> subqueryAliases [] = $tname2 ;
2006-08-22 02:04:13 +04:00
}
2006-08-16 13:17:43 +04:00
$this -> needsSubquery = true ;
}
2006-06-08 14:20:30 +04:00
2006-09-29 00:57:39 +04:00
if ( $fk instanceof Doctrine_Relation_ForeignKey ||
$fk instanceof Doctrine_Relation_LocalKey ) {
2006-06-08 17:17:15 +04:00
2006-06-26 22:55:42 +04:00
$this -> parts [ " join " ][ $tname ][ $tname2 ] = $join . $aliasString . " ON " . $tname . " . " . $fk -> getLocal () . " = " . $tname2 . " . " . $fk -> getForeign ();
2006-06-08 14:20:30 +04:00
2006-09-29 00:57:39 +04:00
} elseif ( $fk instanceof Doctrine_Relation_Association ) {
2006-05-30 12:42:10 +04:00
$asf = $fk -> getAssociationFactory ();
2006-06-08 14:20:30 +04:00
$assocTableName = $asf -> getTableName ();
2006-08-22 02:24:09 +04:00
if ( ! $loadFields ) {
$this -> subqueryAliases [] = $assocTableName ;
}
2006-09-12 01:46:01 +04:00
$this -> parts [ " join " ][ $tname ][ $assocTableName ] = $join . $assocTableName . " ON " . $tname . " . " . $table -> getIdentifier () . " = " . $assocTableName . " . " . $fk -> getLocal ();
$this -> parts [ " join " ][ $tname ][ $tname2 ] = $join . $aliasString . " ON " . $tname2 . " . " . $table -> getIdentifier () . " = " . $assocTableName . " . " . $fk -> getForeign ();
2006-05-30 12:42:10 +04:00
}
2006-06-26 03:42:19 +04:00
$this -> joins [ $tname2 ] = $prevTable ;
2006-06-08 14:20:30 +04:00
2006-06-08 17:17:15 +04:00
2006-05-30 12:42:10 +04:00
$table = $fk -> getTable ();
2006-06-26 03:42:19 +04:00
$this -> tableAliases [ $currPath ] = $tname2 ;
2006-05-30 12:42:10 +04:00
2006-06-26 03:42:19 +04:00
$tableName = $tname2 ;
2006-09-15 01:10:02 +04:00
$this -> relationStack [] = $fk ;
2006-05-30 12:42:10 +04:00
}
2006-09-15 01:10:02 +04:00
$this -> tableStack [] = $table ;
2006-05-30 12:42:10 +04:00
2006-06-26 03:42:19 +04:00
if ( ! isset ( $this -> tables [ $tableName ])) {
$this -> tables [ $tableName ] = $table ;
2006-05-30 12:42:10 +04:00
2006-08-26 03:50:55 +04:00
if ( $loadFields ) {
2006-07-05 02:36:22 +04:00
$this -> parseFields ( $fullname , $tableName , $e2 , $currPath );
2006-05-30 12:42:10 +04:00
}
}
2006-07-05 02:36:22 +04:00
2006-06-26 03:42:19 +04:00
$prevPath = $currPath ;
$prevTable = $tableName ;
2006-05-30 12:42:10 +04:00
} catch ( Exception $e ) {
2006-08-21 23:48:24 +04:00
throw new Doctrine_Query_Exception ( $e -> __toString ());
2006-05-30 12:42:10 +04:00
}
}
2006-06-12 12:44:08 +04:00
return $table ;
2006-05-30 12:42:10 +04:00
}
2006-07-05 02:36:22 +04:00
/**
* parseFields
*
* @ param string $fullName
* @ param string $tableName
* @ param array $exploded
* @ param string $currPath
* @ return void
*/
2006-08-25 22:17:20 +04:00
final public function parseFields ( $fullName , $tableName , array $exploded , $currPath ) {
2006-07-05 02:36:22 +04:00
$table = $this -> tables [ $tableName ];
$fields = array ();
if ( strpos ( $fullName , " - " ) === false ) {
$fetchmode = $table -> getAttribute ( Doctrine :: ATTR_FETCHMODE );
2006-08-25 22:17:20 +04:00
if ( isset ( $exploded [ 1 ])) {
if ( count ( $exploded ) > 2 ) {
$fields = $this -> parseAggregateValues ( $fullName , $tableName , $exploded , $currPath );
} elseif ( count ( $exploded ) == 2 ) {
$fields = explode ( " , " , substr ( $exploded [ 1 ], 0 , - 1 ));
}
}
} else {
if ( isset ( $exploded [ 1 ])) {
$fetchmode = $this -> parseFetchMode ( $exploded [ 1 ]);
} else
$fetchmode = $table -> getAttribute ( Doctrine :: ATTR_FETCHMODE );
2006-07-05 02:36:22 +04:00
2006-08-25 22:17:20 +04:00
if ( isset ( $exploded [ 2 ])) {
if ( substr_count ( $exploded [ 2 ], " ) " ) > 1 ) {
2006-07-05 02:36:22 +04:00
2006-08-25 22:17:20 +04:00
} else {
2006-07-05 02:36:22 +04:00
$fields = explode ( " , " , substr ( $exploded [ 2 ], 0 , - 1 ));
2006-08-25 22:17:20 +04:00
}
2006-07-05 02:36:22 +04:00
}
2006-08-25 22:17:20 +04:00
}
if ( ! $this -> aggregate )
2006-08-26 03:50:55 +04:00
$this -> loadFields ( $table , $fetchmode , $fields , $currPath );
2006-07-05 02:36:22 +04:00
}
2006-09-15 14:48:59 +04:00
/**
* parseAggregateFunction
*
* @ param string $func
* @ param string $reference
* @ return string
*/
2006-08-25 22:17:20 +04:00
public function parseAggregateFunction ( $func , $reference ) {
$pos = strpos ( $func , " ( " );
if ( $pos !== false ) {
$funcs = array ();
$name = substr ( $func , 0 , $pos );
$func = substr ( $func , ( $pos + 1 ), - 1 );
$params = Doctrine_Query :: bracketExplode ( $func , " , " , " ( " , " ) " );
foreach ( $params as $k => $param ) {
$params [ $k ] = $this -> parseAggregateFunction ( $param , $reference );
}
$funcs = $name . " ( " . implode ( " , " , $params ) . " ) " ;
return $funcs ;
} else {
if ( ! is_numeric ( $func )) {
$func = $this -> getTableAlias ( $reference ) . " . " . $func ;
return $func ;
} else {
2006-09-09 03:20:51 +04:00
2006-08-25 22:17:20 +04:00
return $func ;
}
}
}
2006-09-15 14:48:59 +04:00
public function parseAggregateValues ( $fullName , $tableName , array $exploded , $currPath ) {
2006-08-25 22:17:20 +04:00
$this -> aggregate = true ;
$pos = strpos ( $fullName , " ( " );
$name = substr ( $fullName , 0 , $pos );
$string = substr ( $fullName , ( $pos + 1 ), - 1 );
$exploded = Doctrine_Query :: bracketExplode ( $string , ',' );
foreach ( $exploded as $k => $value ) {
2006-09-09 03:20:51 +04:00
$func = $this -> parseAggregateFunction ( $value , $currPath );
$exploded [ $k ] = $func ;
2006-08-25 22:17:20 +04:00
$this -> parts [ " select " ][] = $exploded [ $k ];
}
}
2006-05-30 12:42:10 +04:00
}
2006-09-04 02:46:30 +04:00