[2.0] Accomodate joincolumn names in the metadata, in the selection and the hydration processes. Improved Api of the ProxyFactory. Working implementation of lazy loading for *-to-one associations (affects #2348)
This commit is contained in:
parent
4d146d321f
commit
0c623fdb9e
@ -777,4 +777,4 @@ class Connection
|
||||
}
|
||||
return $this->_schemaManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ class EntityManager
|
||||
*
|
||||
* @return ProxyFactory
|
||||
*/
|
||||
public function getProxyGenerator()
|
||||
public function getProxyFactory()
|
||||
{
|
||||
return $this->_proxyFactory;
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ use Doctrine\Common\DoctrineException;
|
||||
*/
|
||||
abstract class AbstractHydrator
|
||||
{
|
||||
const TYPES_JOINCOLUMN = 'join_column';
|
||||
|
||||
/** The ResultSetMapping. */
|
||||
protected $_rsm;
|
||||
|
||||
@ -184,12 +186,16 @@ abstract class AbstractHydrator
|
||||
} else if (isset($this->_rsm->fieldMappings[$key])) {
|
||||
$classMetadata = $this->_em->getClassMetadata($this->_rsm->getOwningClass($key));
|
||||
$fieldName = $this->_rsm->fieldMappings[$key];
|
||||
if ( ! isset($classMetadata->reflFields[$fieldName])) {
|
||||
if ( ! isset($classMetadata->reflFields[$fieldName]) && ! in_array($fieldName, $classMetadata->joinColumnNames)) {
|
||||
$classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
|
||||
}
|
||||
$cache[$key]['fieldName'] = $fieldName;
|
||||
$cache[$key]['isScalar'] = false;
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
if (isset($classMetadata->fieldMappings[$fieldName])) {
|
||||
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
|
||||
} else {
|
||||
$cache[$key]['type'] = self::TYPES_JOINCOLUMN;
|
||||
}
|
||||
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
|
||||
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
|
||||
} else {
|
||||
@ -217,7 +223,11 @@ abstract class AbstractHydrator
|
||||
$id[$dqlAlias] .= '|' . $value;
|
||||
}
|
||||
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
if ($cache[$key]['type'] == self::TYPES_JOINCOLUMN) {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
|
||||
} else {
|
||||
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
|
||||
$nonemptyComponents[$dqlAlias] = true;
|
||||
@ -302,4 +312,4 @@ abstract class AbstractHydrator
|
||||
|
||||
throw DoctrineException::updateMe("No owner found for field '$fieldName' during hydration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,6 +236,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
$entity = $this->_uow->createEntity($className, $data);
|
||||
|
||||
$joinColumnsValues = array();
|
||||
foreach ($this->_ce[$className]->joinColumnNames as $name) {
|
||||
if (isset($data[$name])) {
|
||||
$joinColumnsValues[$name] = $data[$name];
|
||||
}
|
||||
}
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
if ( ! $this->_allowPartialObjects) {
|
||||
foreach ($this->_ce[$className]->associationMappings as $field => $assoc) {
|
||||
@ -243,7 +250,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
if ($assoc->isOneToOne()) {
|
||||
if ($assoc->isLazilyFetched()) {
|
||||
// Inject proxy
|
||||
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc);
|
||||
$proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnsValues);
|
||||
$this->_ce[$className]->reflFields[$field]->setValue($entity, $proxy);
|
||||
} else {
|
||||
//TODO: Schedule for eager fetching
|
||||
@ -462,4 +469,4 @@ class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
return new \Doctrine\Common\Collections\Collection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -379,12 +379,13 @@ abstract class AssociationMapping
|
||||
|
||||
/**
|
||||
* Loads data in $targetEntity domain object using this association.
|
||||
* The data comes from the association navigated from $owningEntity
|
||||
* The data comes from the association navigated from $sourceEntity
|
||||
* using $em.
|
||||
*
|
||||
* @param object $owningEntity
|
||||
* @param object $sourceEntity
|
||||
* @param object $targetEntity
|
||||
* @param EntityManager $em
|
||||
* @param array $joinColumnValues
|
||||
*/
|
||||
abstract public function load($owningEntity, $targetEntity, $em);
|
||||
abstract public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues);
|
||||
}
|
||||
|
@ -254,6 +254,12 @@ final class ClassMetadata
|
||||
*/
|
||||
public $columnNames = array();
|
||||
|
||||
/**
|
||||
* Array of all column names that does not map to a field.
|
||||
* Foreign keys are here.
|
||||
*/
|
||||
public $joinColumnNames = array();
|
||||
|
||||
/**
|
||||
* Whether to automatically OUTER JOIN subtypes when a basetype is queried.
|
||||
*
|
||||
@ -493,6 +499,11 @@ final class ClassMetadata
|
||||
$this->reflFields[$propName] = $property;
|
||||
}
|
||||
|
||||
public function addJoinColumn($name)
|
||||
{
|
||||
$this->joinColumnNames[] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ReflectionProperty for a specific field of the mapped class.
|
||||
*
|
||||
@ -1315,7 +1326,7 @@ final class ClassMetadata
|
||||
|
||||
/**
|
||||
* Makes some automatic additions to the association mapping to make the life
|
||||
* easier for the user.
|
||||
* easier for the user, and store join columns in the metadata.
|
||||
*
|
||||
* @param array $mapping
|
||||
* @todo Pass param by ref?
|
||||
@ -1326,6 +1337,11 @@ final class ClassMetadata
|
||||
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false) {
|
||||
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
|
||||
}
|
||||
if (isset($mapping['joinColumns'])) {
|
||||
foreach ($mapping['joinColumns'] as $columns) {
|
||||
$this->addJoinColumn($columns['name']);
|
||||
}
|
||||
}
|
||||
return $mapping;
|
||||
}
|
||||
|
||||
@ -1779,4 +1795,4 @@ final class ClassMetadata
|
||||
{
|
||||
return __CLASS__ . '@' . spl_object_hash($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -372,4 +372,4 @@ class ClassMetadataFactory
|
||||
throw new DoctrineException("Unexhaustive match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ class ManyToManyMapping extends AssociationMapping
|
||||
return $this->targetKeyColumns;
|
||||
}
|
||||
|
||||
public function load($owningEntity, $targetEntity, $em)
|
||||
public function load($owningEntity, $targetEntity, $em, array $joinColumnValues)
|
||||
{
|
||||
throw new Exception('Not yet implemented.');
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ class OneToManyMapping extends AssociationMapping
|
||||
return true;
|
||||
}
|
||||
|
||||
public function load($owningEntity, $targetEntity, $em)
|
||||
public function load($owningEntity, $targetEntity, $em, array $joinColumnValues)
|
||||
{
|
||||
throw new Exception('Not yet implemented.');
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ namespace Doctrine\ORM\Mapping;
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||
*/
|
||||
class OneToOneMapping extends AssociationMapping
|
||||
{
|
||||
@ -152,11 +153,13 @@ class OneToOneMapping extends AssociationMapping
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param object $owningEntity
|
||||
* @param object $targetEntity
|
||||
* @param object $sourceEntity the entity source of this association
|
||||
* @param object $targetEntity the entity to load data in
|
||||
* @param EntityManager $em
|
||||
* @param array $joinColumnValues values of the join columns of $sourceEntity. There are no fields
|
||||
* to store this data in $sourceEntity
|
||||
*/
|
||||
public function load($owningEntity, $targetEntity, $em)
|
||||
public function load($sourceEntity, $targetEntity, $em, array $joinColumnValues)
|
||||
{
|
||||
$sourceClass = $em->getClassMetadata($this->sourceEntityName);
|
||||
$targetClass = $em->getClassMetadata($this->targetEntityName);
|
||||
@ -165,24 +168,38 @@ class OneToOneMapping extends AssociationMapping
|
||||
|
||||
if ($this->isOwningSide) {
|
||||
foreach ($this->sourceToTargetKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
|
||||
$conditions[$targetKeyColumn] = $sourceClass->getReflectionProperty(
|
||||
$sourceClass->getFieldName($sourceKeyColumn))->getValue($owningEntity);
|
||||
// getting customer_id
|
||||
if (isset($sourceClass->reflFields[$sourceKeyColumn])) {
|
||||
$conditions[$targetKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn);
|
||||
} else {
|
||||
$conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn];
|
||||
}
|
||||
}
|
||||
if ($targetClass->hasInverseAssociation($this->sourceFieldName)) {
|
||||
if ($targetClass->hasInverseAssociationMapping($this->sourceFieldName)) {
|
||||
$targetClass->setFieldValue(
|
||||
$targetEntity,
|
||||
$targetClass->inverseMappings[$this->_sourceFieldName]->getSourceFieldName(),
|
||||
$owningEntity);
|
||||
$targetClass->inverseMappings[$this->sourceFieldName]->getSourceFieldName(),
|
||||
$sourceEntity);
|
||||
}
|
||||
} else {
|
||||
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName);
|
||||
foreach ($owningAssoc->getTargetToSourceKeyColumns() as $targetKeyColumn => $sourceKeyColumn) {
|
||||
$conditions[$sourceKeyColumn] = $sourceClass->getReflectionProperty(
|
||||
$sourceClass->getFieldName($targetKeyColumn))->getValue($owningEntity);
|
||||
// getting id
|
||||
if (isset($sourceClass->reflFields[$targetKeyColumn])) {
|
||||
$conditions[$sourceKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $targetKeyColumn);
|
||||
} else {
|
||||
$conditions[$sourceKeyColumn] = $joinColumnValues[$targetKeyColumn];
|
||||
}
|
||||
}
|
||||
$targetClass->setFieldValue($targetEntity, $this->mappedByFieldName, $owningEntity);
|
||||
$targetClass->setFieldValue($targetEntity, $this->mappedByFieldName, $sourceEntity);
|
||||
}
|
||||
|
||||
$em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity);
|
||||
}
|
||||
|
||||
protected function _getPrivateValue(ClassMetadata $class, $entity, $column)
|
||||
{
|
||||
$reflField = $class->getReflectionProperty($class->getFieldName($column));
|
||||
return $reflField->getValue($entity);
|
||||
}
|
||||
}
|
||||
|
@ -411,10 +411,15 @@ class StandardEntityPersister
|
||||
$stmt = $this->_conn->prepare($this->_getSelectSingleEntitySql($criteria));
|
||||
$stmt->execute(array_values($criteria));
|
||||
$data = array();
|
||||
$joinColumnValues = array();
|
||||
foreach ($stmt->fetch(Connection::FETCH_ASSOC) as $column => $value) {
|
||||
$fieldName = $this->_class->fieldNames[$column];
|
||||
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
|
||||
if (isset($this->_class->fieldNames[$column])) {
|
||||
$fieldName = $this->_class->fieldNames[$column];
|
||||
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
} else {
|
||||
$joinColumnValues[$column] = $value;
|
||||
}
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
@ -440,9 +445,9 @@ class StandardEntityPersister
|
||||
// empty collections respectively.
|
||||
foreach ($this->_class->associationMappings as $field => $assoc) {
|
||||
if ($assoc->isOneToOne()) {
|
||||
if ($assoc->isLazilyFetched) {
|
||||
if ($assoc->isLazilyFetched()) {
|
||||
// Inject proxy
|
||||
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc);
|
||||
$proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnValues);
|
||||
$this->_class->reflFields[$field]->setValue($entity, $proxy);
|
||||
} else {
|
||||
//TODO: Eager fetch
|
||||
@ -472,11 +477,23 @@ class StandardEntityPersister
|
||||
if ($columnList != '') $columnList .= ', ';
|
||||
$columnList .= $this->_conn->quoteIdentifier($column);
|
||||
}
|
||||
if (!$this->_em->getConfiguration()->getAllowPartialObjects()) {
|
||||
foreach ($this->_class->joinColumnNames as $column) {
|
||||
$columnList .= ', ' . $this->_conn->quoteIdentifier($column);
|
||||
}
|
||||
}
|
||||
|
||||
$conditionSql = '';
|
||||
foreach ($criteria as $field => $value) {
|
||||
if ($conditionSql != '') $conditionSql .= ' AND ';
|
||||
$conditionSql .= $this->_conn->quoteIdentifier($this->_class->columnNames[$field]) . ' = ?';
|
||||
if (isset($this->_class->columnNames[$field])) {
|
||||
$columnName = $this->_class->columnNames[$field];
|
||||
} else if (in_array($field, $this->_class->joinColumnNames)) {
|
||||
$columnName = $field;
|
||||
} else {
|
||||
throw new Exception("Unrecognized field: $field");
|
||||
}
|
||||
$conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?';
|
||||
}
|
||||
|
||||
return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName()
|
||||
|
@ -88,12 +88,13 @@ class ProxyClassGenerator
|
||||
$class = $this->_em->getClassMetadata($originalClassName);
|
||||
$proxyFullyQualifiedClassName = self::$_ns . $proxyClassName;
|
||||
|
||||
$class = $this->_em->getClassMetadata($originalClassName);
|
||||
$this->_em->getMetadataFactory()->setMetadataFor($proxyFullyQualifiedClassName, $class);
|
||||
|
||||
if (class_exists($proxyFullyQualifiedClassName, false)) {
|
||||
return $proxyFullyQualifiedClassName;
|
||||
}
|
||||
|
||||
$class = $this->_em->getClassMetadata($originalClassName);
|
||||
$this->_em->getMetadataFactory()->setMetadataFor($proxyFullyQualifiedClassName, $class);
|
||||
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
|
||||
|
||||
if (file_exists($fileName)) {
|
||||
@ -228,18 +229,21 @@ namespace Doctrine\Generated\Proxies {
|
||||
private $_em;
|
||||
private $_assoc;
|
||||
private $_owner;
|
||||
private $_joinColumnValues;
|
||||
private $_loaded = false;
|
||||
public function __construct($em, $assoc, $owner) {
|
||||
public function __construct($em, $assoc, $owner, array $joinColumnValues) {
|
||||
$this->_em = $em;
|
||||
$this->_assoc = $assoc;
|
||||
$this->_owner = $owner;
|
||||
$this->_joinColumnValues = $joinColumnValues;
|
||||
}
|
||||
private function _load() {
|
||||
if ( ! $this->_loaded) {
|
||||
$this->_assoc->load($this->_owner, $this, $this->_em);
|
||||
$this->_assoc->load($this->_owner, $this, $this->_em, $this->_joinColumnValues);
|
||||
unset($this->_em);
|
||||
unset($this->_owner);
|
||||
unset($this->_assoc);
|
||||
unset($this->_joinColumnValues);
|
||||
$this->_loaded = true;
|
||||
}
|
||||
}
|
||||
|
@ -66,9 +66,9 @@ class ProxyFactory
|
||||
/**
|
||||
* Gets an association proxy instance.
|
||||
*/
|
||||
public function getAssociationProxy($owner, \Doctrine\ORM\Mapping\AssociationMapping $assoc)
|
||||
public function getAssociationProxy($owner, \Doctrine\ORM\Mapping\AssociationMapping $assoc, array $joinColumnValues)
|
||||
{
|
||||
$proxyClassName = $this->_generator->generateAssociationProxyClass($assoc->getTargetEntityName());
|
||||
return new $proxyClassName($this->_em, $assoc, $owner);
|
||||
return new $proxyClassName($this->_em, $assoc, $owner, $joinColumnValues);
|
||||
}
|
||||
}
|
||||
|
@ -2229,4 +2229,4 @@ class Parser
|
||||
{
|
||||
self::$_DATETIME_FUNCTIONS[$name] = $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,6 +470,13 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) . ' AS ' . $columnAlias;
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName);
|
||||
}
|
||||
if (!$this->_em->getConfiguration()->getAllowPartialObjects()) {
|
||||
foreach ($class->joinColumnNames as $name) {
|
||||
$columnAlias = $this->getSqlColumnAlias($name);
|
||||
$sql .= ', ' . $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($name) . ' AS ' . $columnAlias;
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1304,4 +1311,4 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,4 +302,4 @@ class SchemaTool
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Mocks;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
|
||||
/**
|
||||
* Special EntityManager mock used for testing purposes.
|
||||
@ -27,6 +28,7 @@ namespace Doctrine\Tests\Mocks;
|
||||
class EntityManagerMock extends \Doctrine\ORM\EntityManager
|
||||
{
|
||||
private $_uowMock;
|
||||
private $_proxyFactoryMock;
|
||||
private $_idGenerators = array();
|
||||
|
||||
/**
|
||||
@ -48,6 +50,16 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager
|
||||
{
|
||||
$this->_uowMock = $uow;
|
||||
}
|
||||
|
||||
public function setProxyFactory($proxyFactory)
|
||||
{
|
||||
$this->_proxyFactoryMock = $proxyFactory;
|
||||
}
|
||||
|
||||
public function getProxyFactory()
|
||||
{
|
||||
return isset($this->_proxyFactoryMock) ? $this->_proxyFactoryMock : parent::getProxyFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock factory method to create an EntityManager.
|
||||
@ -87,4 +99,4 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager
|
||||
return parent::getIdGenerator($className);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Proxy\ProxyClassGeneratorTest');
|
||||
|
||||
$suite->addTest(Query\AllTests::suite());
|
||||
$suite->addTest(Hydration\AllTests::suite());
|
||||
$suite->addTest(Entity\AllTests::suite());
|
||||
|
@ -36,6 +36,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManySelfReferentialAssociationTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ReferenceProxyTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\LifecycleCallbackTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\StandardEntityPersisterTest');
|
||||
|
||||
$suite->addTest(Locking\AllTests::suite());
|
||||
|
||||
|
@ -65,8 +65,21 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional
|
||||
$this->assertEquals('paypal', $customer->getCart()->getPayment());
|
||||
}
|
||||
|
||||
public function testLazyLoad() {
|
||||
$this->markTestSkipped();
|
||||
public function testLazyLoadsObjectsOnTheOwningSide() {
|
||||
$this->_createFixture();
|
||||
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart');
|
||||
$metadata->getAssociationMapping('customer')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||
|
||||
$query = $this->_em->createQuery('select c from Doctrine\Tests\Models\ECommerce\ECommerceCart c');
|
||||
$result = $query->getResultList();
|
||||
$cart = $result[0];
|
||||
|
||||
$this->assertTrue($cart->getCustomer() instanceof ECommerceCustomer);
|
||||
$this->assertEquals('Giorgio', $cart->getCustomer()->getName());
|
||||
}
|
||||
|
||||
public function testLazyLoadsObjectsOnTheInverseSide() {
|
||||
$this->_createFixture();
|
||||
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer');
|
||||
|
@ -58,8 +58,7 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
|
||||
$this->assertEquals(1, $product->getShipping()->getDays());
|
||||
}
|
||||
|
||||
public function testLazyLoad() {
|
||||
$this->markTestSkipped();
|
||||
public function testLazyLoadsObjects() {
|
||||
$this->_createFixture();
|
||||
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||
@ -73,6 +72,17 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
|
||||
$this->assertEquals(1, $product->getShipping()->getDays());
|
||||
}
|
||||
|
||||
public function testDoesNotLazyLoadObjectsIfConfigurationDoesNotAllowIt() {
|
||||
$this->_createFixture();
|
||||
$this->_em->getConfiguration()->setAllowPartialObjects(true);
|
||||
|
||||
$query = $this->_em->createQuery('select p from Doctrine\Tests\Models\ECommerce\ECommerceProduct p');
|
||||
$result = $query->getResultList();
|
||||
$product = $result[0];
|
||||
|
||||
$this->assertNull($product->getShipping());
|
||||
}
|
||||
|
||||
protected function _createFixture()
|
||||
{
|
||||
$product = new ECommerceProduct;
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
|
||||
use Doctrine\Tests\Models\ECommerce\ECommerceCustomer;
|
||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* Tests capabilities of the persister.
|
||||
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||
*/
|
||||
class StandardEntityPersisterTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('ecommerce');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testAcceptsForeignKeysAsCriteria() {
|
||||
$customer = new ECommerceCustomer();
|
||||
$customer->setName('John Doe');
|
||||
$cart = new ECommerceCart();
|
||||
$cart->setPayment('Credit card');
|
||||
$customer->setCart($cart);
|
||||
$this->_em->persist($customer);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
unset($cart);
|
||||
|
||||
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceCart');
|
||||
$newCart = new ECommerceCart();
|
||||
$persister->load(array('customer_id' => $customer->getId()), $newCart);
|
||||
$this->assertEquals('Credit card', $newCart->getPayment());
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ namespace Doctrine\Tests\ORM\Hydration;
|
||||
|
||||
use Doctrine\Tests\Mocks\HydratorMockStatement;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Mapping\AssociationMapping;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
@ -98,6 +100,50 @@ class ObjectHydratorTest extends HydrationTestCase
|
||||
$this->assertEquals('Cool things II.', $result[3]->topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select p from \Doctrine\Tests\Models\ECommerce\ECommerceProduct p
|
||||
*/
|
||||
public function testCreatesProxyForLazyLoadingWithForeignKeys()
|
||||
{
|
||||
$rsm = new ResultSetMapping;
|
||||
$rsm->addEntityResult('Doctrine\Tests\Models\ECommerce\ECommerceProduct', 'p');
|
||||
$rsm->addFieldResult('p', 'p__id', 'id');
|
||||
$rsm->addFieldResult('p', 'p__name', 'name');
|
||||
$rsm->addFieldResult('p', 'p__shipping_id', 'shipping_id');
|
||||
|
||||
// Faked result set
|
||||
$resultSet = array(
|
||||
array(
|
||||
'p__id' => '1',
|
||||
'p__name' => 'Doctrine Book',
|
||||
'p__shipping_id' => 42
|
||||
)
|
||||
);
|
||||
|
||||
// mocking the proxy factory
|
||||
$proxyFactory = $this->getMock('Doctrine\ORM\Proxy\ProxyFactory', array('getAssociationProxy'), array(), '', false, false, false);
|
||||
$proxyFactory->expects($this->once())
|
||||
->method('getAssociationProxy')
|
||||
->with($this->isInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceProduct'),
|
||||
$this->isInstanceOf('Doctrine\ORM\Mapping\OneToOneMapping'),
|
||||
array('shipping_id' => 42));
|
||||
|
||||
$this->_em->setProxyFactory($proxyFactory);
|
||||
|
||||
// configuring lazy loading
|
||||
$this->_em->getConfiguration()->setAllowPartialObjects(false);
|
||||
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
|
||||
$metadata->getAssociationMapping('shipping')->fetchMode = AssociationMapping::FETCH_LAZY;
|
||||
|
||||
$stmt = new HydratorMockStatement($resultSet);
|
||||
$hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em);
|
||||
|
||||
$result = $hydrator->hydrateAll($stmt, $rsm);
|
||||
|
||||
$this->assertEquals(1, count($result));
|
||||
$this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\ECommerce\ECommerceProduct);
|
||||
}
|
||||
|
||||
/**
|
||||
* select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
|
||||
* join u.phonenumbers p
|
||||
@ -679,4 +725,4 @@ class ObjectHydratorTest extends HydrationTestCase
|
||||
++$rowNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
|
||||
public function testGetMetadataForSingleClass()
|
||||
{
|
||||
$driverMock = new DriverMock();
|
||||
@ -38,6 +37,11 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
$cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
|
||||
// and a mapped association
|
||||
$cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'Other', 'mappedBy' => 'this'));
|
||||
// and an association on the owning side
|
||||
$joinColumns = array(
|
||||
array('name' => 'other_id', 'referencedColumnName' => 'id')
|
||||
);
|
||||
$cm1->mapOneToOne(array('fieldName' => 'association', 'targetEntity' => 'Other', 'joinColumns' => $joinColumns));
|
||||
// and an id generator type
|
||||
$cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
|
||||
|
||||
@ -49,13 +53,14 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertEquals(array(), $cm1->parentClasses);
|
||||
$this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm1->inheritanceType);
|
||||
$this->assertTrue($cm1->hasField('name'));
|
||||
$this->assertEquals(1, count($cm1->associationMappings));
|
||||
$this->assertEquals(2, count($cm1->associationMappings));
|
||||
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $cm1->generatorType);
|
||||
|
||||
// Go
|
||||
$cm1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1');
|
||||
|
||||
$this->assertEquals(array(), $cm1->parentClasses);
|
||||
$this->assertEquals(array('other_id'), $cm1->joinColumnNames);
|
||||
$this->assertTrue($cm1->hasField('name'));
|
||||
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $cm1->generatorType);
|
||||
}
|
||||
@ -93,4 +98,5 @@ class TestEntity1
|
||||
private $id;
|
||||
private $name;
|
||||
private $other;
|
||||
}
|
||||
private $association;
|
||||
}
|
||||
|
@ -62,11 +62,22 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
public function testAllowsIdempotentCreationOfReferenceProxyClass()
|
||||
{
|
||||
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||
$theSameProxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||
$originalClassName = 'Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||
$proxyClass = $this->_generator->generateReferenceProxyClass($originalClassName);
|
||||
$theSameProxyClass = $this->_generator->generateReferenceProxyClass($originalClassName);
|
||||
$this->assertEquals($proxyClass, $theSameProxyClass);
|
||||
}
|
||||
|
||||
public function testRegeneratesMetadataAfterIdempotentCreation()
|
||||
{
|
||||
$originalClassName = 'Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||
$metadataFactory = $this->_emMock->getMetadataFactory();
|
||||
$proxyClass = $this->_generator->generateReferenceProxyClass($originalClassName);
|
||||
$metadataFactory->setMetadataFor($proxyClass, null);
|
||||
$theSameProxyClass = $this->_generator->generateReferenceProxyClass($originalClassName);
|
||||
$this->assertNotNull($metadataFactory->getMetadataFor($theSameProxyClass));
|
||||
}
|
||||
|
||||
public function testReferenceProxyRequiresPersisterInTheConstructor()
|
||||
{
|
||||
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||
@ -140,22 +151,23 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertNotEquals($referenceProxyClass, $associationProxyClass);
|
||||
}
|
||||
|
||||
public function testAssociationProxyRequiresEntityManagerAndAssociationAndOwnerInTheConstructor()
|
||||
public function testAssociationProxyRequiresEntityManagerAssociationOwnerAndForeignKeysInTheConstructor()
|
||||
{
|
||||
$proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||
$product = new ECommerceProduct;
|
||||
$proxy = new $proxyClass($this->_emMock, $this->_getAssociationMock(), $product);
|
||||
$proxy = new $proxyClass($this->_emMock, $this->_getAssociationMock(), $product, array());
|
||||
}
|
||||
|
||||
public function testAssociationProxyDelegatesLoadingToTheAssociation()
|
||||
{
|
||||
$proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||
$product = new ECommerceProduct;
|
||||
$foreignKeys = array('customer_id' => 42);
|
||||
$assoc = $this->_getAssociationMock();
|
||||
$proxy = new $proxyClass($this->_emMock, $assoc, $product);
|
||||
$proxy = new $proxyClass($this->_emMock, $assoc, $product, $foreignKeys);
|
||||
$assoc->expects($this->any())
|
||||
->method('load')
|
||||
->with($product, $this->isInstanceOf($proxyClass), $this->isInstanceOf('Doctrine\Tests\Mocks\EntityManagerMock'));
|
||||
->with($product, $this->isInstanceOf($proxyClass), $this->isInstanceOf('Doctrine\Tests\Mocks\EntityManagerMock'), $foreignKeys);
|
||||
$proxy->getDescription();
|
||||
}
|
||||
|
||||
@ -163,7 +175,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
$proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
|
||||
$assoc = $this->_getAssociationMock();
|
||||
$proxy = new $proxyClass($this->_emMock, $assoc, null);
|
||||
$proxy = new $proxyClass($this->_emMock, $assoc, null, array());
|
||||
$assoc->expects($this->once())
|
||||
->method('load');
|
||||
|
||||
@ -171,7 +183,6 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
$proxy->getDescription();
|
||||
}
|
||||
|
||||
|
||||
protected function _getAssociationMock()
|
||||
{
|
||||
$assoc = $this->getMock('Doctrine\ORM\Mapping\AssociationMapping', array('load'), array(), '', false, false, false);
|
||||
|
@ -36,7 +36,10 @@ class OrmTestCase extends DoctrineTestCase
|
||||
'password' => 'wayne'
|
||||
);
|
||||
}
|
||||
return \Doctrine\ORM\EntityManager::create($conn, $config, $eventManager);
|
||||
if (is_array($conn)) {
|
||||
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager);
|
||||
}
|
||||
return \Doctrine\Tests\Mocks\EntityManagerMock::create($conn, $config, $eventManager);
|
||||
}
|
||||
|
||||
private static function getSharedMetadataCacheImpl()
|
||||
|
Loading…
x
Reference in New Issue
Block a user