1
0
mirror of synced 2025-03-22 16:03:49 +03:00

Merge branch 'master' into DDC-1452

This commit is contained in:
Benjamin Eberlei 2011-11-14 21:53:36 +01:00
commit 98033cc878
93 changed files with 4784 additions and 2754 deletions

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ download/
lib/api/
lib/Doctrine/Common
lib/Doctrine/DBAL
/.settings/
.buildpath
.project

19
.travis.yml Normal file
View File

@ -0,0 +1,19 @@
language: php
php:
- 5.3
- 5.4
env:
- DB=mysql
- DB=pgsql
- DB=sqlite
before_script:
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS doctrine_tests_tmp;' -U postgres; fi"
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'create database doctrine_tests_tmp;' -U postgres; fi"
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS doctrine_tests_tmp;create database IF NOT EXISTS doctrine_tests;'; fi"
- git submodule update --init
script: phpunit --configuration tests/travis/$DB.travis.xml

View File

@ -1,14 +1,18 @@
# Doctrine 2 ORM
Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2)
2.1.x: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2)
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
without requiring unnecessary code duplication.
More resources:
## More resources:
* [Website](http://www.doctrine-project.org)
* [Documentation](http://www.doctrine-project.org/projects/orm/2.0/docs/reference/introduction/en)
* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DDC)
* [Downloads](http://github.com/doctrine/doctrine2/downloads)

View File

@ -16,5 +16,8 @@
"ext-pdo": "*",
"doctrine/common": "master-dev",
"doctrine/dbal": "master-dev"
},
"autoload": {
"psr-0": { "Doctrine\\ORM": "lib/" }
}
}

View File

@ -545,7 +545,7 @@ abstract class AbstractQuery
*
* @param array $params The query parameters.
* @param integer $hydrationMode The hydration mode to use.
* @return IterableResult
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/
public function iterate(array $params = array(), $hydrationMode = null)
{

View File

@ -225,6 +225,14 @@ class EntityRepository implements ObjectRepository
return $this->_entityName;
}
/**
* @return string
*/
public function getClassName()
{
return $this->getEntityName();
}
/**
* @return EntityManager
*/

View File

@ -0,0 +1,53 @@
<?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\Event;
/**
* Provides event arguments for the preFlush event.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 2.0
* @version $Revision$
* @author Roman Borschel <roman@code-factory.de>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class PreFlushEventArgs extends \Doctrine\Common\EventArgs
{
/**
* @var EntityManager
*/
private $_em;
public function __construct($em)
{
$this->_em = $em;
}
/**
* @return EntityManager
*/
public function getEntityManager()
{
return $this->_em;
}
}

View File

@ -109,6 +109,13 @@ final class Events
*/
const loadClassMetadata = 'loadClassMetadata';
/**
* The preFlush event occurs when the EntityManager#flush() operation is invoked,
* but before any changes to managed entites have been calculated. This event is
* always raised right after EntityManager#flush() call.
*/
const preFlush = 'preFlush';
/**
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
* after any changes to managed entities have been determined but before any

View File

@ -20,6 +20,7 @@
namespace Doctrine\ORM\Id;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\ORMException;
/**
@ -43,44 +44,27 @@ class AssignedGenerator extends AbstractIdGenerator
public function generate(EntityManager $em, $entity)
{
$class = $em->getClassMetadata(get_class($entity));
$identifier = array();
if ($class->isIdentifierComposite) {
$idFields = $class->getIdentifierFieldNames();
$identifier = array();
foreach ($idFields as $idField) {
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if ( ! isset($value)) {
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
}
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
if ( ! $em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
} else {
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
}
}
} else {
$idField = $class->identifier[0];
$value = $class->reflFields[$idField]->getValue($entity);
if (isset($value)) {
if (isset($class->associationMappings[$idField])) {
if (!$em->getUnitOfWork()->isInIdentityMap($value)) {
throw ORMException::entityMissingForeignAssignedId($entity, $value);
$value = current($em->getUnitOfWork()->getEntityIdentifier($value));
}
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
$identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value));
} else {
$identifier[$idField] = $value;
}
} else {
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
}
}
return $identifier;
}

View File

@ -46,7 +46,7 @@ class IdentityGenerator extends AbstractIdGenerator
*/
public function generate(EntityManager $em, $entity)
{
return $em->getConnection()->lastInsertId($this->_seqName);
return (int)$em->getConnection()->lastInsertId($this->_seqName);
}
/**

View File

@ -60,9 +60,11 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
// Allocate new values
$conn = $em->getConnection();
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
$this->_nextValue = $conn->fetchColumn($sql);
$this->_nextValue = (int)$conn->fetchColumn($sql);
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
}
return $this->_nextValue++;
}
@ -97,6 +99,7 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
public function unserialize($serialized)
{
$array = unserialize($serialized);
$this->_sequenceName = $array['sequenceName'];
$this->_allocationSize = $array['allocationSize'];
}

View File

@ -50,11 +50,12 @@ class TableGenerator extends AbstractIdGenerator
if ($this->_maxValue === null || $this->_nextValue == $this->_maxValue) {
// Allocate new values
$conn = $em->getConnection();
if ($conn->getTransactionNestingLevel() == 0) {
if ($conn->getTransactionNestingLevel() === 0) {
// use select for update
$sql = $conn->getDatabasePlatform()->getTableHiLoCurrentValSql($this->_tableName, $this->_sequenceName);
$currentLevel = $conn->fetchColumn($sql);
if ($currentLevel != null) {
$this->_nextValue = $currentLevel;
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
@ -74,6 +75,7 @@ class TableGenerator extends AbstractIdGenerator
// or do we want to work with table locks exclusively?
}
}
return $this->_nextValue++;
}
}

View File

@ -22,7 +22,8 @@ namespace Doctrine\ORM\Internal\Hydration;
use PDO,
Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager;
Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata;
/**
* Base class for all hydrators. A hydrator is a class that provides some form
@ -31,6 +32,7 @@ use PDO,
* @since 2.0
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*/
abstract class AbstractHydrator
{
@ -72,6 +74,7 @@ abstract class AbstractHydrator
*
* @param object $stmt
* @param object $resultSetMapping
*
* @return IterableResult
*/
public function iterate($stmt, $resultSetMapping, array $hints = array())
@ -79,7 +82,9 @@ abstract class AbstractHydrator
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$this->_prepare();
$this->prepare();
return new IterableResult($this);
}
@ -95,9 +100,13 @@ abstract class AbstractHydrator
$this->_stmt = $stmt;
$this->_rsm = $resultSetMapping;
$this->_hints = $hints;
$this->_prepare();
$result = $this->_hydrateAll();
$this->_cleanup();
$this->prepare();
$result = $this->hydrateAllData();
$this->cleanup();
return $result;
}
@ -110,12 +119,17 @@ abstract class AbstractHydrator
public function hydrateRow()
{
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
if ( ! $row) {
$this->_cleanup();
$this->cleanup();
return false;
}
$result = array();
$this->_hydrateRow($row, $this->_cache, $result);
$this->hydrateRowData($row, $this->_cache, $result);
return $result;
}
@ -123,16 +137,17 @@ abstract class AbstractHydrator
* Excutes one-time preparation tasks, once each time hydration is started
* through {@link hydrateAll} or {@link iterate()}.
*/
protected function _prepare()
protected function prepare()
{}
/**
* Excutes one-time cleanup tasks at the end of a hydration that was initiated
* through {@link hydrateAll} or {@link iterate()}.
*/
protected function _cleanup()
protected function cleanup()
{
$this->_rsm = null;
$this->_stmt->closeCursor();
$this->_stmt = null;
}
@ -146,23 +161,24 @@ abstract class AbstractHydrator
* @param array $cache The cache to use.
* @param mixed $result The result to fill.
*/
protected function _hydrateRow(array $data, array &$cache, array &$result)
protected function hydrateRowData(array $data, array &$cache, array &$result)
{
throw new HydrationException("_hydrateRow() not implemented by this hydrator.");
throw new HydrationException("hydrateRowData() not implemented by this hydrator.");
}
/**
* Hydrates all rows from the current statement instance at once.
*/
abstract protected function _hydrateAll();
abstract protected function hydrateAllData();
/**
* Processes a row of the result set.
*
* Used for identity-based hydration (HYDRATE_OBJECT and HYDRATE_ARRAY).
* Puts the elements of a result row into a new array, grouped by the class
* Puts the elements of a result row into a new array, grouped by the dql alias
* they belong to. The column names in the result set are mapped to their
* field names during this procedure as well as any necessary conversions on
* the values applied.
* the values applied. Scalar values are kept in a specfic key 'scalars'.
*
* @param array $data SQL Result Row
* @param array &$cache Cache for column to field result information
@ -172,40 +188,51 @@ abstract class AbstractHydrator
* @return array An array with all the fields (name => value) of the data row,
* grouped by their component alias.
*/
protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if (isset($this->_rsm->scalarMappings[$key])) {
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['isScalar'] = true;
} else if (isset($this->_rsm->fieldMappings[$key])) {
switch (true) {
// NOTE: Most of the times it's a field mapping, so keep it first!!!
case (isset($this->_rsm->fieldMappings[$key])):
$fieldName = $this->_rsm->fieldMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else if (!isset($this->_rsm->metaMappings[$key])) {
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue;
} else {
break;
case (isset($this->_rsm->scalarMappings[$key])):
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['isScalar'] = true;
break;
case (isset($this->_rsm->metaMappings[$key])):
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$fieldName = $this->_rsm->metaMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]);
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$cache[$key]['dqlAlias']]);
$cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]);
break;
default:
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue 2;
}
}
if (isset($cache[$key]['isScalar'])) {
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue;
}
@ -216,13 +243,14 @@ abstract class AbstractHydrator
}
if (isset($cache[$key]['isMetaColumn'])) {
if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
if ( ! isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
}
continue;
}
// in an inheritance hierachy the same field could be defined several times.
// in an inheritance hierarchy the same field could be defined several times.
// We overwrite this value so long we dont have a non-null value, that value we keep.
// Per definition it cannot be that a field is defined several times and has several values.
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
@ -241,6 +269,7 @@ abstract class AbstractHydrator
/**
* Processes a row of the result set.
*
* Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that
* simply converts column names to field names and properly converts the
* values according to their types. The resulting row has the same number
@ -248,52 +277,77 @@ abstract class AbstractHydrator
*
* @param array $data
* @param array $cache
*
* @return array The processed row.
*/
protected function _gatherScalarRowData(&$data, &$cache)
protected function gatherScalarRowData(&$data, &$cache)
{
$rowData = array();
foreach ($data as $key => $value) {
// Parse each column name only once. Cache the results.
if ( ! isset($cache[$key])) {
if (isset($this->_rsm->scalarMappings[$key])) {
switch (true) {
// NOTE: During scalar hydration, most of the times it's a scalar mapping, keep it first!!!
case (isset($this->_rsm->scalarMappings[$key])):
$cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key];
$cache[$key]['isScalar'] = true;
} else if (isset($this->_rsm->fieldMappings[$key])) {
break;
case (isset($this->_rsm->fieldMappings[$key])):
$fieldName = $this->_rsm->fieldMappings[$key];
$classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else if (!isset($this->_rsm->metaMappings[$key])) {
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue;
} else {
break;
case (isset($this->_rsm->metaMappings[$key])):
// Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns).
$cache[$key]['isMetaColumn'] = true;
$cache[$key]['fieldName'] = $this->_rsm->metaMappings[$key];
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
break;
default:
// this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
continue 2;
}
}
$fieldName = $cache[$key]['fieldName'];
if (isset($cache[$key]['isScalar'])) {
switch (true) {
case (isset($cache[$key]['isScalar'])):
$rowData[$fieldName] = $value;
} else if (isset($cache[$key]['isMetaColumn'])) {
break;
case (isset($cache[$key]['isMetaColumn'])):
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
break;
default:
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $value;
} else {
$rowData[$cache[$key]['dqlAlias'] . '_' . $fieldName] = $cache[$key]['type']
->convertToPHPValue($value, $this->_platform);
}
}
return $rowData;
}
protected function registerManaged($class, $entity, $data)
/**
* Register entity as managed in UnitOfWork.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param object $entity
* @param array $data
*
* @todo The "$id" generation is the same of UnitOfWork#createEntity. Remove this duplication somehow
*/
protected function registerManaged(ClassMetadata $class, $entity, array $data)
{
if ($class->isIdentifierComposite) {
$id = array();
@ -311,6 +365,7 @@ abstract class AbstractHydrator
$id = array($class->identifier[0] => $data[$class->identifier[0]]);
}
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
}

View File

@ -25,8 +25,14 @@ use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
* The ArrayHydrator produces a nested array "graph" that is often (not always)
* interchangeable with the corresponding object graph for read-only access.
*
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @since 1.0
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*
* @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable.
* Example: SELECT u AS user FROM User u
* The result should contains an array where each array index is an array: array('user' => [User object])
* Problem must be solved somehow by removing the isMixed in ResultSetMapping
*/
class ArrayHydrator extends AbstractHydrator
{
@ -38,14 +44,17 @@ class ArrayHydrator extends AbstractHydrator
private $_idTemplate = array();
private $_resultCounter = 0;
/** @override */
protected function _prepare()
/**
* {@inheritdoc}
*/
protected function prepare()
{
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
$this->_resultCounter = 0;
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array();
@ -53,30 +62,37 @@ class ArrayHydrator extends AbstractHydrator
}
}
/** @override */
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$result = array();
$cache = array();
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result);
$this->hydrateRowData($data, $cache, $result);
}
return $result;
}
/** @override */
protected function _hydrateRow(array $data, array &$cache, array &$result)
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $row, array &$cache, array &$result)
{
// 1) Initialize
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
$scalars = $rowData['scalars'];
unset($rowData['scalars']);
if (empty($rowData)) {
++$this->_resultCounter;
}
@ -111,11 +127,12 @@ class ArrayHydrator extends AbstractHydrator
}
$relationAlias = $this->_rsm->relationMap[$dqlAlias];
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
$relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
// Check the type of the relation (many or single-valued)
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = array();
@ -128,20 +145,21 @@ class ArrayHydrator extends AbstractHydrator
if ( ! $indexExists || ! $indexIsValid) {
$element = $data;
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
$baseElement[$relationAlias][$element[$field]] = $element;
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
} else {
$baseElement[$relationAlias][] = $element;
}
end($baseElement[$relationAlias]);
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] =
key($baseElement[$relationAlias]);
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = key($baseElement[$relationAlias]);
}
} else if ( ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = array();
}
} else {
$oneToOne = true;
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
$baseElement[$relationAlias] = null;
} else if ( ! isset($baseElement[$relationAlias])) {
@ -159,14 +177,16 @@ class ArrayHydrator extends AbstractHydrator
// It's a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array(0 => null);
$result[] = array($entityKey => null);
} else {
$result[] = null;
}
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
continue;
}
@ -174,26 +194,23 @@ class ArrayHydrator extends AbstractHydrator
// Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $rowData[$dqlAlias];
if ($this->_rsm->isMixed) {
$element = array($entityKey => $element);
}
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
if ($this->_rsm->isMixed) {
$result[] = array($element[$field] => $element);
++$this->_resultCounter;
} else {
$result[$element[$field]] = $element;
}
} else {
if ($this->_rsm->isMixed) {
$result[] = array($element);
++$this->_resultCounter;
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
$result[$resultKey] = $element;
} else {
$resultKey = $this->_resultCounter;
$result[] = $element;
++$this->_resultCounter;
}
}
end($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = key($result);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
} else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
$resultKey = $index;
/*if ($this->_rsm->isMixed) {
$result[] =& $result[$index];
++$this->_resultCounter;
@ -205,8 +222,17 @@ class ArrayHydrator extends AbstractHydrator
// Append scalar values to mixed result sets
if (isset($scalars)) {
if ( ! isset($resultKey) ) {
// this only ever happens when no object is fetched (scalar result only)
if (isset($this->_rsm->indexByMap['scalars'])) {
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
} else {
$resultKey = $this->_resultCounter - 1;
}
}
foreach ($scalars as $name => $value) {
$result[$this->_resultCounter - 1][$name] = $value;
$result[$resultKey][$name] = $value;
}
}
}
@ -224,28 +250,45 @@ class ArrayHydrator extends AbstractHydrator
{
if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$this->_resultPointers[$dqlAlias] =& $coll[$index];
return;
} else {
if ($coll) {
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
} else {
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
}
}
}
}
private function _getClassMetadata($className)
if ($index !== false) {
$this->_resultPointers[$dqlAlias] =& $coll[$index];
return;
}
if ( ! $coll) {
return;
}
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
return;
}
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
return;
}
/**
* Retrieve ClassMetadata associated to entity class name.
*
* @param string $className
*
* @return Doctrine\ORM\Mapping\ClassMetadata
*/
private function getClassMetadata($className)
{
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $this->_em->getClassMetadata($className);
}
return $this->_ce[$className];
}
}

View File

@ -14,4 +14,13 @@ class HydrationException extends \Doctrine\ORM\ORMException
return new self("The parent object of entity result with alias '$alias' was not found."
. " The parent alias is '$parentAlias'.");
}
public static function emptyDiscriminatorValue($dqlAlias)
{
return new self("The DQL alias '" . $dqlAlias . "' contains an entity ".
"of an inheritance hierachy with an empty discriminator value. This means " .
"that the database contains inconsistent data with an empty " .
"discriminator value in a table row."
);
}
}

View File

