1
0
mirror of synced 2025-01-18 22:41:43 +03:00

[2.0] Initial commit of a QueryBuilder class. Still needs a lot of work.

This commit is contained in:
jwage 2009-07-09 04:18:58 +00:00
parent 2ffc7f17db
commit 867a34d41b
6 changed files with 1373 additions and 0 deletions

View File

@ -0,0 +1,483 @@
<?php
/*
* $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.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
/**
* This class is used to generate DQL expressions via a set of PHP static functions
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 2.0
* @version $Revision$
*/
class Expr
{
private static $_methodMap = array(
'avg' => '_avgExpr',
'max' => '_maxExpr',
'min' => '_minExpr',
'count' => '_countExpr',
'countDistinct' => '_countDistinctExpr',
'exists' => '_existsExpr',
'all' => '_allExpr',
'some' => '_someExpr',
'any' => '_anyExpr',
'not' => '_notExpr',
'and' => '_andExpr',
'or' => '_orExpr',
'abs' => '_absExpr',
'prod' => '_prodExpr',
'diff' => '_diffExpr',
'sum' => '_sumExpr',
'quot' => '_quotientExpr',
'sqrt' => '_squareRootExpr',
'eq' => '_equalExpr',
'in' => '_inExpr',
'notIn' => '_notInExpr',
'notEqual' => '_notEqualExpr',
'like' => '_likeExpr',
'concat' => '_concatExpr',
'substr' => '_substrExpr',
'lower' => '_lowerExpr',
'upper' => '_upperExpr',
'length' => '_lengthExpr',
'gt' => '_greaterThanExpr',
'lt' => '_lessThanExpr',
'path' => '_pathExpr',
'literal' => '_literalExpr',
'gtoet' => '_greaterThanOrEqualToExpr',
'ltoet' => '_lessThanOrEqualToExpr',
'between' => '_betweenExpr',
'trim' => '_trimExpr'
);
private $_type;
private $_parts;
protected function __construct($type, array $parts)
{
$this->_type = $type;
$this->_parts = $parts;
}
public function getDql()
{
return $this->{self::$_methodMap[$this->_type]}();
}
public function __toString()
{
return $this->getDql();
}
private function _avgExpr()
{
return 'AVG(' . $this->_parts[0] . ')';
}
private function _maxExpr()
{
return 'MAX(' . $this->_parts[0] . ')';
}
private function _minExpr()
{
return 'MIN(' . $this->_parts[0] . ')';
}
private function _countExpr()
{
return 'COUNT(' . $this->_parts[0] . ')';
}
private function _countDistinctExpr()
{
return 'COUNT(DISTINCT ' . $this->_parts[0] . ')';
}
private function _existsExpr()
{
return 'EXISTS(' . $this->_parts[0] . ')';
}
private function _allExpr()
{
return 'ALL(' . $this->_parts[0] . ')';
}
private function _someExpr()
{
return 'SOME(' . $this->_parts[0] . ')';
}
private function _anyExpr()
{
return 'ANY(' . $this->_parts[0] . ')';
}
private function _notExpr()
{
return 'NOT(' . $this->_parts[0] . ')';
}
private function _andExpr()
{
return '(' . $this->_parts[0] . ' AND ' . $this->_parts[1] . ')';
}
private function _orExpr()
{
return '(' . $this->_parts[0] . ' OR ' . $this->_parts[1] . ')';
}
private function _absExpr()
{
return 'ABS(' . $this->_parts[0] . ')';
}
private function _prodExpr()
{
return '(' . $this->_parts[0] . ' * ' . $this->_parts[1] . ')';
}
private function _diffExpr()
{
return '(' . $this->_parts[0] . ' - ' . $this->_parts[1] . ')';
}
private function _sumExpr()
{
return '(' . $this->_parts[0] . ' + ' . $this->_parts[1] . ')';
}
private function _quotientExpr()
{
return '(' . $this->_parts[0] . ' / ' . $this->_parts[1] . ')';
}
private function _squareRootExpr()
{
return 'SQRT(' . $this->_parts[0] . ')';
}
private function _equalExpr()
{
return $this->_parts[0] . ' = ' . $this->_parts[1];
}
private function _inExpr()
{
return $this->_parts[0] . ' IN(' . implode(', ', $this->_parts[1]) . ')';
}
private function _notInExpr()
{
return $this->_parts[0] . ' NOT IN(' . implode(', ', $this->_parts[1]) . ')';
}
private function _notEqualExpr()
{
return $this->_parts[0] . ' != ' . $this->_parts[1];
}
private function _likeExpr()
{
// TODO: How should we use $escapeChar which is in $this->_parts[2]
return '(' . $this->_parts[0] . ' LIKE ' . $this->_parts[1] . ')';
}
private function _concatExpr()
{
return 'CONCAT(' . $this->_parts[0] . ', ' . $this->_parts[1] . ')';
}
private function _substrExpr()
{
return 'SUBSTR(' . $this->_parts[0] . ', ' . $this->_parts[1] . ', ' . $this->_parts[2] . ')';
}
private function _lowerExpr()
{
return 'LOWER(' . $this->_parts[0] . ')';
}
private function _upperExpr()
{
return 'UPPER(' . $this->_parts[0] . ')';
}
private function _lengthExpr()
{
return 'LENGTH(' . $this->_parts[0] . ')';
}
private function _greaterThanExpr()
{
return $this->_parts[0] . ' > ' . $this->_parts[1];
}
private function _lessThanExpr()
{
return $this->_parts[0] . ' < ' . $this->_parts[1];
}
private function _pathExpr()
{
// TODO: What is this?
}
private function _literalExpr()
{
if (is_numeric($this->_parts[0])) {
return (string) $this->_parts[0];
} else {
return "'" . $this->_parts[0] . "'";
}
}
private function _greaterThanOrEqualToExpr()
{
return $this->_parts[0] . ' >= ' . $this->_parts[1];
}
private function _lessThanOrEqualToExpr()
{
return $this->_parts[0] . ' <= ' . $this->_parts[1];
}
private function _betweenExpr()
{
return 'BETWEEN(' . $this->_parts[0] . ', ' . $this->_parts[1] . ', ' . $this->_parts[2] . ')';
}
private function _ltExpr()
{
return '(' . $this->_parts[0] . ' < ' . $this->_parts[1] . ')';
}
private function _trimExpr()
{
return 'TRIM(' . $this->_parts[0] . ')';
}
public static function avg($x)
{
return new self('avg', array($x));
}
public static function max($x)
{
return new self('max', array($x));
}
public static function min($x)
{
return new self('min', array($x));
}
public static function count($x)
{
return new self('count', array($x));
}
public static function countDistinct($x)
{
return new self('countDistinct', array($x));
}
public static function exists($subquery)
{
return new self('exists', array($subquery));
}
public static function all($subquery)
{
return new self('all', array($subquery));
}
public static function some($subquery)
{
return new self('some', array($subquery));
}
public static function any($subquery)
{
return new self('any', array($subquery));
}
public static function not($restriction)
{
return new self('not', array($restriction));
}
public static function andx($x, $y)
{
return new self('and', array($x, $y));
}
public static function orx($x, $y)
{
return new self('or', array($x, $y));
}
public static function abs($x)
{
return new self('abs', array($x));
}
public static function prod($x, $y)
{
return new self('prod', array($x, $y));
}
public static function diff($x, $y)
{
return new self('diff', array($x, $y));
}
public static function sum($x, $y)
{
return new self('sum', array($x, $y));
}
public static function quot($x, $y)
{
return new self('quot', array($x, $y));
}
public static function sqrt($x)
{
return new self('sqrt', array($x));
}
public static function eq($x, $y)
{
return new self('eq', array($x, $y));
}
public static function in($x, $y)
{
return new self('in', array($x, $y));
}
public static function notIn($x, $y)
{
return new self('notIn', array($x, $y));
}
public static function notEqual($x, $y)
{
return new self('notEqual', array($x, $y));
}
public static function like($x, $pattern, $escapeChar = null)
{
return new self('like', array($x, $pattern, $escapeChar));
}
public static function concat($x, $y)
{
return new self('concat', array($x, $y));
}
public static function substr($x, $from = null, $len = null)
{
return new self('substr', array($x, $from, $len));
}
public static function lower($x)
{
return new self('lower', array($x));
}
public static function upper($x)
{
return new self('upper', array($x));
}
public static function length($x)
{
return new self('length', array($x));
}
public static function gt($x, $y)
{
return new self('gt', array($x, $y));
}
public static function greaterThan($x, $y)
{
return new self('gt', array($x, $y));
}
public static function lt($x, $y)
{
return new self('lt', array($x, $y));
}
public static function lessThan($x, $y)
{
return new self('lt', array($x, $y));
}
public static function path($path)
{
return new self('path', array($path));
}
public static function literal($literal)
{
return new self('literal', array($literal));
}
public static function greaterThanOrEqualTo($x, $y)
{
return new self('gtoet', array($x, $y));
}
public static function gtoet($x, $y)
{
return new self('gtoet', array($x, $y));
}
public static function lessThanOrEqualTo($x, $y)
{
return new self('ltoet', array($x, $y));
}
public static function ltoet($x, $y)
{
return new self('ltoet', array($x, $y));
}
public static function between($val, $x, $y)
{
return new self('between', array($val, $x, $y));
}
public static function trim($val, $spec = null, $char = null)
{
return new self('trim', array($val, $spec, $char));
}
}

