[2.0] completed implementation of lazy loading for Collections. Created debug helper class. Fixed default for orderBy clause to ASC
This commit is contained in:
parent
2807a83d5d
commit
c073f1d113
52
lib/Doctrine/Common/Util/Debug.php
Normal file
52
lib/Doctrine/Common/Util/Debug.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?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\Common\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static class containing most used debug methods.
|
||||||
|
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
final class Debug
|
||||||
|
{
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a dump of the public, protected and private properties of $var.
|
||||||
|
* To print a meaningful dump, whose depth is limited, requires xdebug
|
||||||
|
* php extension.
|
||||||
|
* @link http://xdebug.org/
|
||||||
|
* @param mixed $var
|
||||||
|
* @param integer $maxDepth maximum nesting level for object properties
|
||||||
|
*/
|
||||||
|
public static function dump($var, $maxDepth = 2)
|
||||||
|
{
|
||||||
|
ini_set('html_errors', 'On');
|
||||||
|
ini_set('xdebug.var_display_max_depth', $maxDepth);
|
||||||
|
ob_start();
|
||||||
|
var_dump($var);
|
||||||
|
$dump = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
echo strip_tags(html_entity_decode($dump));
|
||||||
|
ini_set('html_errors', 'Off');
|
||||||
|
}
|
||||||
|
}
|
@ -137,7 +137,6 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
* Updates the result pointer for an entity. The result pointers point to the
|
* Updates the result pointer for an entity. The result pointers point to the
|
||||||
* last seen instance of each entity type. This is used for graph construction.
|
* last seen instance of each entity type. This is used for graph construction.
|
||||||
*
|
*
|
||||||
* @param array $resultPointers The result pointers.
|
|
||||||
* @param Collection $coll The element.
|
* @param Collection $coll The element.
|
||||||
* @param boolean|integer $index Index of the element in the collection.
|
* @param boolean|integer $index Index of the element in the collection.
|
||||||
* @param string $dqlAlias
|
* @param string $dqlAlias
|
||||||
@ -173,6 +172,7 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
*
|
*
|
||||||
* @param object $entity The entity to which the collection belongs.
|
* @param object $entity The entity to which the collection belongs.
|
||||||
* @param string $name The name of the field on the entity that holds the collection.
|
* @param string $name The name of the field on the entity that holds the collection.
|
||||||
|
* @return PersistentCollection
|
||||||
*/
|
*/
|
||||||
private function initRelatedCollection($entity, $name)
|
private function initRelatedCollection($entity, $name)
|
||||||
{
|
{
|
||||||
@ -190,6 +190,7 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
$classMetadata->reflFields[$name]->setValue($entity, $coll);
|
$classMetadata->reflFields[$name]->setValue($entity, $coll);
|
||||||
$this->_uow->setOriginalEntityProperty($oid, $name, $coll);
|
$this->_uow->setOriginalEntityProperty($oid, $name, $coll);
|
||||||
$this->_initializedRelations[$oid][$name] = true;
|
$this->_initializedRelations[$oid][$name] = true;
|
||||||
|
return $coll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,13 +264,14 @@ class ObjectHydrator extends AbstractHydrator
|
|||||||
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumns);
|
$assoc->load($entity, new $assoc->targetEntityName, $this->_em, $joinColumns);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//TODO: Eager load
|
|
||||||
// Inject collection
|
// Inject collection
|
||||||
$this->_ce[$className]->reflFields[$field]
|
$collection = $this->initRelatedCollection($entity, $field);
|
||||||
->setValue($entity, new PersistentCollection(
|
if ($assoc->isLazilyFetched()) {
|
||||||
$this->_em,
|
$collection->setLazyInitialization();
|
||||||
$this->_em->getClassMetadata($assoc->targetEntityName)
|
} else {
|
||||||
));
|
//TODO: Allow more efficient and configurable batching of these loads
|
||||||
|
$assoc->load($entity, $collection, $this->_em);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,14 +391,28 @@ abstract class AssociationMapping
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads data in $targetEntity domain object using this association.
|
* Loads data in $target domain object using this association.
|
||||||
* The data comes from the association navigated from $sourceEntity
|
* The data comes from the association navigated from $sourceEntity
|
||||||
* using $em.
|
* using $em.
|
||||||
*
|
*
|
||||||
* @param object $sourceEntity
|
* @param object $sourceEntity
|
||||||
* @param object $targetEntity
|
* @param object $target an entity or a collection
|
||||||
* @param EntityManager $em
|
* @param EntityManager $em
|
||||||
* @param array $joinColumnValues
|
* @param array $joinColumnValues foreign keys (significative for this
|
||||||
|
* association) of $sourceEntity, if needed
|
||||||
*/
|
*/
|
||||||
abstract public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues);
|
abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses reflection to access internal data of entities.
|
||||||
|
* @param ClassMetadata $class
|
||||||
|
* @param $entity a domain object
|
||||||
|
* @param $column name of private field
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function _getPrivateValue(ClassMetadata $class, $entity, $column)
|
||||||
|
{
|
||||||
|
$reflField = $class->getReflectionProperty($class->getFieldName($column));
|
||||||
|
return $reflField->getValue($entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ namespace Doctrine\ORM\Mapping;
|
|||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
|
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||||
*/
|
*/
|
||||||
class ManyToManyMapping extends AssociationMapping
|
class ManyToManyMapping extends AssociationMapping
|
||||||
{
|
{
|
||||||
@ -138,9 +139,47 @@ class ManyToManyMapping extends AssociationMapping
|
|||||||
return $this->targetKeyColumns;
|
return $this->targetKeyColumns;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load($owningEntity, $targetEntity, $em, array $joinColumnValues)
|
/**
|
||||||
|
* Loads entities in $targetCollection using $em.
|
||||||
|
* The data of $sourceEntity are used to restrict the collection
|
||||||
|
* via the join table.
|
||||||
|
*/
|
||||||
|
public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array())
|
||||||
{
|
{
|
||||||
throw new Exception('Not yet implemented.');
|
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
|
||||||
|
$joinTableConditions = array();
|
||||||
|
if ($this->isOwningSide()) {
|
||||||
|
$joinTable = $this->getJoinTable();
|
||||||
|
$joinClauses = $this->getTargetToRelationKeyColumns();
|
||||||
|
foreach ($this->getSourceToRelationKeyColumns() as $sourceKeyColumn => $relationKeyColumn) {
|
||||||
|
// getting id
|
||||||
|
if (isset($sourceClass->reflFields[$sourceKeyColumn])) {
|
||||||
|
$joinTableConditions[$relationKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn);
|
||||||
|
} else {
|
||||||
|
$joinTableConditions[$relationKeyColumn] = $joinColumnValues[$sourceKeyColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName);
|
||||||
|
$joinTable = $owningAssoc->getJoinTable();
|
||||||
|
// TRICKY: since the association is inverted source and target are flipped
|
||||||
|
$joinClauses = $owningAssoc->getSourceToRelationKeyColumns();
|
||||||
|
foreach ($owningAssoc->getTargetToRelationKeyColumns() as $sourceKeyColumn => $relationKeyColumn) {
|
||||||
|
// getting id
|
||||||
|
if (isset($sourceClass->reflFields[$sourceKeyColumn])) {
|
||||||
|
$joinTableConditions[$relationKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn);
|
||||||
|
} else {
|
||||||
|
$joinTableConditions[$relationKeyColumn] = $joinColumnValues[$sourceKeyColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$joinTableCriteria = array(
|
||||||
|
'table' => $joinTable['name'],
|
||||||
|
'join' => $joinClauses,
|
||||||
|
'criteria' => $joinTableConditions
|
||||||
|
);
|
||||||
|
$persister = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName);
|
||||||
|
$persister->loadCollection(array($joinTableCriteria), $targetCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +38,7 @@ namespace Doctrine\ORM\Mapping;
|
|||||||
* the serialized representation).
|
* the serialized representation).
|
||||||
*
|
*
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
|
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
class OneToManyMapping extends AssociationMapping
|
class OneToManyMapping extends AssociationMapping
|
||||||
@ -47,6 +48,13 @@ class OneToManyMapping extends AssociationMapping
|
|||||||
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
|
/** FUTURE: The key column mapping, if any. The key column holds the keys of the Collection. */
|
||||||
//public $keyColumn;
|
//public $keyColumn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Allow any combination of source/target columns in lazy loading.
|
||||||
|
* What is supported now is primary key (that can spread on multiple fields)
|
||||||
|
* pointed to foreign keys on the target
|
||||||
|
public $targetColumns;
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new OneToManyMapping.
|
* Initializes a new OneToManyMapping.
|
||||||
*
|
*
|
||||||
@ -97,8 +105,22 @@ class OneToManyMapping extends AssociationMapping
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load($owningEntity, $targetEntity, $em, array $joinColumnValues)
|
public function load($sourceEntity, $targetCollection, $em, array $joinColumnValues = array())
|
||||||
{
|
{
|
||||||
throw new Exception('Not yet implemented.');
|
$persister = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName);
|
||||||
|
// a one-to-many is always inverse (does not have foreign key)
|
||||||
|
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
|
||||||
|
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName);
|
||||||
|
// TRICKY: since the association is specular source and target are flipped
|
||||||
|
foreach ($owningAssoc->getTargetToSourceKeyColumns() as $sourceKeyColumn => $targetKeyColumn) {
|
||||||
|
// getting id
|
||||||
|
if (isset($sourceClass->reflFields[$sourceKeyColumn])) {
|
||||||
|
$conditions[$targetKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn);
|
||||||
|
} else {
|
||||||
|
$conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$persister->loadCollection($conditions, $targetCollection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ class OneToOneMapping extends AssociationMapping
|
|||||||
* @param array $joinColumnValues values of the join columns of $sourceEntity. There are no fields
|
* @param array $joinColumnValues values of the join columns of $sourceEntity. There are no fields
|
||||||
* to store this data in $sourceEntity
|
* to store this data in $sourceEntity
|
||||||
*/
|
*/
|
||||||
public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues)
|
public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues = array())
|
||||||
{
|
{
|
||||||
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
|
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
|
||||||
$targetClass = $em->getClassMetadata($this->targetEntityName);
|
$targetClass = $em->getClassMetadata($this->targetEntityName);
|
||||||
@ -189,12 +189,13 @@ class OneToOneMapping extends AssociationMapping
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName);
|
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName);
|
||||||
foreach ($owningAssoc->getTargetToSourceKeyColumns() as $targetKeyColumn => $sourceKeyColumn) {
|
// TRICKY: since the association is specular source and target are flipped
|
||||||
|
foreach ($owningAssoc->getTargetToSourceKeyColumns() as $sourceKeyColumn => $targetKeyColumn) {
|
||||||
// getting id
|
// getting id
|
||||||
if (isset($sourceClass->reflFields[$targetKeyColumn])) {
|
if (isset($sourceClass->reflFields[$sourceKeyColumn])) {
|
||||||
$conditions[$sourceKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $targetKeyColumn);
|
$conditions[$targetKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn);
|
||||||
} else {
|
} else {
|
||||||
$conditions[$sourceKeyColumn] = $joinColumnValues[$targetKeyColumn];
|
$conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,10 +204,4 @@ class OneToOneMapping extends AssociationMapping
|
|||||||
$targetClass->setFieldValue($targetEntity, $this->mappedByFieldName, $sourceEntity);
|
$targetClass->setFieldValue($targetEntity, $this->mappedByFieldName, $sourceEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _getPrivateValue(ClassMetadata $class, $entity, $column)
|
|
||||||
{
|
|
||||||
$reflField = $class->getReflectionProperty($class->getFieldName($column));
|
|
||||||
return $reflField->getValue($entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ namespace Doctrine\ORM;
|
|||||||
|
|
||||||
use Doctrine\Common\DoctrineException;
|
use Doctrine\Common\DoctrineException;
|
||||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
use \Closure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A PersistentCollection represents a collection of elements that have persistent state.
|
* A PersistentCollection represents a collection of elements that have persistent state.
|
||||||
@ -41,6 +42,7 @@ use Doctrine\ORM\Mapping\AssociationMapping;
|
|||||||
* @version $Revision: 4930 $
|
* @version $Revision: 4930 $
|
||||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
|
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||||
*/
|
*/
|
||||||
final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
||||||
{
|
{
|
||||||
@ -110,7 +112,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
|||||||
private $_isDirty = false;
|
private $_isDirty = false;
|
||||||
|
|
||||||
/** Whether the collection has already been initialized. */
|
/** Whether the collection has already been initialized. */
|
||||||
private $_initialized = false;
|
protected $_initialized = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new persistent collection.
|
* Creates a new persistent collection.
|
||||||
@ -188,74 +190,36 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
|||||||
return $this->_typeClass;
|
return $this->_typeClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setLazyInitialization()
|
||||||
* Removes an element from the collection.
|
|
||||||
*
|
|
||||||
* @param mixed $key
|
|
||||||
* @return boolean
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
public function remove($key)
|
|
||||||
{
|
{
|
||||||
$removed = parent::remove($key);
|
$this->_initialized = false;
|
||||||
if ($removed) {
|
|
||||||
$this->_changed();
|
|
||||||
if ($this->_association->isOneToMany() && $this->_association->orphanRemoval) {
|
|
||||||
$this->_em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the collection is used as a Map this is like put(key,value)/add(key,value).
|
|
||||||
* When the collection is used as a List this is like add(position,value).
|
|
||||||
*
|
|
||||||
* @param integer $key
|
|
||||||
* @param mixed $value
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
public function set($key, $value)
|
|
||||||
{
|
|
||||||
parent::set($key, $value);
|
|
||||||
$this->_changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an element to the collection.
|
|
||||||
*
|
|
||||||
* @param mixed $value
|
|
||||||
* @param string $key
|
|
||||||
* @return boolean Always TRUE.
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
public function add($value)
|
|
||||||
{
|
|
||||||
parent::add($value);
|
|
||||||
$this->_changed();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL:
|
* INTERNAL:
|
||||||
* Adds an element to a collection during hydration.
|
* Adds an element to a collection during hydration, if it not already set.
|
||||||
|
* This control is performed to avoid infinite recursion during hydration
|
||||||
|
* of bidirectional many-to-many collections.
|
||||||
*
|
*
|
||||||
* @param mixed $value The element to add.
|
* @param mixed $element The element to add.
|
||||||
*/
|
*/
|
||||||
public function hydrateAdd($value)
|
public function hydrateAdd($element)
|
||||||
{
|
{
|
||||||
parent::add($value);
|
if (parent::contains($element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent::add($element);
|
||||||
if ($this->_backRefFieldName) {
|
if ($this->_backRefFieldName) {
|
||||||
// Set back reference to owner
|
// Set back reference to owner
|
||||||
if ($this->_association->isOneToMany()) {
|
if ($this->_association->isOneToMany()) {
|
||||||
// OneToMany
|
// OneToMany
|
||||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||||
->setValue($value, $this->_owner);
|
->setValue($element, $this->_owner);
|
||||||
} else {
|
} else {
|
||||||
// ManyToMany
|
// ManyToMany
|
||||||
$this->_typeClass->reflFields[$this->_backRefFieldName]
|
$otherCollection = $this->_typeClass->reflFields[$this->_backRefFieldName]
|
||||||
->getValue($value)->add($this->_owner);
|
->getValue($element);
|
||||||
|
$otherCollection->hydrateAdd($this->_owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,42 +236,15 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
|||||||
parent::set($key, $value);
|
parent::set($key, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether an element is contained in the collection.
|
|
||||||
* This is an O(n) operation.
|
|
||||||
*/
|
|
||||||
public function contains($element)
|
|
||||||
{
|
|
||||||
|
|
||||||
if ( ! $this->_initialized) {
|
|
||||||
//TODO: Probably need to hit the database here...?
|
|
||||||
//return $this->_checkElementExistence($element);
|
|
||||||
}
|
|
||||||
return parent::contains($element);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
public function count()
|
|
||||||
{
|
|
||||||
if ( ! $this->_initialized) {
|
|
||||||
//TODO: Initialize
|
|
||||||
}
|
|
||||||
return parent::count();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _checkElementExistence($element)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the collection by loading its contents from the database.
|
* Initializes the collection by loading its contents from the database.
|
||||||
*/
|
*/
|
||||||
private function _initialize()
|
private function _initialize()
|
||||||
{
|
{
|
||||||
|
if (!$this->_initialized) {
|
||||||
|
$this->_association->load($this->_owner, $this, $this->_em);
|
||||||
|
$this->_initialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -371,24 +308,6 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
|||||||
return $this->_association;
|
return $this->_association;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the collection.
|
|
||||||
*/
|
|
||||||
public function clear()
|
|
||||||
{
|
|
||||||
//TODO: Register collection as dirty with the UoW if necessary
|
|
||||||
//TODO: If oneToMany() && shouldDeleteOrphan() delete entities
|
|
||||||
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans) {
|
|
||||||
foreach ($this->_data as $entity) {
|
|
||||||
$this->_em->remove($entity);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
parent::clear();
|
|
||||||
if ($this->_association->isOwningSide) {
|
|
||||||
$this->_changed();
|
|
||||||
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this collection as changed/dirty.
|
* Marks this collection as changed/dirty.
|
||||||
@ -440,6 +359,236 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
|
|||||||
*/
|
*/
|
||||||
public function __sleep()
|
public function __sleep()
|
||||||
{
|
{
|
||||||
return array('_elements');
|
return array('_elements', '_initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorated Collection methods.
|
||||||
|
*/
|
||||||
|
public function unwrap()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function first()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function last()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::last();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::key();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an element from the collection.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @return boolean
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
public function remove($key)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$removed = parent::remove($key);
|
||||||
|
if ($removed) {
|
||||||
|
$this->_changed();
|
||||||
|
if ($this->_association->isOneToMany() && $this->_association->orphanRemoval) {
|
||||||
|
$this->_em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeElement($element)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$result = parent::removeElement($element);
|
||||||
|
$this->_changed();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::offsetExists($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::offsetGet($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$result = parent::offsetSet($offset, $value);
|
||||||
|
$this->_changed();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$result = parent::offsetUnset($offset);
|
||||||
|
$this->_changed();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function containsKey($key)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::containsKey($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether an element is contained in the collection.
|
||||||
|
* This is an O(n) operation.
|
||||||
|
*/
|
||||||
|
public function contains($element)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::contains($element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists(Closure $p)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::exists($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function search($element)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::search($element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get($key)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeys()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getElements()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::getElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the collection is used as a Map this is like put(key,value)/add(key,value).
|
||||||
|
* When the collection is used as a List this is like add(position,value).
|
||||||
|
*
|
||||||
|
* @param integer $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
public function set($key, $value)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$result = parent::set($key, $value);
|
||||||
|
$this->_changed();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an element to the collection.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @param string $key
|
||||||
|
* @return boolean Always TRUE.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
public function add($value)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$result = parent::add($value);
|
||||||
|
$this->_changed();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::getIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function map(Closure $func)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
$result = parent::map($func);
|
||||||
|
$this->_changed();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(Closure $p)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::filter($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forAll(Closure $p)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::forAll($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function partition(Closure $p)
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
return parent::partition($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the collection.
|
||||||
|
*/
|
||||||
|
public function clear()
|
||||||
|
{
|
||||||
|
$this->_initialize();
|
||||||
|
//TODO: Register collection as dirty with the UoW if necessary
|
||||||
|
//TODO: If oneToMany() && shouldDeleteOrphan() delete entities
|
||||||
|
/*if ($this->_association->isOneToMany() && $this->_association->shouldDeleteOrphans) {
|
||||||
|
foreach ($this->_data as $entity) {
|
||||||
|
$this->_em->remove($entity);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
$result = parent::clear();
|
||||||
|
if ($this->_association->isOwningSide) {
|
||||||
|
$this->_changed();
|
||||||
|
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -273,7 +273,7 @@ class JoinedSubclassPersister extends StandardEntityPersister
|
|||||||
* @todo Quote identifier.
|
* @todo Quote identifier.
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
protected function _getSelectSingleEntitySql(array $criteria)
|
protected function _getSelectSingleEntitySql(array &$criteria)
|
||||||
{
|
{
|
||||||
$tableAliases = array();
|
$tableAliases = array();
|
||||||
$aliasIndex = 1;
|
$aliasIndex = 1;
|
||||||
|
@ -35,6 +35,7 @@ use Doctrine\ORM\Events;
|
|||||||
* how to persist (and to some extent how to load) entities of a specific type.
|
* how to persist (and to some extent how to load) entities of a specific type.
|
||||||
*
|
*
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
* @author Roman Borschel <roman@code-factory.org>
|
||||||
|
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||||
* @version $Revision: 3406 $
|
* @version $Revision: 3406 $
|
||||||
* @link www.doctrine-project.org
|
* @link www.doctrine-project.org
|
||||||
@ -396,10 +397,26 @@ class StandardEntityPersister
|
|||||||
{
|
{
|
||||||
$stmt = $this->_conn->prepare($this->_getSelectSingleEntitySql($criteria));
|
$stmt = $this->_conn->prepare($this->_getSelectSingleEntitySql($criteria));
|
||||||
$stmt->execute(array_values($criteria));
|
$stmt->execute(array_values($criteria));
|
||||||
$data = array();
|
|
||||||
$joinColumnValues = array();
|
|
||||||
$result = $stmt->fetch(Connection::FETCH_ASSOC);
|
$result = $stmt->fetch(Connection::FETCH_ASSOC);
|
||||||
$stmt->closeCursor();
|
$stmt->closeCursor();
|
||||||
|
return $this->_createEntity($result, $entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadCollection(array $criteria, PersistentCollection $collection)
|
||||||
|
{
|
||||||
|
$sql = $this->_getSelectSingleEntitySql($criteria);
|
||||||
|
$stmt = $this->_conn->prepare($sql);
|
||||||
|
$stmt->execute(array_values($criteria));
|
||||||
|
while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) {
|
||||||
|
$collection->hydrateAdd($this->_createEntity($result));
|
||||||
|
}
|
||||||
|
$stmt->closeCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _createEntity($result, $entity = null)
|
||||||
|
{
|
||||||
|
$data = array();
|
||||||
|
$joinColumnValues = array();
|
||||||
|
|
||||||
if ($result === false) {
|
if ($result === false) {
|
||||||
return null;
|
return null;
|
||||||
@ -448,10 +465,15 @@ class StandardEntityPersister
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Inject collection
|
// Inject collection
|
||||||
//TODO: Eager load
|
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName));
|
||||||
$this->_class->reflFields[$field]->setValue(
|
$coll->setOwner($entity, $assoc);
|
||||||
$entity, new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName)
|
$this->_class->reflFields[$field]->setValue($entity, $coll);
|
||||||
));
|
if ($assoc->isLazilyFetched()) {
|
||||||
|
$coll->setLazyInitialization();
|
||||||
|
} else {
|
||||||
|
//TODO: Allow more efficient and configurable batching of these loads
|
||||||
|
$assoc->load($entity, $coll, $this->_em);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,11 +483,14 @@ class StandardEntityPersister
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the SELECT SQL to select a single entity by a set of field criteria.
|
* Gets the SELECT SQL to select a single entity by a set of field criteria.
|
||||||
|
* No joins are used but the query can return multiple rows.
|
||||||
*
|
*
|
||||||
* @param array $criteria
|
* @param array $criteria every element can be a value or an joinClause
|
||||||
|
* if it is a joinClause, it will be removed and correspondent nested values will be added to $criteria.
|
||||||
|
* JoinClauses are used to restrict the result returned but only columns of this entity are selected (@see _getJoinSql()).
|
||||||
* @return string The SQL.
|
* @return string The SQL.
|
||||||
*/
|
*/
|
||||||
protected function _getSelectSingleEntitySql(array $criteria)
|
protected function _getSelectSingleEntitySql(array &$criteria)
|
||||||
{
|
{
|
||||||
$columnList = '';
|
$columnList = '';
|
||||||
foreach ($this->_class->columnNames as $column) {
|
foreach ($this->_class->columnNames as $column) {
|
||||||
@ -485,9 +510,23 @@ class StandardEntityPersister
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$joinSql = '';
|
||||||
$conditionSql = '';
|
$conditionSql = '';
|
||||||
foreach ($criteria as $field => $value) {
|
foreach ($criteria as $field => $value) {
|
||||||
if ($conditionSql != '') $conditionSql .= ' AND ';
|
if ($conditionSql != '') {
|
||||||
|
$conditionSql .= ' AND ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
$joinSql .= $this->_getJoinSql($value);
|
||||||
|
foreach ($value['criteria'] as $nestedField => $nestedValue) {
|
||||||
|
$columnName = "{$value['table']}.{$nestedField}";
|
||||||
|
$conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?';
|
||||||
|
$criteria[$columnName] = $nestedValue;
|
||||||
|
}
|
||||||
|
unset($criteria[$field]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (isset($this->_class->columnNames[$field])) {
|
if (isset($this->_class->columnNames[$field])) {
|
||||||
$columnName = $this->_class->columnNames[$field];
|
$columnName = $this->_class->columnNames[$field];
|
||||||
} else if (in_array($field, $joinColumnNames)) {
|
} else if (in_array($field, $joinColumnNames)) {
|
||||||
@ -498,7 +537,27 @@ class StandardEntityPersister
|
|||||||
$conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?';
|
$conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName()
|
return 'SELECT ' . $columnList
|
||||||
|
. ' FROM ' . $this->_class->getTableName()
|
||||||
|
. $joinSql
|
||||||
. ' WHERE ' . $conditionSql;
|
. ' WHERE ' . $conditionSql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a LEFT JOIN sql query part using $joinClause
|
||||||
|
* @param array $joinClause keys are:
|
||||||
|
* 'table' => table to join
|
||||||
|
* 'join' => array of [sourceField => joinTableField]
|
||||||
|
* 'criteria' => array of [joinTableField => value]
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function _getJoinSql(array $joinClause)
|
||||||
|
{
|
||||||
|
$clauses = array();
|
||||||
|
foreach ($joinClause['join'] as $sourceField => $joinTableField) {
|
||||||
|
$clauses[] = $this->_class->getTableName() . ".{$sourceField} = "
|
||||||
|
. "{$joinClause['table']}.{$joinTableField}";
|
||||||
|
}
|
||||||
|
return " LEFT JOIN {$joinClause['table']} ON " . implode(' AND ', $clauses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1164,16 +1164,16 @@ class Parser
|
|||||||
if ($this->_lexer->isNextToken(Lexer::T_ASC)) {
|
if ($this->_lexer->isNextToken(Lexer::T_ASC)) {
|
||||||
$this->match(Lexer::T_ASC);
|
$this->match(Lexer::T_ASC);
|
||||||
$item->setAsc(true);
|
$item->setAsc(true);
|
||||||
|
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->_lexer->isNextToken(Lexer::T_DESC)) {
|
if ($this->_lexer->isNextToken(Lexer::T_DESC)) {
|
||||||
$this->match(Lexer::T_DESC);
|
$this->match(Lexer::T_DESC);
|
||||||
|
$item->setDesc(true);
|
||||||
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
$item->setDesc(true);
|
$item->setAsc(true);
|
||||||
|
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +248,11 @@ class SqlWalker implements TreeWalker
|
|||||||
$qComp = $this->_queryComponents[$dqlAlias];
|
$qComp = $this->_queryComponents[$dqlAlias];
|
||||||
$columnName = $qComp['metadata']->getColumnName($parts[0]);
|
$columnName = $qComp['metadata']->getColumnName($parts[0]);
|
||||||
$sql = $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' . $columnName;
|
$sql = $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' . $columnName;
|
||||||
$sql .= $orderByItem->isAsc() ? ' ASC' : ' DESC';
|
if ($orderByItem->isAsc()) {
|
||||||
|
$sql .= ' ASC ';
|
||||||
|
} else if ($orderByItem->isDesc()) {
|
||||||
|
$sql .= ' DESC ';
|
||||||
|
}
|
||||||
return $sql;
|
return $sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,5 +86,6 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$this->assertTrue($this->_em->contains($phonenumbers[0]));
|
$this->assertTrue($this->_em->contains($phonenumbers[0]));
|
||||||
$this->assertTrue($this->_em->contains($phonenumbers[1]));
|
$this->assertTrue($this->_em->contains($phonenumbers[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,13 @@ namespace Doctrine\Tests\ORM\Functional;
|
|||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceCategory;
|
use Doctrine\Tests\Models\ECommerce\ECommerceCategory;
|
||||||
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests a bidirectional many-to-many association mapping (without inheritance).
|
* Tests a bidirectional many-to-many association mapping (without inheritance).
|
||||||
* Inverse side is not present.
|
* Owning side is ECommerceProduct, inverse side is ECommerceCategory.
|
||||||
*/
|
*/
|
||||||
class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociationTestCase
|
class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociationTestCase
|
||||||
{
|
{
|
||||||
@ -67,36 +68,45 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
|||||||
$this->_createLoadingFixture();
|
$this->_createLoadingFixture();
|
||||||
list ($firstProduct, $secondProduct) = $this->_findProducts();
|
list ($firstProduct, $secondProduct) = $this->_findProducts();
|
||||||
$categories = $firstProduct->getCategories();
|
$categories = $firstProduct->getCategories();
|
||||||
|
$this->assertLoadingOfInverseSide($categories);
|
||||||
$this->assertTrue($categories[0] instanceof ECommerceCategory);
|
$this->assertLoadingOfInverseSide($secondProduct->getCategories());
|
||||||
$this->assertTrue($categories[1] instanceof ECommerceCategory);
|
|
||||||
$this->assertCollectionEquals($categories, $secondProduct->getCategories());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEagerLoadsOwningSide()
|
public function testEagerLoadsOwningSide()
|
||||||
{
|
{
|
||||||
$this->_createLoadingFixture();
|
$this->_createLoadingFixture();
|
||||||
list ($firstProduct, $secondProduct) = $this->_findProducts();
|
$products = $this->_findProducts();
|
||||||
|
$this->assertLoadingOfOwningSide($products);
|
||||||
$this->assertEquals(2, count($firstProduct->getCategories()));
|
|
||||||
$this->assertEquals(2, count($secondProduct->getCategories()));
|
|
||||||
|
|
||||||
$categories = $firstProduct->getCategories();
|
|
||||||
$firstCategoryProducts = $categories[0]->getProducts();
|
|
||||||
$secondCategoryProducts = $categories[1]->getProducts();
|
|
||||||
|
|
||||||
$this->assertEquals(2, count($firstCategoryProducts));
|
|
||||||
$this->assertEquals(2, count($secondCategoryProducts));
|
|
||||||
|
|
||||||
$this->assertTrue($firstCategoryProducts[0] instanceof ECommerceProduct);
|
|
||||||
$this->assertTrue($firstCategoryProducts[1] instanceof ECommerceProduct);
|
|
||||||
$this->assertTrue($secondCategoryProducts[0] instanceof ECommerceProduct);
|
|
||||||
$this->assertTrue($secondCategoryProducts[1] instanceof ECommerceProduct);
|
|
||||||
|
|
||||||
$this->assertCollectionEquals($firstCategoryProducts, $secondCategoryProducts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function _createLoadingFixture()
|
public function testLazyLoadsCollectionOnTheInverseSide()
|
||||||
|
{
|
||||||
|
$this->_createLoadingFixture();
|
||||||
|
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCategory');
|
||||||
|
$metadata->getAssociationMapping('products')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\ECommerce\ECommerceCategory c order by c.id');
|
||||||
|
$categories = $query->getResultList();
|
||||||
|
$this->assertLoadingOfInverseSide($categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLazyLoadsCollectionOnTheOwningSide()
|
||||||
|
{
|
||||||
|
$this->_createLoadingFixture();
|
||||||
|
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||||
|
$metadata->getAssociationMapping('categories')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p order by p.id');
|
||||||
|
$products = $query->getResultList();
|
||||||
|
$this->assertLoadingOfOwningSide($products);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function _createLoadingFixture()
|
||||||
{
|
{
|
||||||
$this->firstProduct->addCategory($this->firstCategory);
|
$this->firstProduct->addCategory($this->firstCategory);
|
||||||
$this->firstProduct->addCategory($this->secondCategory);
|
$this->firstProduct->addCategory($this->secondCategory);
|
||||||
@ -115,8 +125,30 @@ class ManyToManyBidirectionalAssociationTest extends AbstractManyToManyAssociati
|
|||||||
return $query->getResultList();
|
return $query->getResultList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: not yet implemented
|
public function assertLoadingOfOwningSide($products)
|
||||||
public function testLazyLoad() {
|
{
|
||||||
|
list ($firstProduct, $secondProduct) = $products;
|
||||||
|
$this->assertEquals(2, count($firstProduct->getCategories()));
|
||||||
|
$this->assertEquals(2, count($secondProduct->getCategories()));
|
||||||
|
|
||||||
}*/
|
$categories = $firstProduct->getCategories();
|
||||||
|
$firstCategoryProducts = $categories[0]->getProducts();
|
||||||
|
$secondCategoryProducts = $categories[1]->getProducts();
|
||||||
|
|
||||||
|
$this->assertEquals(2, count($firstCategoryProducts));
|
||||||
|
$this->assertEquals(2, count($secondCategoryProducts));
|
||||||
|
|
||||||
|
$this->assertTrue($firstCategoryProducts[0] instanceof ECommerceProduct);
|
||||||
|
$this->assertTrue($firstCategoryProducts[1] instanceof ECommerceProduct);
|
||||||
|
$this->assertTrue($secondCategoryProducts[0] instanceof ECommerceProduct);
|
||||||
|
$this->assertTrue($secondCategoryProducts[1] instanceof ECommerceProduct);
|
||||||
|
|
||||||
|
$this->assertCollectionEquals($firstCategoryProducts, $secondCategoryProducts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertLoadingOfInverseSide($categories)
|
||||||
|
{
|
||||||
|
$this->assertTrue($categories[0] instanceof ECommerceCategory);
|
||||||
|
$this->assertTrue($categories[1] instanceof ECommerceCategory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
|||||||
|
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||||
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
@ -65,8 +66,26 @@ class ManyToManySelfReferentialAssociationTest extends AbstractManyToManyAssocia
|
|||||||
public function testEagerLoadsOwningSide()
|
public function testEagerLoadsOwningSide()
|
||||||
{
|
{
|
||||||
$this->_createLoadingFixture();
|
$this->_createLoadingFixture();
|
||||||
list ($firstProduct, $secondProduct) = $this->_findProducts();
|
$products = $this->_findProducts();
|
||||||
|
$this->assertLoadingOfOwningSide($products);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLazyLoadsOwningSide()
|
||||||
|
{
|
||||||
|
$this->_createLoadingFixture();
|
||||||
|
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||||
|
$metadata->getAssociationMapping('related')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p');
|
||||||
|
$products = $query->getResultList();
|
||||||
|
$this->assertLoadingOfOwningSide($products);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertLoadingOfOwningSide($products)
|
||||||
|
{
|
||||||
|
list ($firstProduct, $secondProduct) = $products;
|
||||||
$this->assertEquals(2, count($firstProduct->getRelated()));
|
$this->assertEquals(2, count($firstProduct->getRelated()));
|
||||||
$this->assertEquals(2, count($secondProduct->getRelated()));
|
$this->assertEquals(2, count($secondProduct->getRelated()));
|
||||||
|
|
||||||
@ -103,9 +122,4 @@ class ManyToManySelfReferentialAssociationTest extends AbstractManyToManyAssocia
|
|||||||
$query = $this->_em->createQuery('SELECT p, r FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p LEFT JOIN p.related r ORDER BY p.id, r.id');
|
$query = $this->_em->createQuery('SELECT p, r FROM Doctrine\Tests\Models\ECommerce\ECommerceProduct p LEFT JOIN p.related r ORDER BY p.id, r.id');
|
||||||
return $query->getResultList();
|
return $query->getResultList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: not yet implemented
|
|
||||||
public function testLazyLoad() {
|
|
||||||
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
|||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
|
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||||
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
@ -60,15 +61,7 @@ class ManyToManyUnidirectionalAssociationTest extends AbstractManyToManyAssociat
|
|||||||
|
|
||||||
public function testEagerLoad()
|
public function testEagerLoad()
|
||||||
{
|
{
|
||||||
$this->firstCart->addProduct($this->firstProduct);
|
$this->_createFixture();
|
||||||
$this->firstCart->addProduct($this->secondProduct);
|
|
||||||
$this->secondCart->addProduct($this->firstProduct);
|
|
||||||
$this->secondCart->addProduct($this->secondProduct);
|
|
||||||
$this->_em->persist($this->firstCart);
|
|
||||||
$this->_em->persist($this->secondCart);
|
|
||||||
|
|
||||||
$this->_em->flush();
|
|
||||||
$this->_em->clear();
|
|
||||||
|
|
||||||
$query = $this->_em->createQuery('SELECT c, p FROM Doctrine\Tests\Models\ECommerce\ECommerceCart c LEFT JOIN c.products p ORDER BY c.id, p.id');
|
$query = $this->_em->createQuery('SELECT c, p FROM Doctrine\Tests\Models\ECommerce\ECommerceCart c LEFT JOIN c.products p ORDER BY c.id, p.id');
|
||||||
$result = $query->getResultList();
|
$result = $query->getResultList();
|
||||||
@ -83,8 +76,34 @@ class ManyToManyUnidirectionalAssociationTest extends AbstractManyToManyAssociat
|
|||||||
//$this->assertEquals("Doctrine 2.x Manual", $products[1]->getName());
|
//$this->assertEquals("Doctrine 2.x Manual", $products[1]->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: not yet implemented
|
public function testLazyLoadsCollection()
|
||||||
public function testLazyLoad() {
|
{
|
||||||
|
$this->_createFixture();
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart');
|
||||||
|
$metadata->getAssociationMapping('products')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
}*/
|
$query = $this->_em->createQuery('SELECT c FROM Doctrine\Tests\Models\ECommerce\ECommerceCart c');
|
||||||
|
$result = $query->getResultList();
|
||||||
|
$firstCart = $result[0];
|
||||||
|
$products = $firstCart->getProducts();
|
||||||
|
$secondCart = $result[1];
|
||||||
|
|
||||||
|
$this->assertTrue($products[0] instanceof ECommerceProduct);
|
||||||
|
$this->assertTrue($products[1] instanceof ECommerceProduct);
|
||||||
|
$this->assertCollectionEquals($products, $secondCart->getProducts());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _createFixture()
|
||||||
|
{
|
||||||
|
$this->firstCart->addProduct($this->firstProduct);
|
||||||
|
$this->firstCart->addProduct($this->secondProduct);
|
||||||
|
$this->secondCart->addProduct($this->firstProduct);
|
||||||
|
$this->secondCart->addProduct($this->secondProduct);
|
||||||
|
$this->_em->persist($this->firstCart);
|
||||||
|
$this->_em->persist($this->secondCart);
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
|||||||
|
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceFeature;
|
use Doctrine\Tests\Models\ECommerce\ECommerceFeature;
|
||||||
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
@ -69,13 +70,7 @@ class OneToManyBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
|
|||||||
|
|
||||||
public function testEagerLoadsOneToManyAssociation()
|
public function testEagerLoadsOneToManyAssociation()
|
||||||
{
|
{
|
||||||
$this->product->addFeature($this->firstFeature);
|
$this->_createFixture();
|
||||||
$this->product->addFeature($this->secondFeature);
|
|
||||||
$this->_em->persist($this->product);
|
|
||||||
|
|
||||||
$this->_em->flush();
|
|
||||||
$this->_em->clear();
|
|
||||||
|
|
||||||
$query = $this->_em->createQuery('select p, f from Doctrine\Tests\Models\ECommerce\ECommerceProduct p join p.features f');
|
$query = $this->_em->createQuery('select p, f from Doctrine\Tests\Models\ECommerce\ECommerceProduct p join p.features f');
|
||||||
$result = $query->getResultList();
|
$result = $query->getResultList();
|
||||||
$product = $result[0];
|
$product = $result[0];
|
||||||
@ -89,10 +84,50 @@ class OneToManyBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
|
|||||||
$this->assertEquals('Annotations examples', $features[1]->getDescription());
|
$this->assertEquals('Annotations examples', $features[1]->getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: not yet implemented
|
public function testLazyLoadsObjectsOnTheOwningSide()
|
||||||
public function testLazyLoad() {
|
{
|
||||||
|
$this->_createFixture();
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||||
|
$metadata->getAssociationMapping('features')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
}*/
|
$query = $this->_em->createQuery('select p from Doctrine\Tests\Models\ECommerce\ECommerceProduct p');
|
||||||
|
$result = $query->getResultList();
|
||||||
|
$product = $result[0];
|
||||||
|
$features = $product->getFeatures();
|
||||||
|
|
||||||
|
$this->assertTrue($features[0] instanceof ECommerceFeature);
|
||||||
|
$this->assertSame($product, $features[0]->getProduct());
|
||||||
|
$this->assertEquals('Model writing tutorial', $features[0]->getDescription());
|
||||||
|
$this->assertTrue($features[1] instanceof ECommerceFeature);
|
||||||
|
$this->assertSame($product, $features[1]->getProduct());
|
||||||
|
$this->assertEquals('Annotations examples', $features[1]->getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLazyLoadsObjectsOnTheInverseSide()
|
||||||
|
{
|
||||||
|
$this->_createFixture();
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||||
|
$metadata->getAssociationMapping('product')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('select f from Doctrine\Tests\Models\ECommerce\ECommerceFeature f');
|
||||||
|
$features = $query->getResultList();
|
||||||
|
|
||||||
|
$product = $features[0]->getProduct();
|
||||||
|
$this->assertTrue($product instanceof ECommerceProduct);
|
||||||
|
$this->assertSame('Doctrine Cookbook', $product->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _createFixture()
|
||||||
|
{
|
||||||
|
$this->product->addFeature($this->firstFeature);
|
||||||
|
$this->product->addFeature($this->secondFeature);
|
||||||
|
$this->_em->persist($this->product);
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
}
|
||||||
|
|
||||||
public function assertFeatureForeignKeyIs($value, ECommerceFeature $feature) {
|
public function assertFeatureForeignKeyIs($value, ECommerceFeature $feature) {
|
||||||
$foreignKey = $this->_em->getConnection()->execute('SELECT product_id FROM ecommerce_features WHERE id=?', array($feature->getId()))->fetchColumn();
|
$foreignKey = $this->_em->getConnection()->execute('SELECT product_id FROM ecommerce_features WHERE id=?', array($feature->getId()))->fetchColumn();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Doctrine\Tests\ORM\Functional;
|
namespace Doctrine\Tests\ORM\Functional;
|
||||||
|
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceCategory;
|
use Doctrine\Tests\Models\ECommerce\ECommerceCategory;
|
||||||
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
@ -69,12 +70,7 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio
|
|||||||
|
|
||||||
public function testEagerLoadsOneToManyAssociation()
|
public function testEagerLoadsOneToManyAssociation()
|
||||||
{
|
{
|
||||||
$this->parent->addChild($this->firstChild);
|
$this->_createFixture();
|
||||||
$this->parent->addChild($this->secondChild);
|
|
||||||
$this->_em->persist($this->parent);
|
|
||||||
|
|
||||||
$this->_em->flush();
|
|
||||||
$this->_em->clear();
|
|
||||||
|
|
||||||
$query = $this->_em->createQuery('select c1, c2 from Doctrine\Tests\Models\ECommerce\ECommerceCategory c1 join c1.children c2');
|
$query = $this->_em->createQuery('select c1, c2 from Doctrine\Tests\Models\ECommerce\ECommerceCategory c1 join c1.children c2');
|
||||||
$result = $query->getResultList();
|
$result = $query->getResultList();
|
||||||
@ -90,10 +86,35 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio
|
|||||||
$this->assertEquals(' books', strstr($children[1]->getName(), ' books'));
|
$this->assertEquals(' books', strstr($children[1]->getName(), ' books'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: not yet implemented
|
public function testLazyLoadsOneToManyAssociation()
|
||||||
public function testLazyLoad() {
|
{
|
||||||
|
$this->_createFixture();
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCategory');
|
||||||
|
$metadata->getAssociationMapping('children')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
}*/
|
$query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCategory c order by c.id asc');
|
||||||
|
$result = $query->getResultList();
|
||||||
|
$parent = $result[0];
|
||||||
|
$children = $parent->getChildren();
|
||||||
|
|
||||||
|
$this->assertTrue($children[0] instanceof ECommerceCategory);
|
||||||
|
$this->assertSame($parent, $children[0]->getParent());
|
||||||
|
$this->assertEquals(' books', strstr($children[0]->getName(), ' books'));
|
||||||
|
$this->assertTrue($children[1] instanceof ECommerceCategory);
|
||||||
|
$this->assertSame($parent, $children[1]->getParent());
|
||||||
|
$this->assertEquals(' books', strstr($children[1]->getName(), ' books'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _createFixture()
|
||||||
|
{
|
||||||
|
$this->parent->addChild($this->firstChild);
|
||||||
|
$this->parent->addChild($this->secondChild);
|
||||||
|
$this->_em->persist($this->parent);
|
||||||
|
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
}
|
||||||
|
|
||||||
public function assertForeignKeyIs($value, ECommerceCategory $child) {
|
public function assertForeignKeyIs($value, ECommerceCategory $child) {
|
||||||
$foreignKey = $this->_em->getConnection()->execute('SELECT parent_id FROM ecommerce_categories WHERE id=?', array($child->getId()))->fetchColumn();
|
$foreignKey = $this->_em->getConnection()->execute('SELECT parent_id FROM ecommerce_categories WHERE id=?', array($child->getId()))->fetchColumn();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Doctrine\Tests\ORM\Functional;
|
namespace Doctrine\Tests\ORM\Functional;
|
||||||
|
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceCustomer;
|
use Doctrine\Tests\Models\ECommerce\ECommerceCustomer;
|
||||||
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
|
|
||||||
@ -47,7 +48,42 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
|
|||||||
$this->assertForeignKeyIs(null);
|
$this->assertForeignKeyIs(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEagerLoad()
|
public function testEagerLoadsAssociation()
|
||||||
|
{
|
||||||
|
$this->_createFixture();
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('select c, m from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c left join c.mentor m order by c.id asc');
|
||||||
|
$result = $query->getResultList();
|
||||||
|
$customer = $result[0];
|
||||||
|
$this->assertLoadingOfAssociation($customer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLazyLoadsAssociation()
|
||||||
|
{
|
||||||
|
$this->_createFixture();
|
||||||
|
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer');
|
||||||
|
$metadata->getAssociationMapping('mentor')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c');
|
||||||
|
$result = $query->getResultList();
|
||||||
|
$customer = $result[0];
|
||||||
|
$this->assertLoadingOfAssociation($customer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertLoadingOfAssociation($customer)
|
||||||
|
{
|
||||||
|
$this->assertTrue($customer->getMentor() instanceof ECommerceCustomer);
|
||||||
|
$this->assertEquals('Obi-wan Kenobi', $customer->getMentor()->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertForeignKeyIs($value) {
|
||||||
|
$foreignKey = $this->_em->getConnection()->execute('SELECT mentor_id FROM ecommerce_customers WHERE id=?', array($this->customer->getId()))->fetchColumn();
|
||||||
|
$this->assertEquals($value, $foreignKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _createFixture()
|
||||||
{
|
{
|
||||||
$customer = new ECommerceCustomer;
|
$customer = new ECommerceCustomer;
|
||||||
$customer->setName('Luke Skywalker');
|
$customer->setName('Luke Skywalker');
|
||||||
@ -59,22 +95,5 @@ class OneToOneSelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunction
|
|||||||
|
|
||||||
$this->_em->flush();
|
$this->_em->flush();
|
||||||
$this->_em->clear();
|
$this->_em->clear();
|
||||||
|
|
||||||
$query = $this->_em->createQuery('select c, m from Doctrine\Tests\Models\ECommerce\ECommerceCustomer c left join c.mentor m order by c.id asc');
|
|
||||||
$result = $query->getResultList();
|
|
||||||
$customer = $result[0];
|
|
||||||
|
|
||||||
$this->assertTrue($customer->getMentor() instanceof ECommerceCustomer);
|
|
||||||
$this->assertEquals('Obi-wan Kenobi', $customer->getMentor()->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: not yet implemented
|
|
||||||
public function testLazyLoad() {
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public function assertForeignKeyIs($value) {
|
|
||||||
$foreignKey = $this->_em->getConnection()->execute('SELECT mentor_id FROM ecommerce_customers WHERE id=?', array($this->customer->getId()))->fetchColumn();
|
|
||||||
$this->assertEquals($value, $foreignKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
|||||||
|
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
|
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
|
||||||
use Doctrine\Tests\Models\ECommerce\ECommerceCustomer;
|
use Doctrine\Tests\Models\ECommerce\ECommerceCustomer;
|
||||||
|
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||||
|
|
||||||
require_once __DIR__ . '/../../TestInit.php';
|
require_once __DIR__ . '/../../TestInit.php';
|
||||||
@ -20,7 +21,8 @@ class StandardEntityPersisterTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcceptsForeignKeysAsCriteria() {
|
public function testAcceptsForeignKeysAsCriteria()
|
||||||
|
{
|
||||||
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
|
||||||
$customer = new ECommerceCustomer();
|
$customer = new ECommerceCustomer();
|
||||||
@ -38,4 +40,30 @@ class StandardEntityPersisterTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
|||||||
$persister->load(array('customer_id' => $customer->getId()), $newCart);
|
$persister->load(array('customer_id' => $customer->getId()), $newCart);
|
||||||
$this->assertEquals('Credit card', $newCart->getPayment());
|
$this->assertEquals('Credit card', $newCart->getPayment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAcceptsJoinTableAsCriteria()
|
||||||
|
{
|
||||||
|
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||||
|
|
||||||
|
$cart = new ECommerceCart();
|
||||||
|
$product = new ECommerceProduct();
|
||||||
|
$product->setName('Star Wars: A New Hope');
|
||||||
|
$cart->addProduct($product);
|
||||||
|
$this->_em->persist($cart);
|
||||||
|
$this->_em->flush();
|
||||||
|
$this->_em->clear();
|
||||||
|
unset($product);
|
||||||
|
|
||||||
|
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||||
|
$newProduct = new ECommerceProduct();
|
||||||
|
$criteria = array(
|
||||||
|
array(
|
||||||
|
'table' => 'ecommerce_carts_products',
|
||||||
|
'join' => array('id' => 'product_id'),
|
||||||
|
'criteria' => array('cart_id' => $cart->getId())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$persister->load($criteria, $newProduct);
|
||||||
|
$this->assertEquals('Star Wars: A New Hope', $newProduct->getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
52
tests/Doctrine/Tests/ORM/PersistentCollectionTest.php
Normal file
52
tests/Doctrine/Tests/ORM/PersistentCollectionTest.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\ORM;
|
||||||
|
|
||||||
|
use Doctrine\ORM\PersistentCollection;
|
||||||
|
use Doctrine\Tests\Mocks\ConnectionMock;
|
||||||
|
use Doctrine\Tests\Mocks\EntityManagerMock;
|
||||||
|
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../TestInit.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the lazy-loading capabilities of the PersistentCollection.
|
||||||
|
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||||
|
*/
|
||||||
|
class PersistentCollectionTest extends \Doctrine\Tests\OrmTestCase
|
||||||
|
{
|
||||||
|
private $_connectionMock;
|
||||||
|
private $_emMock;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
// SUT
|
||||||
|
$this->_connectionMock = new ConnectionMock(array(), new \Doctrine\Tests\Mocks\DriverMock());
|
||||||
|
$this->_emMock = EntityManagerMock::create($this->_connectionMock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanBePutInLazyLoadingMode()
|
||||||
|
{
|
||||||
|
$class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||||
|
$collection = new PersistentCollection($this->_emMock, $class);
|
||||||
|
$collection->setLazyInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQueriesAssociationToLoadItself()
|
||||||
|
{
|
||||||
|
$class = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||||
|
$collection = new PersistentCollection($this->_emMock, $class);
|
||||||
|
$collection->setLazyInitialization();
|
||||||
|
|
||||||
|
$association = $this->getMock('Doctrine\ORM\Mapping\OneToManyMapping', array('load'), array(), '', false, false, false);
|
||||||
|
$association->targetEntityName = 'Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||||
|
$product = new ECommerceProduct();
|
||||||
|
$association->expects($this->once())
|
||||||
|
->method('load')
|
||||||
|
->with($product, $this->isInstanceOf($collection), $this->isInstanceOf($this->_emMock));
|
||||||
|
$collection->setOwner($product, $association);
|
||||||
|
|
||||||
|
count($collection);
|
||||||
|
}
|
||||||
|
}
|
@ -90,7 +90,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
|||||||
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||||
$persister = $this->_getMockPersister();
|
$persister = $this->_getMockPersister();
|
||||||
$proxy = new $proxyClass($persister, $identifier);
|
$proxy = new $proxyClass($persister, $identifier);
|
||||||
$persister->expects($this->any())
|
$persister->expects($this->atLeastOnce())
|
||||||
->method('load')
|
->method('load')
|
||||||
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass));
|
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass));
|
||||||
$proxy->getDescription();
|
$proxy->getDescription();
|
||||||
@ -102,7 +102,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
|||||||
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||||
$persister = $this->_getMockPersister();
|
$persister = $this->_getMockPersister();
|
||||||
$proxy = new $proxyClass($persister, $identifier);
|
$proxy = new $proxyClass($persister, $identifier);
|
||||||
$persister->expects($this->once())
|
$persister->expects($this->atLeastOnce())
|
||||||
->method('load')
|
->method('load')
|
||||||
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass));
|
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass));
|
||||||
$proxy->getId();
|
$proxy->getId();
|
||||||
@ -165,7 +165,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
|||||||
$foreignKeys = array('customer_id' => 42);
|
$foreignKeys = array('customer_id' => 42);
|
||||||
$assoc = $this->_getAssociationMock();
|
$assoc = $this->_getAssociationMock();
|
||||||
$proxy = new $proxyClass($this->_emMock, $assoc, $product, $foreignKeys);
|
$proxy = new $proxyClass($this->_emMock, $assoc, $product, $foreignKeys);
|
||||||
$assoc->expects($this->any())
|
$assoc->expects($this->atLeastOnce())
|
||||||
->method('load')
|
->method('load')
|
||||||
->with($product, $this->isInstanceOf($proxyClass), $this->isInstanceOf('Doctrine\Tests\Mocks\EntityManagerMock'), $foreignKeys);
|
->with($product, $this->isInstanceOf($proxyClass), $this->isInstanceOf('Doctrine\Tests\Mocks\EntityManagerMock'), $foreignKeys);
|
||||||
$proxy->getDescription();
|
$proxy->getDescription();
|
||||||
|
@ -66,6 +66,29 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSupportsOrderByWithAscAsDefault()
|
||||||
|
{
|
||||||
|
$this->assertSqlGeneration(
|
||||||
|
'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id',
|
||||||
|
'SELECT f0_.id AS id0, f0_.username AS username1 FROM forum_users f0_ ORDER BY f0_.id ASC '
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSupportsOrderByAsc()
|
||||||
|
{
|
||||||
|
$this->assertSqlGeneration(
|
||||||
|
'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id asc',
|
||||||
|
'SELECT f0_.id AS id0, f0_.username AS username1 FROM forum_users f0_ ORDER BY f0_.id ASC '
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function testSupportsOrderByDesc()
|
||||||
|
{
|
||||||
|
$this->assertSqlGeneration(
|
||||||
|
'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id desc',
|
||||||
|
'SELECT f0_.id AS id0, f0_.username AS username1 FROM forum_users f0_ ORDER BY f0_.id DESC '
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testSupportsSelectDistinct()
|
public function testSupportsSelectDistinct()
|
||||||
{
|
{
|
||||||
$this->assertSqlGeneration(
|
$this->assertSqlGeneration(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user