@ -29,9 +29,16 @@ use PDO,
/**
* The ObjectHydrator constructs an object graph out of an SQL result set.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*
* @internal Highly performance-sensitive code.
*
* @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable.
* Example: SELECT u AS user FROM User u
* The result should contains an array where each array index is an array: array('user' => [User object])
* Problem must be solved somehow by removing the isMixed in ResultSetMapping
*/
class ObjectHydrator extends AbstractHydrator
{
@ -53,28 +60,32 @@ class ObjectHydrator extends AbstractHydrator
/** @override */
protected function _prepare()
protected function prepare()
{
$this->_identifierMap =
$this->_resultPointers =
$this->_idTemplate = array();
$this->_resultCounter = 0;
if (!isset($this->_hints['deferEagerLoad'])) {
if ( ! isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = '';
$class = $this->_em->getClassMetadata($className);
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $class;
$this->_ce[$className] = $this->_em->getClassMetadata($className);
}
// Remember which associations are "fetch joined", so that we know where to inject
// collection stubs or proxies and where not.
if (isset($this->_rsm->relationMap[$dqlAlias])) {
if ( ! isset($this->_rsm->relationMap[$dqlAlias])) {
continue;
}
if ( ! isset($this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]])) {
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $this->_rsm->parentAliasMap[$dqlAlias]);
}
@ -82,21 +93,36 @@ class ObjectHydrator extends AbstractHydrator
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
$sourceClass = $this->_getClassMetadata($sourceClassName);
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
$this->_hints['fetched'][$sourceClassName][$assoc['fieldName']] = true;
if ($sourceClass->subClasses) {
foreach ($sourceClass->subClasses as $sourceSubclassName) {
$this->_hints['fetched'][$sourceSubclassName][$assoc['fieldName']] = true;
}
}
if ($assoc['type'] != ClassMetadata::MANY_TO_MANY) {
if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
continue;
}
// Mark any non-collection opposite sides as fetched, too.
if ($assoc['mappedBy']) {
$this->_hints['fetched'][$className][$assoc['mappedBy']] = true;
} else {
continue;
}
if ($assoc['inversedBy']) {
$class = $this->_ce[$className];
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
continue;
}
$this->_hints['fetched'][$className][$inverseAssoc['fieldName']] = true;
if ($class->subClasses) {
foreach ($class->subClasses as $targetSubclassName) {
$this->_hints['fetched'][$targetSubclassName][$inverseAssoc['fieldName']] = true;
@ -105,19 +131,16 @@ class ObjectHydrator extends AbstractHydrator
}
}
}
}
}
}
}
/**
* {@inheritdoc}
*/
protected function _cleanup()
protected function cleanup()
{
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
parent::_cleanup();
parent::cleanup();
$this->_identifierMap =
$this->_initializedCollections =
$this->_existingCollections =
@ -131,13 +154,13 @@ class ObjectHydrator extends AbstractHydrator
/**
* {@inheritdoc}
*/
protected function _hydrateAll()
protected function hydrateAllData()
{
$result = array();
$cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result);
$this->hydrateRowData($row, $cache, $result);
}
// Take snapshots from all newly initialized collections
@ -158,29 +181,32 @@ class ObjectHydrator extends AbstractHydrator
{
$oid = spl_object_hash($entity);
$relation = $class->associationMappings[$fieldName];
$value = $class->reflFields[$fieldName]->getValue($entity);
if ($value === null) {
$value = new ArrayCollection;
}
if ( ! $value instanceof PersistentCollection) {
$value = new PersistentCollection(
$this->_em,
$this->_ce[$relation['targetEntity']],
$value
$this->_em, $this->_ce[$relation['targetEntity']], $value
);
$value->setOwner($entity, $relation);
$class->reflFields[$fieldName]->setValue($entity, $value);
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
$this->_initializedCollections[$oid . $fieldName] = $value;
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
} else if (
isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
! $value->isInitialized()) {
! $value->isInitialized()
) {
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
$value->setDirty(false);
$value->setInitialized(true);
$value->unwrap()->clear();
$this->_initializedCollections[$oid . $fieldName] = $value;
} else {
// Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
@ -200,15 +226,21 @@ class ObjectHydrator extends AbstractHydrator
private function _getEntity(array $data, $dqlAlias)
{
$className = $this->_rsm->aliasMap[$dqlAlias];
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
$discrColumn = $this->_rsm->metaMappings[$this->_rsm->discriminatorColumns[$dqlAlias]];
if ($data[$discrColumn] === "") {
throw HydrationException::emptyDiscriminatorValue($dqlAlias);
}
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
unset($data[$discrColumn]);
}
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
$class = $this->_ce[$className];
$this->registerManaged($class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
$this->registerManaged($this->_ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
}
return $this->_uow->createEntity($className, $data, $this->_hints);
@ -218,6 +250,7 @@ class ObjectHydrator extends AbstractHydrator
{
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
$class = $this->_ce[$className];
/* @var $class ClassMetadata */
if ($class->isIdentifierComposite) {
$idHash = '';
@ -249,6 +282,7 @@ class ObjectHydrator extends AbstractHydrator
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $this->_em->getClassMetadata($className);
}
return $this->_ce[$className];
}
@ -273,13 +307,13 @@ class ObjectHydrator extends AbstractHydrator
* @param array $cache The cache to use.
* @param array $result The result array to fill.
*/
protected function _hydrateRow(array $data, array &$cache, array &$result)
protected function hydrateRowData(array $row, array &$cache, array &$result)
{
// Initialize
$id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array();
// Split the row data into chunks of class data.
$rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
// Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) {
@ -352,8 +386,7 @@ class ObjectHydrator extends AbstractHydrator
$element = $this->_getEntity($data, $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
$indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
$reflFieldValue->hydrateSet($indexValue, $element);
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
} else {
@ -380,6 +413,7 @@ class ObjectHydrator extends AbstractHydrator
$reflField->setValue($parentObject, $element);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
$targetClass = $this->_ce[$relation['targetEntity']];
if ($relation['isOwningSide']) {
//TODO: Just check hints['fetched'] here?
// If there is an inverse mapping on the target class its bidirectional
@ -410,14 +444,16 @@ class ObjectHydrator extends AbstractHydrator
} else {
// PATH C: Its a root result element
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
if ($this->_rsm->isMixed) {
$result[] = array(0 => null);
$result[] = array($entityKey => null);
} else {
$result[] = null;
}
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
continue;
}
@ -425,35 +461,31 @@ class ObjectHydrator extends AbstractHydrator
// check for existing result from the iterations before
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$field = $this->_rsm->indexByMap[$dqlAlias];
$key = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
if ($this->_rsm->isMixed) {
$element = array($key => $element);
$result[] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
++$this->_resultCounter;
} else {
$result[$key] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
$element = array($entityKey => $element);
}
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
if (isset($this->_hints['collection'])) {
$this->_hints['collection']->hydrateSet($key, $element);
$this->_hints['collection']->hydrateSet($resultKey, $element);
}
$result[$resultKey] = $element;
} else {
if ($this->_rsm->isMixed) {
$element = array(0 => $element);
}
$result[] = $element;
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
if (isset($this->_hints['collection'])) {
$this->_hints['collection']->hydrateAdd($element);
}
$result[] = $element;
}
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
// Update result pointer
$this->_resultPointers[$dqlAlias] = $element;
@ -461,6 +493,7 @@ class ObjectHydrator extends AbstractHydrator
// Update result pointer
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
$this->_resultPointers[$dqlAlias] = $result[$index];
$resultKey = $index;
/*if ($this->_rsm->isMixed) {
$result[] = $result[$index];
++$this->_resultCounter;
@ -471,8 +504,16 @@ class ObjectHydrator extends AbstractHydrator
// Append scalar values to mixed result sets
if (isset($scalars)) {
if ( ! isset($resultKey) ) {
if (isset($this->_rsm->indexByMap['scalars'])) {
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
} else {
$resultKey = $this->_resultCounter - 1;
}
}
foreach ($scalars as $name => $value) {
$result[$this->_resultCounter - 1][$name] = $value;
$result[$resultKey][$name] = $value;
}
}
}

View File

@ -26,25 +26,32 @@ use Doctrine\DBAL\Connection;
* The created result is almost the same as a regular SQL result set, except
* that column names are mapped to field names and data type conversions take place.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class ScalarHydrator extends AbstractHydrator
{
/** @override */
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$result = array();
$cache = array();
while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
$result[] = $this->_gatherScalarRowData($data, $cache);
$this->hydrateRowData($data, $cache, $result);
}
return $result;
}
/** @override */
protected function _hydrateRow(array $data, array &$cache, array &$result)
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $data, array &$cache, array &$result)
{
$result[] = $this->_gatherScalarRowData($data, $cache);
$result[] = $this->gatherScalarRowData($data, $cache);
}
}

View File

@ -17,7 +17,6 @@
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Internal\Hydration;
use \PDO;
@ -32,15 +31,21 @@ class SimpleObjectHydrator extends AbstractHydrator
*/
private $class;
/**
* @var array
*/
private $declaringClasses = array();
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$result = array();
$cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result);
$this->hydrateRowData($row, $cache, $result);
}
$this->_em->getUnitOfWork()->triggerEagerLoads();
@ -48,79 +53,73 @@ class SimpleObjectHydrator extends AbstractHydrator
return $result;
}
protected function _prepare()
/**
* {@inheritdoc}
*/
protected function prepare()
{
if (count($this->_rsm->aliasMap) == 1) {
if (count($this->_rsm->aliasMap) !== 1) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.");
}
if ($this->_rsm->scalarMappings) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
}
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
// We only need to add declaring classes if we have inheritance.
if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) {
return;
}
foreach ($this->_rsm->declaringClasses AS $column => $class) {
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
}
}
} else {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
}
if ($this->_rsm->scalarMappings) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
}
}
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
/**
* {@inheritdoc}
*/
protected function hydrateRowData(array $sqlResult, array &$cache, array &$result)
{
$data = array();
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) {
if (isset($this->_rsm->fieldMappings[$column])) {
$cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
$cache[$column]['field'] = true;
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
}
if (isset($cache[$column]['field'])) {
$value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
}
$data[$cache[$column]['name']] = $value;
}
$entityName = $this->class->name;
} else {
$data = array();
// We need to find the correct entity class name if we have inheritance in resultset
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
if ($sqlResult[$discrColumnName] === '') {
throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
}
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]);
}
foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) {
if (isset($this->_rsm->fieldMappings[$column])) {
$field = $this->_rsm->fieldMappings[$column];
$class = $this->declaringClasses[$column];
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
$cache[$column]['name'] = $field;
$cache[$column]['class'] = $class;
}
} else if (isset($this->_rsm->relationMap[$column])) {
if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
$cache[$column]['name'] = $field;
}
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
// Hydrate column information if not yet present
if ( ! isset($cache[$column])) {
if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) {
continue;
}
if (isset($cache[$column]['class'])) {
$value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
$cache[$column] = $info;
}
// the second and part is to prevent overwrites in case of multiple
// inheritance classes using the same property name (See AbstractHydrator)
if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) {
// Convert field to a valid PHP value
if (isset($cache[$column]['field'])) {
$type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
$value = $type->convertToPHPValue($value, $this->_platform);
}
// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) {
$data[$cache[$column]['name']] = $value;
}
}
}
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
@ -128,4 +127,52 @@ class SimpleObjectHydrator extends AbstractHydrator
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
}
/**
* Retrieve column information form ResultSetMapping.
*
* @param string $entityName
* @param string $column
*
* @return array
*/
protected function hydrateColumnInfo($entityName, $column)
{
switch (true) {
case (isset($this->_rsm->fieldMappings[$column])):
$class = isset($this->declaringClasses[$column])
? $this->declaringClasses[$column]
: $this->class;
// If class is not part of the inheritance, ignore
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
return null;
}
return array(
'class' => $class,
'name' => $this->_rsm->fieldMappings[$column],
'field' => true,
);
case (isset($this->_rsm->relationMap[$column])):
$class = isset($this->_rsm->relationMap[$column])
? $this->_rsm->relationMap[$column]
: $this->class;
// If class is not self referencing, ignore
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
return null;
}
// TODO: Decide what to do with associations. It seems original code is incomplete.
// One solution is to load the association, but it might require extra efforts.
return array('name' => $column);
default:
return array(
'name' => $this->_rsm->metaMappings[$column]
);
}
}
}

View File

@ -19,30 +19,37 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connection,
Doctrine\ORM\NoResultException,
Doctrine\ORM\NonUniqueResultException;
/**
* Hydrator that hydrates a single scalar value from the result set.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
* @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class SingleScalarHydrator extends AbstractHydrator
{
/** @override */
protected function _hydrateAll()
/**
* {@inheritdoc}
*/
protected function hydrateAllData()
{
$cache = array();
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
$num = count($result);
$data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
$numRows = count($data);
if ($num == 0) {
throw new \Doctrine\ORM\NoResultException;
} else if ($num > 1 || count($result[key($result)]) > 1) {
throw new \Doctrine\ORM\NonUniqueResultException;
if ($numRows === 0) {
throw new NoResultException();
}
$result = $this->_gatherScalarRowData($result[key($result)], $cache);
if ($numRows > 1 || count($data[key($data)]) > 1) {
throw new NonUniqueResultException();
}
$cache = array();
$result = $this->gatherScalarRowData($data[key($data)], $cache);
return array_shift($result);
}

View File

@ -313,6 +313,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class->containsForeignIdentifier = true;
}
if ($parent && !empty ($parent->namedQueries)) {
$this->addInheritedNamedQueries($class, $parent);
}
$class->setParentClasses($visited);
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
@ -429,6 +433,25 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
}
}
/**
* Adds inherited named queries to the subclass mapping.
*
* @since 2.2
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/
private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->namedQueries as $name => $query) {
if (!isset ($subClass->namedQueries[$name])) {
$subClass->addNamedQuery(array(
'name' => $query['name'],
'query' => $query['query']
));
}
}
}
/**
* Completes the ID generator mapping. If "auto" is specified we choose the generator
* most appropriate for the targeted database platform.

View File

@ -136,10 +136,6 @@ class ClassMetadataInfo implements ClassMetadata
* Identifies a many-to-one association.
*/
const MANY_TO_ONE = 2;
/**
* Combined bitmask for to-one (single-valued) associations.
*/
const TO_ONE = 3;
/**
* Identifies a one-to-many association.
*/
@ -148,6 +144,10 @@ class ClassMetadataInfo implements ClassMetadata
* Identifies a many-to-many association.
*/
const MANY_TO_MANY = 8;
/**
* Combined bitmask for to-one (single-valued) associations.
*/
const TO_ONE = 3;
/**
* Combined bitmask for to-many (collection-valued) associations.
*/
@ -685,7 +685,7 @@ class ClassMetadataInfo implements ClassMetadata
if ( ! isset($this->namedQueries[$queryName])) {
throw MappingException::queryNotFound($this->name, $queryName);
}
return $this->namedQueries[$queryName];
return $this->namedQueries[$queryName]['dql'];
}
/**
@ -1111,23 +1111,23 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getIdentifierColumnNames()
{
if ($this->isIdentifierComposite) {
$columnNames = array();
foreach ($this->identifier as $idField) {
if (isset($this->associationMappings[$idField])) {
// no composite pk as fk entity assumption:
$columnNames[] = $this->associationMappings[$idField]['joinColumns'][0]['name'];
} else {
$columnNames[] = $this->fieldMappings[$idField]['columnName'];
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
$columnNames = array_merge($columnNames, $assocColumnNames);
}
return $columnNames;
} else if(isset($this->fieldMappings[$this->identifier[0]])) {
return array($this->fieldMappings[$this->identifier[0]]['columnName']);
} else {
// no composite pk as fk entity assumption:
return array($this->associationMappings[$this->identifier[0]]['joinColumns'][0]['name']);
}
}
/**
@ -1448,8 +1448,15 @@ class ClassMetadataInfo implements ClassMetadata
if (isset($this->namedQueries[$queryMapping['name']])) {
throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
}
$query = str_replace('__CLASS__', $this->name, $queryMapping['query']);
$this->namedQueries[$queryMapping['name']] = $query;
$name = $queryMapping['name'];
$query = $queryMapping['query'];
$dql = str_replace('__CLASS__', $this->name, $query);
$this->namedQueries[$name] = array(
'name' => $name,
'query' => $query,
'dql' => $dql
);
}
/**
@ -1504,14 +1511,16 @@ class ClassMetadataInfo implements ClassMetadata
/**
* Stores the association mapping.
*
* @param AssociationMapping $assocMapping
* @param array $assocMapping
*/
protected function _storeAssociationMapping(array $assocMapping)
{
$sourceFieldName = $assocMapping['fieldName'];
if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
}
$this->associationMappings[$sourceFieldName] = $assocMapping;
}
@ -1904,6 +1913,42 @@ class ClassMetadataInfo implements ClassMetadata
return $this->name;
}
/**
* Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
*
* @param AbstractPlatform $platform
* @return array
*/
public function getQuotedIdentifierColumnNames($platform)
{
$quotedColumnNames = array();
foreach ($this->identifier as $idProperty) {
if (isset($this->fieldMappings[$idProperty])) {
$quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
: $this->fieldMappings[$idProperty]['columnName'];
continue;
}
// Association defined as Id field
$joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
$assocQuotedColumnNames = array_map(
function ($joinColumn) {
return isset($joinColumn['quoted'])
? $platform->quoteIdentifier($joinColumn['name'])
: $joinColumn['name'];
},
$joinColumns
);
$quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
}
return $quotedColumnNames;
}
/**
* Gets the (possibly quoted) column name of a mapped field for safe use
* in an SQL statement.
@ -1914,7 +1959,9 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getQuotedColumnName($field, $platform)
{
return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName'];
return isset($this->fieldMappings[$field]['quoted'])
? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
: $this->fieldMappings[$field]['columnName'];
}
/**

View File

@ -331,7 +331,7 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $oneToOneAnnot->inversedBy;
$mapping['cascade'] = $oneToOneAnnot->cascade;
$mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToOneAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $oneToOneAnnot->fetch);
$metadata->mapOneToOne($mapping);
} else if ($oneToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OneToMany')) {
$mapping['mappedBy'] = $oneToManyAnnot->mappedBy;
@ -339,7 +339,7 @@ class AnnotationDriver implements Driver
$mapping['cascade'] = $oneToManyAnnot->cascade;
$mapping['indexBy'] = $oneToManyAnnot->indexBy;
$mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $oneToManyAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $oneToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
@ -355,7 +355,7 @@ class AnnotationDriver implements Driver
$mapping['cascade'] = $manyToOneAnnot->cascade;
$mapping['inversedBy'] = $manyToOneAnnot->inversedBy;
$mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToOneAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $manyToOneAnnot->fetch);
$metadata->mapManyToOne($mapping);
} else if ($manyToManyAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\ManyToMany')) {
$joinTable = array();
@ -395,7 +395,7 @@ class AnnotationDriver implements Driver
$mapping['inversedBy'] = $manyToManyAnnot->inversedBy;
$mapping['cascade'] = $manyToManyAnnot->cascade;
$mapping['indexBy'] = $manyToManyAnnot->indexBy;
$mapping['fetch'] = constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $manyToManyAnnot->fetch);
$mapping['fetch'] = $this->getFetchMode($className, $manyToManyAnnot->fetch);
if ($orderByAnnot = $this->_reader->getPropertyAnnotation($property, 'Doctrine\ORM\Mapping\OrderBy')) {
$mapping['orderBy'] = $orderByAnnot->value;
@ -446,6 +446,10 @@ class AnnotationDriver implements Driver
if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad);
}
if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) {
$metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush);
}
}
}
}
@ -536,6 +540,22 @@ class AnnotationDriver implements Driver
return $classes;
}
/**
* Attempts to resolve the fetch mode.
*
* @param string $className The class name
* @param string $fetchMode The fetch mode
* @return integer The fetch mode as defined in ClassMetadata
* @throws MappingException If the fetch mode is not valid
*/
private function getFetchMode($className, $fetchMode)
{
if(!defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode)) {
throw MappingException::invalidFetchMode($className, $fetchMode);
}
return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' . $fetchMode);
}
/**
* Factory method for the Annotation Driver
*

View File

@ -387,3 +387,9 @@ final class PostRemove implements Annotation {}
* @Target("METHOD")
*/
final class PostLoad implements Annotation {}
/**
* @Annotation
* @Target("METHOD")
*/
final class PreFlush implements Annotation {}

View File