View File

@ -0,0 +1,357 @@
<?php
/*
* $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.doctrine-project.org>.
*/
namespace Doctrine\ORM;
use Doctrine\ORM\Query\Expr;
/**
* This class is responsible for building DQL query strings via a object oriented
* PHP interface
*
* TODO: I don't like the API of using the Expr::*() syntax inside of the QueryBuilder
* methods. What can we do to allow them to do it more fluently with the QueryBuilder.
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 2.0
* @version $Revision$
*/
class QueryBuilder
{
const SELECT = 0;
const DELETE = 1;
const UPDATE = 2;
const STATE_DIRTY = 0;
const STATE_CLEAN = 1;
protected $_entityManager;
protected $_dqlParts = array(
'select' => array(),
'from' => array(),
'where' => array(),
'groupBy' => array(),
'having' => array(),
'orderBy' => array(),
'limit' => array(),
'offset' => array()
);
protected $_type = self::SELECT;
protected $_state = self::STATE_CLEAN;
protected $_dql;
public function __construct(EntityManager $entityManager)
{
$this->_entityManager = $entityManager;
}
public static function create(EntityManager $entityManager)
{
return new self($entityManager);
}
public function getType()
{
return $this->_type;
}
public function getState()
{
return $this->_state;
}
public function getDql()
{
if ($this->_dql !== null && self::STATE_CLEAN) {
return $this->_dql;
}
$dql = '';
switch ($this->_type) {
case self::DELETE:
$dql = $this->_getDqlForDelete();
break;
case self::UPDATE:
$dql = $this->_getDqlForUpdate();
break;
case self::SELECT:
default:
$dql = $this->_getDqlForSelect();
break;
}
$this->_dql = $dql;
return $dql;
}
public function getQuery()
{
$q = new Query($this->_entityManager);
$q->setDql($this->getDql());
return $q;
}
public function select($select = null)
{
$this->_type = self::SELECT;
if ( ! $select) {
return $this;
}
return $this->_addDqlQueryPart('select', $select, true);
}
public function delete($delete = null, $alias = null)
{
$this->_type = self::DELETE;
if ( ! $delete) {
return $this;
}
return $this->_addDqlQueryPart('from', $delete . ' ' . $alias);
}
public function update($update = null, $alias = null)
{
$this->_type = self::UPDATE;
if ( ! $update) {
return $this;
}
return $this->_addDqlQueryPart('from', $update . ' ' . $alias);
}
public function set($key, $value = null)
{
return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
}
public function from($from, $alias)
{
return $this->_addDqlQueryPart('from', $from . ' ' . $alias, true);
}
public function join($join, $alias)
{
return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join . ' ' . $alias, true);
}
public function innerJoin($join, $alias)
{
return $this->join($join, $alias);
}
public function leftJoin($join, $alias)
{
return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join . ' ' . $alias, true);
}
public function where($where)
{
return $this->_addDqlQueryPart('where', $where, false);
}
public function andWhere($where)
{
if (count($this->getDqlQueryPart('where')) > 0) {
$this->_addDqlQueryPart('where', 'AND', true);
}
return $this->_addDqlQueryPart('where', $where, true);
}
public function orWhere($where)
{
if (count($this->getDqlQueryPart('where')) > 0) {
$this->_addDqlQueryPart('where', 'OR', true);
}
return $this->_addDqlQueryPart('where', $where, true);
}
public function groupBy($groupBy)
{
return $this->_addDqlQueryPart('groupBy', $groupBy, false);
}
public function having($having)
{
return $this->_addDqlQueryPart('having', $having, false);
}
public function andHaving($having)
{
if (count($this->getDqlQueryPart('having')) > 0) {
$this->_addDqlQueryPart('having', 'AND', true);
}
return $this->_addDqlQueryPart('having', $having, true);
}
public function orHaving($having)
{
if (count($this->getDqlQueryPart('having')) > 0) {
$this->_addDqlQueryPart('having', 'OR', true);
}
return $this->_addDqlQueryPart('having', $having, true);
}
public function orderBy($sort, $order)
{
return $this->_addDqlQueryPart('orderBy', $sort . ' ' . $order, false);
}
public function addOrderBy($sort, $order)
{
return $this->_addDqlQueryPart('orderBy', $sort . ' ' . $order, true);
}
public function limit($limit)
{
return $this->_addDqlQueryPart('limit', $limit);
}
public function offset($offset)
{
return $this->_addDqlQueryPart('offset', $offset);
}
/**
* Get the DQL query string for DELETE queries
*
* BNF:
*
* DeleteStatement = DeleteClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
* DeleteClause = "DELETE" "FROM" RangeVariableDeclaration
* WhereClause = "WHERE" ConditionalExpression
* OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
* LimitClause = "LIMIT" integer
* OffsetClause = "OFFSET" integer
*
* @return string $dql
*/
private function _getDqlForDelete()
{
return 'DELETE'
. $this->_getReducedDqlQueryPart('from', array('pre' => ' ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' '));
}
/**
* Get the DQL query string for UPDATE queries
*
* BNF:
*
* UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
* UpdateClause = "UPDATE" RangeVariableDeclaration "SET" UpdateItem {"," UpdateItem}
* WhereClause = "WHERE" ConditionalExpression
* OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
* LimitClause = "LIMIT" integer
* OffsetClause = "OFFSET" integer
*
* @return string $dql
*/
private function _getDqlForUpdate()
{
return 'UPDATE'
. $this->_getReducedDqlQueryPart('from', array('pre' => ' ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('set', array('pre' => ' SET ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' '));
}
/**
* Get the DQL query string for SELECT queries
*
* BNF:
*
* SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause]
* SelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}
* FromClause = "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}
* WhereClause = "WHERE" ConditionalExpression
* GroupByClause = "GROUP" "BY" GroupByItem {"," GroupByItem}
* HavingClause = "HAVING" ConditionalExpression
* OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
* LimitClause = "LIMIT" integer
* OffsetClause = "OFFSET" integer
*
* @return string $dql
*/
private function _getDqlForSelect()
{
return 'SELECT'
. $this->_getReducedDqlQueryPart('select', array('pre' => ' ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('from', array('pre' => ' FROM ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('where', array('pre' => ' WHERE ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('having', array('pre' => ' HAVING ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', '))
. $this->_getReducedDqlQueryPart('limit', array('pre' => ' LIMIT ', 'separator' => ' '))
. $this->_getReducedDqlQueryPart('offset', array('pre' => ' OFFSET ', 'separator' => ' '));
}
private function _getReducedDqlQueryPart($queryPartName, $options = array())
{
if (empty($this->_dqlParts[$queryPartName])) {
return (isset($options['empty']) ? $options['empty'] : '');
}
$str = (isset($options['pre']) ? $options['pre'] : '');
$str .= implode($options['separator'], $this->getDqlQueryPart($queryPartName));
$str .= (isset($options['post']) ? $options['post'] : '');
return $str;
}
private function getDqlQueryPart($queryPartName)
{
return $this->_dqlParts[$queryPartName];
}
private function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
{
if ($append) {
$this->_dqlParts[$queryPartName][] = $queryPart;
} else {
$this->_dqlParts[$queryPartName] = array($queryPart);
}
$this->_state = self::STATE_DIRTY;
return $this;
}
}

View File

@ -31,6 +31,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\UnitOfWorkTest');
$suite->addTestSuite('Doctrine\Tests\ORM\EntityManagerTest');
$suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest');
$suite->addTest(Query\AllTests::suite());
$suite->addTest(Hydration\AllTests::suite());

View File

@ -24,6 +24,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Query\LexerTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\DeleteSqlGenerationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\UpdateSqlGenerationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Query\ExprTest');
return $suite;
}

View File

@ -0,0 +1,231 @@
<?php
/*
* $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.doctrine-project.org>.
*/
namespace Doctrine\Tests\ORM\Query;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query;
require_once __DIR__ . '/../../TestInit.php';
/**
* Test case for the DQL Expr class used for generating DQL snippets through
* a programmatic interface
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 2.0
* @version $Revision$
*/
class ExprTest extends \Doctrine\Tests\OrmTestCase
{
private $_em;
protected function setUp()
{
$this->_em = $this->_getTestEntityManager();
}
public function testAvgExpr()
{
$this->assertEquals('AVG(u.id)', (string) Expr::avg('u.id'));
}
public function testMaxExpr()
{
$this->assertEquals('MAX(u.id)', (string) Expr::max('u.id'));
}
public function testMinExpr()
{
$this->assertEquals('MIN(u.id)', (string) Expr::min('u.id'));
}
public function testCountExpr()
{
$this->assertEquals('MAX(u.id)', (string) Expr::max('u.id'));
}
public function testCountDistinctExpr()
{
$this->assertEquals('COUNT(DISTINCT u.id)', (string) Expr::countDistinct('u.id'));
}
public function testExistsExpr()
{
$this->assertEquals('EXISTS(SUBQUERY)', (string) Expr::exists('SUBQUERY'));
}
public function testAllExpr()
{
$this->assertEquals('ALL(SUBQUERY)', (string) Expr::all('SUBQUERY'));
}
public function testSomeExpr()
{
$this->assertEquals('SOME(SUBQUERY)', (string) Expr::some('SUBQUERY'));
}
public function testAnyExpr()
{
$this->assertEquals('ANY(SUBQUERY)', (string) Expr::any('SUBQUERY'));
}
public function testNotExpr()
{
$this->assertEquals('NOT(SUBQUERY)', (string) Expr::not('SUBQUERY'));
}
public function testAndExpr()
{
$this->assertEquals('(1 = 1 AND 2 = 2)', (string) Expr::andx((string) Expr::eq(1, 1), (string) Expr::eq(2, 2)));
}
public function testOrExpr()
{
$this->assertEquals('(1 = 1 OR 2 = 2)', (string) Expr::orx((string) Expr::eq(1, 1), (string) Expr::eq(2, 2)));
}
public function testAbsExpr()
{
$this->assertEquals('ABS(1)', (string) Expr::abs(1));
}
public function testProdExpr()
{
$this->assertEquals('(1 * 2)', (string) Expr::prod(1, 2));
}
public function testDiffExpr()
{
$this->assertEquals('(1 - 2)', (string) Expr::diff(1, 2));
}
public function testSumExpr()
{
$this->assertEquals('(1 + 2)', (string) Expr::sum(1, 2));
}
public function testQuotientExpr()
{
$this->assertEquals('(10 / 2)', (string) Expr::quot(10, 2));
}
public function testSquareRootExpr()
{
$this->assertEquals('SQRT(1)', (string) Expr::sqrt(1));
}
public function testEqualExpr()
{
$this->assertEquals('1 = 1', (string) Expr::eq(1, 1));
}
public function testNotEqualExpr()
{
$this->assertEquals('1 != 2', (string) Expr::notEqual(1, 2));
}
public function testLikeExpr()
{
$this->assertEquals('(a.description LIKE :description)', (string) Expr::like('a.description', ':description'));
}
public function testConcatExpr()
{
$this->assertEquals('CONCAT(u.first_name, u.last_name)', (string) Expr::concat('u.first_name', 'u.last_name'));
}
public function testSubstrExpr()
{
$this->assertEquals('SUBSTR(a.title, 0, 25)', (string) Expr::substr('a.title', 0, 25));
}
public function testLowerExpr()
{
$this->assertEquals('LOWER(u.first_name)', (string) Expr::lower('u.first_name'));
}
public function testUpperExpr()
{
$this->assertEquals('UPPER(u.first_name)', (string) Expr::upper('u.first_name'));
}
public function testLengthExpr()
{
$this->assertEquals('LENGTH(u.first_name)', (string) Expr::length('u.first_name'));
}
public function testGreaterThanExpr()
{
$this->assertEquals('5 > 2', (string) Expr::gt(5, 2));
$this->assertEquals('5 > 2', (string) Expr::greaterThan(5, 2));
}
public function testLessThanExpr()
{
$this->assertEquals('2 < 5', (string) Expr::lt(2, 5));
$this->assertEquals('2 < 5', (string) Expr::lessThan(2, 5));
}
public function testPathExpr()
{
// TODO: This functionality still needs to be written and tested
}
public function testStringLiteralExpr()
{
$this->assertEquals("'word'", (string) Expr::literal('word'));
}
public function testNumericLiteralExpr()
{
$this->assertEquals(5, (string) Expr::literal(5));
}
public function testGreaterThanOrEqualToExpr()
{
$this->assertEquals('5 >= 2', (string) Expr::gtoet(5, 2));
$this->assertEquals('5 >= 2', (string) Expr::greaterThanOrEqualTo(5, 2));
}
public function testLessThanOrEqualTo()
{
$this->assertEquals('2 <= 5', (string) Expr::ltoet(2, 5));
$this->assertEquals('2 <= 5', (string) Expr::lessThanOrEqualTo(2, 5));
}
public function testBetweenExpr()
{
$this->assertEquals('BETWEEN(u.id, 3, 6)', (string) Expr::between('u.id', 3, 6));
}
public function testTrimExpr()
{
$this->assertEquals('TRIM(u.id)', (string) Expr::trim('u.id'));
}
public function testInExpr()
{
$this->assertEquals('u.id IN(1, 2, 3)', (string) Expr::in('u.id', array(1, 2, 3)));
}
}

View File

@ -0,0 +1,300 @@
<?php
/*
* $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.doctrine-project.org>.
*/
namespace Doctrine\Tests\ORM;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Expr;
require_once __DIR__ . '/../TestInit.php';
/**
* Test case for the QueryBuilder class used to build DQL query string in a
* object oriented way.
*
* @author Jonathan H. Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
* @since 2.0
* @version $Revision$
*/
class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
{
private $_em;
protected function setUp()
{
$this->_em = $this->_getTestEntityManager();
}
protected function assertValidQueryBuilder(QueryBuilder $qb, $expectedDql)
{
$dql = $qb->getDql();
$q = $qb->getQuery();
try {
$q->getSql();
} catch (\Exception $e) {
echo $dql . "\n";
echo $e->getTraceAsString();
$this->fail($e->getMessage());
}
$this->assertEquals($expectedDql, $dql);
}
public function testSimpleSelect()
{
$qb = QueryBuilder::create($this->_em)
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->select('u.id, u.username');
$this->assertValidQueryBuilder($qb, 'SELECT u.id, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testSimpleDelete()
{
$qb = QueryBuilder::create($this->_em)
->delete('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$this->assertValidQueryBuilder($qb, 'DELETE Doctrine\Tests\Models\CMS\CmsUser u');
}
public function testSimpleUpdate()
{
$qb = QueryBuilder::create($this->_em)
->update('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->set('u.username', ':username', 'jonwage');
$this->assertValidQueryBuilder($qb, 'UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.username = :username');
}
public function testJoin()
{
$qb = QueryBuilder::create($this->_em)
->select('u, a')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->join('u.articles', 'a');
$this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a');
}
public function testInnerJoin()
{
$qb = QueryBuilder::create($this->_em)
->select('u, a')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->innerJoin('u.articles', 'a');
$this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a');
}
public function testLeftJoin()
{
$qb = QueryBuilder::create($this->_em)
->select('u, a')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->leftJoin('u.articles', 'a');
$this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a');
}
public function testWhere()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :uid')
->where('u.id = :id');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id');
}
public function testAndWhere()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id')
->andWhere('u.username = :username');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id AND u.username = :username');
}
public function testOrWhere()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id')
->orWhere('u.username = :username');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id OR u.username = :username');
}
/*
public function testWhereIn()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->whereIn('u.id', array(1));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN(1)');
}
public function testWhereNotIn()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->whereNotIn('u.id', array(1));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN(1)');
}
public function testAndWhereIn()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id')
->andWhereIn('u.id', array(1, 2, 3));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id AND u.id IN(1, 2, 3)');
}
public function testAndWhereNotIn()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id')
->andWhereNotIn('u.id', array(1, 2, 3));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id AND u.id NOT IN(1, 2, 3)');
}
public function testOrWhereIn()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id')
->orWhereIn('u.id', array(1, 2, 3));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id OR u.id IN(1, 2, 3)');
}
public function testOrWhereNotIn()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id')
->orWhereNotIn('u.id', array(1, 2, 3));
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = :id OR u.id NOT IN(1, 2, 3)');
}
*/
public function testGroupBy()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->groupBy('u.id');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id');
}
public function testHaving()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->groupBy('u.id')
->having('COUNT(u.id) > 1');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id HAVING COUNT(u.id) > 1');
}
public function testAndHaving()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->groupBy('u.id')
->having('COUNT(u.id) > 1')
->andHaving('COUNT(u.id) < 1');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id HAVING COUNT(u.id) > 1 AND COUNT(u.id) < 1');
}
public function testOrHaving()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->groupBy('u.id')
->having('COUNT(u.id) > 1')
->andHaving('COUNT(u.id) < 1')
->orHaving('COUNT(u.id) > 1');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u.id HAVING COUNT(u.id) > 1 AND COUNT(u.id) < 1 OR COUNT(u.id) > 1');
}
public function testOrderBy()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->orderBy('u.username', 'ASC');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username ASC');
}
public function testAddOrderBy()
{
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->orderBy('u.username', 'ASC')
->addOrderBy('u.username', 'DESC');
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username ASC, u.username DESC');
}
public function testLimit()
{
/*
TODO: Limit fails. Is this not implemented in the DQL parser? Will look tomorrow.
$qb = QueryBuilder::create($this->_em)
->select('u')
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->limit(10)
->offset(0);
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LIMIT 10');
*/
}
}