Merge commit 'upstream/master'
Conflicts: lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php
This commit is contained in:
commit
3fc9971e98
@ -52,6 +52,7 @@ $cli->addCommands(array(
|
||||
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
|
||||
|
||||
));
|
||||
$cli->run();
|
@ -179,7 +179,7 @@ class AnnotationReader
|
||||
// Attempt to grab data from cache
|
||||
if (($data = $this->_cache->fetch($cacheKey)) !== false) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
$context = 'method ' . $method->getDeclaringClass()->getName() . '::' . $method->getName() . '()';
|
||||
$annotations = $this->_parser->parse($method->getDocComment(), $context);
|
||||
|
@ -310,9 +310,8 @@ class Connection implements DriverConnection
|
||||
* @param string $statement The SQL query.
|
||||
* @param array $params The query parameters.
|
||||
* @return array
|
||||
* @todo Rename: fetchAssoc
|
||||
*/
|
||||
public function fetchRow($statement, array $params = array())
|
||||
public function fetchAssoc($statement, array $params = array())
|
||||
{
|
||||
return $this->executeQuery($statement, $params)->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
@ -583,10 +582,10 @@ class Connection implements DriverConnection
|
||||
* represents a row of the result set.
|
||||
* @return mixed The projected result of the query.
|
||||
*/
|
||||
public function project($query, array $params = array(), Closure $function)
|
||||
public function project($query, array $params, Closure $function)
|
||||
{
|
||||
$result = array();
|
||||
$stmt = $this->executeQuery($query, $params);
|
||||
$stmt = $this->executeQuery($query, $params ?: array());
|
||||
|
||||
while ($row = $stmt->fetch()) {
|
||||
$result[] = $function($row);
|
||||
@ -789,7 +788,7 @@ class Connection implements DriverConnection
|
||||
* Gets the SchemaManager that can be used to inspect or change the
|
||||
* database schema through the connection.
|
||||
*
|
||||
* @return Doctrine\DBAL\Schema\AbstractSchemaManager
|
||||
* @return Doctrine\DBAL\Schema\SchemaManager
|
||||
*/
|
||||
public function getSchemaManager()
|
||||
{
|
||||
@ -820,7 +819,7 @@ class Connection implements DriverConnection
|
||||
* @return boolean
|
||||
* @throws ConnectionException If no transaction is active.
|
||||
*/
|
||||
public function getRollbackOnly()
|
||||
public function isRollbackOnly()
|
||||
{
|
||||
if ($this->_transactionNestingLevel == 0) {
|
||||
throw ConnectionException::noActiveTransaction();
|
||||
@ -911,4 +910,4 @@ class Connection implements DriverConnection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,21 @@
|
||||
<?php
|
||||
/*
|
||||
* 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\DBAL;
|
||||
|
||||
@ -51,5 +68,5 @@ interface Driver
|
||||
* @param Doctrine\DBAL\Connection $conn
|
||||
* @return string $database
|
||||
*/
|
||||
public function getDatabase(\Doctrine\DBAL\Connection $conn);
|
||||
public function getDatabase(Connection $conn);
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -25,13 +23,10 @@ use Doctrine\DBAL\Driver,
|
||||
Doctrine\DBAL\Connection;
|
||||
|
||||
/**
|
||||
* IBM Db2 Driver
|
||||
* IBM DB2 Driver
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class DB2Driver implements Driver
|
||||
{
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -21,17 +19,17 @@
|
||||
|
||||
namespace Doctrine\DBAL\Driver\PDOMsSql;
|
||||
|
||||
use PDO, Doctrine\DBAL\Driver\Connection as DriverConnection;
|
||||
|
||||
/**
|
||||
* MsSql Connection implementation.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class Connection extends \PDO implements \Doctrine\DBAL\Driver\Connection
|
||||
class Connection extends PDO implements DriverConnection
|
||||
{
|
||||
/**
|
||||
* Performs the rollback.
|
||||
*
|
||||
* @override
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
@ -39,9 +37,7 @@ class Connection extends \PDO implements \Doctrine\DBAL\Driver\Connection
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the commit.
|
||||
*
|
||||
* @override
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
@ -49,12 +45,21 @@ class Connection extends \PDO implements \Doctrine\DBAL\Driver\Connection
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins a database transaction.
|
||||
*
|
||||
* @override
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->exec('BEGIN TRANSACTION');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function lastInsertId($name = null)
|
||||
{
|
||||
$stmt = $this->query('SELECT SCOPE_IDENTITY()');
|
||||
$id = $stmt->fetchColumn();
|
||||
$stmt->closeCursor();
|
||||
return $id;
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
|
@ -1864,4 +1864,4 @@ abstract class AbstractPlatform
|
||||
{
|
||||
return 'TRUNCATE '.$tableName;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -30,10 +28,7 @@ use Doctrine\Common\EventManager,
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
@ -154,11 +149,12 @@ class EntityManager
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder();
|
||||
* $expr = $em->getExpressionBuilder();
|
||||
* $qb->select('u')->from('User', 'u')
|
||||
* ->where($expr->orX($expr->eq('u.id', 1), $expr->eq('u.id', 2)));
|
||||
* </code>
|
||||
*
|
||||
* @return ExpressionBuilder
|
||||
*/
|
||||
@ -172,6 +168,8 @@ class EntityManager
|
||||
|
||||
/**
|
||||
* Starts a transaction on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.beginTransaction().
|
||||
*/
|
||||
public function beginTransaction()
|
||||
{
|
||||
@ -180,6 +178,8 @@ class EntityManager
|
||||
|
||||
/**
|
||||
* Commits a transaction on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.commit().
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
@ -187,13 +187,13 @@ class EntityManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rollback on the underlying database connection and closes the
|
||||
* EntityManager as it may now be in a corrupted state.
|
||||
* Performs a rollback on the underlying database connection.
|
||||
*
|
||||
* @deprecated Use {@link getConnection}.rollback().
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->_conn->rollback();
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -274,6 +274,9 @@ class EntityManager
|
||||
* Flushes all changes to objects that have been queued up to now to the database.
|
||||
* This effectively synchronizes the in-memory state of managed objects with the
|
||||
* database.
|
||||
*
|
||||
* @throws Doctrine\ORM\OptimisticLockException If a version check on an entity that
|
||||
* makes use of optimistic locking fails.
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
|
@ -21,23 +21,36 @@ namespace Doctrine\ORM\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
/**
|
||||
* Id generator that obtains IDs from special "identity" columns. These are columns
|
||||
* that automatically get a database-generated, auto-incremented identifier on INSERT.
|
||||
* This generator obtains the last insert id after such an insert.
|
||||
*/
|
||||
class IdentityGenerator extends AbstractIdGenerator
|
||||
{
|
||||
/** @var string The name of the sequence to pass to lastInsertId(), if any. */
|
||||
private $_seqName;
|
||||
|
||||
/**
|
||||
* Generates an ID for the given entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return integer|float
|
||||
* @override
|
||||
* @param string $seqName The name of the sequence to pass to lastInsertId()
|
||||
* to obtain the last generated identifier within the current
|
||||
* database session/connection, if any.
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
public function __construct($seqName = null)
|
||||
{
|
||||
return $em->getConnection()->lastInsertId();
|
||||
$this->_seqName = $seqName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @override
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
return $em->getConnection()->lastInsertId($this->_seqName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPostInsertGenerator()
|
||||
{
|
||||
|
@ -1,46 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* 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\Id;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
class SequenceIdentityGenerator extends IdentityGenerator
|
||||
{
|
||||
private $_sequenceName;
|
||||
|
||||
public function __construct($sequenceName)
|
||||
{
|
||||
$this->_sequenceName = $sequenceName;
|
||||
}
|
||||
|
||||
public function generate(EntityManager $em, $entity)
|
||||
{
|
||||
return $em->getConnection()->lastInsertId($this->_sequenceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @override
|
||||
*/
|
||||
public function isPostInsertGenerator()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -19,8 +19,10 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\ORM\ORMException,
|
||||
Doctrine\DBAL\Platforms\AbstractPlatform,
|
||||
use ReflectionException,
|
||||
Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
Doctrine\DBAL\Platforms,
|
||||
Doctrine\ORM\Events;
|
||||
|
||||
/**
|
||||
@ -53,7 +55,7 @@ class ClassMetadataFactory
|
||||
*
|
||||
* @param $driver The metadata driver to use.
|
||||
*/
|
||||
public function __construct(\Doctrine\ORM\EntityManager $em)
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
@ -94,15 +96,15 @@ class ClassMetadataFactory
|
||||
if ( ! $this->_initialized) {
|
||||
$this->_initialize();
|
||||
}
|
||||
|
||||
|
||||
$metadata = array();
|
||||
foreach ($this->_driver->getAllClassNames() as $className) {
|
||||
$metadata[] = $this->getMetadataFor($className);
|
||||
}
|
||||
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lazy initialization of this stuff, especially the metadata driver,
|
||||
* since these are not needed at all when a metadata cache is active.
|
||||
@ -252,7 +254,7 @@ class ClassMetadataFactory
|
||||
// Invoke driver
|
||||
try {
|
||||
$this->_driver->loadMetadataForClass($className, $class);
|
||||
} catch(\ReflectionException $e) {
|
||||
} catch(ReflectionException $e) {
|
||||
throw MappingException::reflectionFailure($className, $e);
|
||||
}
|
||||
|
||||
@ -275,9 +277,9 @@ class ClassMetadataFactory
|
||||
} else {
|
||||
$this->_completeIdGeneratorMapping($class);
|
||||
}
|
||||
|
||||
|
||||
if ($parent && $parent->isInheritanceTypeSingleTable()) {
|
||||
$class->setTableName($parent->getTableName());
|
||||
$class->setPrimaryTable($parent->table);
|
||||
}
|
||||
|
||||
$class->setParentClasses($visited);
|
||||
@ -376,7 +378,13 @@ class ClassMetadataFactory
|
||||
// Create & assign an appropriate ID generator instance
|
||||
switch ($class->generatorType) {
|
||||
case ClassMetadata::GENERATOR_TYPE_IDENTITY:
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator());
|
||||
// For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
|
||||
// <table>_<column>_seq in PostgreSQL for SERIAL columns.
|
||||
// Not pretty but necessary and the simplest solution that currently works.
|
||||
$seqName = $this->_targetPlatform instanceof Platforms\PostgreSQLPlatform ?
|
||||
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
|
||||
null;
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
|
||||
// If there is no sequence definition yet, create a default definition
|
||||
|
@ -317,7 +317,7 @@ class ClassMetadataInfo
|
||||
* READ-ONLY: The ID generator used for generating IDs for this class.
|
||||
*
|
||||
* @var AbstractIdGenerator
|
||||
* @todo Remove
|
||||
* @todo Remove!
|
||||
*/
|
||||
public $idGenerator;
|
||||
|
||||
@ -335,6 +335,7 @@ class ClassMetadataInfo
|
||||
* </code>
|
||||
*
|
||||
* @var array
|
||||
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
|
||||
*/
|
||||
public $sequenceGeneratorDefinition;
|
||||
|
||||
@ -343,6 +344,7 @@ class ClassMetadataInfo
|
||||
* TABLE generation strategy.
|
||||
*
|
||||
* @var array
|
||||
* @todo Merge with tableGeneratorDefinition into generic generatorDefinition
|
||||
*/
|
||||
public $tableGeneratorDefinition;
|
||||
|
||||
@ -901,8 +903,8 @@ class ClassMetadataInfo
|
||||
/**
|
||||
* Sets the name of the primary table the class is mapped to.
|
||||
*
|
||||
* @param string $tableName The table name.
|
||||
* @deprecated
|
||||
* @param string $tableName The table name.
|
||||
* @deprecated Use {@link setPrimaryTable}.
|
||||
*/
|
||||
public function setTableName($tableName)
|
||||
{
|
||||
@ -910,18 +912,22 @@ class ClassMetadataInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the primary table definition. The provided array must have the
|
||||
* Sets the primary table definition. The provided array supports the
|
||||
* following structure:
|
||||
*
|
||||
* name => <tableName>
|
||||
* schema => <schemaName>
|
||||
* catalog => <catalogName>
|
||||
* name => <tableName> (optional, defaults to class name)
|
||||
* indexes => array of indexes (optional)
|
||||
* uniqueConstraints => array of constraints (optional)
|
||||
*
|
||||
* @param array $primaryTableDefinition
|
||||
* @param array $table
|
||||
*/
|
||||
public function setPrimaryTable(array $primaryTableDefinition)
|
||||
public function setPrimaryTable(array $table)
|
||||
{
|
||||
$this->table = $primaryTableDefinition;
|
||||
if (isset($table['name']) && $table['name'][0] == '`') {
|
||||
$table['name'] = trim($table['name'], '`');
|
||||
$table['quoted'] = true;
|
||||
}
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -31,14 +29,11 @@ require __DIR__ . '/DoctrineAnnotations.php';
|
||||
/**
|
||||
* The AnnotationDriver reads the mapping metadata from docblock annotations.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class AnnotationDriver implements Driver
|
||||
{
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
|
@ -62,14 +62,18 @@ class XmlDriver extends AbstractFileDriver
|
||||
}
|
||||
|
||||
// Evaluate <entity...> attributes
|
||||
$table = array();
|
||||
if (isset($xmlRoot['table'])) {
|
||||
$metadata->table['name'] = (string)$xmlRoot['table'];
|
||||
$table['name'] = (string)$xmlRoot['table'];
|
||||
}
|
||||
|
||||
$metadata->setPrimaryTable($table);
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($xmlRoot['schema'])) {
|
||||
$metadata->table['schema'] = (string)$xmlRoot['schema'];
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
if (isset($xmlRoot['inheritance-type'])) {
|
||||
$inheritanceType = (string)$xmlRoot['inheritance-type'];
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
|
||||
@ -479,4 +483,4 @@ class XmlDriver extends AbstractFileDriver
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -27,14 +25,11 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo,
|
||||
/**
|
||||
* The YamlDriver reads the mapping metadata from yaml schema files.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan H. Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class YamlDriver extends AbstractFileDriver
|
||||
{
|
||||
@ -61,13 +56,16 @@ class YamlDriver extends AbstractFileDriver
|
||||
}
|
||||
|
||||
// Evaluate root level properties
|
||||
$table = array();
|
||||
if (isset($element['table'])) {
|
||||
$metadata->table['name'] = $element['table'];
|
||||
$table['name'] = $element['table'];
|
||||
}
|
||||
$metadata->setPrimaryTable($table);
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($element['schema'])) {
|
||||
$metadata->table['schema'] = $element['schema'];
|
||||
}
|
||||
}*/
|
||||
|
||||
if (isset($element['inheritanceType'])) {
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType'])));
|
||||
|
@ -19,13 +19,15 @@
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Base exception class for all ORM exceptions.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ORMException extends \Exception
|
||||
class ORMException extends Exception
|
||||
{
|
||||
public static function missingMappingDriverImpl()
|
||||
{
|
||||
|
@ -20,15 +20,33 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
/**
|
||||
* OptimisticLockException
|
||||
* An OptimisticLockException is thrown when a version check on an object
|
||||
* that uses optimistic locking through a version field fails.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
class OptimisticLockException extends ORMException
|
||||
{
|
||||
public static function lockFailed()
|
||||
private $entity;
|
||||
|
||||
public function __construct($msg, $entity)
|
||||
{
|
||||
return new self("The optimistic lock failed.");
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity that caused the exception.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getEntity()
|
||||
{
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
public static function lockFailed($entity)
|
||||
{
|
||||
return new self("The optimistic lock on an entity failed.", $entity);
|
||||
}
|
||||
}
|
@ -330,7 +330,7 @@ class BasicEntityPersister
|
||||
$result = $this->_conn->executeUpdate($sql, $params, $types);
|
||||
|
||||
if ($this->_class->isVersioned && ! $result) {
|
||||
throw OptimisticLockException::lockFailed();
|
||||
throw OptimisticLockException::lockFailed($entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,11 +267,11 @@ class Parser
|
||||
{
|
||||
$AST = $this->getAST();
|
||||
|
||||
if ($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) {
|
||||
if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
|
||||
$this->_customTreeWalkers = $customWalkers;
|
||||
}
|
||||
|
||||
if ($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) {
|
||||
if (($customOutputWalker = $this->_query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER)) !== false) {
|
||||
$this->_customOutputWalker = $customOutputWalker;
|
||||
}
|
||||
|
||||
@ -1786,6 +1786,12 @@ class Parser
|
||||
$conditionalTerms[] = $this->ConditionalTerm();
|
||||
}
|
||||
|
||||
// Phase 1 AST optimization: Prevent AST\ConditionalExpression
|
||||
// if only one AST\ConditionalTerm is defined
|
||||
if (count($conditionalTerms) == 1) {
|
||||
return $conditionalTerms[0];
|
||||
}
|
||||
|
||||
return new AST\ConditionalExpression($conditionalTerms);
|
||||
}
|
||||
|
||||
@ -1804,6 +1810,12 @@ class Parser
|
||||
$conditionalFactors[] = $this->ConditionalFactor();
|
||||
}
|
||||
|
||||
// Phase 1 AST optimization: Prevent AST\ConditionalTerm
|
||||
// if only one AST\ConditionalFactor is defined
|
||||
if (count($conditionalFactors) == 1) {
|
||||
return $conditionalFactors[0];
|
||||
}
|
||||
|
||||
return new AST\ConditionalTerm($conditionalFactors);
|
||||
}
|
||||
|
||||
@ -1820,11 +1832,19 @@ class Parser
|
||||
$this->match(Lexer::T_NOT);
|
||||
$not = true;
|
||||
}
|
||||
|
||||
$conditionalPrimary = $this->ConditionalPrimary();
|
||||
|
||||
$condFactor = new AST\ConditionalFactor($this->ConditionalPrimary());
|
||||
$condFactor->not = $not;
|
||||
// Phase 1 AST optimization: Prevent AST\ConditionalFactor
|
||||
// if only one AST\ConditionalPrimary is defined
|
||||
if ( ! $not) {
|
||||
return $conditionalPrimary;
|
||||
}
|
||||
|
||||
return $condFactor;
|
||||
$conditionalFactor = new AST\ConditionalFactor($conditionalPrimary);
|
||||
$conditionalFactor->not = $not;
|
||||
|
||||
return $conditionalFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2104,6 +2124,12 @@ class Parser
|
||||
$terms[] = $this->ArithmeticTerm();
|
||||
}
|
||||
|
||||
// Phase 1 AST optimization: Prevent AST\SimpleArithmeticExpression
|
||||
// if only one AST\ArithmeticTerm is defined
|
||||
if (count($terms) == 1) {
|
||||
return $terms[0];
|
||||
}
|
||||
|
||||
return new AST\SimpleArithmeticExpression($terms);
|
||||
}
|
||||
|
||||
@ -2124,6 +2150,12 @@ class Parser
|
||||
$factors[] = $this->ArithmeticFactor();
|
||||
}
|
||||
|
||||
// Phase 1 AST optimization: Prevent AST\ArithmeticTerm
|
||||
// if only one AST\ArithmeticFactor is defined
|
||||
if (count($factors) == 1) {
|
||||
return $factors[0];
|
||||
}
|
||||
|
||||
return new AST\ArithmeticTerm($factors);
|
||||
}
|
||||
|
||||
@ -2134,14 +2166,22 @@ class Parser
|
||||
*/
|
||||
public function ArithmeticFactor()
|
||||
{
|
||||
$sign = null;
|
||||
$sign = null;
|
||||
|
||||
if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
|
||||
$this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
|
||||
$sign = $isPlus;
|
||||
}
|
||||
if (($isPlus = $this->_lexer->isNextToken(Lexer::T_PLUS)) || $this->_lexer->isNextToken(Lexer::T_MINUS)) {
|
||||
$this->match(($isPlus) ? Lexer::T_PLUS : Lexer::T_MINUS);
|
||||
$sign = $isPlus;
|
||||
}
|
||||
|
||||
$primary = $this->ArithmeticPrimary();
|
||||
|
||||
return new AST\ArithmeticFactor($this->ArithmeticPrimary(), $sign);
|
||||
// Phase 1 AST optimization: Prevent AST\ArithmeticFactor
|
||||
// if only one AST\ArithmeticPrimary is defined
|
||||
if ($sign === null) {
|
||||
return $primary;
|
||||
}
|
||||
|
||||
return new AST\ArithmeticFactor($primary, $sign);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -665,9 +665,7 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkHavingClause($havingClause)
|
||||
{
|
||||
return ' HAVING ' . implode(
|
||||
' OR ', array_map(array($this, 'walkConditionalTerm'), $havingClause->conditionalExpression->conditionalTerms)
|
||||
);
|
||||
return ' HAVING ' . $this->walkConditionalExpression($havingClause->conditionalExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -775,10 +773,10 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
// Handle WITH clause
|
||||
if ($join->conditionalExpression !== null) {
|
||||
$sql .= ' AND (' . implode(' OR ',
|
||||
array_map(array($this, 'walkConditionalTerm'), $join->conditionalExpression->conditionalTerms)
|
||||
). ')';
|
||||
if (($condExpr = $join->conditionalExpression) !== null) {
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
}
|
||||
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($joinedDqlAlias);
|
||||
@ -787,7 +785,7 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= ' AND ' . $discrSql;
|
||||
}
|
||||
|
||||
//FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
|
||||
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
|
||||
if ($targetClass->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
|
||||
}
|
||||
@ -879,7 +877,12 @@ class SqlWalker implements TreeWalker
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
else if ($expr instanceof AST\SimpleArithmeticExpression) {
|
||||
else if (
|
||||
$expr instanceof AST\SimpleArithmeticExpression ||
|
||||
$expr instanceof AST\ArithmeticTerm ||
|
||||
$expr instanceof AST\ArithmeticFactor ||
|
||||
$expr instanceof AST\ArithmeticPrimary
|
||||
) {
|
||||
if ( ! $selectExpression->fieldIdentificationVariable) {
|
||||
$resultAlias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
@ -1211,20 +1214,27 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkWhereClause($whereClause)
|
||||
{
|
||||
$sql = ' WHERE ';
|
||||
$condExpr = $whereClause->conditionalExpression;
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_currentRootAlias);
|
||||
$condSql = $this->walkConditionalExpression($whereClause->conditionalExpression);
|
||||
|
||||
$sql .= implode(
|
||||
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
|
||||
);
|
||||
return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql);
|
||||
}
|
||||
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias);
|
||||
|
||||
if ($discrSql) {
|
||||
$sql .= ' AND ' . $discrSql;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
/**
|
||||
* Walk down a ConditionalExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkConditionalExpression($condExpr)
|
||||
{
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
return ( ! ($condExpr instanceof AST\ConditionalExpression))
|
||||
? $this->walkConditionalTerm($condExpr)
|
||||
: implode(
|
||||
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1235,9 +1245,13 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkConditionalTerm($condTerm)
|
||||
{
|
||||
return implode(
|
||||
' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)
|
||||
);
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalTerm
|
||||
// if only one ConditionalFactor is defined
|
||||
return ( ! ($condTerm instanceof AST\ConditionalTerm))
|
||||
? $this->walkConditionalFactor($condTerm)
|
||||
: implode(
|
||||
' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->conditionalFactors)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1248,21 +1262,28 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkConditionalFactor($factor)
|
||||
{
|
||||
$sql = ($factor->not) ? 'NOT ' : '';
|
||||
|
||||
$primary = $factor->conditionalPrimary;
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalFactor
|
||||
// if only one ConditionalPrimary is defined
|
||||
return ( ! ($factor instanceof AST\ConditionalFactor))
|
||||
? $this->walkConditionalPrimary($factor)
|
||||
: ($factor->not ? 'NOT ' : '') . $this->walkConditionalPrimary($factor->conditionalPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalPrimary
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkConditionalPrimary($primary)
|
||||
{
|
||||
if ($primary->isSimpleConditionalExpression()) {
|
||||
$sql .= $primary->simpleConditionalExpression->dispatch($this);
|
||||
return $primary->simpleConditionalExpression->dispatch($this);
|
||||
} else if ($primary->isConditionalExpression()) {
|
||||
$condExpr = $primary->conditionalExpression;
|
||||
|
||||
$sql .= '(' . implode(
|
||||
' OR ', array_map(array($this, 'walkConditionalTerm'), $condExpr->conditionalTerms)
|
||||
) . ')';
|
||||
return '(' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1602,6 +1623,21 @@ class SqlWalker implements TreeWalker
|
||||
: $this->walkSubselect($arithmeticExpr->subselect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param SimpleArithmeticExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
|
||||
{
|
||||
return ( ! ($simpleArithmeticExpr instanceof AST\SimpleArithmeticExpression))
|
||||
? $this->walkArithmeticTerm($simpleArithmeticExpr)
|
||||
: implode(
|
||||
' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -1610,11 +1646,55 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkArithmeticTerm($term)
|
||||
{
|
||||
if (is_string($term)) return $term;
|
||||
if (is_string($term)) {
|
||||
return $term;
|
||||
}
|
||||
|
||||
return implode(
|
||||
' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)
|
||||
);
|
||||
// Phase 2 AST optimization: Skip processment of ArithmeticTerm
|
||||
// if only one ArithmeticFactor is defined
|
||||
return ( ! ($term instanceof AST\ArithmeticTerm))
|
||||
? $this->walkArithmeticFactor($term)
|
||||
: implode(
|
||||
' ', array_map(array($this, 'walkArithmeticFactor'), $term->arithmeticFactors)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param mixed
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkArithmeticFactor($factor)
|
||||
{
|
||||
if (is_string($factor)) {
|
||||
return $factor;
|
||||
}
|
||||
|
||||
// Phase 2 AST optimization: Skip processment of ArithmeticFactor
|
||||
// if only one ArithmeticPrimary is defined
|
||||
return ( ! ($factor instanceof AST\ArithmeticFactor))
|
||||
? $this->walkArithmeticPrimary($factor)
|
||||
: ($factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''))
|
||||
. $this->walkArithmeticPrimary($factor->arithmeticPrimary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an ArithmeticPrimary that represents an AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param mixed
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkArithmeticPrimary($primary)
|
||||
{
|
||||
if ($primary instanceof AST\SimpleArithmeticExpression) {
|
||||
return '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
|
||||
} else if ($primary instanceof AST\Node) {
|
||||
return $primary->dispatch($this);
|
||||
}
|
||||
|
||||
// We need to deal with IdentificationVariable here
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1629,41 +1709,4 @@ class SqlWalker implements TreeWalker
|
||||
? $this->_conn->quote($stringPrimary)
|
||||
: $stringPrimary->dispatch($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param mixed
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkArithmeticFactor($factor)
|
||||
{
|
||||
if (is_string($factor)) return $factor;
|
||||
|
||||
$sql = ($factor->isNegativeSigned() ? '-' : ($factor->isPositiveSigned() ? '+' : ''));
|
||||
$primary = $factor->arithmeticPrimary;
|
||||
|
||||
if ($primary instanceof AST\SimpleArithmeticExpression) {
|
||||
$sql .= '(' . $this->walkSimpleArithmeticExpression($primary) . ')';
|
||||
} else if ($primary instanceof AST\Node) {
|
||||
$sql .= $primary->dispatch($this);
|
||||
} else if (is_string($primary)) {
|
||||
// We need to deal with IdentificationVariable here
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param SimpleArithmeticExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkSimpleArithmeticExpression($simpleArithmeticExpr)
|
||||
{
|
||||
return implode(
|
||||
' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->arithmeticTerms)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +218,14 @@ interface TreeWalker
|
||||
*/
|
||||
function walkWhereClause($whereClause);
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
function walkConditionalExpression($condExpr);
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -234,6 +242,14 @@ interface TreeWalker
|
||||
*/
|
||||
function walkConditionalFactor($factor);
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalPrimary
|
||||
* @return string The SQL.
|
||||
*/
|
||||
function walkConditionalPrimary($primary);
|
||||
|
||||
/**
|
||||
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
|
@ -252,6 +252,14 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
*/
|
||||
public function walkWhereClause($whereClause) {}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkConditionalExpression($condExpr) {}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -268,6 +276,14 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
*/
|
||||
public function walkConditionalFactor($factor) {}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalPrimary
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkConditionalPrimary($primary) {}
|
||||
|
||||
/**
|
||||
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
|
@ -355,6 +355,19 @@ class TreeWalkerChain implements TreeWalker
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkConditionalExpression($condExpr)
|
||||
{
|
||||
foreach ($this->_walkers as $walker) {
|
||||
$walker->walkConditionalExpression($condExpr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -381,6 +394,19 @@ class TreeWalkerChain implements TreeWalker
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a ConditionalPrimary AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param ConditionalPrimary
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkConditionalPrimary($condPrimary)
|
||||
{
|
||||
foreach ($this->_walkers as $walker) {
|
||||
$walker->walkConditionalPrimary($condPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an ExistsExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -27,30 +25,29 @@ use Doctrine\ORM\Query\Expr;
|
||||
* This class is responsible for building DQL query strings via an object oriented
|
||||
* PHP interface.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class QueryBuilder
|
||||
{
|
||||
/* The query types. */
|
||||
const SELECT = 0;
|
||||
const DELETE = 1;
|
||||
const UPDATE = 2;
|
||||
|
||||
/** The builder states. */
|
||||
const STATE_DIRTY = 0;
|
||||
const STATE_CLEAN = 1;
|
||||
|
||||
/**
|
||||
* @var EntityManager $em The EntityManager used by this QueryBuilder.
|
||||
* @var EntityManager The EntityManager used by this QueryBuilder.
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
/**
|
||||
* @var array $dqlParts The array of DQL parts collected.
|
||||
* @var array The array of DQL parts collected.
|
||||
*/
|
||||
private $_dqlParts = array(
|
||||
'select' => array(),
|
||||
@ -105,13 +102,17 @@ class QueryBuilder
|
||||
|
||||
/**
|
||||
* Gets an ExpressionBuilder used for object-oriented construction of query expressions.
|
||||
* Intended for convenient inline usage. Example:
|
||||
* This producer method is intended for convenient inline usage. Example:
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where($qb->expr()->eq('u.id', 1));
|
||||
* </code>
|
||||
*
|
||||
* For more complex expression construction, consider storing the expression
|
||||
* builder object in a local variable.
|
||||
*
|
||||
* @return Expr
|
||||
*/
|
||||
@ -141,16 +142,9 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of this query builder instance
|
||||
* Get the state of this query builder instance.
|
||||
*
|
||||
* [php]
|
||||
* if ($qb->getState() == QueryBuilder::STATE_DIRTY) {
|
||||
* echo 'Query builder is dirty';
|
||||
* } else {
|
||||
* echo 'Query builder is clean';
|
||||
* }
|
||||
*
|
||||
* @return integer
|
||||
* @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
@ -158,15 +152,16 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the complete DQL string for this query builder instance
|
||||
* Get the complete DQL string formed by the current specifications of this QueryBuilder.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* echo $qb->getDql(); // SELECT u FROM User u
|
||||
* </code>
|
||||
*
|
||||
* @return string The DQL string
|
||||
* @return string The DQL query string.
|
||||
*/
|
||||
public function getDQL()
|
||||
{
|
||||
@ -198,14 +193,15 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Query instance from the current configuration of the builder.
|
||||
* Constructs a Query instance from the current specifications of the builder.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
* $q = $qb->getQuery();
|
||||
* $results = $q->execute();
|
||||
* </code>
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
@ -218,17 +214,19 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root alias for the query. This is the first entity alias involved
|
||||
* in the construction of the query
|
||||
* Gets the root alias of the query. This is the first entity alias involved
|
||||
* in the construction of the query.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u');
|
||||
*
|
||||
* echo $qb->getRootAlias(); // u
|
||||
* </code>
|
||||
*
|
||||
* @return string $rootAlias
|
||||
* @todo Rename/Refactor: getRootAliases(), there can be multiple roots!
|
||||
*/
|
||||
public function getRootAlias()
|
||||
{
|
||||
@ -236,14 +234,15 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query parameter.
|
||||
* Sets a query parameter for the query being constructed.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = :user_id')
|
||||
* ->setParameter(':user_id', 1);
|
||||
* </code>
|
||||
*
|
||||
* @param string|integer $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
@ -256,9 +255,9 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a collection of query parameters.
|
||||
* Sets a collection of query parameters for the query being constructed.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
@ -267,8 +266,9 @@ class QueryBuilder
|
||||
* ':user_id1' => 1,
|
||||
* ':user_id2' => 2
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @param array $params
|
||||
* @param array $params The query parameters to set.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function setParameters(array $params)
|
||||
@ -278,17 +278,17 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defined parameters
|
||||
* Gets all defined query parameters for the query being constructed.
|
||||
*
|
||||
* @return array Defined parameters
|
||||
* @return array The currently defined query parameters.
|
||||
*/
|
||||
public function getParameters($params = array())
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->_params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a query parameter.
|
||||
* Gets a (previously set) query parameter of the query being constructed.
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
* @return mixed The value of the bound parameter.
|
||||
@ -297,7 +297,7 @@ class QueryBuilder
|
||||
{
|
||||
return isset($this->_params[$key]) ? $this->_params[$key] : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the position of the first result to retrieve (the "offset").
|
||||
*
|
||||
@ -309,10 +309,10 @@ class QueryBuilder
|
||||
$this->_firstResult = $firstResult;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the position of the first result the query object was set to retrieve (the "offset").
|
||||
* Returns NULL if {@link setFirstResult} was not applied to this query builder.
|
||||
* Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
|
||||
*
|
||||
* @return integer The position of the first result.
|
||||
*/
|
||||
@ -324,7 +324,7 @@ class QueryBuilder
|
||||
/**
|
||||
* Sets the maximum number of results to retrieve (the "limit").
|
||||
*
|
||||
* @param integer $maxResults
|
||||
* @param integer $maxResults The maximum number of results to retrieve.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function setMaxResults($maxResults)
|
||||
@ -345,7 +345,10 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single DQL query part to the array of parts
|
||||
* Either appends to or replaces a single, generic query part.
|
||||
*
|
||||
* The available parts are: 'select', 'from', 'join', 'set', 'where',
|
||||
* 'groupBy', 'having' and 'orderBy'.
|
||||
*
|
||||
* @param string $dqlPartName
|
||||
* @param string $dqlPart
|
||||
@ -368,15 +371,17 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SELECT statement
|
||||
* Specifies an item that is to be returned in the query result.
|
||||
* Replaces any previously specified selections, if any.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u', 'p')
|
||||
* ->from('User', 'u')
|
||||
* ->leftJoin('u.Phonenumbers', 'p');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $select String SELECT statement or SELECT Expr instance
|
||||
* @param mixed $select The selection expressions.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function select($select = null)
|
||||
@ -393,16 +398,17 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the SELECT statement
|
||||
* Adds an item that is to be returned in the query result.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->addSelect('p')
|
||||
* ->from('User', 'u')
|
||||
* ->leftJoin('u.Phonenumbers', 'p');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $select String SELECT statement or SELECT Expr instance
|
||||
* @param mixed $select The selection expression.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function addSelect($select = null)
|
||||
@ -419,16 +425,18 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a DQL DELETE query
|
||||
* Turns the query being built into a bulk delete query that ranges over
|
||||
* a certain entity type.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->delete('User', 'u')
|
||||
* ->where('u.id = :user_id');
|
||||
* ->setParameter(':user_id', 1);
|
||||
* </code>
|
||||
*
|
||||
* @param string $delete The model to delete
|
||||
* @param string $alias The alias of the model
|
||||
* @param string $delete The class/type whose instances are subject to the deletion.
|
||||
* @param string $alias The class/type alias used in the constructed query.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function delete($delete = null, $alias = null)
|
||||
@ -443,16 +451,18 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a DQL UPDATE query
|
||||
* Turns the query being built into a bulk update query that ranges over
|
||||
* a certain entity type.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->update('User', 'u')
|
||||
* ->set('u.password', md5('password'))
|
||||
* ->where('u.id = ?');
|
||||
* </code>
|
||||
*
|
||||
* @param string $update The model to update
|
||||
* @param string $alias The alias of the model
|
||||
* @param string $update The class/type whose instances are subject to the update.
|
||||
* @param string $alias The class/type alias used in the constructed query.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function update($update = null, $alias = null)
|
||||
@ -467,12 +477,14 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the FROM part when constructing a SELECT DQL query
|
||||
* Create and add a query root corresponding to the entity identified by the given alias,
|
||||
* forming a cartesian product with any existing query roots.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* </code>
|
||||
*
|
||||
* @param string $from The class name.
|
||||
* @param string $alias The alias of the class.
|
||||
@ -482,20 +494,25 @@ class QueryBuilder
|
||||
{
|
||||
return $this->add('from', new Expr\From($from, $alias), true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a INNER JOIN to an associated class.
|
||||
* Creates and adds a join over an entity association to the query.
|
||||
*
|
||||
* [php]
|
||||
* The entities in the joined association will be fetched as part of the query
|
||||
* result if the alias used for the joined association is placed in the select
|
||||
* expressions.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
* ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param string $join The relationship to join
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @param string $join The relationship to join
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function join($join, $alias, $conditionType = null, $condition = null)
|
||||
@ -504,7 +521,11 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an INNER JOIN to an associated class.
|
||||
* Creates and adds a join over an entity association to the query.
|
||||
*
|
||||
* The entities in the joined association will be fetched as part of the query
|
||||
* result if the alias used for the joined association is placed in the select
|
||||
* expressions.
|
||||
*
|
||||
* [php]
|
||||
* $qb = $em->createQueryBuilder()
|
||||
@ -512,10 +533,10 @@ class QueryBuilder
|
||||
* ->from('User', 'u')
|
||||
* ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
*
|
||||
* @param string $join The relationship to join
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @param string $join The relationship to join
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function innerJoin($join, $alias, $conditionType = null, $condition = null)
|
||||
@ -526,19 +547,24 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a LEFT JOIN
|
||||
* Creates and adds a left join over an entity association to the query.
|
||||
*
|
||||
* [php]
|
||||
* The entities in the joined association will be fetched as part of the query
|
||||
* result if the alias used for the joined association is placed in the select
|
||||
* expressions.
|
||||
*
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param string $join The relationship to join
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @return QueryBuilder $qb
|
||||
* @param string $join The relationship to join
|
||||
* @param string $alias The alias of the join
|
||||
* @param string $conditionType The condition type constant. Either ON or WITH.
|
||||
* @param string $condition The condition for the join
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function leftJoin($join, $alias, $conditionType = null, $condition = null)
|
||||
{
|
||||
@ -548,17 +574,18 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a SET statement for a DQL UPDATE query
|
||||
* Sets a new value for a field in a bulk update query.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->update('User', 'u')
|
||||
* ->set('u.password', md5('password'))
|
||||
* ->where('u.id = ?');
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The key/field to set
|
||||
* @param string $value The value, expression, placeholder, etc. to use in the SET
|
||||
* @return QueryBuilder $qb
|
||||
* @param string $key The key/field to set.
|
||||
* @param string $value The value, expression, placeholder, etc.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
@ -566,9 +593,10 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and override any existing WHERE statements
|
||||
* Specifies one or more restrictions to the query result.
|
||||
* Replaces any previously specified restrictions, if any.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
@ -584,9 +612,10 @@ class QueryBuilder
|
||||
* $qb->update('User', 'u')
|
||||
* ->set('u.password', md5('password'))
|
||||
* ->where($or);
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $predicates The predicates.
|
||||
* @return QueryBuilder
|
||||
* @param mixed $predicates The restriction predicates.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function where($predicates)
|
||||
{
|
||||
@ -598,17 +627,19 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new WHERE statement with an AND
|
||||
* Adds one or more restrictions to the query results, forming a logical
|
||||
* conjunction with any previously specified restrictions.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.username LIKE ?')
|
||||
* ->andWhere('u.is_active = 1');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $where The WHERE statement
|
||||
* @return QueryBuilder $qb
|
||||
* @param mixed $where The query restrictions.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
* @see where()
|
||||
*/
|
||||
public function andWhere($where)
|
||||
@ -627,14 +658,16 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new WHERE statement with an OR
|
||||
* Adds one or more restrictions to the query results, forming a logical
|
||||
* disjunction with any previously specified restrictions.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->where('u.id = 1')
|
||||
* ->orWhere('u.id = 2');
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $where The WHERE statement
|
||||
* @return QueryBuilder $qb
|
||||
@ -656,16 +689,18 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the GROUP BY clause
|
||||
* Specifies a grouping over the results of the query.
|
||||
* Replaces any previously specified groupings, if any.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->groupBy('u.id');
|
||||
* </code>
|
||||
*
|
||||
* @param string $groupBy The GROUP BY clause
|
||||
* @return QueryBuilder $qb
|
||||
* @param string $groupBy The grouping expression.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function groupBy($groupBy)
|
||||
{
|
||||
@ -674,17 +709,18 @@ class QueryBuilder
|
||||
|
||||
|
||||
/**
|
||||
* Add to the existing GROUP BY clause
|
||||
* Adds a grouping expression to the query.
|
||||
*
|
||||
* [php]
|
||||
* <code>
|
||||
* $qb = $em->createQueryBuilder()
|
||||
* ->select('u')
|
||||
* ->from('User', 'u')
|
||||
* ->groupBy('u.last_login');
|
||||
* ->addGroupBy('u.created_at')
|
||||
* ->groupBy('u.lastLogin');
|
||||
* ->addGroupBy('u.createdAt')
|
||||
* </code>
|
||||
*
|
||||
* @param string $groupBy The GROUP BY clause
|
||||
* @return QueryBuilder $qb
|
||||
* @param string $groupBy The grouping expression.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function addGroupBy($groupBy)
|
||||
{
|
||||
@ -692,10 +728,11 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HAVING clause
|
||||
* Specifies a restriction over the groups of the query.
|
||||
* Replaces any previous having restrictions, if any.
|
||||
*
|
||||
* @param mixed $having
|
||||
* @return QueryBuilder $qb
|
||||
* @param mixed $having The restriction over the groups.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function having($having)
|
||||
{
|
||||
@ -707,10 +744,11 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the existing HAVING clause with an AND
|
||||
* Adds a restriction over the groups of the query, forming a logical
|
||||
* conjunction with any existing having restrictions.
|
||||
*
|
||||
* @param mixed $having
|
||||
* @return QueryBuilder $qb
|
||||
* @param mixed $having The restriction to append.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function andHaving($having)
|
||||
{
|
||||
@ -728,10 +766,11 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the existing HAVING clause with an OR
|
||||
* Adds a restriction over the groups of the query, forming a logical
|
||||
* disjunction with any existing having restrictions.
|
||||
*
|
||||
* @param mixed $having
|
||||
* @return QueryBuilder $qb
|
||||
* @param mixed $having The restriction to add.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function orHaving($having)
|
||||
{
|
||||
@ -749,11 +788,12 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ORDER BY clause
|
||||
* Specifies an ordering for the query results.
|
||||
* Replaces any previously specified orderings, if any.
|
||||
*
|
||||
* @param string $sort What to sort on
|
||||
* @param string $order Optional: The order to sort the results.
|
||||
* @return QueryBuilder $qb
|
||||
* @param string $sort The ordering expression.
|
||||
* @param string $order The ordering direction.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function orderBy($sort, $order = null)
|
||||
{
|
||||
@ -762,11 +802,11 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the existing ORDER BY clause
|
||||
* Adds an ordering to the query results.
|
||||
*
|
||||
* @param string $sort What to sort on
|
||||
* @param string $order Optional: The order to sort the results.
|
||||
* @return QueryBuilder $qb
|
||||
* @param string $sort The ordering expression.
|
||||
* @param string $order The ordering direction.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function addOrderBy($sort, $order = null)
|
||||
{
|
||||
@ -774,10 +814,11 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DQL part or parts by the part name
|
||||
* Get a query part by its name.
|
||||
*
|
||||
* @param string $queryPartName
|
||||
* @return mixed $queryPart
|
||||
* @todo Rename: getQueryPart (or remove?)
|
||||
*/
|
||||
public function getDQLPart($queryPartName)
|
||||
{
|
||||
@ -785,9 +826,10 @@ class QueryBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full DQL parts array
|
||||
* Get all query parts.
|
||||
*
|
||||
* @return array $dqlParts
|
||||
* @todo Rename: getQueryParts (or remove?)
|
||||
*/
|
||||
public function getDQLParts()
|
||||
{
|
||||
@ -836,6 +878,12 @@ class QueryBuilder
|
||||
. (isset($options['post']) ? $options['post'] : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representation of this QueryBuilder which corresponds to
|
||||
* the final DQL query being constructed.
|
||||
*
|
||||
* @return string The string representation of this QueryBuilder.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getDQL();
|
||||
|
@ -0,0 +1,80 @@
|
||||
<?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\Tools\Console\Command;
|
||||
|
||||
use Symfony\Components\Console\Input\InputArgument,
|
||||
Symfony\Components\Console\Input\InputOption,
|
||||
Symfony\Components\Console;
|
||||
|
||||
/**
|
||||
* Schema Validator Command
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class SchemaValidatorCommand extends Console\Command\Command
|
||||
{
|
||||
/**
|
||||
* @see Console\Command\Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('orm:validate-schema')
|
||||
->setDescription('Validate that the mapping files.')
|
||||
->setHelp(<<<EOT
|
||||
'Validate that the mapping files are correct and in sync with the database.'
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Console\Command\Command
|
||||
*/
|
||||
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
|
||||
{
|
||||
$em = $this->getHelper('em')->getEntityManager();
|
||||
|
||||
$validator = new \Doctrine\ORM\Tools\SchemaValidator($em);
|
||||
$errors = $validator->validateMapping();
|
||||
|
||||
if ($errors) {
|
||||
foreach ($errors AS $className => $errorMessages) {
|
||||
$output->write("The entity-class '" . $className . "' is invalid:\n");
|
||||
foreach ($errorMessages AS $errorMessage) {
|
||||
$output->write('* ' . $errorMessage . "\n");
|
||||
}
|
||||
$output->write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$validator->schemaInSyncWithMetadata()) {
|
||||
$output->write('The database schema is not in sync with the current mapping file.');
|
||||
}
|
||||
}
|
||||
}
|
@ -44,35 +44,44 @@ class ValidateSchemaCommand extends Console\Command\Command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('orm:validate-schema')
|
||||
->setDescription('Validate that the current metadata schema is valid.');
|
||||
$this
|
||||
->setName('orm:validate-schema')
|
||||
->setDescription('Validate that the mapping files.')
|
||||
->setHelp(<<<EOT
|
||||
'Validate that the mapping files are correct and in sync with the database.'
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @see Console\Command\Command
|
||||
*/
|
||||
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
|
||||
{
|
||||
$emHelper = $this->getHelper('em');
|
||||
$em = $this->getHelper('em')->getEntityManager();
|
||||
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
$em = $emHelper->getEntityManager();
|
||||
$validator = new \Doctrine\ORM\Tools\SchemaValidator($em);
|
||||
$errors = $validator->validateMapping();
|
||||
|
||||
$metadatas = $em->getMetadataFactory()->getAllMetadata();
|
||||
|
||||
if ( ! empty($metadatas)) {
|
||||
// Create SchemaTool
|
||||
$tool = new \Doctrine\ORM\Tools\SchemaTool($em);
|
||||
$updateSql = $tool->getUpdateSchemaSql($metadatas, false);
|
||||
|
||||
if (count($updateSql) == 0) {
|
||||
$output->write("[Database] OK - Metadata schema exactly matches the database schema.");
|
||||
} else {
|
||||
$output->write("[Database] FAIL - There are differences between metadata and database schema.");
|
||||
$exit = 0;
|
||||
if ($errors) {
|
||||
foreach ($errors AS $className => $errorMessages) {
|
||||
$output->write("<error>[Mapping] FAIL - The entity-class '" . $className . "' mapping is invalid:</error>\n");
|
||||
foreach ($errorMessages AS $errorMessage) {
|
||||
$output->write('* ' . $errorMessage . "\n");
|
||||
}
|
||||
$output->write("\n");
|
||||
}
|
||||
} else {
|
||||
$output->write("No metadata mappings found");
|
||||
$exit += 1;
|
||||
}
|
||||
|
||||
if (!$validator->schemaInSyncWithMetadata()) {
|
||||
$output->write('<error>[Database] FAIL - The database schema is not in sync with the current mapping file.</error>' . "\n");
|
||||
$exit += 2;
|
||||
} else {
|
||||
$output->write('<info>[Database] OK - The database schema is in sync with the mapping files.</info>' . "\n");
|
||||
}
|
||||
|
||||
exit($exit);
|
||||
}
|
||||
}
|
197
lib/Doctrine/ORM/Tools/SchemaValidator.php
Normal file
197
lib/Doctrine/ORM/Tools/SchemaValidator.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?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\Tools;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Mapping\ManyToManyMapping;
|
||||
use Doctrine\ORM\Mapping\OneToOneMapping;
|
||||
|
||||
/**
|
||||
* Performs strict validation of the mapping schema
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 1.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class SchemaValidator
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the internal consistency of mapping files.
|
||||
*
|
||||
* There are several checks that can't be done at runtime or are to expensive, which can be verified
|
||||
* with this command. For example:
|
||||
*
|
||||
* 1. Check if a relation with "mappedBy" is actually connected to that specified field.
|
||||
* 2. Check if "mappedBy" and "inversedBy" are consistent to each other.
|
||||
* 3. Check if "referencedColumnName" attributes are really pointing to primary key columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validateMapping()
|
||||
{
|
||||
$errors = array();
|
||||
$cmf = $this->em->getMetadataFactory();
|
||||
$classes = $cmf->getAllMetadata();
|
||||
|
||||
foreach ($classes AS $class) {
|
||||
/* @var $class ClassMetadata */
|
||||
foreach ($class->associationMappings AS $fieldName => $assoc) {
|
||||
$ce = array();
|
||||
if (!$cmf->hasMetadataFor($assoc->targetEntityName)) {
|
||||
$ce[] = "The target entity '" . $assoc->targetEntityName . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.';
|
||||
}
|
||||
|
||||
if ($assoc->mappedBy && $assoc->inversedBy) {
|
||||
$ce[] = "The association " . $class . "#" . $fieldName . " cannot be defined as both inverse and owning.";
|
||||
}
|
||||
|
||||
$targetMetadata = $cmf->getMetadataFor($assoc->targetEntityName);
|
||||
|
||||
/* @var $assoc AssociationMapping */
|
||||
if ($assoc->mappedBy) {
|
||||
if ($targetMetadata->hasField($assoc->mappedBy)) {
|
||||
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
|
||||
"field " . $assoc->targetEntityName . "#" . $assoc->mappedBy . " which is not defined as association.";
|
||||
}
|
||||
if (!$targetMetadata->hasAssociation($assoc->mappedBy)) {
|
||||
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the owning side ".
|
||||
"field " . $assoc->targetEntityName . "#" . $assoc->mappedBy . " which does not exist.";
|
||||
}
|
||||
|
||||
if ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy == null) {
|
||||
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
|
||||
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
|
||||
$assoc->targetEntityName . "#" . $assoc->mappedBy . " does not contain the required ".
|
||||
"'inversedBy' attribute.";
|
||||
} else if ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy != $fieldName) {
|
||||
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
|
||||
$assoc->targetEntityName . "#" . $assoc->mappedBy . " are ".
|
||||
"incosistent with each other.";
|
||||
}
|
||||
}
|
||||
|
||||
if ($assoc->inversedBy) {
|
||||
if ($targetMetadata->hasField($assoc->inversedBy)) {
|
||||
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
|
||||
"field " . $assoc->targetEntityName . "#" . $assoc->inversedBy . " which is not defined as association.";
|
||||
}
|
||||
if (!$targetMetadata->hasAssociation($assoc->inversedBy)) {
|
||||
$ce[] = "The association " . $class->name . "#" . $fieldName . " refers to the inverse side ".
|
||||
"field " . $assoc->targetEntityName . "#" . $assoc->inversedBy . " which does not exist.";
|
||||
}
|
||||
|
||||
if (isset($targetMetadata->associationMappings[$assoc->mappedBy]) &&
|
||||
$targetMetadata->associationMappings[$assoc->mappedBy]->mappedBy == null) {
|
||||
$ce[] = "The field " . $class->name . "#" . $fieldName . " is on the inverse side of a ".
|
||||
"bi-directional relationship, but the specified mappedBy association on the target-entity ".
|
||||
$assoc->targetEntityName . "#" . $assoc->mappedBy . " does not contain the required ".
|
||||
"'inversedBy' attribute.";
|
||||
} else if ($targetMetadata->associationMappings[$assoc->inversedBy]->mappedBy != $fieldName) {
|
||||
$ce[] = "The mappings " . $class->name . "#" . $fieldName . " and " .
|
||||
$assoc->targetEntityName . "#" . $assoc->inversedBy . " are ".
|
||||
"incosistent with each other.";
|
||||
}
|
||||
}
|
||||
|
||||
if ($assoc instanceof ManyToManyMapping && $assoc->isOwningSide) {
|
||||
foreach ($assoc->joinTable['joinColumns'] AS $joinColumn) {
|
||||
if (!isset($class->fieldNames[$joinColumn['referencedColumnName']])) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
|
||||
"have a corresponding field with this column name on the class '" . $class->name . "'.";
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldName = $class->fieldNames[$joinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $class->identifier)) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
foreach ($assoc->joinTable['inverseJoinColumns'] AS $inverseJoinColumn) {
|
||||
if (!isset($class->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
|
||||
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " .
|
||||
"have a corresponding field with this column name on the class '" . $class->name . "'.";
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldName = $class->fieldNames[$inverseJoinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $class->identifier)) {
|
||||
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
} else if ($assoc instanceof OneToOneMapping) {
|
||||
foreach ($assoc->joinColumns AS $joinColumn) {
|
||||
if (!isset($class->fieldNames[$joinColumn['referencedColumnName']])) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
|
||||
"have a corresponding field with this column name on the class '" . $class->name . "'.";
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldName = $class->fieldNames[$joinColumn['referencedColumnName']];
|
||||
if (!in_array($fieldName, $class->identifier)) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ce) {
|
||||
$errors[$class->name] = $ce;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Database Schema is in sync with the current metadata state.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function schemaInSyncWithMetadata()
|
||||
{
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
|
||||
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
return (count($schemaTool->getUpdateSchemaSql($allMetadata, false)) == 0);
|
||||
}
|
||||
}
|
@ -19,7 +19,8 @@
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection,
|
||||
use Exception,
|
||||
Doctrine\Common\Collections\ArrayCollection,
|
||||
Doctrine\Common\Collections\Collection,
|
||||
Doctrine\Common\NotifyPropertyChanged,
|
||||
Doctrine\Common\PropertyChangedListener,
|
||||
@ -280,13 +281,13 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$conn = $this->_em->getConnection();
|
||||
|
||||
$conn->beginTransaction();
|
||||
try {
|
||||
try {
|
||||
if ($this->_entityInsertions) {
|
||||
foreach ($commitOrder as $class) {
|
||||
$this->_executeInserts($class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($this->_entityUpdates) {
|
||||
foreach ($commitOrder as $class) {
|
||||
$this->_executeUpdates($class);
|
||||
@ -317,10 +318,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
$conn->commit();
|
||||
} catch (\Exception $e) {
|
||||
$conn->setRollbackOnly();
|
||||
$conn->rollback();
|
||||
} catch (Exception $e) {
|
||||
$this->_em->close();
|
||||
$conn->rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@ -1288,6 +1288,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*
|
||||
* @param object $entity
|
||||
* @return object The managed copy of the entity.
|
||||
* @throws OptimisticLockException If the entity uses optimistic locking through a version
|
||||
* attribute and the version check against the managed copy fails.
|
||||
*/
|
||||
public function merge($entity)
|
||||
{
|
||||
@ -1314,7 +1316,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
throw new \InvalidArgumentException('New entity detected during merge.'
|
||||
. ' Persist the new entity before merging.');
|
||||
}
|
||||
|
||||
|
||||
// MANAGED entities are ignored by the merge operation
|
||||
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
|
||||
$managedCopy = $entity;
|
||||
@ -1342,7 +1344,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
|
||||
// Throw exception if versions dont match.
|
||||
if ($managedCopyVersion != $entityVersion) {
|
||||
throw OptimisticLockException::lockFailed();
|
||||
throw OptimisticLockException::lockFailed($entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,12 +56,11 @@ class DriverManagerTest extends \Doctrine\Tests\DbalTestCase
|
||||
|
||||
public function testCustomWrapper()
|
||||
{
|
||||
$wrapperMock = $this->getMock('\Doctrine\DBAL\Connection', array(), array(), '', false);
|
||||
$wrapperClass = get_class($wrapperMock);
|
||||
$wrapperClass = 'Doctrine\Tests\Mocks\ConnectionMock';
|
||||
|
||||
$options = array(
|
||||
'pdo' => new \PDO('sqlite::memory:'),
|
||||
'wrapperClass' => $wrapperClass
|
||||
'wrapperClass' => $wrapperClass,
|
||||
);
|
||||
|
||||
$conn = \Doctrine\DBAL\DriverManager::getConnection($options);
|
||||
|
@ -25,7 +25,7 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
|
||||
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
|
||||
//no rethrow
|
||||
}
|
||||
$this->assertTrue($this->_conn->getRollbackOnly());
|
||||
$this->assertTrue($this->_conn->isRollbackOnly());
|
||||
|
||||
$this->_conn->commit(); // should throw exception
|
||||
$this->fail('Transaction commit after failed nested transaction should fail.');
|
||||
@ -44,7 +44,7 @@ class ConnectionTest extends \Doctrine\Tests\DbalFunctionalTestCase
|
||||
|
||||
throw new \Exception;
|
||||
|
||||
$this->_conn->commit(); // never reached
|
||||
$this->_connx->commit(); // never reached
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals(1, $this->_conn->getTransactionNestingLevel());
|
||||
$this->_conn->rollback();
|
||||
|
@ -4,7 +4,6 @@ namespace Doctrine\Tests\DBAL\Platforms;
|
||||
|
||||
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
@ -74,19 +73,19 @@ class PostgreSqlPlatformTest extends AbstractPlatformTestCase
|
||||
{
|
||||
$this->assertEquals(
|
||||
'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
|
||||
$this->_platform->getSetTransactionIsolationSQL(Connection::TRANSACTION_READ_UNCOMMITTED)
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED',
|
||||
$this->_platform->getSetTransactionIsolationSQL(Connection::TRANSACTION_READ_COMMITTED)
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ',
|
||||
$this->_platform->getSetTransactionIsolationSQL(Connection::TRANSACTION_REPEATABLE_READ)
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE',
|
||||
$this->_platform->getSetTransactionIsolationSQL(Connection::TRANSACTION_SERIALIZABLE)
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -36,10 +36,22 @@ class SqlitePlatformTest extends AbstractPlatformTestCase
|
||||
|
||||
public function testGeneratesTransactionCommands()
|
||||
{
|
||||
$this->assertEquals('PRAGMA read_uncommitted = 0', $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED));
|
||||
$this->assertEquals('PRAGMA read_uncommitted = 1', $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED));
|
||||
$this->assertEquals('PRAGMA read_uncommitted = 1', $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ));
|
||||
$this->assertEquals('PRAGMA read_uncommitted = 1', $this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE));
|
||||
$this->assertEquals(
|
||||
'PRAGMA read_uncommitted = 0',
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_UNCOMMITTED)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'PRAGMA read_uncommitted = 1',
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_READ_COMMITTED)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'PRAGMA read_uncommitted = 1',
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_REPEATABLE_READ)
|
||||
);
|
||||
$this->assertEquals(
|
||||
'PRAGMA read_uncommitted = 1',
|
||||
$this->_platform->getSetTransactionIsolationSQL(\Doctrine\DBAL\Connection::TRANSACTION_SERIALIZABLE)
|
||||
);
|
||||
}
|
||||
|
||||
public function testPrefersIdentityColumns()
|
||||
|
@ -11,8 +11,11 @@ class ConnectionMock extends \Doctrine\DBAL\Connection
|
||||
|
||||
public function __construct(array $params, $driver, $config = null, $eventManager = null)
|
||||
{
|
||||
parent::__construct($params, $driver, $config, $eventManager);
|
||||
$this->_platformMock = new DatabasePlatformMock();
|
||||
|
||||
parent::__construct($params, $driver, $config, $eventManager);
|
||||
|
||||
// Override possible assignment of platform to database platform mock
|
||||
$this->_platform = $this->_platformMock;
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\EntityRepositoryTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\IdentityMapTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\DatabaseDriverTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\PostgreSQLIdentityStrategyTest');
|
||||
|
||||
$suite->addTest(Locking\AllTests::suite());
|
||||
$suite->addTest(SchemaTool\AllTests::suite());
|
||||
|
@ -39,20 +39,43 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
|
||||
public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed)
|
||||
{
|
||||
try {
|
||||
$query = $this->_em->createQuery($dqlToBeTested);
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker'))
|
||||
->useQueryCache(false);
|
||||
|
||||
parent::assertEquals($sqlToBeConfirmed, $query->getSql());
|
||||
$query->free();
|
||||
} catch (\Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testSupportsQueriesWithoutWhere()
|
||||
{
|
||||
|
||||
$q = $this->_em->createQuery('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName');
|
||||
$q->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker'));
|
||||
|
||||
$this->assertEquals("SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1", $q->getSql());
|
||||
|
||||
$q->setDql('select u from Doctrine\Tests\Models\CMS\CmsUser u');
|
||||
$this->assertEquals("SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = 1", $q->getSql());
|
||||
|
||||
$q->setDql('select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name');
|
||||
$this->assertEquals("SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1", $q->getSql());
|
||||
$this->assertSqlGeneration(
|
||||
'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName',
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1"
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsQueriesWithSimpleConditionalExpressions()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name',
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1"
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsQueriesWithMultipleConditionalExpressions()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'select u from Doctrine\Tests\Models\CMS\CmsUser u',
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +85,7 @@ class CustomTreeWalker extends Query\TreeWalkerAdapter
|
||||
{
|
||||
// Get the DQL aliases of all the classes we want to modify
|
||||
$dqlAliases = array();
|
||||
|
||||
foreach ($this->_getQueryComponents() as $dqlAlias => $comp) {
|
||||
// Hard-coded check just for demonstration: We want to modify the query if
|
||||
// it involves the CmsUser class.
|
||||
@ -84,10 +108,19 @@ class CustomTreeWalker extends Query\TreeWalkerAdapter
|
||||
$factors[] = $factor;
|
||||
}
|
||||
|
||||
if ($selectStatement->whereClause !== null) {
|
||||
if (($whereClause = $selectStatement->whereClause) !== null) {
|
||||
// There is already a WHERE clause, so append the conditions
|
||||
|
||||
$existingTerms = $selectStatement->whereClause->conditionalExpression->conditionalTerms;
|
||||
$condExpr = $whereClause->conditionalExpression;
|
||||
|
||||
// Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression
|
||||
if ( ! ($condExpr instanceof Query\AST\ConditionalExpression)) {
|
||||
$condExpr = new Query\AST\ConditionalExpression(array($condExpr));
|
||||
|
||||
$whereClause->conditionalExpression = $condExpr;
|
||||
}
|
||||
|
||||
$existingTerms = $whereClause->conditionalExpression->conditionalTerms;
|
||||
|
||||
if (count($existingTerms) > 1) {
|
||||
// More than one term, so we need to wrap all these terms in a single root term
|
||||
// i.e: "WHERE u.name = :foo or u.other = :bar" => "WHERE (u.name = :foo or u.other = :bar) AND <our condition>"
|
||||
@ -100,8 +133,15 @@ class CustomTreeWalker extends Query\TreeWalkerAdapter
|
||||
$selectStatement->whereClause->conditionalExpression->conditionalTerms = array($term);
|
||||
} else {
|
||||
// Just one term so we can simply append our factors to that term
|
||||
|
||||
$singleTerm = $selectStatement->whereClause->conditionalExpression->conditionalTerms[0];
|
||||
|
||||
// Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression
|
||||
if ( ! ($singleTerm instanceof Query\AST\ConditionalTerm)) {
|
||||
$singleTerm = new Query\AST\ConditionalTerm(array($singleTerm));
|
||||
|
||||
$selectStatement->whereClause->conditionalExpression->conditionalTerms[0] = $singleTerm;
|
||||
}
|
||||
|
||||
$singleTerm->conditionalFactors = array_merge($singleTerm->conditionalFactors, $factors);
|
||||
$selectStatement->whereClause->conditionalExpression->conditionalTerms = array($singleTerm);
|
||||
}
|
||||
|
@ -84,6 +84,44 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$phonenumbers = $user->getPhonenumbers();
|
||||
$this->assertTrue($this->_em->contains($phonenumbers[0]));
|
||||
$this->assertTrue($this->_em->contains($phonenumbers[1]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-518
|
||||
*/
|
||||
/*public function testMergeDetachedEntityWithNewlyPersistentOneToOneAssoc()
|
||||
{
|
||||
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
|
||||
// Create a detached user
|
||||
$user = new CmsUser;
|
||||
$user->name = 'Roman';
|
||||
$user->username = 'romanb';
|
||||
$user->status = 'dev';
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
//$address = new CmsAddress;
|
||||
//$address->city = 'Berlin';
|
||||
//$address->country = 'Germany';
|
||||
//$address->street = 'Sesamestreet';
|
||||
//$address->zip = 12345;
|
||||
//$address->setUser($user);
|
||||
|
||||
$phone = new CmsPhonenumber();
|
||||
$phone->phonenumber = '12345';
|
||||
|
||||
$user2 = $this->_em->merge($user);
|
||||
|
||||
$user2->addPhonenumber($phone);
|
||||
$this->_em->persist($phone);
|
||||
|
||||
//$address->setUser($user2);
|
||||
//$this->_em->persist($address);
|
||||
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertEquals(1,1);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Doctrine\Tests\ORM\Functional\Locking;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\Tests\TestUtil;
|
||||
@ -37,15 +38,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertEquals(1, $test->version);
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\OptimisticLockException
|
||||
* @depends testJoinedChildInsertSetsInitialVersionValue
|
||||
*/
|
||||
public function testJoinedChildFailureThrowsException()
|
||||
public function testJoinedChildFailureThrowsException(OptimisticJoinedChild $child)
|
||||
{
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild t WHERE t.name = :name');
|
||||
$q->setParameter('name', 'child');
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild t WHERE t.id = :id');
|
||||
$q->setParameter('id', $child->id);
|
||||
$test = $q->getSingleResult();
|
||||
|
||||
// Manually update/increment the version so we can try and save the same
|
||||
@ -55,7 +58,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
// Now lets change a property and try and save it again
|
||||
$test->whatever = 'ok';
|
||||
$this->_em->flush();
|
||||
try {
|
||||
$this->_em->flush();
|
||||
} catch (OptimisticLockException $e) {
|
||||
$this->assertSame($test, $e->getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
public function testJoinedParentInsertSetsInitialVersionValue()
|
||||
@ -66,15 +73,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertEquals(1, $test->version);
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\OptimisticLockException
|
||||
* @depends testJoinedParentInsertSetsInitialVersionValue
|
||||
*/
|
||||
public function testJoinedParentFailureThrowsException()
|
||||
public function testJoinedParentFailureThrowsException(OptimisticJoinedParent $parent)
|
||||
{
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent t WHERE t.name = :name');
|
||||
$q->setParameter('name', 'parent');
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent t WHERE t.id = :id');
|
||||
$q->setParameter('id', $parent->id);
|
||||
$test = $q->getSingleResult();
|
||||
|
||||
// Manually update/increment the version so we can try and save the same
|
||||
@ -84,7 +93,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
// Now lets change a property and try and save it again
|
||||
$test->name = 'WHATT???';
|
||||
$this->_em->flush();
|
||||
try {
|
||||
$this->_em->flush();
|
||||
} catch (OptimisticLockException $e) {
|
||||
$this->assertSame($test, $e->getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
public function testStandardInsertSetsInitialVersionValue()
|
||||
@ -95,15 +108,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertEquals(1, $test->getVersion());
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\OptimisticLockException
|
||||
* @depends testStandardInsertSetsInitialVersionValue
|
||||
*/
|
||||
public function testStandardFailureThrowsException()
|
||||
public function testStandardFailureThrowsException(OptimisticStandard $entity)
|
||||
{
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard t WHERE t.name = :name');
|
||||
$q->setParameter('name', 'test');
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard t WHERE t.id = :id');
|
||||
$q->setParameter('id', $entity->id);
|
||||
$test = $q->getSingleResult();
|
||||
|
||||
// Manually update/increment the version so we can try and save the same
|
||||
@ -113,7 +128,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
// Now lets change a property and try and save it again
|
||||
$test->name = 'WHATT???';
|
||||
$this->_em->flush();
|
||||
try {
|
||||
$this->_em->flush();
|
||||
} catch (OptimisticLockException $e) {
|
||||
$this->assertSame($test, $e->getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
public function testOptimisticTimestampSetsDefaultValue()
|
||||
@ -124,15 +143,17 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertTrue(strtotime($test->version) > 0);
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\OptimisticLockException
|
||||
* @depends testOptimisticTimestampSetsDefaultValue
|
||||
*/
|
||||
public function testOptimisticTimestampFailureThrowsException()
|
||||
public function testOptimisticTimestampFailureThrowsException(OptimisticTimestamp $entity)
|
||||
{
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.name = :name');
|
||||
$q->setParameter('name', 'Testing');
|
||||
$q = $this->_em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
|
||||
$q->setParameter('id', $entity->id);
|
||||
$test = $q->getSingleResult();
|
||||
|
||||
$this->assertType('DateTime', $test->version);
|
||||
@ -143,7 +164,11 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
// Try and update the record and it should throw an exception
|
||||
$test->name = 'Testing again';
|
||||
$this->_em->flush();
|
||||
try {
|
||||
$this->_em->flush();
|
||||
} catch (OptimisticLockException $e) {
|
||||
$this->assertSame($test, $e->getEntity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\ORM\Event\PreUpdateEventArgs;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class PostgreSQLIdentityStrategyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
if ($this->_em->getConnection()->getDatabasePlatform()->getName() != 'postgresql') {
|
||||
$this->markTestSkipped('This test is special to the PostgreSQL IDENTITY key generation strategy.');
|
||||
} else {
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\PostgreSQLIdentityEntity'),
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
// Swallow all exceptions. We do not test the schema tool here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
parent::tearDown();
|
||||
// drop sequence manually due to dependency
|
||||
$this->_em->getConnection()->exec('DROP SEQUENCE postgresqlidentityentity_id_seq CASCADE');
|
||||
}
|
||||
|
||||
public function testPreSavePostSaveCallbacksAreInvoked()
|
||||
{
|
||||
$entity = new PostgreSQLIdentityEntity();
|
||||
$entity->setValue('hello');
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
$this->assertTrue(is_numeric($entity->getId()));
|
||||
$this->assertTrue($entity->getId() > 0);
|
||||
$this->assertTrue($this->_em->contains($entity));
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class PostgreSQLIdentityEntity {
|
||||
/** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $value;
|
||||
public function getId() {return $this->id;}
|
||||
public function getValue() {return $this->value;}
|
||||
public function setValue($value) {$this->value = $value;}
|
||||
}
|
@ -26,6 +26,7 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
// Self-made metadata
|
||||
$cm1 = new ClassMetadata('Doctrine\Tests\ORM\Mapping\TestEntity1');
|
||||
$cm1->setPrimaryTable(array('name' => '`group`'));
|
||||
// Add a mapped field
|
||||
$cm1->mapField(array('fieldName' => 'name', 'type' => 'varchar'));
|
||||
// Add a mapped field
|
||||
@ -54,6 +55,8 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
// Go
|
||||
$cm1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1');
|
||||
|
||||
$this->assertEquals('group', $cm1->table['name']);
|
||||
$this->assertTrue($cm1->table['quoted']);
|
||||
$this->assertEquals(array(), $cm1->parentClasses);
|
||||
$this->assertTrue($cm1->hasField('name'));
|
||||
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $cm1->generatorType);
|
||||
|
@ -177,7 +177,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE ((c0_.id + 5000) * c0_.id + 3) < 10000000'
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.id + 5000) * c0_.id + 3 < 10000000'
|
||||
);
|
||||
}
|
||||
|
||||
@ -456,7 +456,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"select u from Doctrine\Tests\Models\CMS\CmsUser u where u.id > 10 and u.id < 42 and ((u.id * 2) > 5)",
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND ((c0_.id * 2) > 5)"
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND (c0_.id * 2 > 5)"
|
||||
);
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"select u from Doctrine\Tests\Models\CMS\CmsUser u where (u.id > 10) and (u.id < 42 and ((u.id * 2) > 5)) or u.id <> 42",
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND ((c0_.id * 2) > 5)) OR c0_.id <> 42"
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND (c0_.id * 2 > 5)) OR c0_.id <> 42"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?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
|
||||
@ -32,10 +30,7 @@ require_once __DIR__ . '/../TestInit.php';
|
||||
*
|
||||
* @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
|
||||
{
|
||||
@ -181,6 +176,20 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.id = :uid) OR (u.id = :uid2)');
|
||||
}
|
||||
|
||||
public function testComplexAndWhereOrWhereNesting()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$qb->select('u')
|
||||
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
|
||||
->where('u.id = :uid')
|
||||
->orWhere('u.id = :uid2')
|
||||
->andWhere('u.id = :uid3')
|
||||
->orWhere('u.name = :name1', 'u.name = :name2')
|
||||
->andWhere('u.name <> :noname');
|
||||
|
||||
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((((u.id = :uid) OR (u.id = :uid2)) AND (u.id = :uid3)) OR (u.name = :name1) OR (u.name = :name2)) AND (u.name <> :noname)');
|
||||
}
|
||||
|
||||
public function testAndWhereIn()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
|
@ -27,6 +27,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Tools\ConvertDoctrine1SchemaTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Tools\SchemaToolTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Tools\EntityGeneratorTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Tools\SchemaValidatorTest');
|
||||
|
||||
return $suite;
|
||||
}
|
||||
|
@ -67,7 +67,16 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest
|
||||
|
||||
protected function _createMetadataDriver($type, $path)
|
||||
{
|
||||
$class = 'Doctrine\ORM\Mapping\Driver\\' . ucfirst($type) . 'Driver';
|
||||
$mappingDriver = array(
|
||||
'php' => 'PHPDriver',
|
||||
'annotation' => 'AnnotationDriver',
|
||||
'xml' => 'XmlDriver',
|
||||
'yaml' => 'YamlDriver',
|
||||
);
|
||||
$this->assertArrayHasKey($type, $mappingDriver, "There is no metadata driver for the type '" . $type . "'.");
|
||||
$driverName = $mappingDriver[$type];
|
||||
|
||||
$class = 'Doctrine\ORM\Mapping\Driver\\' . $driverName;
|
||||
if ($type === 'annotation') {
|
||||
$driver = $class::create($path);
|
||||
} else {
|
||||
|
74
tests/Doctrine/Tests/ORM/Tools/SchemaValidatorTest.php
Normal file
74
tests/Doctrine/Tests/ORM/Tools/SchemaValidatorTest.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Tools;
|
||||
|
||||
use Doctrine\ORM\Tools\SchemaValidator;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class SchemaValidatorTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em = null;
|
||||
|
||||
/**
|
||||
* @var SchemaValidator
|
||||
*/
|
||||
private $validator = null;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->em = $this->_getTestEntityManager();
|
||||
$this->validator = new SchemaValidator($this->em);
|
||||
}
|
||||
|
||||
public function testCmsModelSet()
|
||||
{
|
||||
$this->em->getConfiguration()->getMetadataDriverImpl()->addPaths(array(
|
||||
__DIR__ . "/../../Models/CMS"
|
||||
));
|
||||
$this->validator->validateMapping();
|
||||
}
|
||||
|
||||
public function testCompanyModelSet()
|
||||
{
|
||||
$this->em->getConfiguration()->getMetadataDriverImpl()->addPaths(array(
|
||||
__DIR__ . "/../../Models/Company"
|
||||
));
|
||||
$this->validator->validateMapping();
|
||||
}
|
||||
|
||||
public function testECommerceModelSet()
|
||||
{
|
||||
$this->em->getConfiguration()->getMetadataDriverImpl()->addPaths(array(
|
||||
__DIR__ . "/../../Models/ECommerce"
|
||||
));
|
||||
$this->validator->validateMapping();
|
||||
}
|
||||
|
||||
public function testForumModelSet()
|
||||
{
|
||||
$this->em->getConfiguration()->getMetadataDriverImpl()->addPaths(array(
|
||||
__DIR__ . "/../../Models/Forum"
|
||||
));
|
||||
$this->validator->validateMapping();
|
||||
}
|
||||
|
||||
public function testNavigationModelSet()
|
||||
{
|
||||
$this->em->getConfiguration()->getMetadataDriverImpl()->addPaths(array(
|
||||
__DIR__ . "/../../Models/Navigation"
|
||||
));
|
||||
$this->validator->validateMapping();
|
||||
}
|
||||
|
||||
public function testRoutingModelSet()
|
||||
{
|
||||
$this->em->getConfiguration()->getMetadataDriverImpl()->addPaths(array(
|
||||
__DIR__ . "/../../Models/Routing"
|
||||
));
|
||||
$this->validator->validateMapping();
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ $cli->addCommands(array(
|
||||
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
|
||||
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
|
||||
|
||||
));
|
||||
$cli->run();
|
Loading…
Reference in New Issue
Block a user