@ -166,9 +166,12 @@ class XmlDriver extends AbstractFileDriver
foreach ($xmlRoot->field as $fieldMapping) {
$mapping = array(
'fieldName' => (string)$fieldMapping['name'],
'type' => (string)$fieldMapping['type']
);
if (isset($fieldMapping['type'])) {
$mapping['type'] = (string)$fieldMapping['type'];
}
if (isset($fieldMapping['column'])) {
$mapping['columnName'] = (string)$fieldMapping['column'];
}
@ -219,10 +222,13 @@ class XmlDriver extends AbstractFileDriver
$mapping = array(
'id' => true,
'fieldName' => (string)$idElement['name'],
'type' => (string)$idElement['type']
'fieldName' => (string)$idElement['name']
);
if (isset($fieldMapping['type'])) {
$mapping['type'] = (string)$idElement['type'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = (string)$idElement['column'];
}
@ -327,6 +333,8 @@ class XmlDriver extends AbstractFileDriver
if (isset($oneToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$oneToManyElement['index-by'];
} else if (isset($oneToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
}
$metadata->mapOneToMany($mapping);
@ -432,8 +440,10 @@ class XmlDriver extends AbstractFileDriver
$mapping['orderBy'] = $orderBy;
}
if (isset($manyToManyElement->{'index-by'})) {
$mapping['indexBy'] = (string)$manyToManyElement->{'index-by'};
if (isset($manyToManyElement['index-by'])) {
$mapping['indexBy'] = (string)$manyToManyElement['index-by'];
} else if (isset($manyToManyElement->{'index-by'})) {
throw new \InvalidArgumentException("<index-by /> is not a valid tag");
}
$metadata->mapManyToMany($mapping);

View File

@ -165,16 +165,15 @@ class YamlDriver extends AbstractFileDriver
continue;
}
if (!isset($idElement['type'])) {
throw MappingException::propertyTypeIsRequired($className, $name);
}
$mapping = array(
'id' => true,
'fieldName' => $name,
'type' => $idElement['type']
'fieldName' => $name
);
if (isset($idElement['type'])) {
$mapping['type'] = $idElement['type'];
}
if (isset($idElement['column'])) {
$mapping['columnName'] = $idElement['column'];
}
@ -201,19 +200,21 @@ class YamlDriver extends AbstractFileDriver
// Evaluate fields
if (isset($element['fields'])) {
foreach ($element['fields'] as $name => $fieldMapping) {
if (!isset($fieldMapping['type'])) {
throw MappingException::propertyTypeIsRequired($className, $name);
}
$mapping = array(
'fieldName' => $name
);
if (isset($fieldMapping['type'])) {
$e = explode('(', $fieldMapping['type']);
$fieldMapping['type'] = $e[0];
$mapping['type'] = $fieldMapping['type'];
if (isset($e[1])) {
$fieldMapping['length'] = substr($e[1], 0, strlen($e[1]) - 1);
}
$mapping = array(
'fieldName' => $name,
'type' => $fieldMapping['type']
);
}
if (isset($fieldMapping['id'])) {
$mapping['id'] = true;
if (isset($fieldMapping['generator']['strategy'])) {

View File

@ -298,4 +298,9 @@ class MappingException extends \Doctrine\ORM\ORMException
{
return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback.");
}
public static function invalidFetchMode($className, $annotation)
{
return new self("Entity '" . $className . "' has a mapping with invalid fetch mode '" . $annotation . "'");
}
}

View File

@ -468,7 +468,7 @@ final class PersistentCollection implements Collection
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->count($this) + $this->coll->count();
->count($this) + ($this->isDirty ? $this->coll->count() : 0);
}
$this->initialize();
@ -672,7 +672,10 @@ final class PersistentCollection implements Collection
*/
public function slice($offset, $length = null)
{
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
if ( ! $this->initialized &&
! $this->isDirty &&
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
return $this->em->getUnitOfWork()
->getCollectionPersister($this->association)
->slice($this, $offset, $length);

View File

@ -62,7 +62,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$columnAlias = $this->getSQLColumnAlias($columnName);
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
return $sql . ' AS ' . $columnAlias;
@ -70,9 +70,8 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
{
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
$columnAlias = $this->getSQLColumnAlias($joinColumnName);
$this->_rsm->addMetaResult('r', $columnAlias, $joinColumnName);
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
}

View File

@ -1001,26 +1001,25 @@ class BasicEntityPersister
$columnList .= $assoc2ColumnSQL;
}
}
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
$first = true;
if ($assoc['isOwningSide']) {
$this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($assoc['joinColumns']);
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
if ( ! $first) {
$this->_selectJoinSql .= ' AND ';
}
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = '
. $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol . ' ';
. $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol;
$first = false;
}
} else {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
$this->_selectJoinSql .= ' LEFT JOIN';
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON ';
@ -1030,7 +1029,7 @@ class BasicEntityPersister
}
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol;
$first = false;
}
}
@ -1060,8 +1059,7 @@ class BasicEntityPersister
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList) $columnList .= ', ';
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$resultColumnName = $this->getSQLColumnAlias($srcColumn);
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $resultColumnName;
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
@ -1180,10 +1178,9 @@ class BasicEntityPersister
*/
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{
$columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
. '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$columnAlias = $this->getSQLColumnAlias($class->columnNames[$field]);
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
@ -1500,4 +1497,37 @@ class BasicEntityPersister
return (bool) $this->_conn->fetchColumn($sql, $params);
}
/**
* Generates the appropriate join SQL for the given join column.
*
* @param array $joinColumns The join columns definition of an association.
* @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise.
*/
protected function getJoinSQLForJoinColumns($joinColumns)
{
// if one of the join columns is nullable, return left join
foreach($joinColumns as $joinColumn) {
if(isset($joinColumn['nullable']) && $joinColumn['nullable']){
return 'LEFT JOIN';
}
}
return 'INNER JOIN';
}
/**
* Gets an SQL column alias for a column name.
*
* @param string $columnName
* @return string
*/
public function getSQLColumnAlias($columnName)
{
// Trim the column alias to the maximum identifier length of the platform.
// If the alias is to long, characters are cut off from the beginning.
return $this->_platform->getSQLResultCasing(
substr($columnName . $this->_sqlAliasCounter++, -$this->_platform->getMaxIdentifierLength())
);
}
}

View File

@ -165,11 +165,13 @@ class ProxyFactory
{
$methods = '';
$methodNames = array();
foreach ($class->reflClass->getMethods() as $method) {
/* @var $method ReflectionMethod */
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone"))) {
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) {
continue;
}
$methodNames[$method->getName()] = true;
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
$methods .= "\n" . ' public function ';
@ -210,8 +212,12 @@ class ProxyFactory
$methods .= $parameterString . ')';
$methods .= "\n" . ' {' . "\n";
if ($this->isShortIdentifierGetter($method, $class)) {
$identifier = lcfirst(substr($method->getName(), 3));
$cast = in_array($class->fieldMappings[$identifier]['type'], array('integer', 'smallint')) ? '(int) ' : '';
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
$methods .= ' return $this->_identifier["' . lcfirst(substr($method->getName(), 3)) . '"];' . "\n";
$methods .= ' return ' . $cast . '$this->_identifier["' . $identifier . '"];' . "\n";
$methods .= ' }' . "\n";
}
$methods .= ' $this->__load();' . "\n";
@ -237,6 +243,7 @@ class ProxyFactory
in_array($identifier, $class->identifier, true) &&
$class->hasField($identifier) &&
(($method->getEndLine() - $method->getStartLine()) <= 4)
&& in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string'))
);
}

View File

@ -526,7 +526,7 @@ final class Query extends AbstractQuery
*
* @param array $params The query parameters.
* @param integer $hydrationMode The hydration mode to use.
* @return IterableResult
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{

View File

@ -59,7 +59,7 @@ class Composite extends Base
}
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
if (mb_stripos($queryPart, ' OR ') !== false || mb_stripos($queryPart, ' AND ') !== false) {
if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) {
return $this->_preSeparator . $queryPart . $this->_postSeparator;
}

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,11 @@ class QueryException extends \Doctrine\ORM\ORMException
return new self('[Semantical Error] ' . $message);
}
public static function invalidLockMode()
{
return new self('Invalid lock mode hint provided.');
}
public static function invalidParameterType($expected, $received)
{
return new self('Invalid parameter type, ' . $received . ' given, but ' . $expected . ' expected.');

View File

@ -36,87 +36,79 @@ namespace Doctrine\ORM\Query;
class ResultSetMapping
{
/**
* Whether the result is mixed (contains scalar values together with field values).
*
* @ignore
* @var boolean
* @var boolean Whether the result is mixed (contains scalar values together with field values).
*/
public $isMixed = false;
/**
* Maps alias names to class names.
*
* @ignore
* @var array
* @var array Maps alias names to class names.
*/
public $aliasMap = array();
/**
* Maps alias names to related association field names.
*
* @ignore
* @var array
* @var array Maps alias names to related association field names.
*/
public $relationMap = array();
/**
* Maps alias names to parent alias names.
*
* @ignore
* @var array
* @var array Maps alias names to parent alias names.
*/
public $parentAliasMap = array();
/**
* Maps column names in the result set to field names for each class.
*
* @ignore
* @var array
* @var array Maps column names in the result set to field names for each class.
*/
public $fieldMappings = array();
/**
* Maps column names in the result set to the alias/field name to use in the mapped result.
*
* @ignore
* @var array
* @var array Maps column names in the result set to the alias/field name to use in the mapped result.
*/
public $scalarMappings = array();
/**
* Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
*
* @ignore
* @var array
* @var array Maps entities in the result set to the alias name to use in the mapped result.
*/
public $entityMappings = array();
/**
* @ignore
* @var array Maps column names of meta columns (foreign keys, discriminator columns, ...) to field names.
*/
public $metaMappings = array();
/**
* Maps column names in the result set to the alias they belong to.
*
* @ignore
* @var array
* @var array Maps column names in the result set to the alias they belong to.
*/
public $columnOwnerMap = array();
/**
* List of columns in the result set that are used as discriminator columns.
*
* @ignore
* @var array
* @var array List of columns in the result set that are used as discriminator columns.
*/
public $discriminatorColumns = array();
/**
* Maps alias names to field names that should be used for indexing.
*
* @ignore
* @var array
* @var array Maps alias names to field names that should be used for indexing.
*/
public $indexByMap = array();
/**
* Map from column names to class names that declare the field the column is mapped to.
*
* @ignore
* @var array
* @var array Map from column names to class names that declare the field the column is mapped to.
*/
public $declaringClasses = array();
/**
* This is necessary to hydrate derivate foreign keys correctly.
*
* @var array
* @var array This is necessary to hydrate derivate foreign keys correctly.
*/
public $isIdentifierColumn = array();
@ -126,11 +118,15 @@ class ResultSetMapping
* @param string $class The class name of the entity.
* @param string $alias The alias for the class. The alias must be unique among all entity
* results or joined entity results within this ResultSetMapping.
* @param string $resultAlias The result alias with which the entity result should be
* placed in the result structure.
*
* @todo Rename: addRootEntity
*/
public function addEntityResult($class, $alias)
public function addEntityResult($class, $alias, $resultAlias = null)
{
$this->aliasMap[$alias] = $class;
$this->entityMappings[$alias] = $resultAlias;
}
/**
@ -141,6 +137,7 @@ class ResultSetMapping
* @param string $alias The alias of the entity result or joined entity result the discriminator
* column should be used for.
* @param string $discrColumn The name of the discriminator column in the SQL result set.
*
* @todo Rename: addDiscriminatorColumn
*/
public function setDiscriminatorColumn($alias, $discrColumn)
@ -157,7 +154,51 @@ class ResultSetMapping
*/
public function addIndexBy($alias, $fieldName)
{
$this->indexByMap[$alias] = $fieldName;
$found = false;
foreach ($this->fieldMappings AS $columnName => $columnFieldName) {
if ( ! ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] === $alias)) continue;
$this->addIndexByColumn($alias, $columnName);
$found = true;
break;
}
/* TODO: check if this exception can be put back, for now it's gone because of assumptions made by some ORM internals
if ( ! $found) {
$message = sprintf(
'Cannot add index by for DQL alias %s and field %s without calling addFieldResult() for them before.',
$alias,
$fieldName
);
throw new \LogicException($message);
}
*/
}
/**
* Set to index by a scalar result column name
*
* @param $resultColumnName
* @return void
*/
public function addIndexByScalar($resultColumnName)
{
$this->indexByMap['scalars'] = $resultColumnName;
}
/**
* Sets a column to use for indexing an entity or joined entity result by the given alias name.
*
* @param $alias
* @param $resultColumnName
* @return void
*/
public function addIndexByColumn($alias, $resultColumnName)
{
$this->indexByMap[$alias] = $resultColumnName;
}
/**
@ -207,6 +248,7 @@ class ResultSetMapping
$this->columnOwnerMap[$columnName] = $alias;
// field name => class name of declaring class
$this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
if ( ! $this->isMixed && $this->scalarMappings) {
$this->isMixed = true;
}
@ -238,6 +280,7 @@ class ResultSetMapping
public function addScalarResult($columnName, $alias)
{
$this->scalarMappings[$columnName] = $alias;
if ( ! $this->isMixed && $this->fieldMappings) {
$this->isMixed = true;
}
@ -397,6 +440,7 @@ class ResultSetMapping
{
$this->metaMappings[$columnName] = $fieldName;
$this->columnOwnerMap[$columnName] = $alias;
if ($isIdentifierColumn) {
$this->isIdentifierColumn[$alias][$columnName] = true;
}

View File

@ -86,20 +86,22 @@ class ResultSetMappingBuilder extends ResultSetMapping
if (isset($renamedColumns[$columnName])) {
$columnName = $renamedColumns[$columnName];
}
$columnName = $platform->getSQLResultCasing($columnName);
if (isset($this->fieldMappings[$columnName])) {
throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper.");
}
$this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName);
$this->addFieldResult($alias, $columnName, $propertyName);
}
foreach ($classMetadata->associationMappings AS $associationMapping) {
if ($associationMapping['isOwningSide'] && $associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($associationMapping['joinColumns'] AS $joinColumn) {
$columnName = $joinColumn['name'];
$renamedColumnName = isset($renamedColumns[$columnName]) ? $renamedColumns[$columnName] : $columnName;
$renamedColumnName = $platform->getSQLResultCasing($renamedColumnName);
if (isset($this->metaMappings[$renamedColumnName])) {
throw new \InvalidArgumentException("The column '$renamedColumnName' conflicts with another column in the mapper.");
}
$this->addMetaResult($alias, $platform->getSQLResultCasing($renamedColumnName), $platform->getSQLResultCasing($columnName));
$this->addMetaResult($alias, $renamedColumnName, $columnName);
}
}
}

View File

@ -28,6 +28,7 @@ use Doctrine\DBAL\LockMode,
* The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
* the corresponding SQL.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
@ -71,6 +72,13 @@ class SqlWalker implements TreeWalker
/** Map from result variable names to their SQL column alias names. */
private $_scalarResultAliasMap = array();
/**
* Map from DQL-Alias + Field-Name to SQL Column Alias
*
* @var array
*/
private $_scalarFields = array();
/** Map of all components/classes that appear in the DQL query. */
private $_queryComponents;
@ -221,7 +229,11 @@ class SqlWalker implements TreeWalker
*/
public function getSQLColumnAlias($columnName)
{
return $columnName . $this->_aliasCounter++;
// Trim the column alias to the maximum identifier length of the platform.
// If the alias is to long, characters are cut off from the beginning.
return $this->_platform->getSQLResultCasing(
substr($columnName . $this->_aliasCounter++, -$this->_platform->getMaxIdentifierLength())
);
}
/**
@ -246,31 +258,35 @@ class SqlWalker implements TreeWalker
// If this is a joined association we must use left joins to preserve the correct result.
$sql .= isset($this->_queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
if ($first) $first = false; else $sql .= ' AND ';
$sqlParts = array();
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
$sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
// LEFT JOIN subclass tables, if partial objects disallowed.
if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
$sql .= implode(' AND ', $sqlParts);
}
// Ignore subclassing inclusion if partial objects is disallowed
if ($this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
return $sql;
}
// LEFT JOIN child class tables
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true;
foreach ($class->identifier as $idField) {
if ($first) $first = false; else $sql .= ' AND ';
$sqlParts = array();
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
foreach ($subClass->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
$sqlParts[] = $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
}
$sql .= implode(' AND ', $sqlParts);
}
return $sql;
@ -278,28 +294,25 @@ class SqlWalker implements TreeWalker
private function _generateOrderedCollectionOrderByItems()
{
$sql = '';
$sqlParts = array();
foreach ($this->_selectedClasses AS $dqlAlias => $class) {
foreach ($this->_selectedClasses AS $selectedClass) {
$dqlAlias = $selectedClass['dqlAlias'];
$qComp = $this->_queryComponents[$dqlAlias];
if (isset($qComp['relation']['orderBy'])) {
if ( ! isset($qComp['relation']['orderBy'])) continue;
foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) {
$columnName = $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform);
$tableName = ($qComp['metadata']->isInheritanceTypeJoined())
? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
? $this->_em->getUnitOfWork()->getEntityPersister($qComp['metadata']->name)->getOwningTable($fieldName)
: $qComp['metadata']->getTableName();
if ($sql != '') {
$sql .= ', ';
}
$sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.'
. $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . ' ' . $orientation;
}
$sqlParts[] = $this->getSQLTableAlias($tableName, $dqlAlias) . '.' . $columnName . ' ' . $orientation;
}
}
return $sql;
return implode(', ', $sqlParts);
}
/**
@ -310,13 +323,13 @@ class SqlWalker implements TreeWalker
*/
private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases)
{
$encapsulate = false;
$sql = '';
$sqlParts = array();
foreach ($dqlAliases as $dqlAlias) {
$class = $this->_queryComponents[$dqlAlias]['metadata'];
if ($class->isInheritanceTypeSingleTable()) {
if ( ! $class->isInheritanceTypeSingleTable()) continue;
$conn = $this->_em->getConnection();
$values = array();
@ -328,18 +341,13 @@ class SqlWalker implements TreeWalker
$values[] = $conn->quote($this->_em->getClassMetadata($subclassName)->discriminatorValue);
}
if ($sql != '') {
$sql .= ' AND ';
$encapsulate = true;
}
$sql .= ($sql != '' ? ' AND ' : '')
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
$sqlParts[] = (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
}
}
return ($encapsulate) ? '(' . $sql . ')' : $sql;
$sql = implode(' AND ', $sqlParts);
return (count($sqlParts) > 1) ? '(' . $sql . ')' : $sql;
}
/**
@ -366,16 +374,25 @@ class SqlWalker implements TreeWalker
);
if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) {
if ($lockMode == LockMode::PESSIMISTIC_READ) {
switch ($lockMode) {
case LockMode::PESSIMISTIC_READ:
$sql .= ' ' . $this->_platform->getReadLockSQL();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
break;
case LockMode::PESSIMISTIC_WRITE:
$sql .= ' ' . $this->_platform->getWriteLockSQL();
} else if ($lockMode == LockMode::OPTIMISTIC) {
foreach ($this->_selectedClasses AS $class) {
break;
case LockMode::PESSIMISTIC_OPTIMISTIC:
foreach ($this->_selectedClasses AS $selectedClass) {
if ( ! $class->isVersioned) {
throw \Doctrine\ORM\OptimisticLockException::lockFailed();
throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
}
}
break;
default:
throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
}
}
@ -506,13 +523,18 @@ class SqlWalker implements TreeWalker
$this->_query->getHydrationMode() != Query::HYDRATE_OBJECT &&
$this->_query->getHint(Query::HINT_INCLUDE_META_COLUMNS);
foreach ($this->_selectedClasses as $dqlAlias => $class) {
foreach ($this->_selectedClasses as $selectedClass) {
$class = $selectedClass['class'];
$dqlAlias = $selectedClass['dqlAlias'];
$resultAlias = $selectedClass['resultAlias'];
// Register as entity or joined entity result
if ($this->_queryComponents[$dqlAlias]['relation'] === null) {
$this->_rsm->addEntityResult($class->name, $dqlAlias);
$this->_rsm->addEntityResult($class->name, $dqlAlias, $resultAlias);
} else {
$this->_rsm->addJoinedEntityResult(
$class->name, $dqlAlias,
$class->name,
$dqlAlias,
$this->_queryComponents[$dqlAlias]['parent'],
$this->_queryComponents[$dqlAlias]['relation']['fieldName']
);
@ -527,49 +549,46 @@ class SqlWalker implements TreeWalker
$sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']);
}
// Add foreign key columns to SQL, if necessary
if ($addMetaColumns) {
//FIXME: Include foreign key columns of child classes also!!??
if ( ! $addMetaColumns) continue;
// Add foreign key columns of class and also parent classes
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
if (isset($assoc['inherited'])) {
$owningClass = $this->_em->getClassMetadata($assoc['inherited']);
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
$owningClass = (isset($assoc['inherited'])) ? $this->_em->getClassMetadata($assoc['inherited']) : $class;
$sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
} else {
$sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
}
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
}
}
}
} else {
// Add foreign key columns to SQL, if necessary
if ($addMetaColumns) {
$sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
// Add foreign key columns of subclasses
foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName);
$sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
foreach ($subClass->associationMappings as $assoc) {
// Skip if association is inherited
if (isset($assoc['inherited'])) continue;
if ( ! ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) continue;
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, (isset($assoc['id']) && $assoc['id'] === true));
}
}
$this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn);
}
}
}
@ -611,11 +630,18 @@ class SqlWalker implements TreeWalker
}
if ($identificationVariableDecl->indexBy) {
$alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable;
$field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field;
if (isset($this->_scalarFields[$alias][$field])) {
$this->_rsm->addIndexByScalar($this->_scalarFields[$alias][$field]);
} else {
$this->_rsm->addIndexBy(
$identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
$identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field
);
}
}
$sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE));
}
@ -641,15 +667,13 @@ class SqlWalker implements TreeWalker
*/
public function walkOrderByClause($orderByClause)
{
$colSql = $this->_generateOrderedCollectionOrderByItems();
if ($colSql != '') {
$colSql = ", ".$colSql;
$orderByItems = array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems);
if (($collectionOrderByItems = $this->_generateOrderedCollectionOrderByItems()) !== '') {
$orderByItems = array_merge($orderByItems, (array) $collectionOrderByItems);
}
// OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
return ' ORDER BY ' . implode(
', ', array_map(array($this, 'walkOrderByItem'), $orderByClause->orderByItems)
) . $colSql;
return ' ORDER BY ' . implode(', ', $orderByItems);
}
/**
@ -660,16 +684,10 @@ class SqlWalker implements TreeWalker
*/
public function walkOrderByItem($orderByItem)
{
$sql = '';
$expr = $orderByItem->expression;
if ($expr instanceof AST\PathExpression) {
$sql = $this->walkPathExpression($expr);
} else {
$columnName = $this->_queryComponents[$expr]['token']['value'];
$sql = $this->_scalarResultAliasMap[$columnName];
}
$sql = ($expr instanceof AST\PathExpression)
? $this->walkPathExpression($expr)
: $this->_scalarResultAliasMap[$this->_queryComponents[$expr]['token']['value']];
return $sql . ' ' . strtoupper($orderByItem->type);
}
@ -695,6 +713,9 @@ class SqlWalker implements TreeWalker
{
$join = $joinVarDecl->join;
$joinType = $join->joinType;
$sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
? ' LEFT JOIN '
: ' INNER JOIN ';
if ($joinVarDecl->indexBy) {
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
@ -704,12 +725,6 @@ class SqlWalker implements TreeWalker
);
}
if ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) {
$sql = ' LEFT JOIN ';
} else {
$sql = ' INNER JOIN ';
}
$joinAssocPathExpr = $join->joinAssociationPathExpression;
$joinedDqlAlias = $join->aliasIdentificationVariable;
@ -724,11 +739,9 @@ class SqlWalker implements TreeWalker
// Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true) {
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $relation['type'] & ClassMetadata::TO_MANY) {
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
}
}
if ($joinVarDecl->indexBy) {
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
@ -744,7 +757,6 @@ class SqlWalker implements TreeWalker
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
// The owning side is necessary at this point because only it contains the JoinColumn information.
if ($assoc['type'] & ClassMetadata::TO_ONE) {
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
$first = true;
@ -971,7 +983,8 @@ class SqlWalker implements TreeWalker
$expr = $selectExpression->expression;
$hidden = $selectExpression->hiddenAliasResultVariable;
if ($expr instanceof AST\PathExpression) {
switch (true) {
case ($expr instanceof AST\PathExpression):
if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
throw QueryException::invalidPathExpression($expr->type);
}
@ -981,128 +994,60 @@ class SqlWalker implements TreeWalker
$qComp = $this->_queryComponents[$dqlAlias];
$class = $qComp['metadata'];
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $fieldName;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
if ($class->isInheritanceTypeJoined()) {
$tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName);
} else {
$tableName = $class->getTableName();
}
$resultAlias = $selectExpression->fieldIdentificationVariable ?: $fieldName;
$tableName = ($class->isInheritanceTypeJoined())
? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
: $class->getTableName();
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->getSQLColumnAlias($columnName);
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
$this->_scalarFields[$dqlAlias][$fieldName] = $columnAlias;
}
} else if ($expr instanceof AST\AggregateExpression) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
break;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkAggregateExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
case ($expr instanceof AST\AggregateExpression):
case ($expr instanceof AST\Functions\FunctionNode):
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\ArithmeticPrimary):
case ($expr instanceof AST\Literal):
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$columnAlias = $this->getSQLColumnAlias('sclr');
$resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\Subselect) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= '(' . $this->walkSubselect($expr) . ') AS '.$columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\Functions\FunctionNode) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if (
$expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm ||
$expr instanceof AST\ArithmeticFactor ||
$expr instanceof AST\ArithmeticPrimary ||
$expr instanceof AST\Literal
) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
if ($expr instanceof AST\Literal) {
$sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias;
} else {
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
}
$sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if (
$expr instanceof AST\NullIfExpression ||
$expr instanceof AST\CoalesceExpression ||
$expr instanceof AST\GeneralCaseExpression ||
$expr instanceof AST\SimpleCaseExpression
) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
break;
$columnAlias = 'sclr' . $this->_aliasCounter++;
case ($expr instanceof AST\Subselect):
$columnAlias = $this->getSQLColumnAlias('sclr');
$resultAlias = $selectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
$sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else {
break;
default:
// IdentificationVariable or PartialObjectExpression
if ($expr instanceof AST\PartialObjectExpression) {
$dqlAlias = $expr->identificationVariable;
@ -1114,16 +1059,21 @@ class SqlWalker implements TreeWalker
$queryComp = $this->_queryComponents[$dqlAlias];
$class = $queryComp['metadata'];
$resultAlias = $selectExpression->fieldIdentificationVariable ?: null;
if ( ! isset($this->_selectedClasses[$dqlAlias])) {
$this->_selectedClasses[$dqlAlias] = $class;
$this->_selectedClasses[$dqlAlias] = array(
'class' => $class,
'dqlAlias' => $dqlAlias,
'resultAlias' => $resultAlias
);
}
$beginning = true;
$sqlParts = array();
// Select all fields from the queried class
foreach ($class->fieldMappings as $fieldName => $mapping) {
if ($partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
if ($partialFieldSet && ! in_array($fieldName, $partialFieldSet)) {
continue;
}
@ -1131,14 +1081,11 @@ class SqlWalker implements TreeWalker
? $this->_em->getClassMetadata($mapping['inherited'])->getTableName()
: $class->getTableName();
if ($beginning) $beginning = false; else $sql .= ', ';
$sqlTableAlias = $this->getSQLTableAlias($tableName, $dqlAlias);
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
$sql .= $sqlTableAlias . '.' . $class->getQuotedColumnName($fieldName, $this->_platform)
. ' AS ' . $columnAlias;
$quotedColumnName = $class->getQuotedColumnName($fieldName, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$sqlParts[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS '. $columnAlias;
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
}
@ -1157,32 +1104,17 @@ class SqlWalker implements TreeWalker
continue;
}
if ($beginning) $beginning = false; else $sql .= ', ';
$columnAlias = $this->getSQLColumnAlias($mapping['columnName']);
$sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform)
. ' AS ' . $columnAlias;
$quotedColumnName = $subClass->getQuotedColumnName($fieldName, $this->_platform);
$sqlParts[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
}
}
}
// Add join columns (foreign keys) of the subclass
//TODO: Probably better do this in walkSelectClause to honor the INCLUDE_META_COLUMNS hint
foreach ($subClass->associationMappings as $fieldName => $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($beginning) $beginning = false; else $sql .= ', ';
$columnAlias = $this->getSQLColumnAlias($srcColumn);
$sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
}
}
}
}
}
$sql .= implode(', ', $sqlParts);
}
return $sql;
@ -1196,8 +1128,7 @@ class SqlWalker implements TreeWalker
*/
public function walkQuantifiedExpression($qExpr)
{
return ' ' . strtoupper($qExpr->type)
. '(' . $this->walkSubselect($qExpr->subselect) . ')';
return ' ' . strtoupper($qExpr->type) . '(' . $this->walkSubselect($qExpr->subselect) . ')';
}
/**
@ -1217,6 +1148,7 @@ class SqlWalker implements TreeWalker
$sql = $this->walkSimpleSelectClause($subselect->simpleSelectClause);
$sql .= $this->walkSubselectFromClause($subselect->subselectFromClause);
$sql .= $this->walkWhereClause($subselect->whereClause);
$sql .= $subselect->groupByClause ? $this->walkGroupByClause($subselect->groupByClause) : '';
$sql .= $subselect->havingClause ? $this->walkHavingClause($subselect->havingClause) : '';
$sql .= $subselect->orderByClause ? $this->walkOrderByClause($subselect->orderByClause) : '';
@ -1308,37 +1240,21 @@ class SqlWalker implements TreeWalker
break;
case ($expr instanceof AST\Functions\FunctionNode):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\SimpleArithmeticExpression):
case ($expr instanceof AST\ArithmeticTerm):
case ($expr instanceof AST\ArithmeticFactor):
case ($expr instanceof AST\ArithmeticPrimary):
case ($expr instanceof AST\Literal):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
break;
case ($expr instanceof AST\NullIfExpression):
case ($expr instanceof AST\CoalesceExpression):
case ($expr instanceof AST\GeneralCaseExpression):
case ($expr instanceof AST\SimpleCaseExpression):
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->_scalarResultCounter++;
$columnAlias = 'sclr' . $this->_aliasCounter++;
$columnAlias = $this->getSQLColumnAlias('sclr');
$this->_scalarResultAliasMap[$alias] = $columnAlias;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
$sql .= $expr->dispatch($this) . ' AS ' . $columnAlias;
break;
default: // IdentificationVariable
@ -1346,8 +1262,8 @@ class SqlWalker implements TreeWalker
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
$sqlParts = array();
foreach ($class->identifier as $identifier) {
$sqlParts[] = $tableAlias . '.' . $class->getQuotedColumnName($identifier, $this->_platform);
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
$sqlParts[] = $tableAlias . '.' . $columnName;
}
$sql .= implode(', ', $sqlParts);
@ -1634,12 +1550,11 @@ class SqlWalker implements TreeWalker
$sql .= ' AND ';
$first = true;
foreach ($targetClass->identifier as $idField) {
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.'
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
}
} else { // many-to-many
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
@ -1684,12 +1599,11 @@ class SqlWalker implements TreeWalker
$sql .= ' AND ';
$first = true;
foreach ($targetClass->identifier as $idField) {
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
if ($first) $first = false; else $sql .= ' AND ';
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
$sql .= $targetTableAlias . '.'
. $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?';
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
}
}

View File

@ -117,7 +117,7 @@ public function <methodName>()
* @param <variableType>$<variableName>
* @return <entity>
*/
public function <methodName>(<methodTypeHint>$<variableName>)
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
{
<spaces>$this-><fieldName> = $<variableName>;
<spaces>return $this;
@ -406,7 +406,7 @@ public function <methodName>()
}
if ($collections) {
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate));
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->_spaces, $collections), self::$_constructorMethodTemplate));
}
return '';
@ -634,7 +634,8 @@ public function <methodName>()
foreach ($metadata->associationMappings as $associationMapping) {
if ($associationMapping['type'] & ClassMetadataInfo::TO_ONE) {
if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
$nullable = $this->_isAssociationIsNullable($associationMapping) ? 'null' : null;
if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'], $nullable)) {
$methods[] = $code;
}
if ($code = $this->_generateEntityStubMethod($metadata, 'get', $associationMapping['fieldName'], $associationMapping['targetEntity'])) {
@ -653,6 +654,22 @@ public function <methodName>()
return implode("\n\n", $methods);
}
private function _isAssociationIsNullable($associationMapping)
{
if (isset($associationMapping['joinColumns'])) {
$joinColumns = $associationMapping['joinColumns'];
} else {
//@todo thereis no way to retreive targetEntity metadata
$joinColumns = array();
}
foreach ($joinColumns as $joinColumn) {
if(isset($joinColumn['nullable']) && !$joinColumn['nullable']) {
return false;
}
}
return true;
}
private function _generateEntityLifecycleCallbackMethods(ClassMetadataInfo $metadata)
{
if (isset($metadata->lifecycleCallbacks) && $metadata->lifecycleCallbacks) {
@ -707,7 +724,7 @@ public function <methodName>()
return implode("\n", $lines);
}
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null)
private function _generateEntityStubMethod(ClassMetadataInfo $metadata, $type, $fieldName, $typeHint = null, $defaultValue = null)
{
if ($type == "add") {
$addMethod = explode("\\", $typeHint);
@ -737,6 +754,7 @@ public function <methodName>()
'<variableName>' => Inflector::camelize($fieldName),
'<methodName>' => $methodName,
'<fieldName>' => $fieldName,
'<variableDefault>' => ($defaultValue !== null ) ? ('='.$defaultValue) : '',
'<entity>' => $this->_getClassName($metadata)
);
@ -805,7 +823,12 @@ public function <methodName>()
{
$lines = array();
$lines[] = $this->_spaces . '/**';
if ($associationMapping['type'] & ClassMetadataInfo::TO_MANY) {
$lines[] = $this->_spaces . ' * @var \Doctrine\Common\Collections\ArrayCollection';
} else {
$lines[] = $this->_spaces . ' * @var ' . $associationMapping['targetEntity'];
}
if ($this->_generateAnnotations) {
$lines[] = $this->_spaces . ' *';

View File

@ -67,7 +67,9 @@ class SchemaTool
/**
* Creates the database schema for the given array of ClassMetadata instances.
*
* @throws ToolsException
* @param array $classes
* @return void
*/
public function createSchema(array $classes)
{
@ -75,7 +77,11 @@ class SchemaTool
$conn = $this->_em->getConnection();
foreach ($createSchemaSql as $sql) {
try {
$conn->executeQuery($sql);
} catch(\Exception $e) {
throw ToolsException::schemaToolFailure($sql, $e);
}
}
}
@ -139,7 +145,7 @@ class SchemaTool
$this->_gatherRelationsSql($class, $table, $schema);
// Add the discriminator column
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table);
$this->addDiscriminatorColumnDefinition($class, $table);
// Aggregate all the information from all classes in the hierarchy
foreach ($class->parentClasses as $parentClassName) {
@ -171,7 +177,7 @@ class SchemaTool
// Add the discriminator column only to the root table
if ($class->name == $class->rootEntityName) {
$discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table);
$this->addDiscriminatorColumnDefinition($class, $table);
} else {
// Add an ID FK column to child tables
/* @var Doctrine\ORM\Mapping\ClassMetadata $class */
@ -261,7 +267,7 @@ class SchemaTool
* @return array The portable column definition of the discriminator column as required by
* the DBAL.
*/
private function _getDiscriminatorColumnDefinition($class, $table)
private function addDiscriminatorColumnDefinition($class, $table)
{
$discrColumn = $class->discriminatorColumn;

View File

@ -50,7 +50,7 @@ class SchemaValidator
}
/**
* Checks the internal consistency of mapping files.
* Checks the internal consistency of all mapping files.
*
* There are several checks that can't be done at runtime or are too expensive, which can be verified
* with this command. For example:
@ -69,11 +69,29 @@ class SchemaValidator
$classes = $cmf->getAllMetadata();
foreach ($classes AS $class) {
if ($ce = $this->validateClass($class)) {
$errors[$class->name] = $ce;
}
}
return $errors;
}
/**
* Validate a single class of the current
*
* @param ClassMetadataInfo $class
* @return array
*/
public function validateClass(ClassMetadataInfo $class)
{
$ce = array();
/* @var $class ClassMetadata */
$cmf = $this->em->getMetadataFactory();
foreach ($class->associationMappings AS $fieldName => $assoc) {
if (!$cmf->hasMetadataFor($assoc['targetEntity'])) {
$ce[] = "The target entity '" . $assoc['targetEntity'] . "' specified on " . $class->name . '#' . $fieldName . ' is unknown.';
return $ce;
}
if ($assoc['mappedBy'] && $assoc['inversedBy']) {
@ -139,59 +157,67 @@ class SchemaValidator
}
}
foreach ($assoc['joinTable']['inverseJoinColumns'] AS $inverseJoinColumn) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
if (!isset($targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
if (!isset($targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']])) {
$ce[] = "The inverse referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetClass->name . "'.";
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
break;
}
$fieldName = $targetClass->fieldNames[$inverseJoinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetClass->identifier)) {
$fieldName = $targetMetadata->fieldNames[$inverseJoinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetMetadata->identifier)) {
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
if (count($targetClass->identifier) != count($assoc['joinTable']['inverseJoinColumns'])) {
if (count($targetMetadata->getIdentifierColumnNames()) != count($assoc['joinTable']['inverseJoinColumns'])) {
$ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to match to ALL identifier columns of the target entity '". $targetClass->name . "'";
"have to contain to ALL identifier columns of the target entity '". $targetMetadata->name . "', " .
"however '" . implode(", ", array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc['relationToTargetKeyColumns']))) .
"' are missing.";
}
if (count($class->identifier) != count($assoc['joinTable']['joinColumns'])) {
if (count($class->getIdentifierColumnNames()) != count($assoc['joinTable']['joinColumns'])) {
$ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
"have to contain to ALL identifier columns of the source entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), array_values($assoc['relationToSourceKeyColumns']))) .
"' are missing.";
}
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
foreach ($assoc['joinColumns'] AS $joinColumn) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
if (!isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
if (!isset($targetMetadata->fieldNames[$joinColumn['referencedColumnName']])) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' does not " .
"have a corresponding field with this column name on the class '" . $targetClass->name . "'.";
"have a corresponding field with this column name on the class '" . $targetMetadata->name . "'.";
break;
}
$fieldName = $targetClass->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetClass->identifier)) {
$fieldName = $targetMetadata->fieldNames[$joinColumn['referencedColumnName']];
if (!in_array($fieldName, $targetMetadata->identifier)) {
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
"has to be a primary key column.";
}
}
if (count($class->identifier) != count($assoc['joinColumns'])) {
if (count($class->getIdentifierColumnNames()) != count($assoc['joinColumns'])) {
$ids = array();
foreach ($assoc['joinColumns'] AS $joinColumn) {
$ids[] = $joinColumn['name'];
}
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
"have to match to ALL identifier columns of the source entity '". $class->name . "', " .
"however '" . implode(", ", array_diff($class->getIdentifierColumnNames(), $ids)) .
"' are missing.";
}
}
}
if (isset($assoc['orderBy']) && $assoc['orderBy'] !== null) {
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
foreach ($assoc['orderBy'] AS $orderField => $orientation) {
if (!$targetClass->hasField($orderField)) {
if (!$targetMetadata->hasField($orderField)) {
$ce[] = "The association " . $class->name."#".$fieldName." is ordered by a foreign field " .
$orderField . " that is not a field on the target entity " . $targetClass->name;
$orderField . " that is not a field on the target entity " . $targetMetadata->name;
}
}
}
@ -212,12 +238,7 @@ class SchemaValidator
}
}
if ($ce) {
$errors[$class->name] = $ce;
}
}
return $errors;
return $ce;
}
/**
@ -230,6 +251,6 @@ class SchemaValidator
$schemaTool = new SchemaTool($this->em);
$allMetadata = $this->em->getMetadataFactory()->getAllMetadata();
return (count($schemaTool->getUpdateSchemaSql($allMetadata, false)) == 0);
return (count($schemaTool->getUpdateSchemaSql($allMetadata, true)) == 0);
}
}

View File

@ -1,11 +1,38 @@
<?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\Tools;
use Doctrine\ORM\ORMException;
/**
* Tools related Exceptions
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ToolsException extends ORMException
{
public static function schemaToolFailure($sql, \Exception $e)
{
return new self("Schema-Tool failed with Error '" . $e->getMessage() . "' while executing DDL: " . $sql, "0", $e);
}
public static function couldNotMapDoctrine1Type($type)
{
return new self("Could not map doctrine 1 type '$type'!");

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@ class ConnectionMock extends \Doctrine\DBAL\Connection
/**
* @override
*/
public function insert($tableName, array $data)
public function insert($tableName, array $data, array $types = array())
{
$this->_inserts[$tableName][] = $data;
}

View File

@ -87,4 +87,11 @@ class DatabasePlatformMock extends \Doctrine\DBAL\Platforms\AbstractPlatform
{
}
/**
* Gets the SQL Snippet used to declare a BLOB column type.
*/
public function getBlobTypeDeclarationSQL(array $field)
{
throw DBALException::notSupported(__METHOD__);
}
}

View File

@ -8,7 +8,7 @@ namespace Doctrine\Tests\Mocks;
*
* @author Roman Borschel <roman@code-factory.org>
*/
class HydratorMockStatement implements \Doctrine\DBAL\Driver\Statement
class HydratorMockStatement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement
{
private $_resultSet;
@ -98,4 +98,14 @@ class HydratorMockStatement implements \Doctrine\DBAL\Driver\Statement
public function rowCount()
{
}
public function getIterator()
{
return $this->_resultSet;
}
public function setFetchMode($fetchMode)
{
}
}

View File

@ -9,6 +9,7 @@ class DDC117Article
{
/** @Id @Column(type="integer", name="article_id") @GeneratedValue */
private $id;
/** @Column */
private $title;

View File

@ -0,0 +1,76 @@
<?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\Tests\Models\DDC1476;
/**
* @Entity()
*/
class DDC1476EntityWithDefaultFieldType
{
/**
* @Id
* @Column()
* @GeneratedValue("NONE")
*/
protected $id;
/** @column() */
protected $name;
/**
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
{
$metadata->mapField(array(
'id' => true,
'fieldName' => 'id',
));
$metadata->mapField(array(
'fieldName' => 'name',
));
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadataInfo::GENERATOR_TYPE_NONE);
}
}

View File

@ -21,7 +21,7 @@ class LegacyUser
*/
public $_username;
/**
* @Column(type="string", length=255)
* @Column(type="string", length=255, name="name")
*/
public $_name;
/**

View File

@ -23,12 +23,12 @@ class LegacyUserReference
private $_target;
/**
* @column(type="string")
* @column(type="string", name="description")
*/
private $_description;
/**
* @column(type="datetime")
* @column(type="datetime", name="created")
*/
private $_created;

View File

@ -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
@ -33,11 +31,13 @@ require_once __DIR__ . '/../../TestInit.php';
* @link http://www.doctrine-project.org
* @since 2.0
*/
class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
class CustomTreeWalkersTest extends \Doctrine\Tests\OrmTestCase
{
protected function setUp() {
$this->useModelSet('cms');
parent::setUp();
private $_em;
protected function setUp()
{
$this->_em = $this->_getTestEntityManager();
}
public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed)

View File

@ -20,7 +20,9 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function tearDown()
{
if ($this->_em) {
$this->_em->getConfiguration()->setEntityNamespaces(array());
}
parent::tearDown();
}

View File

@ -304,6 +304,49 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
}
/**
* @group DDC-1399
*/
public function testCountAfterAddThenFlush()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
$newGroup->name = "Test4";
$user->addGroup($newGroup);
$this->_em->persist($newGroup);
$this->assertFalse($user->groups->isInitialized());
$this->assertEquals(4, count($user->groups));
$this->assertFalse($user->groups->isInitialized());
$this->_em->flush();
$this->assertEquals(4, count($user->groups));
}
/**
* @group DDC-1462
*/
public function testSliceOnDirtyCollection()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
/* @var $user CmsUser */
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
$newGroup->name = "Test4";
$user->addGroup($newGroup);
$this->_em->persist($newGroup);
$qc = $this->getCurrentQueryCount();
$groups = $user->groups->slice(0, 10);
$this->assertEquals(4, count($groups));
$this->assertEquals($qc + 1, $this->getCurrentQueryCount());
}
private function loadFixture()
{
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser();

View File

@ -43,6 +43,29 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('changed from preUpdate callback!', $result[0]->value);
}
public function testPreFlushCallbacksAreInvoked()
{
$entity = new LifecycleCallbackTestEntity;
$entity->value = 'hello';
$this->_em->persist($entity);
$this->_em->flush();
$this->assertTrue($entity->prePersistCallbackInvoked);
$this->assertTrue($entity->preFlushCallbackInvoked);
$entity->preFlushCallbackInvoked = false;
$this->_em->flush();
$this->assertTrue($entity->preFlushCallbackInvoked);
$entity->value = 'bye';
$entity->preFlushCallbackInvoked = false;
$this->_em->flush();
$this->assertTrue($entity->preFlushCallbackInvoked);
}
public function testChangesDontGetLost()
{
$user = new LifecycleCallbackTestUser;
@ -190,6 +213,8 @@ class LifecycleCallbackTestEntity
public $postPersistCallbackInvoked = false;
public $postLoadCallbackInvoked = false;
public $preFlushCallbackInvoked = false;
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
@ -233,6 +258,11 @@ class LifecycleCallbackTestEntity
public function doStuffOnPreUpdate() {
$this->value = 'changed from preUpdate callback!';
}
/** @PreFlush */
public function doStuffOnPreFlush() {
$this->preFlushCallbackInvoked = true;
}
}
/**

View File

@ -39,6 +39,7 @@ class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase
$cleanFile = $this->_em->find(get_class($file), $file->getId());
$this->assertInstanceOf('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $cleanFile->getParent());
$this->assertEquals($directory->getId(), $cleanFile->getParent()->getId());
$this->assertInstanceOf('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()->getParent());
$this->assertEquals($root->getId(), $cleanFile->getParent()->getParent()->getId());

View File

@ -73,7 +73,14 @@ class OneToManyUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunction
$this->_em->persist($routeA);
$this->_em->persist($routeB);
$this->setExpectedException('Exception'); // depends on the underyling Database Driver
$this->_em->flush(); // Exception
$exceptionThrown = false;
try {
// exception depending on the underyling Database Driver
$this->_em->flush();
} catch(\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
}
}

View File

@ -19,6 +19,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Train'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\TrainDriver'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\TrainOwner'),
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Waggon'),
));
} catch(\Exception $e) {}
@ -26,7 +27,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testEagerLoadOneToOneOwningSide()
{
$train = new Train();
$train = new Train(new TrainOwner("Alexander"));
$driver = new TrainDriver("Benjamin");
$waggon = new Waggon();
@ -48,7 +49,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testEagerLoadOneToOneNullOwningSide()
{
$train = new Train();
$train = new Train(new TrainOwner("Alexander"));
$this->_em->persist($train); // cascades
$this->_em->flush();
@ -65,9 +66,8 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testEagerLoadOneToOneInverseSide()
{
$train = new Train();
$driver = new TrainDriver("Benjamin");
$train->setDriver($driver);
$owner = new TrainOwner("Alexander");
$train = new Train($owner);
$this->_em->persist($train); // cascades
$this->_em->flush();
@ -75,9 +75,9 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$sqlCount = count($this->_sqlLoggerStack->queries);
$driver = $this->_em->find(get_class($driver), $driver->id);
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $driver->train);
$this->assertNotNull($driver->train);
$driver = $this->_em->find(get_class($owner), $owner->id);
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $owner->train);
$this->assertNotNull($owner->train);
$this->assertEquals($sqlCount + 1, count($this->_sqlLoggerStack->queries));
}
@ -103,7 +103,7 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testEagerLoadManyToOne()
{
$train = new Train();
$train = new Train(new TrainOwner("Alexander"));
$waggon = new Waggon();
$train->addWaggon($waggon);
@ -115,6 +115,59 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $waggon->train);
$this->assertNotNull($waggon->train);
}
public function testEagerLoadWithNullableColumnsGeneratesLeftJoinOnBothSides()
{
$train = new Train(new TrainOwner("Alexander"));
$driver = new TrainDriver("Benjamin");
$train->setDriver($driver);
$this->_em->persist($train);
$this->_em->flush();
$this->_em->clear();
$train = $this->_em->find(get_class($train), $train->id);
$this->assertEquals(
"SELECT t0.id AS id1, t0.driver_id AS driver_id2, t3.id AS id4, t3.name AS name5, t0.owner_id AS owner_id6, t7.id AS id8, t7.name AS name9 FROM Train t0 LEFT JOIN TrainDriver t3 ON t0.driver_id = t3.id INNER JOIN TrainOwner t7 ON t0.owner_id = t7.id WHERE t0.id = ?",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
$this->_em->clear();
$driver = $this->_em->find(get_class($driver), $driver->id);
$this->assertEquals(
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id IN (?)",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
}
public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoinOnOwningSide()
{
$waggon = new Waggon();
$this->_em->persist($waggon);
$this->_em->flush();
$this->_em->clear();
$waggon = $this->_em->find(get_class($waggon), $waggon->id);
$this->assertEquals(
"SELECT t0.id AS id1, t0.train_id AS train_id2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
}
public function testEagerLoadWithNonNullableColumnsGeneratesLeftJoinOnNonOwningSide()
{
$owner = new TrainOwner('Alexander');
$train = new Train($owner);
$this->_em->persist($train);
$this->_em->flush();
$this->_em->clear();
$waggon = $this->_em->find(get_class($owner), $owner->id);
$this->assertEquals(
"SELECT t0.id AS id1, t0.name AS name2, t3.id AS id4, t3.driver_id AS driver_id5, t3.owner_id AS owner_id6 FROM TrainOwner t0 LEFT JOIN Train t3 ON t3.owner_id = t0.id WHERE t0.id = ?",
$this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql']
);
}
}
/**
@ -130,16 +183,23 @@ class Train
/**
* Owning side
* @OneToOne(targetEntity="TrainDriver", inversedBy="train", fetch="EAGER", cascade={"persist"})
* @JoinColumn(nullable=true)
*/
public $driver;
/**
* Owning side
* @OneToOne(targetEntity="TrainOwner", inversedBy="train", fetch="EAGER", cascade={"persist"})
*/
public $owner;
/**
* @oneToMany(targetEntity="Waggon", mappedBy="train", cascade={"persist"})
*/
public $waggons;
public function __construct()
public function __construct(TrainOwner $owner)
{
$this->waggons = new \Doctrine\Common\Collections\ArrayCollection();
$this->setOwner($owner);
}
public function setDriver(TrainDriver $driver)
@ -148,6 +208,12 @@ class Train
$driver->setTrain($this);
}
public function setOwner(TrainOwner $owner)
{
$this->owner = $owner;
$owner->setTrain($this);
}
public function addWaggon(Waggon $w)
{
$w->setTrain($this);
@ -181,6 +247,32 @@ class TrainDriver
}
}
/**
* @Entity
*/
class TrainOwner
{
/** @Id @Column(type="integer") @GeneratedValue */
public $id;
/** @column(type="string") */
public $name;
/**
* Inverse side
* @OneToOne(targetEntity="Train", mappedBy="owner", fetch="EAGER")
*/
public $train;
public function __construct($name)
{
$this->name = $name;
}
public function setTrain(Train $t)
{
$this->train = $t;
}
}
/**
* @Entity
*/

View File

@ -27,14 +27,14 @@ class ReadOnlyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->flush();
$readOnly->name = "Test2";
$readOnly->number = 4321;
$readOnly->numericValue = 4321;
$this->_em->flush();
$this->_em->clear();
$dbReadOnly = $this->_em->find('Doctrine\Tests\ORM\Functional\ReadOnlyEntity', $readOnly->id);
$this->assertEquals("Test1", $dbReadOnly->name);
$this->assertEquals(1234, $dbReadOnly->number);
$this->assertEquals(1234, $dbReadOnly->numericValue);
}
}
@ -51,11 +51,11 @@ class ReadOnlyEntity
/** @column(type="string") */
public $name;
/** @Column(type="integer") */
public $number;
public $numericValue;
public function __construct($name, $number)
{
$this->name = $name;
$this->number = $number;
$this->numericValue = $number;
}
}

View File

@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Proxy\ProxyClassGenerator;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
require_once __DIR__ . '/../../TestInit.php';
@ -160,6 +161,29 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
}
public function testDoNotInitializeProxyOnGettingTheIdentifierAndReturnTheRightType()
{
$product = new ECommerceProduct();
$product->setName('Doctrine Cookbook');
$shipping = new ECommerceShipping();
$shipping->setDays(1);
$product->setShipping($shipping);
$this->_em->persist($product);
$this->_em->flush();
$this->_em->clear();
$id = $shipping->getId();
$product = $this->_em->getRepository('Doctrine\Tests\Models\ECommerce\ECommerceProduct')->find($product->getId());
$entity = $product->getShipping();
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
$this->assertEquals($id, $entity->getId());
$this->assertSame($id, $entity->getId(), "Check that the id's are the same value, and type.");
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
}
public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier()
{
$id = $this->createProduct();

View File

@ -32,24 +32,28 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$tool = new SchemaTool($this->_em);
$sql = $tool->getCreateSchemaSql($classes);
$sqlCount = count($sql);
$this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", $sql[0]);
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_ACAC157BA76ED395 ON cms_addresses (user_id)", $sql[1]);
$this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, status VARCHAR(50) NOT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", $sql[2]);
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_3AF03EC5F85E0677 ON cms_users (username)", $sql[3]);
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", $sql[4]);
$this->assertEquals("CREATE INDEX IDX_7EA9409AA76ED395 ON cms_users_groups (user_id)", $sql[5]);
$this->assertEquals("CREATE INDEX IDX_7EA9409AFE54D947 ON cms_users_groups (group_id)", $sql[6]);
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", $sql[7]);
$this->assertEquals("CREATE INDEX IDX_F21F790FA76ED395 ON cms_phonenumbers (user_id)", $sql[8]);
$this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[9]);
$this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[10]);
$this->assertEquals("ALTER TABLE cms_addresses ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[11]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[12]);
$this->assertEquals("ALTER TABLE cms_users_groups ADD FOREIGN KEY (group_id) REFERENCES cms_groups(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[13]);
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES cms_users(id) NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[14]);
$this->assertEquals("CREATE TABLE cms_addresses (id INT NOT NULL, user_id INT DEFAULT NULL, country VARCHAR(50) NOT NULL, zip VARCHAR(50) NOT NULL, city VARCHAR(50) NOT NULL, PRIMARY KEY(id))", array_shift($sql));
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_ACAC157BA76ED395 ON cms_addresses (user_id)", array_shift($sql));
$this->assertEquals("CREATE TABLE cms_users (id INT NOT NULL, email_id INT DEFAULT NULL, status VARCHAR(50) DEFAULT NULL, username VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))", array_shift($sql));
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_3AF03EC5F85E0677 ON cms_users (username)", array_shift($sql));
$this->assertEquals("CREATE UNIQUE INDEX UNIQ_3AF03EC5A832C1C9 ON cms_users (email_id)", array_shift($sql));
$this->assertEquals("CREATE TABLE cms_users_groups (user_id INT NOT NULL, group_id INT NOT NULL, PRIMARY KEY(user_id, group_id))", array_shift($sql));
$this->assertEquals("CREATE INDEX IDX_7EA9409AA76ED395 ON cms_users_groups (user_id)", array_shift($sql));
$this->assertEquals("CREATE INDEX IDX_7EA9409AFE54D947 ON cms_users_groups (group_id)", array_shift($sql));
$this->assertEquals("CREATE TABLE cms_phonenumbers (phonenumber VARCHAR(50) NOT NULL, user_id INT DEFAULT NULL, PRIMARY KEY(phonenumber))", array_shift($sql));
$this->assertEquals("CREATE INDEX IDX_F21F790FA76ED395 ON cms_phonenumbers (user_id)", array_shift($sql));
$this->assertEquals("CREATE SEQUENCE cms_addresses_id_seq INCREMENT BY 1 MINVALUE 1 START 1", array_shift($sql));
$this->assertEquals("CREATE SEQUENCE cms_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1", array_shift($sql));
$this->assertEquals("ALTER TABLE cms_addresses ADD CONSTRAINT FK_ACAC157BA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
$this->assertEquals("ALTER TABLE cms_users ADD CONSTRAINT FK_3AF03EC5A832C1C9 FOREIGN KEY (email_id) REFERENCES cms_emails (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
$this->assertEquals("ALTER TABLE cms_users_groups ADD CONSTRAINT FK_7EA9409AFE54D947 FOREIGN KEY (group_id) REFERENCES cms_groups (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
$this->assertEquals("ALTER TABLE cms_phonenumbers ADD CONSTRAINT FK_F21F790FA76ED395 FOREIGN KEY (user_id) REFERENCES cms_users (id) NOT DEFERRABLE INITIALLY IMMEDIATE", array_shift($sql));
$this->assertEquals(count($sql), 15);
$this->assertEquals(array(), $sql, "SQL Array should be empty now.");
$this->assertEquals(17, $sqlCount, "Total of 17 queries should be executed");
}
public function testGetCreateSchemaSql2()
@ -92,7 +96,7 @@ class PostgreSqlSchemaToolTest extends \Doctrine\Tests\OrmFunctionalTestCase
$tool = new SchemaTool($this->_em);
$sql = $tool->getDropSchemaSQL($classes);
$this->assertEquals(13, count($sql));
$this->assertEquals(14, count($sql));
$dropSequenceSQLs = 0;
foreach ($sql AS $stmt) {
if (strpos($stmt, "DROP SEQUENCE") === 0) {

View File

@ -44,11 +44,10 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
->setParameter('author', $user)
->getResult();
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author AND a.text = :text";
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = :topic AND a.user = :author AND a.user = :author";
$farticle = $this->_em->createQuery($dql)
->setParameter('author', $user)
->setParameter('topic', 'This is John Galt speaking!')
->setParameter('text', 'Yadda Yadda!')
->getSingleResult();
$this->assertSame($article, $farticle);
@ -70,12 +69,11 @@ class DDC1040Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($article);
$this->_em->flush();
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3 AND a.text = ?4";
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1 AND a.user = ?2 AND a.user = ?3";
$farticle = $this->_em->createQuery($dql)
->setParameter(1, 'This is John Galt speaking!')
->setParameter(2, $user)
->setParameter(3, $user)
->setParameter(4, 'Yadda Yadda!')
->getSingleResult();
$this->assertSame($article, $farticle);

View File

@ -27,8 +27,8 @@ class DDC1151Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals("CREATE TABLE \"Group\" (id INT NOT NULL, PRIMARY KEY(id))", $sql[4]);
$this->assertEquals("CREATE SEQUENCE User_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[5]);
$this->assertEquals("CREATE SEQUENCE Group_id_seq INCREMENT BY 1 MINVALUE 1 START 1", $sql[6]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD FOREIGN KEY (ddc1151user_id) REFERENCES \"User\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD FOREIGN KEY (ddc1151group_id) REFERENCES \"Group\"(id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD CONSTRAINT FK_88A3259AC5AD08A FOREIGN KEY (ddc1151user_id) REFERENCES \"User\" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[7]);
$this->assertEquals("ALTER TABLE ddc1151user_ddc1151group ADD CONSTRAINT FK_88A32597357E0B1 FOREIGN KEY (ddc1151group_id) REFERENCES \"Group\" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE", $sql[8]);
}
}

View File

@ -209,8 +209,15 @@ class DDC117Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->article1->addTranslation('en', 'Bar');
$this->article1->addTranslation('en', 'Baz');
$this->setExpectedException('Exception');
$exceptionThrown = false;
try {
// exception depending on the underyling Database Driver
$this->_em->flush();
} catch(\Exception $e) {
$exceptionThrown = true;
}
$this->assertTrue($exceptionThrown, "The underlying database driver throws an exception.");
}
/**

View File

@ -106,7 +106,7 @@ class DDC1209_3
{
/**
* @Id
* @Column(type="datetime")
* @Column(type="datetime", name="somedate")
*/
private $date;

View File

@ -34,8 +34,8 @@ class DDC1225Test extends \Doctrine\Tests\OrmFunctionalTestCase
->setParameter(1, 0);
$this->assertEquals(
'SELECT t0_.test_entity2_id AS test_entity2_id0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?',
$qb->getQuery()->getSQL()
strtolower('SELECT t0_.test_entity2_id AS test_entity2_id0 FROM te1 t0_ WHERE t0_.test_entity2_id = ?'),
strtolower($qb->getQuery()->getSQL())
);
}
}

View File

@ -21,7 +21,7 @@ class DDC1228Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1228User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1228Profile'),
));
} catch(\PDOException $e) {
} catch(\Exception $e) {
}
}
@ -90,10 +90,10 @@ class DDC1228User
public $id;
/**
* @column(type="string")
* @Column(type="string")
* @var string
*/
public $name = '';
public $name = 'Bar';
/**
* @OneToOne(targetEntity="DDC1228Profile")

View File

@ -19,7 +19,7 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC1238User'),
));
} catch(\PDOException $e) {
} catch(\Exception $e) {
}
}

View File

@ -7,17 +7,17 @@ use DateTime;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1135
* @group DDC-1335
*/
class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
class DDC1335Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135Phone'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1335User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1335Phone'),
));
$this->loadFixture();
} catch(\Exception $e) {
@ -27,7 +27,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testDql()
{
$dql = 'SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id';
$dql = 'SELECT u FROM ' . __NAMESPACE__ . '\DDC1335User u INDEX BY u.id';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
@ -36,7 +36,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result);
$dql = 'SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id';
$dql = 'SELECT u, p FROM '.__NAMESPACE__ . '\DDC1335User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
@ -65,7 +65,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testTicket()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.id');
$builder->select('u')->from(__NAMESPACE__ . '\DDC1335User', 'u', 'u.id');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
@ -74,13 +74,13 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id', $dql);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1335User u INDEX BY u.id', $dql);
}
public function testIndexByUnique()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email');
$builder->select('u')->from(__NAMESPACE__ . '\DDC1335User', 'u', 'u.email');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
@ -89,14 +89,14 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.email', $dql);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1335User u INDEX BY u.email', $dql);
}
public function testIndexWithJoin()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u','p')
->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email')
->from(__NAMESPACE__ . '\DDC1335User', 'u', 'u.email')
->join('u.phones', 'p', null, null, 'p.id');
$dql = $builder->getQuery()->getDQL();
@ -123,7 +123,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());
$this->assertEquals('SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql);
$this->assertEquals('SELECT u, p FROM '.__NAMESPACE__ . '\DDC1335User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql);
}
private function loadFixture()
@ -132,9 +132,9 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
$p2 = array('22 xxxx-xxxx','22 yyyy-yyyy','22 zzzz-zzzz');
$p3 = array('33 xxxx-xxxx','33 yyyy-yyyy','33 zzzz-zzzz');
$u1 = new DDC1135User("foo@foo.com", "Foo",$p1);
$u2 = new DDC1135User("bar@bar.com", "Bar",$p2);
$u3 = new DDC1135User("foobar@foobar.com", "Foo Bar",$p3);
$u1 = new DDC1335User("foo@foo.com", "Foo",$p1);
$u2 = new DDC1335User("bar@bar.com", "Bar",$p2);
$u3 = new DDC1335User("foobar@foobar.com", "Foo Bar",$p3);
$this->_em->persist($u1);
$this->_em->persist($u2);
@ -148,7 +148,7 @@ class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
/**
* @Entity
*/
class DDC1135User
class DDC1335User
{
/**
* @Id @Column(type="integer")
@ -167,7 +167,7 @@ class DDC1135User
public $name;
/**
* @OneToMany(targetEntity="DDC1135Phone", mappedBy="user", cascade={"persist", "remove"})
* @OneToMany(targetEntity="DDC1335Phone", mappedBy="user", cascade={"persist", "remove"})
*/
public $phones;
@ -178,7 +178,7 @@ class DDC1135User
$this->phones = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($numbers as $number) {
$this->phones->add(new DDC1135Phone($this,$number));
$this->phones->add(new DDC1335Phone($this,$number));
}
}
}
@ -186,22 +186,22 @@ class DDC1135User
/**
* @Entity
*/
class DDC1135Phone
class DDC1335Phone
{
/**
* @Id
* @Column(name="id", type="integer")
* @GeneratedValue(strategy="AUTO")
* @GeneratedValue
*/
public $id;
/**
* @Column(name="number", type="string", nullable = false)
* @Column(name="numericalValue", type="string", nullable = false)
*/
public $number;
public $numericalValue;
/**
* @ManyToOne(targetEntity="DDC1135User", inversedBy="phones")
* @ManyToOne(targetEntity="DDC1335User", inversedBy="phones")
* @JoinColumn(name="user_id", referencedColumnName="id", nullable = false)
*/
public $user;
@ -209,6 +209,6 @@ class DDC1135Phone
public function __construct($user, $number)
{
$this->user = $user;
$this->number = $number;
$this->numericalValue = $number;
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1404
*/
class DDC1404Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1404ParentEntity'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1404ChildEntity'),
));
$this->loadFixtures();
} catch (Exception $exc) {
}
}
public function testTicket()
{
$repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC1404ChildEntity');
$queryAll = $repository->createNamedQuery('all');
$queryFirst = $repository->createNamedQuery('first');
$querySecond = $repository->createNamedQuery('second');
$this->assertEquals('SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1404ChildEntity p', $queryAll->getDQL());
$this->assertEquals('SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1404ChildEntity p WHERE p.id = 1', $queryFirst->getDQL());
$this->assertEquals('SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\DDC1404ChildEntity p WHERE p.id = 2', $querySecond->getDQL());
$this->assertEquals(sizeof($queryAll->getResult()), 2);
$this->assertEquals(sizeof($queryFirst->getResult()), 1);
$this->assertEquals(sizeof($querySecond->getResult()), 1);
}
public function loadFixtures()
{
$c1 = new DDC1404ChildEntity("ChildEntity 1");
$c2 = new DDC1404ChildEntity("ChildEntity 2");
$this->_em->persist($c1);
$this->_em->persist($c2);
$this->_em->flush();
}
}
/**
* @MappedSuperclass
*
* @NamedQueries({
* @NamedQuery(name="all", query="SELECT p FROM __CLASS__ p"),
* @NamedQuery(name="first", query="SELECT p FROM __CLASS__ p WHERE p.id = 1"),
* })
*/
class DDC1404ParentEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue()
*/
protected $id;
/**
* @return integer
*/
public function getId()
{
return $this->id;
}
}
/**
* @Entity
*
* @NamedQueries({
* @NamedQuery(name="first", query="SELECT p FROM __CLASS__ p WHERE p.id = 1"),
* @NamedQuery(name="second", query="SELECT p FROM __CLASS__ p WHERE p.id = 2")
* })
*/
class DDC1404ChildEntity extends DDC1404ParentEntity
{
/**
* @column(type="string")
*/
private $name;
/**
* @param string $name
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
}

View File

@ -0,0 +1,131 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
require_once __DIR__ . '/../../../TestInit.php';
class DDC1258Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\TestEntity'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\TestAdditionalEntity')
));
}
public function testIssue()
{
$testEntity = new TestEntity();
$testEntity->setValue(3);
$testEntity->setAdditional(new TestAdditionalEntity());
$this->_em->persist($testEntity);
$this->_em->flush();
$this->_em->clear();
// So here the value is 3
$this->assertEquals(3, $testEntity->getValue());
$test = $this->_em->getRepository(__NAMESPACE__ . '\TestEntity')->find(1);
// New value is set
$test->setValue(5);
// So here the value is 5
$this->assertEquals(5, $test->getValue());
// Get the additional entity
$additional = $test->getAdditional();
// Still 5..
$this->assertEquals(5, $test->getValue());
// Force the proxy to load
$additional->getBool();
// The value should still be 5
$this->assertEquals(5, $test->getValue());
}
}
/**
* @Entity
*/
class TestEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @Column(type="integer")
*/
protected $value;
/**
* @OneToOne(targetEntity="TestAdditionalEntity", inversedBy="entity", orphanRemoval=true, cascade={"persist", "remove"})
*/
protected $additional;
public function getValue()
{
return $this->value;
}
public function setValue($value)
{
$this->value = $value;
}
public function getAdditional()
{
return $this->additional;
}
public function setAdditional($additional)
{
$this->additional = $additional;
}
}
/**
* @Entity
*/
class TestAdditionalEntity
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @OneToOne(targetEntity="TestEntity", mappedBy="additional")
*/
protected $entity;
/**
* @Column(type="boolean")
*/
protected $bool;
public function __construct()
{
$this->bool = false;
}
public function getBool()
{
return $this->bool;
}
public function setBool($bool)
{
$this->bool = $bool;
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsUser;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1461
*/
class DDC1461Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1461TwitterAccount'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1461User')
));
} catch(\Exception $e) {
}
}
public function testChangeDetectionDeferredExplicit()
{
$user = new DDC1461User;
$this->_em->persist($user);
$this->_em->flush();
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user, \Doctrine\ORM\UnitOfWork::STATE_NEW), "Entity should be managed.");
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user), "Entity should be managed.");
$acc = new DDC1461TwitterAccount;
$user->twitterAccount = $acc;
$this->_em->persist($user);
$this->_em->flush();
$user = $this->_em->find(get_class($user), $user->id);
$this->assertNotNull($user->twitterAccount);
}
}
/**
* @Entity
* @ChangeTrackingPolicy("DEFERRED_EXPLICIT")
*/
class DDC1461User
{
/**
* @Id
* @GeneratedValue(strategy="AUTO")
* @Column(type="integer")
*/
public $id;
/**
* @OneToOne(targetEntity="DDC1461TwitterAccount", orphanRemoval=true, fetch="EAGER", cascade = {"persist"}, inversedBy="user")
* @var TwitterAccount
*/
public $twitterAccount;
}
/**
* @Entity
* @ChangeTrackingPolicy("DEFERRED_EXPLICIT")
*/
class DDC1461TwitterAccount
{
/**
* @Id
* @GeneratedValue(strategy="AUTO")
* @Column(type="integer")
*/
public $id;
/**
* @OneToOne(targetEntity="DDC1461User", fetch="EAGER")
*/
public $user;
}

View File

@ -25,18 +25,15 @@ class DDC331Test extends \Doctrine\Tests\OrmFunctionalTestCase
parent::setUp();
}
/**
* @group DDC-331
*/
public function testSelectFieldOnRootEntity()
{
$employee = new CompanyEmployee;
$employee->setName('Roman S. Borschel');
$employee->setSalary(100000);
$employee->setDepartment('IT');
$this->_em->persist($employee);
$this->_em->flush();
$this->_em->clear();
$q = $this->_em->createQuery('SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e');
$this->assertEquals('SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id', $q->getSql());
$this->assertEquals(
strtolower('SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id'),
strtolower($q->getSql())
);
}
}

View File

@ -18,7 +18,10 @@ class DDC448Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testIssue()
{
$q = $this->_em->createQuery("select b from ".__NAMESPACE__."\\DDC448SubTable b where b.connectedClassId = ?1");
$this->assertEquals('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.connectedClassId AS connectedClassId2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?', $q->getSQL());
$this->assertEquals(
strtolower('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.connectedClassId AS connectedClassId2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?'),
strtolower($q->getSQL())
);
}
}

View File

@ -18,7 +18,10 @@ class DDC493Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testIssue()
{
$q = $this->_em->createQuery("select u, c.data from ".__NAMESPACE__."\\DDC493Distributor u JOIN u.contact c");
$this->assertEquals('SELECT d0_.id AS id0, d1_.data AS data1, d0_.discr AS discr2, d0_.contact AS contact3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id', $q->getSQL());
$this->assertEquals(
strtolower('SELECT d0_.id AS id0, d1_.data AS data1, d0_.discr AS discr2, d0_.contact AS contact3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id'),
strtolower($q->getSQL())
);
}
}

View File

@ -18,7 +18,10 @@ class DDC513Test extends \Doctrine\Tests\OrmFunctionalTestCase
public function testIssue()
{
$q = $this->_em->createQuery("select u from ".__NAMESPACE__."\\DDC513OfferItem u left join u.price p");
$this->assertEquals('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.price AS price2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id', $q->getSQL());
$this->assertEquals(
strtolower('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.price AS price2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id'),
strtolower($q->getSQL())
);
}
}

View File

@ -30,7 +30,10 @@ class DDC698Test extends \Doctrine\Tests\OrmFunctionalTestCase
$sql = $qb->getQuery()->getSQL();
$this->assertEquals('SELECT p0_.privilegeID AS privilegeID0, p0_.name AS name1, r1_.roleID AS roleID2, r1_.name AS name3, r1_.shortName AS shortName4 FROM Privileges p0_ LEFT JOIN RolePrivileges r2_ ON p0_.privilegeID = r2_.privilegeID LEFT JOIN Roles r1_ ON r1_.roleID = r2_.roleID', $sql);
$this->assertEquals(
strtolower('SELECT p0_.privilegeID AS privilegeID0, p0_.name AS name1, r1_.roleID AS roleID2, r1_.name AS name3, r1_.shortName AS shortName4 FROM Privileges p0_ LEFT JOIN RolePrivileges r2_ ON p0_.privilegeID = r2_.privilegeID LEFT JOIN Roles r1_ ON r1_.roleID = r2_.roleID'),
strtolower($sql)
);
}
}

View File

@ -19,7 +19,10 @@ class DDC719Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
$q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY');
$this->assertEquals('SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0', $q->getSQL());
$this->assertEquals(
strtolower('SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0'),
strtolower($q->getSQL())
);
}
}

View File

@ -34,10 +34,10 @@ class DDC949Test extends \Doctrine\Tests\OrmFunctionalTestCase
$true = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => true));
$false = $this->_em->getRepository('Doctrine\Tests\Models\Generic\BooleanModel')->findOneBy(array('booleanField' => false));
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $true);
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $true, "True model not found");
$this->assertTrue($true->booleanField, "True Boolean Model should be true.");
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $false);
$this->assertInstanceOf('Doctrine\Tests\Models\Generic\BooleanModel', $false, "False model not found");
$this->assertFalse($false->booleanField, "False Boolean Model should be false.");
}
}

View File

@ -9,13 +9,34 @@ require_once __DIR__ . '/../../TestInit.php';
class ArrayHydratorTest extends HydrationTestCase
{
public function provideDataForUserEntityResult()
{
return array(
array(0),
array('user'),
);
}
public function provideDataForMultipleRootEntityResult()
{
return array(
array(0, 0),
array('user', 0),
array(0, 'article'),
array('user', 'article'),
);
}
/**
* Select u.id, u.name from Doctrine\Tests\Models\CMS\CmsUser u
* SELECT PARTIAL u.{id, name}
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @dataProvider provideDataForUserEntityResult
*/
public function testSimpleEntityQuery()
public function testSimpleEntityQuery($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
@ -31,28 +52,32 @@ class ArrayHydratorTest extends HydrationTestCase
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('romanb', $result[0]['name']);
$this->assertEquals(2, $result[1]['id']);
$this->assertEquals('jwage', $result[1]['name']);
}
/**
* SELECT PARTIAL u.{id, name}, PARTIAL a.{id, topic}
* FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a
*
* @dataProvider provideDataForMultipleRootEntityResult
*/
public function testSimpleMultipleRootEntityQuery()
public function testSimpleMultipleRootEntityQuery($userEntityKey, $articleEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsArticle', 'a');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsArticle', 'a', $articleEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
$rsm->addFieldResult('a', 'a__id', 'id');
@ -74,38 +99,42 @@ class ArrayHydratorTest extends HydrationTestCase
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(4, count($result));
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('romanb', $result[0]['name']);
$this->assertEquals(1, $result[1]['id']);
$this->assertEquals('Cool things.', $result[1]['topic']);
$this->assertEquals(2, $result[2]['id']);
$this->assertEquals('jwage', $result[2]['name']);
$this->assertEquals(2, $result[3]['id']);
$this->assertEquals('Cool things II.', $result[3]['topic']);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
* join u.phonenumbers p
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
* SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* JOIN u.phonenumbers p
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryFetchJoin()
public function testMixedQueryFetchJoin($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p',
'u', 'phonenumbers');
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
'u',
'phonenumbers'
);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
@ -136,37 +165,39 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]['phonenumbers']));
$this->assertEquals(2, count($result[0][$userEntityKey]['phonenumbers']));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber
$this->assertEquals(1, count($result[1][0]['phonenumbers']));
$this->assertEquals(1, count($result[1][$userEntityKey]['phonenumbers']));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']);
$this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(42, $result[0][$userEntityKey]['phonenumbers'][0]['phonenumber']);
$this->assertEquals(43, $result[0][$userEntityKey]['phonenumbers'][1]['phonenumber']);
$this->assertEquals(91, $result[1][$userEntityKey]['phonenumbers'][0]['phonenumber']);
}
/**
* select u.id, u.status, count(p.phonenumber) numPhones from User u
* join u.phonenumbers p group by u.status, u.id
* =
* select u.id, u.status, count(p.phonenumber) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id group by u.id, u.status
* SELECT PARTIAL u.{id, status}, COUNT(p.phonenumber) AS numPhones
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* JOIN u.phonenumbers p
* GROUP BY u.status, u.id
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryNormalJoin()
public function testMixedQueryNormalJoin($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'numPhones');
@ -188,30 +219,35 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
// first user => 2 phonenumbers
$this->assertArrayHasKey($userEntityKey, $result[0]);
$this->assertEquals(2, $result[0]['numPhones']);
// second user => 1 phonenumber
$this->assertArrayHasKey($userEntityKey, $result[1]);
$this->assertEquals(1, $result[1]['numPhones']);
}
/**
* select u.id, u.status, upper(u.name) nameUpper from User u index by u.id
* join u.phonenumbers p indexby p.phonenumber
* =
* select u.id, u.status, upper(u.name) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
* SELECT PARTIAL u.{id, status}, UPPER(u.name) nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* INDEX BY u.id
* JOIN u.phonenumbers p
* INDEX BY p.phonenumber
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryFetchJoinCustomIndex()
public function testMixedQueryFetchJoinCustomIndex($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
@ -251,25 +287,28 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertTrue(is_array($result[2]));
// test the scalar values
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals('ROMANB', $result[1]['nameUpper']);
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']['phonenumbers']));
$this->assertEquals(2, count($result[1][$userEntityKey]['phonenumbers']));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']['phonenumbers']));
$this->assertEquals(1, count($result[2][$userEntityKey]['phonenumbers']));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']['phonenumbers']['42']));
$this->assertTrue(isset($result[0]['1']['phonenumbers']['43']));
$this->assertTrue(isset($result[1]['2']['phonenumbers']['91']));
$this->assertTrue(isset($result[1][$userEntityKey]['phonenumbers']['42']));
$this->assertTrue(isset($result[1][$userEntityKey]['phonenumbers']['43']));
$this->assertTrue(isset($result[2][$userEntityKey]['phonenumbers']['91']));
}
/**
@ -361,7 +400,6 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
@ -501,7 +539,6 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
@ -570,6 +607,7 @@ class ArrayHydratorTest extends HydrationTestCase
'c',
'boards'
);
$rsm->addFieldResult('c', 'c__id', 'id');
$rsm->addFieldResult('c', 'c__position', 'position');
$rsm->addFieldResult('c', 'c__name', 'name');
@ -614,7 +652,6 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
@ -628,13 +665,17 @@ class ArrayHydratorTest extends HydrationTestCase
}
/**
* DQL: select partial u.{id,status}, a.id, a.topic, c.id as cid, c.topic as ctopic from CmsUser u left join u.articles a left join a.comments c
* SELECT PARTIAL u.{id,status}, a.id, a.topic, c.id as cid, c.topic as ctopic
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* LEFT JOIN u.articles a
* LEFT JOIN a.comments c
*
* @dataProvider provideDataForUserEntityResult
*/
/*public function testChainedJoinWithScalars()
public function testChainedJoinWithScalars($entityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $entityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('a__id', 'id');
@ -673,34 +714,39 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(3, count($result));
$this->assertEquals(2, count($result[0][0])); // User array
$this->assertEquals(2, count($result[0][$entityKey])); // User array
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('The First', $result[0]['topic']);
$this->assertEquals(1, $result[0]['cid']);
$this->assertEquals('First Comment', $result[0]['ctopic']);
$this->assertEquals(2, count($result[1][0])); // User array, duplicated
$this->assertEquals(2, count($result[1][$entityKey])); // User array, duplicated
$this->assertEquals(1, $result[1]['id']); // duplicated
$this->assertEquals('The First', $result[1]['topic']); // duplicated
$this->assertEquals(2, $result[1]['cid']);
$this->assertEquals('Second Comment', $result[1]['ctopic']);
$this->assertEquals(2, count($result[2][0])); // User array, duplicated
$this->assertEquals(2, count($result[2][$entityKey])); // User array, duplicated
$this->assertEquals(42, $result[2]['id']);
$this->assertEquals('The Answer', $result[2]['topic']);
$this->assertNull($result[2]['cid']);
$this->assertNull($result[2]['ctopic']);
}*/
}
public function testResultIteration()
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @dataProvider provideDataForUserEntityResult
*/
public function testResultIteration($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
@ -716,16 +762,15 @@ class ArrayHydratorTest extends HydrationTestCase
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$iterableResult = $hydrator->iterate($stmt, $rsm);
$iterator = $hydrator->iterate($stmt, $rsm);
$rowNum = 0;
while (($row = $iterableResult->next()) !== false) {
while (($row = $iterator->next()) !== false) {
$this->assertEquals(1, count($row));
$this->assertTrue(is_array($row[0]));
if ($rowNum == 0) {
$this->assertEquals(1, $row[0]['id']);
$this->assertEquals('romanb', $row[0]['name']);
@ -733,17 +778,22 @@ class ArrayHydratorTest extends HydrationTestCase
$this->assertEquals(2, $row[0]['id']);
$this->assertEquals('jwage', $row[0]['name']);
}
++$rowNum;
}
}
/**
* SELECT PARTIAL u.{id, name}
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-644
* @dataProvider provideDataForUserEntityResult
*/
public function testSkipUnknownColumns()
public function testSkipUnknownColumns($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
@ -758,19 +808,25 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(1, count($result));
$this->assertArrayHasKey('id', $result[0]);
$this->assertArrayHasKey('name', $result[0]);
$this->assertArrayNotHasKey('foo', $result[0]);
}
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-1358
* @dataProvider provideDataForUserEntityResult
*/
public function testMissingIdForRootEntity()
public function testMissingIdForRootEntity($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
@ -802,7 +858,6 @@ class ArrayHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(4, count($result), "Should hydrate four results.");
@ -812,9 +867,54 @@ class ArrayHydratorTest extends HydrationTestCase
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertEquals('JWAGE', $result[3]['nameUpper']);
$this->assertEquals(array('id' => 1, 'status' => 'developer'), $result[0][0]);
$this->assertNull($result[1][0]);
$this->assertEquals(array('id' => 2, 'status' => 'developer'), $result[2][0]);
$this->assertNull($result[3][0]);
$this->assertEquals(array('id' => 1, 'status' => 'developer'), $result[0][$userEntityKey]);
$this->assertNull($result[1][$userEntityKey]);
$this->assertEquals(array('id' => 2, 'status' => 'developer'), $result[2][$userEntityKey]);
$this->assertNull($result[3][$userEntityKey]);
}
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* INDEX BY u.id
*
* @group DDC-1385
* @dataProvider provideDataForUserEntityResult
*/
public function testIndexByAndMixedResult($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addIndexBy('u', 'id');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(2, count($result));
$this->assertTrue(isset($result[1]));
$this->assertEquals(1, $result[1][$userEntityKey]['id']);
$this->assertTrue(isset($result[2]));
$this->assertEquals(2, $result[2][$userEntityKey]['id']);
}
}

View File

@ -22,7 +22,7 @@ class CustomHydratorTest extends HydrationTestCase
class CustomHydrator extends AbstractHydrator
{
protected function _hydrateAll()
protected function hydrateAllData()
{
return $this->_stmt->fetchAll(PDO::FETCH_ASSOC);
}

View File

@ -15,13 +15,42 @@ require_once __DIR__ . '/../../TestInit.php';
class ObjectHydratorTest extends HydrationTestCase
{
public function provideDataForUserEntityResult()
{
return array(
array(0),
array('user'),
);
}
public function provideDataForMultipleRootEntityResult()
{
return array(
array(0, 0),
array('user', 0),
array(0, 'article'),
array('user', 'article'),
);
}
public function provideDataForProductEntityResult()
{
return array(
array(0),
array('product'),
);
}
/**
* Select u.id, u.name from \Doctrine\Tests\Models\CMS\CmsUser u
* SELECT PARTIAL u.{id,name}
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @dataProvider provideDataForUserEntityResult
*/
public function testSimpleEntityQuery()
public function testSimpleEntityScalarFieldsQuery($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
@ -37,28 +66,33 @@ class ObjectHydratorTest extends HydrationTestCase
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1]);
$this->assertEquals(1, $result[0]->id);
$this->assertEquals('romanb', $result[0]->name);
$this->assertEquals(2, $result[1]->id);
$this->assertEquals('jwage', $result[1]->name);
}
/**
* SELECT PARTIAL u.{id,name}
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-644
* @dataProvider provideDataForUserEntityResult
*/
public function testSkipUnknownColumns()
public function testSkipUnknownColumns($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
@ -73,20 +107,63 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(1, count($result));
}
/**
* Select u.id, u.name from \Doctrine\Tests\Models\CMS\CmsUser u
* SELECT u.id, u.name
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @dataProvider provideDataForUserEntityResult
*/
public function testSimpleMultipleRootEntityQuery()
public function testScalarQueryWithoutResultVariables($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsArticle', 'a');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addScalarResult('sclr0', 'id');
$rsm->addScalarResult('sclr1', 'name');
// Faked result set
$resultSet = array(
array(
'sclr0' => '1',
'sclr1' => 'romanb'
),
array(
'sclr0' => '2',
'sclr1' => 'jwage'
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInternalType('array', $result[0]);
$this->assertInternalType('array', $result[1]);
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals('romanb', $result[0]['name']);
$this->assertEquals(2, $result[1]['id']);
$this->assertEquals('jwage', $result[1]['name']);
}
/**
* SELECT PARTIAL u.{id, name}, PARTIAL a.{id, topic}
* FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a
*
* @dataProvider provideDataForMultipleRootEntityResult
*/
public function testSimpleMultipleRootEntityQuery($userEntityKey, $articleEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsArticle', 'a', $articleEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
$rsm->addFieldResult('a', 'a__id', 'id');
@ -108,10 +185,8 @@ class ObjectHydratorTest extends HydrationTestCase
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(4, count($result));
@ -123,21 +198,27 @@ class ObjectHydratorTest extends HydrationTestCase
$this->assertEquals(1, $result[0]->id);
$this->assertEquals('romanb', $result[0]->name);
$this->assertEquals(1, $result[1]->id);
$this->assertEquals('Cool things.', $result[1]->topic);
$this->assertEquals(2, $result[2]->id);
$this->assertEquals('jwage', $result[2]->name);
$this->assertEquals(2, $result[3]->id);
$this->assertEquals('Cool things II.', $result[3]->topic);
}
/**
* Select p from \Doctrine\Tests\Models\ECommerce\ECommerceProduct p
* SELECT p
* FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p
*
* @dataProvider provideDataForProductEntityResult
*/
public function testCreatesProxyForLazyLoadingWithForeignKeys()
public function testCreatesProxyForLazyLoadingWithForeignKeys($productEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\ECommerce\ECommerceProduct', 'p');
$rsm->addEntityResult('Doctrine\Tests\Models\ECommerce\ECommerceProduct', 'p', $productEntityKey ?: null);
$rsm->addFieldResult('p', 'p__id', 'id');
$rsm->addFieldResult('p', 'p__name', 'name');
$rsm->addMetaResult('p', 'p__shipping_id', 'shipping_id');
@ -157,8 +238,7 @@ class ObjectHydratorTest extends HydrationTestCase
$proxyFactory = $this->getMock('Doctrine\ORM\Proxy\ProxyFactory', array('getProxy'), array(), '', false, false, false);
$proxyFactory->expects($this->once())
->method('getProxy')
->with($this->equalTo('Doctrine\Tests\Models\ECommerce\ECommerceShipping'),
array('id' => 42))
->with($this->equalTo('Doctrine\Tests\Models\ECommerce\ECommerceShipping'), array('id' => 42))
->will($this->returnValue($proxyInstance));
$this->_em->setProxyFactory($proxyFactory);
@ -169,24 +249,24 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm);
$this->assertEquals(1, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $result[0]);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
* join u.phonenumbers p
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
* SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* JOIN u.phonenumbers p
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryFetchJoin()
public function testMixedQueryFetchJoin($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
@ -195,8 +275,8 @@ class ObjectHydratorTest extends HydrationTestCase
);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber');
$rsm->addScalarResult('sclr0', 'nameUpper');
// Faked result set
$resultSet = array(
@ -204,63 +284,66 @@ class ObjectHydratorTest extends HydrationTestCase
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'p__phonenumber' => '42',
'sclr0' => 'ROMANB',
),
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
'p__phonenumber' => '43',
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'p__phonenumber' => '91',
'sclr0' => 'JWAGE',
'p__phonenumber' => '91'
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][0]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][0]->phonenumbers[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][0]->phonenumbers);
$this->assertInternalType('array', $result);
$this->assertInternalType('array', $result[0]);
$this->assertInternalType('array', $result[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][$userEntityKey]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][$userEntityKey]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][$userEntityKey]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][$userEntityKey]->phonenumbers[1]);
// first user => 2 phonenumbers
$this->assertEquals(2, count($result[0][0]->phonenumbers));
$this->assertEquals(2, count($result[0][$userEntityKey]->phonenumbers));
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
// second user => 1 phonenumber
$this->assertEquals(1, count($result[1][0]->phonenumbers));
$this->assertEquals(1, count($result[1][$userEntityKey]->phonenumbers));
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals(42, $result[0][0]->phonenumbers[0]->phonenumber);
$this->assertEquals(43, $result[0][0]->phonenumbers[1]->phonenumber);
$this->assertEquals(91, $result[1][0]->phonenumbers[0]->phonenumber);
$this->assertEquals(42, $result[0][$userEntityKey]->phonenumbers[0]->phonenumber);
$this->assertEquals(43, $result[0][$userEntityKey]->phonenumbers[1]->phonenumber);
$this->assertEquals(91, $result[1][$userEntityKey]->phonenumbers[0]->phonenumber);
}
/**
* select u.id, u.status, count(p.phonenumber) numPhones from User u
* join u.phonenumbers p group by u.status, u.id
* =
* select u.id, u.status, count(p.phonenumber) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id group by u.id, u.status
* SELECT PARTIAL u.{id, status}, COUNT(p.phonenumber) numPhones
* FROM User u
* JOIN u.phonenumbers p
* GROUP BY u.id
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryNormalJoin()
public function testMixedQueryNormalJoin($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'numPhones');
@ -282,32 +365,36 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertInternalType('array', $result);
$this->assertInternalType('array', $result[0]);
$this->assertInternalType('array', $result[1]);
// first user => 2 phonenumbers
$this->assertEquals(2, $result[0]['numPhones']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][$userEntityKey]);
// second user => 1 phonenumber
$this->assertEquals(1, $result[1]['numPhones']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][$userEntityKey]);
}
/**
* select u.id, u.status, upper(u.name) nameUpper from User u index by u.id
* join u.phonenumbers p indexby p.phonenumber
* =
* select u.id, u.status, upper(u.name) as p__0 from USERS u
* INNER JOIN PHONENUMBERS p ON u.id = p.user_id
* SELECT u, p, UPPER(u.name) nameUpper
* FROM User u
* INDEX BY u.id
* JOIN u.phonenumbers p
* INDEX BY p.phonenumber
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryFetchJoinCustomIndex()
public function testMixedQueryFetchJoinCustomIndex($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
@ -347,46 +434,46 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertInternalType('array', $result);
$this->assertInternalType('array', $result[1]);
$this->assertInternalType('array', $result[2]);
// test the scalar values
$this->assertEquals('ROMANB', $result[0]['nameUpper']);
$this->assertEquals('JWAGE', $result[1]['nameUpper']);
$this->assertEquals('ROMANB', $result[1]['nameUpper']);
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][$userEntityKey]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][$userEntityKey]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]['1']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1]['2']);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0]['1']->phonenumbers);
// first user => 2 phonenumbers. notice the custom indexing by user id
$this->assertEquals(2, count($result[0]['1']->phonenumbers));
$this->assertEquals(2, count($result[1][$userEntityKey]->phonenumbers));
// second user => 1 phonenumber. notice the custom indexing by user id
$this->assertEquals(1, count($result[1]['2']->phonenumbers));
$this->assertEquals(1, count($result[2][$userEntityKey]->phonenumbers));
// test the custom indexing of the phonenumbers
$this->assertTrue(isset($result[0]['1']->phonenumbers['42']));
$this->assertTrue(isset($result[0]['1']->phonenumbers['43']));
$this->assertTrue(isset($result[1]['2']->phonenumbers['91']));
$this->assertTrue(isset($result[1][$userEntityKey]->phonenumbers['42']));
$this->assertTrue(isset($result[1][$userEntityKey]->phonenumbers['43']));
$this->assertTrue(isset($result[2][$userEntityKey]->phonenumbers['91']));
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic
* from User u
* join u.phonenumbers p
* join u.articles a
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0, a.id, a.topic
* from USERS u
* inner join PHONENUMBERS p ON u.id = p.user_id
* inner join ARTICLES a ON u.id = a.user_id
* SELECT u, p, UPPER(u.name) nameUpper, a
* FROM User u
* JOIN u.phonenumbers p
* JOIN u.articles a
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryMultipleFetchJoin()
public function testMixedQueryMultipleFetchJoin($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
@ -461,47 +548,42 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][0]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][0]->phonenumbers[1]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->articles);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][0]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][0]->articles[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][0]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[1][0]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][0]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][0]->articles[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][$userEntityKey]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][$userEntityKey]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][$userEntityKey]->phonenumbers[1]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->articles);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][$userEntityKey]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][$userEntityKey]->articles[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][$userEntityKey]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[1][$userEntityKey]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][$userEntityKey]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][$userEntityKey]->articles[1]);
}
/**
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper, a.id, a.topic,
* c.id, c.topic
* from User u
* join u.phonenumbers p
* join u.articles a
* left join a.comments c
* =
* select u.id, u.status, p.phonenumber, upper(u.name) as u__0, a.id, a.topic,
* c.id, c.topic
* from USERS u
* inner join PHONENUMBERS p ON u.id = p.user_id
* inner join ARTICLES a ON u.id = a.user_id
* left outer join COMMENTS c ON a.id = c.article_id
* SELECT u, p, UPPER(u.name) nameUpper, a, c
* FROM User u
* JOIN u.phonenumbers p
* JOIN u.articles a
* LEFT JOIN a.comments c
*
* @dataProvider provideDataForUserEntityResult
*/
public function testMixedQueryMultipleDeepMixedFetchJoin()
public function testMixedQueryMultipleDeepMixedFetchJoin($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
@ -596,38 +678,45 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertTrue(is_array($result));
$this->assertTrue(is_array($result[0]));
$this->assertTrue(is_array($result[1]));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][$userEntityKey]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][$userEntityKey]);
// phonenumbers
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][0]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][0]->phonenumbers[1]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][0]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[1][0]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][$userEntityKey]->phonenumbers[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[0][$userEntityKey]->phonenumbers[1]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][$userEntityKey]->phonenumbers);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $result[1][$userEntityKey]->phonenumbers[0]);
// articles
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->articles);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][0]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][0]->articles[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][0]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][0]->articles[1]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->articles);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][$userEntityKey]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[0][$userEntityKey]->articles[1]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][$userEntityKey]->articles[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsArticle', $result[1][$userEntityKey]->articles[1]);
// article comments
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->articles[0]->comments);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsComment', $result[0][0]->articles[0]->comments[0]);
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->articles[0]->comments);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsComment', $result[0][$userEntityKey]->articles[0]->comments[0]);
// empty comment collections
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][0]->articles[1]->comments);
$this->assertEquals(0, count($result[0][0]->articles[1]->comments));
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][0]->articles[0]->comments);
$this->assertEquals(0, count($result[1][0]->articles[0]->comments));
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][0]->articles[1]->comments);
$this->assertEquals(0, count($result[1][0]->articles[1]->comments));
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[0][$userEntityKey]->articles[1]->comments);
$this->assertEquals(0, count($result[0][$userEntityKey]->articles[1]->comments));
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][$userEntityKey]->articles[0]->comments);
$this->assertEquals(0, count($result[1][$userEntityKey]->articles[0]->comments));
$this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $result[1][$userEntityKey]->articles[1]->comments);
$this->assertEquals(0, count($result[1][$userEntityKey]->articles[1]->comments));
}
/**
@ -702,26 +791,38 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\Forum\ForumCategory', $result[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\Forum\ForumCategory', $result[1]);
$this->assertTrue($result[0] !== $result[1]);
$this->assertEquals(1, $result[0]->getId());
$this->assertEquals(2, $result[1]->getId());
$this->assertTrue(isset($result[0]->boards));
$this->assertEquals(3, count($result[0]->boards));
$this->assertTrue(isset($result[1]->boards));
$this->assertEquals(1, count($result[1]->boards));
}
public function testChainedJoinWithEmptyCollections()
/**
* SELECT PARTIAL u.{id, status}, PARTIAL a.{id, topic}, PARTIAL c.{id, topic}
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* LEFT JOIN u.articles a
* LEFT JOIN a.comments c
*
* @dataProvider provideDataForUserEntityResult
*/
public function testChainedJoinWithEmptyCollections($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsArticle',
'a',
@ -764,25 +865,30 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1]);
$this->assertEquals(0, $result[0]->articles->count());
$this->assertEquals(0, $result[1]->articles->count());
}
/**
* DQL: select partial u.{id,status}, a.id, a.topic, c.id as cid, c.topic as ctopic from CmsUser u left join u.articles a left join a.comments c
* SELECT PARTIAL u.{id,status}, a.id, a.topic, c.id as cid, c.topic as ctopic
* FROM CmsUser u
* LEFT JOIN u.articles a
* LEFT JOIN a.comments c
*
* @group bubu
* @dataProvider provideDataForUserEntityResult
*/
/*public function testChainedJoinWithScalars()
/*public function testChainedJoinWithScalars($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('a__id', 'id');
@ -821,38 +927,26 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(3, count($result));
\Doctrine\Common\Util\Debug::dump($result, 3);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]); // User object
$this->assertEquals(1, $result[0]['id']);
$this->assertEquals(1, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][$userEntityKey]); // User object
$this->assertEquals(42, $result[0]['id']);
$this->assertEquals('The First', $result[0]['topic']);
$this->assertEquals(1, $result[0]['cid']);
$this->assertEquals('First Comment', $result[0]['ctopic']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[1][0]); // Same User object
$this->assertEquals(1, $result[1]['id']); // duplicated
$this->assertEquals('The First', $result[1]['topic']); // duplicated
$this->assertEquals(2, $result[1]['cid']);
$this->assertEquals('Second Comment', $result[1]['ctopic']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][0]); // Same User object
$this->assertEquals(42, $result[2]['id']);
$this->assertEquals('The Answer', $result[2]['topic']);
$this->assertNull($result[2]['cid']);
$this->assertNull($result[2]['ctopic']);
$this->assertTrue($result[0][0] === $result[1][0]);
$this->assertTrue($result[1][0] === $result[2][0]);
$this->assertTrue($result[0][0] === $result[2][0]);
}*/
public function testResultIteration()
/**
* @dataProvider provideDataForUserEntityResult
*/
public function testResultIteration($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
@ -868,16 +962,15 @@ class ObjectHydratorTest extends HydrationTestCase
)
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$iterableResult = $hydrator->iterate($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$rowNum = 0;
while (($row = $iterableResult->next()) !== false) {
$this->assertEquals(1, count($row));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $row[0]);
if ($rowNum == 0) {
$this->assertEquals(1, $row[0]->id);
$this->assertEquals('romanb', $row[0]->name);
@ -885,21 +978,24 @@ class ObjectHydratorTest extends HydrationTestCase
$this->assertEquals(2, $row[0]->id);
$this->assertEquals('jwage', $row[0]->name);
}
++$rowNum;
}
}
/**
* This issue tests if, with multiple joined multiple-valued collections the hydration is done correctly.
* Checks if multiple joined multiple-valued collections is hydrated correctly.
*
* User x Phonenumbers x Groups blow up the resultset quite a bit, however the hydration should correctly assemble those.
* SELECT PARTIAL u.{id, status}, PARTIAL g.{id, name}, PARTIAL p.{phonenumber}
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-809
* @dataProvider provideDataForUserEntityResult
*/
public function testManyToManyHydration()
public function testManyToManyHydration($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__name', 'name');
$rsm->addJoinedEntityResult('Doctrine\Tests\Models\CMS\CmsGroup', 'g', 'u', 'groups');
@ -998,24 +1094,30 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsUser', $result);
$this->assertEquals(2, count($result[0]->groups));
$this->assertEquals(2, count($result[0]->phonenumbers));
$this->assertEquals(4, count($result[1]->groups));
$this->assertEquals(2, count($result[1]->phonenumbers));
}
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) as nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-1358
* @dataProvider provideDataForUserEntityResult
*/
public function testMissingIdForRootEntity()
public function testMissingIdForRootEntity($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
@ -1047,7 +1149,6 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(4, count($result), "Should hydrate four results.");
@ -1057,20 +1158,25 @@ class ObjectHydratorTest extends HydrationTestCase
$this->assertEquals('JWAGE', $result[2]['nameUpper']);
$this->assertEquals('JWAGE', $result[3]['nameUpper']);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]);
$this->assertNull($result[1][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][0]);
$this->assertNull($result[3][0]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][$userEntityKey]);
$this->assertNull($result[1][$userEntityKey]);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][$userEntityKey]);
$this->assertNull($result[3][$userEntityKey]);
}
/**
* SELECT PARTIAL u.{id, status}, PARTIAL p.{phonenumber}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* LEFT JOIN u.phonenumbers u
*
* @group DDC-1358
* @return void
* @dataProvider provideDataForUserEntityResult
*/
public function testMissingIdForCollectionValuedChildEntity()
public function testMissingIdForCollectionValuedChildEntity($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsPhonenumber',
'p',
@ -1113,21 +1219,26 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertEquals(1, $result[0][0]->phonenumbers->count());
$this->assertEquals(1, $result[1][0]->phonenumbers->count());
$this->assertEquals(1, $result[0][$userEntityKey]->phonenumbers->count());
$this->assertEquals(1, $result[1][$userEntityKey]->phonenumbers->count());
}
/**
* SELECT PARTIAL u.{id, status}, PARTIAL a.{id, city}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* JOIN u.address a
*
* @group DDC-1358
* @dataProvider provideDataForUserEntityResult
*/
public function testMissingIdForSingleValuedChildEntity()
public function testMissingIdForSingleValuedChildEntity($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u');
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addJoinedEntityResult(
'Doctrine\Tests\Models\CMS\CmsAddress',
'a',
@ -1162,11 +1273,94 @@ class ObjectHydratorTest extends HydrationTestCase
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0][0]->address);
$this->assertNull($result[1][0]->address);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0][$userEntityKey]->address);
$this->assertNull($result[1][$userEntityKey]->address);
}
/**
* SELECT PARTIAL u.{id, status}, UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
* INDEX BY u.id
*
* @group DDC-1385
* @dataProvider provideDataForUserEntityResult
*/
public function testIndexByAndMixedResult($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addFieldResult('u', 'u__id', 'id');
$rsm->addFieldResult('u', 'u__status', 'status');
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addIndexBy('u', 'id');
// Faked result set
$resultSet = array(
//row1
array(
'u__id' => '1',
'u__status' => 'developer',
'sclr0' => 'ROMANB',
),
array(
'u__id' => '2',
'u__status' => 'developer',
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(2, count($result));
$this->assertTrue(isset($result[1]));
$this->assertEquals(1, $result[1][$userEntityKey]->id);
$this->assertTrue(isset($result[2]));
$this->assertEquals(2, $result[2][$userEntityKey]->id);
}
/**
* SELECT UPPER(u.name) AS nameUpper
* FROM Doctrine\Tests\Models\CMS\CmsUser u
*
* @group DDC-1385
* @dataProvider provideDataForUserEntityResult
*/
public function testIndexByScalarsOnly($userEntityKey)
{
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u', $userEntityKey ?: null);
$rsm->addScalarResult('sclr0', 'nameUpper');
$rsm->addIndexByScalar('sclr0');
// Faked result set
$resultSet = array(
//row1
array(
'sclr0' => 'ROMANB',
),
array(
'sclr0' => 'JWAGE',
),
);
$stmt = new HydratorMockStatement($resultSet);
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
$result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true));
$this->assertEquals(
array(
'ROMANB' => array('nameUpper' => 'ROMANB'),
'JWAGE' => array('nameUpper' => 'JWAGE')
),
$result
);
}
}

View File

@ -327,6 +327,51 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$em->getRepository("Doctrine\Tests\Models\DDC869\DDC869ChequePayment"));
$this->assertTrue($em->getRepository("Doctrine\Tests\Models\DDC869\DDC869ChequePayment")->isTrue());
}
/**
* @group DDC-1476
*/
public function testDefaultFieldType()
{
$driver = $this->_loadDriver();
$em = $this->_getTestEntityManager();
$factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory();
$em->getConfiguration()->setMetadataDriverImpl($driver);
$factory->setEntityManager($em);
$class = $factory->getMetadataFor('Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType');
$this->assertArrayHasKey('id', $class->fieldMappings);
$this->assertArrayHasKey('name', $class->fieldMappings);
$this->assertArrayHasKey('type', $class->fieldMappings['id']);
$this->assertArrayHasKey('type', $class->fieldMappings['name']);
$this->assertEquals('string', $class->fieldMappings['id']['type']);
$this->assertEquals('string', $class->fieldMappings['name']['type']);
$this->assertArrayHasKey('fieldName', $class->fieldMappings['id']);
$this->assertArrayHasKey('fieldName', $class->fieldMappings['name']);
$this->assertEquals('id', $class->fieldMappings['id']['fieldName']);
$this->assertEquals('name', $class->fieldMappings['name']['fieldName']);
$this->assertArrayHasKey('columnName', $class->fieldMappings['id']);
$this->assertArrayHasKey('columnName', $class->fieldMappings['name']);
$this->assertEquals('id', $class->fieldMappings['id']['columnName']);
$this->assertEquals('name', $class->fieldMappings['name']['columnName']);
$this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_NONE, $class->generatorType);
}
}
/**

View File

@ -199,6 +199,20 @@ class AnnotationDriverTest extends AbstractMappingDriverTest
$cm = $factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\ChildEntity');
}
public function testInvalidFetchOptionThrowsException()
{
$annotationDriver = $this->_loadDriver();
$em = $this->_getTestEntityManager();
$em->getConfiguration()->setMetadataDriverImpl($annotationDriver);
$factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory();
$factory->setEntityManager($em);
$this->setExpectedException('Doctrine\ORM\Mapping\MappingException',
"Entity 'Doctrine\Tests\ORM\Mapping\InvalidFetchOption' has a mapping with invalid fetch mode 'eager");
$cm = $factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\InvalidFetchOption');
}
}
/**
@ -311,3 +325,14 @@ class ChildEntity extends MiddleMappedSuperclass
*/
private $text;
}
/**
* @Entity
*/
class InvalidFetchOption
{
/**
* @OneToMany(targetEntity="Doctrine\Tests\Models\CMS\CmsUser", fetch="eager")
*/
private $collection;
}

View File

@ -54,7 +54,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('phonenumbers', $oneOneMapping['fieldName']);
$this->assertEquals('Doctrine\Tests\Models\CMS\Bar', $oneOneMapping['targetEntity']);
$this->assertTrue($cm->isReadOnly);
$this->assertEquals(array('dql' => 'foo'), $cm->namedQueries);
$this->assertEquals(array('dql' => array('name'=>'dql','query'=>'foo','dql'=>'foo')), $cm->namedQueries);
}
public function testFieldIsNullable()

View File

@ -0,0 +1,12 @@
<?php
use Doctrine\ORM\Mapping\ClassMetadataInfo;
$metadata->mapField(array(
'id' => true,
'fieldName' => 'id',
));
$metadata->mapField(array(
'fieldName' => 'name'
));
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE);

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType">
<id name="id">
<generator strategy="NONE"/>
</id>
<field name="name"/>
</entity>
</doctrine-mapping>

View File

@ -0,0 +1,8 @@
Doctrine\Tests\Models\DDC1476\DDC1476EntityWithDefaultFieldType:
type: entity
id:
id:
generator:
strategy: NONE
fields:
name:

View File

@ -563,7 +563,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->_em->getClassMetadata(get_class($person))->setIdentifierValues($person, array('id' => 101));
$q3->setParameter('param', $person);
$this->assertEquals(
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c0_.discr AS discr7, c0_.spouse_id AS spouse_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.friend_id = c4_.id WHERE c3_.person_id = c0_.id AND c4_.id = ?)',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c2_.salary AS salary3, c2_.department AS department4, c2_.startDate AS startDate5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7, c1_.car_id AS car_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.friend_id = c4_.id WHERE c3_.person_id = c0_.id AND c4_.id = ?)',
$q3->getSql()
);
}
@ -746,12 +746,12 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->assertSqlGeneration(
"SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = true",
"SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = true"
"SELECT b0_.id AS id0, b0_.booleanField AS booleanfield1 FROM boolean_model b0_ WHERE b0_.booleanField = true"
);
$this->assertSqlGeneration(
"SELECT b FROM Doctrine\Tests\Models\Generic\BooleanModel b WHERE b.booleanField = false",
"SELECT b0_.id AS id0, b0_.booleanField AS booleanField1 FROM boolean_model b0_ WHERE b0_.booleanField = false"
"SELECT b0_.id AS id0, b0_.booleanField AS booleanfield1 FROM boolean_model b0_ WHERE b0_.booleanField = false"
);
$this->_em->getConnection()->setDatabasePlatform($oldPlat);
@ -894,7 +894,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
$this->assertSqlGeneration(
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'",
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 ".
"SELECT c0_.id AS ID0, c0_.status AS STATUS1, c0_.username AS USERNAME2, c0_.name AS NAME3 ".
"FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE",
array(Query::HINT_LOCK_MODE => \Doctrine\DBAL\LockMode::PESSIMISTIC_READ)
);
@ -1120,7 +1120,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c0_.discr AS discr7, c0_.spouse_id AS spouse_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c2_.salary AS salary3, c2_.department AS department4, c2_.startDate AS startDate5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7, c1_.car_id AS car_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
@ -1144,7 +1144,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c2_.car_id AS car_id6, c0_.discr AS discr7, c0_.spouse_id AS spouse_id8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id',
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7, c2_.car_id AS car_id8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id',
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
@ -1264,10 +1264,43 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
{
$this->assertSqlGeneration(
'SELECT p, pp FROM Doctrine\Tests\Models\Company\CompanyPerson p JOIN p.spouse pp',
"SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c3_.id AS id7, c3_.name AS name8, c4_.title AS title9, c4_.car_id AS car_id10, c5_.salary AS salary11, c5_.department AS department12, c5_.startDate AS startDate13, c0_.discr AS discr14, c0_.spouse_id AS spouse_id15, c3_.discr AS discr16, c3_.spouse_id AS spouse_id17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id",
"SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c2_.salary AS salary3, c2_.department AS department4, c2_.startDate AS startDate5, c3_.id AS id6, c3_.name AS name7, c4_.title AS title8, c5_.salary AS salary9, c5_.department AS department10, c5_.startDate AS startDate11, c0_.discr AS discr12, c0_.spouse_id AS spouse_id13, c1_.car_id AS car_id14, c3_.discr AS discr15, c3_.spouse_id AS spouse_id16, c4_.car_id AS car_id17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id",
array(Query::HINT_FORCE_PARTIAL_LOAD => false)
);
}
/**
* @group DDC-1384
*/
public function testAliasDoesNotExceedPlatformDefinedLength()
{
$this->assertSqlGeneration(
'SELECT m FROM ' . __NAMESPACE__ . '\\DDC1384Model m',
"SELECT d0_.aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalker_fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo AS fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0 FROM DDC1384Model d0_"
);
}
/**
* @group DDC-331
* @group DDC-1384
*/
public function testIssue331()
{
$this->assertSqlGeneration(
'SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e',
'SELECT c0_.name AS name0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id'
);
}
/**
* @group DDC-1435
*/
public function testForeignKeyAsPrimaryKeySubselect()
{
$this->assertSqlGeneration(
"SELECT s FROM Doctrine\Tests\Models\DDC117\DDC117Article s WHERE EXISTS (SELECT r FROM Doctrine\Tests\Models\DDC117\DDC117Reference r WHERE r.source = s)",
"SELECT d0_.article_id AS article_id0, d0_.title AS title1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)"
);
}
}
@ -1298,3 +1331,15 @@ class MyAbsFunction extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
$parser->match(\Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS);
}
}
/**
* @Entity
*/
class DDC1384Model
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
protected $aVeryLongIdentifierThatShouldBeShortenedByTheSQLWalker_fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo;
}

View File

@ -71,4 +71,88 @@ class SchemaValidatorTest extends \Doctrine\Tests\OrmTestCase
));
$this->validator->validateMapping();
}
/**
* @group DDC-1439
*/
public function testInvalidManyToManyJoinColumnSchema()
{
$class1 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity1');
$class2 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity2');
$ce = $this->validator->validateClass($class1);
$this->assertEquals(
array(
"The inverse join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the target entity 'Doctrine\Tests\ORM\Tools\InvalidEntity2', however 'key4' are missing.",
"The join columns of the many-to-many table 'Entity1Entity2' have to contain to ALL identifier columns of the source entity 'Doctrine\Tests\ORM\Tools\InvalidEntity1', however 'key2' are missing."
),
$ce
);
}
/**
* @group DDC-1439
*/
public function testInvalidToOneJoinColumnSchema()
{
$class1 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity1');
$class2 = $this->em->getClassMetadata(__NAMESPACE__ . '\InvalidEntity2');
$ce = $this->validator->validateClass($class2);
$this->assertEquals(
array(
"The referenced column name 'id' does not have a corresponding field with this column name on the class 'Doctrine\Tests\ORM\Tools\InvalidEntity1'.",
"The join columns of the association 'assoc' have to match to ALL identifier columns of the source entity 'Doctrine\Tests\ORM\Tools\InvalidEntity2', however 'key3, key4' are missing."
),
$ce
);
}
}
/**
* @Entity
*/
class InvalidEntity1
{
/**
* @Id @Column
*/
protected $key1;
/**
* @Id @Column
*/
protected $key2;
/**
* @ManyToMany (targetEntity="InvalidEntity2")
* @JoinTable (name="Entity1Entity2",
* joinColumns={@JoinColumn(name="key1", referencedColumnName="key1")},
* inverseJoinColumns={@JoinColumn(name="key3", referencedColumnName="key3")}
* )
*/
protected $entity2;
}
/**
* @Entity
*/
class InvalidEntity2
{
/**
* @Id @Column
* @GeneratedValue(strategy="AUTO")
*/
protected $key3;
/**
* @Id @Column
* @GeneratedValue(strategy="AUTO")
*/
protected $key4;
/**
* @ManyToOne(targetEntity="InvalidEntity1")
*/
protected $assoc;
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit>
<php>
<var name="db_type" value="pdo_mysql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="travis" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="tmpdb_type" value="pdo_mysql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="travis" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_name" value="doctrine_tests_tmp" />
<var name="tmpdb_port" value="3306"/>
</php>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./../Doctrine/Tests/ORM</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit>
<php>
<!-- "Real" test database -->
<var name="db_type" value="pdo_pgsql"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="postgres" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="5432"/>
<!--<var name="db_event_subscribers" value="Doctrine\DBAL\Event\Listeners\OracleSessionInit">-->
<!-- Database for temporary connections (i.e. to drop/create the main database) -->
<var name="tmpdb_type" value="pdo_pgsql"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="postgres" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_name" value="doctrine_tests_tmp" />
<var name="tmpdb_port" value="5432"/>
</php>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./../Doctrine/Tests/ORM</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit>
<testsuites>
<testsuite name="Doctrine ORM Test Suite">
<directory>./../Doctrine/Tests/ORM</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>