Commit 0c623fdb authored by piccoloprincipe's avatar piccoloprincipe

[2.0] Accomodate joincolumn names in the metadata, in the selection and the...

[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)
parent 4d146d32
...@@ -572,7 +572,7 @@ class EntityManager ...@@ -572,7 +572,7 @@ class EntityManager
* *
* @return ProxyFactory * @return ProxyFactory
*/ */
public function getProxyGenerator() public function getProxyFactory()
{ {
return $this->_proxyFactory; return $this->_proxyFactory;
} }
......
...@@ -38,6 +38,8 @@ use Doctrine\Common\DoctrineException; ...@@ -38,6 +38,8 @@ use Doctrine\Common\DoctrineException;
*/ */
abstract class AbstractHydrator abstract class AbstractHydrator
{ {
const TYPES_JOINCOLUMN = 'join_column';
/** The ResultSetMapping. */ /** The ResultSetMapping. */
protected $_rsm; protected $_rsm;
...@@ -184,12 +186,16 @@ abstract class AbstractHydrator ...@@ -184,12 +186,16 @@ abstract class AbstractHydrator
} else if (isset($this->_rsm->fieldMappings[$key])) { } else if (isset($this->_rsm->fieldMappings[$key])) {
$classMetadata = $this->_em->getClassMetadata($this->_rsm->getOwningClass($key)); $classMetadata = $this->_em->getClassMetadata($this->_rsm->getOwningClass($key));
$fieldName = $this->_rsm->fieldMappings[$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); $classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
} }
$cache[$key]['fieldName'] = $fieldName; $cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false; $cache[$key]['isScalar'] = false;
if (isset($classMetadata->fieldMappings[$fieldName])) {
$cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']); $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']);
} else {
$cache[$key]['type'] = self::TYPES_JOINCOLUMN;
}
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key];
} else { } else {
...@@ -217,7 +223,11 @@ abstract class AbstractHydrator ...@@ -217,7 +223,11 @@ abstract class AbstractHydrator
$id[$dqlAlias] .= '|' . $value; $id[$dqlAlias] .= '|' . $value;
} }
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); $rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
}
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) { if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
$nonemptyComponents[$dqlAlias] = true; $nonemptyComponents[$dqlAlias] = true;
......
...@@ -236,6 +236,13 @@ class ObjectHydrator extends AbstractHydrator ...@@ -236,6 +236,13 @@ class ObjectHydrator extends AbstractHydrator
} }
$entity = $this->_uow->createEntity($className, $data); $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. // Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! $this->_allowPartialObjects) { if ( ! $this->_allowPartialObjects) {
foreach ($this->_ce[$className]->associationMappings as $field => $assoc) { foreach ($this->_ce[$className]->associationMappings as $field => $assoc) {
...@@ -243,7 +250,7 @@ class ObjectHydrator extends AbstractHydrator ...@@ -243,7 +250,7 @@ class ObjectHydrator extends AbstractHydrator
if ($assoc->isOneToOne()) { if ($assoc->isOneToOne()) {
if ($assoc->isLazilyFetched()) { if ($assoc->isLazilyFetched()) {
// Inject proxy // 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); $this->_ce[$className]->reflFields[$field]->setValue($entity, $proxy);
} else { } else {
//TODO: Schedule for eager fetching //TODO: Schedule for eager fetching
......
...@@ -379,12 +379,13 @@ abstract class AssociationMapping ...@@ -379,12 +379,13 @@ abstract class AssociationMapping
/** /**
* Loads data in $targetEntity domain object using this association. * 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. * using $em.
* *
* @param object $owningEntity * @param object $sourceEntity
* @param object $targetEntity * @param object $targetEntity
* @param EntityManager $em * @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 ...@@ -254,6 +254,12 @@ final class ClassMetadata
*/ */
public $columnNames = array(); 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. * Whether to automatically OUTER JOIN subtypes when a basetype is queried.
* *
...@@ -493,6 +499,11 @@ final class ClassMetadata ...@@ -493,6 +499,11 @@ final class ClassMetadata
$this->reflFields[$propName] = $property; $this->reflFields[$propName] = $property;
} }
public function addJoinColumn($name)
{
$this->joinColumnNames[] = $name;
}
/** /**
* Gets a ReflectionProperty for a specific field of the mapped class. * Gets a ReflectionProperty for a specific field of the mapped class.
* *
...@@ -1315,7 +1326,7 @@ final class ClassMetadata ...@@ -1315,7 +1326,7 @@ final class ClassMetadata
/** /**
* Makes some automatic additions to the association mapping to make the life * 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 * @param array $mapping
* @todo Pass param by ref? * @todo Pass param by ref?
...@@ -1326,6 +1337,11 @@ final class ClassMetadata ...@@ -1326,6 +1337,11 @@ final class ClassMetadata
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false) { if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false) {
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity']; $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
} }
if (isset($mapping['joinColumns'])) {
foreach ($mapping['joinColumns'] as $columns) {
$this->addJoinColumn($columns['name']);
}
}
return $mapping; return $mapping;
} }
......
...@@ -138,7 +138,7 @@ class ManyToManyMapping extends AssociationMapping ...@@ -138,7 +138,7 @@ class ManyToManyMapping extends AssociationMapping
return $this->targetKeyColumns; return $this->targetKeyColumns;
} }
public function load($owningEntity, $targetEntity, $em) public function load($owningEntity, $targetEntity, $em, array $joinColumnValues)
{ {
throw new Exception('Not yet implemented.'); throw new Exception('Not yet implemented.');
} }
......
...@@ -97,7 +97,7 @@ class OneToManyMapping extends AssociationMapping ...@@ -97,7 +97,7 @@ class OneToManyMapping extends AssociationMapping
return true; return true;
} }
public function load($owningEntity, $targetEntity, $em) public function load($owningEntity, $targetEntity, $em, array $joinColumnValues)
{ {
throw new Exception('Not yet implemented.'); throw new Exception('Not yet implemented.');
} }
......
...@@ -35,6 +35,7 @@ namespace Doctrine\ORM\Mapping; ...@@ -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 OneToOneMapping extends AssociationMapping class OneToOneMapping extends AssociationMapping
{ {
...@@ -152,11 +153,13 @@ class OneToOneMapping extends AssociationMapping ...@@ -152,11 +153,13 @@ class OneToOneMapping extends AssociationMapping
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @param object $owningEntity * @param object $sourceEntity the entity source of this association
* @param object $targetEntity * @param object $targetEntity the entity to load data in
* @param EntityManager $em * @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); $sourceClass = $em->getClassMetadata($this->sourceEntityName);
$targetClass = $em->getClassMetadata($this->targetEntityName); $targetClass = $em->getClassMetadata($this->targetEntityName);
...@@ -165,24 +168,38 @@ class OneToOneMapping extends AssociationMapping ...@@ -165,24 +168,38 @@ class OneToOneMapping extends AssociationMapping
if ($this->isOwningSide) { if ($this->isOwningSide) {
foreach ($this->sourceToTargetKeyColumns as $sourceKeyColumn => $targetKeyColumn) { foreach ($this->sourceToTargetKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
$conditions[$targetKeyColumn] = $sourceClass->getReflectionProperty( // getting customer_id
$sourceClass->getFieldName($sourceKeyColumn))->getValue($owningEntity); 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( $targetClass->setFieldValue(
$targetEntity, $targetEntity,
$targetClass->inverseMappings[$this->_sourceFieldName]->getSourceFieldName(), $targetClass->inverseMappings[$this->sourceFieldName]->getSourceFieldName(),
$owningEntity); $sourceEntity);
} }
} else { } else {
$owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName); $owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName);
foreach ($owningAssoc->getTargetToSourceKeyColumns() as $targetKeyColumn => $sourceKeyColumn) { foreach ($owningAssoc->getTargetToSourceKeyColumns() as $targetKeyColumn => $sourceKeyColumn) {
$conditions[$sourceKeyColumn] = $sourceClass->getReflectionProperty( // getting id
$sourceClass->getFieldName($targetKeyColumn))->getValue($owningEntity); 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); $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 ...@@ -411,10 +411,15 @@ 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(); $data = array();
$joinColumnValues = array();
foreach ($stmt->fetch(Connection::FETCH_ASSOC) as $column => $value) { foreach ($stmt->fetch(Connection::FETCH_ASSOC) as $column => $value) {
if (isset($this->_class->fieldNames[$column])) {
$fieldName = $this->_class->fieldNames[$column]; $fieldName = $this->_class->fieldNames[$column];
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName)) $data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
->convertToPHPValue($value, $this->_platform); ->convertToPHPValue($value, $this->_platform);
} else {
$joinColumnValues[$column] = $value;
}
} }
$stmt->closeCursor(); $stmt->closeCursor();
...@@ -440,9 +445,9 @@ class StandardEntityPersister ...@@ -440,9 +445,9 @@ class StandardEntityPersister
// empty collections respectively. // empty collections respectively.
foreach ($this->_class->associationMappings as $field => $assoc) { foreach ($this->_class->associationMappings as $field => $assoc) {
if ($assoc->isOneToOne()) { if ($assoc->isOneToOne()) {
if ($assoc->isLazilyFetched) { if ($assoc->isLazilyFetched()) {
// Inject proxy // Inject proxy
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc); $proxy = $this->_em->getProxyFactory()->getAssociationProxy($entity, $assoc, $joinColumnValues);
$this->_class->reflFields[$field]->setValue($entity, $proxy); $this->_class->reflFields[$field]->setValue($entity, $proxy);
} else { } else {
//TODO: Eager fetch //TODO: Eager fetch
...@@ -472,11 +477,23 @@ class StandardEntityPersister ...@@ -472,11 +477,23 @@ class StandardEntityPersister
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_conn->quoteIdentifier($column); $columnList .= $this->_conn->quoteIdentifier($column);
} }
if (!$this->_em->getConfiguration()->getAllowPartialObjects()) {
foreach ($this->_class->joinColumnNames as $column) {
$columnList .= ', ' . $this->_conn->quoteIdentifier($column);
}
}
$conditionSql = ''; $conditionSql = '';
foreach ($criteria as $field => $value) { foreach ($criteria as $field => $value) {
if ($conditionSql != '') $conditionSql .= ' AND '; 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() return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName()
......
...@@ -88,12 +88,13 @@ class ProxyClassGenerator ...@@ -88,12 +88,13 @@ class ProxyClassGenerator
$class = $this->_em->getClassMetadata($originalClassName); $class = $this->_em->getClassMetadata($originalClassName);
$proxyFullyQualifiedClassName = self::$_ns . $proxyClassName; $proxyFullyQualifiedClassName = self::$_ns . $proxyClassName;
$class = $this->_em->getClassMetadata($originalClassName);
$this->_em->getMetadataFactory()->setMetadataFor($proxyFullyQualifiedClassName, $class);
if (class_exists($proxyFullyQualifiedClassName, false)) { if (class_exists($proxyFullyQualifiedClassName, false)) {
return $proxyFullyQualifiedClassName; return $proxyFullyQualifiedClassName;
} }
$class = $this->_em->getClassMetadata($originalClassName);
$this->_em->getMetadataFactory()->setMetadataFor($proxyFullyQualifiedClassName, $class);
$fileName = $this->_cacheDir . $proxyClassName . '.g.php'; $fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if (file_exists($fileName)) { if (file_exists($fileName)) {
...@@ -228,18 +229,21 @@ namespace Doctrine\Generated\Proxies { ...@@ -228,18 +229,21 @@ namespace Doctrine\Generated\Proxies {
private $_em; private $_em;
private $_assoc; private $_assoc;
private $_owner; private $_owner;
private $_joinColumnValues;
private $_loaded = false; private $_loaded = false;
public function __construct($em, $assoc, $owner) { public function __construct($em, $assoc, $owner, array $joinColumnValues) {
$this->_em = $em; $this->_em = $em;
$this->_assoc = $assoc; $this->_assoc = $assoc;
$this->_owner = $owner; $this->_owner = $owner;
$this->_joinColumnValues = $joinColumnValues;
} }
private function _load() { private function _load() {
if ( ! $this->_loaded) { 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->_em);
unset($this->_owner); unset($this->_owner);
unset($this->_assoc); unset($this->_assoc);
unset($this->_joinColumnValues);
$this->_loaded = true; $this->_loaded = true;
} }
} }
......
...@@ -66,9 +66,9 @@ class ProxyFactory ...@@ -66,9 +66,9 @@ class ProxyFactory
/** /**
* Gets an association proxy instance. * 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()); $proxyClassName = $this->_generator->generateAssociationProxyClass($assoc->getTargetEntityName());
return new $proxyClassName($this->_em, $assoc, $owner); return new $proxyClassName($this->_em, $assoc, $owner, $joinColumnValues);
} }
} }
...@@ -470,6 +470,13 @@ class SqlWalker implements TreeWalker ...@@ -470,6 +470,13 @@ class SqlWalker implements TreeWalker
$sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) . ' AS ' . $columnAlias; $sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) . ' AS ' . $columnAlias;
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); $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);
}
}
} }
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
*/ */
namespace Doctrine\Tests\Mocks; namespace Doctrine\Tests\Mocks;
use Doctrine\ORM\Proxy\ProxyFactory;
/** /**
* Special EntityManager mock used for testing purposes. * Special EntityManager mock used for testing purposes.
...@@ -27,6 +28,7 @@ namespace Doctrine\Tests\Mocks; ...@@ -27,6 +28,7 @@ namespace Doctrine\Tests\Mocks;
class EntityManagerMock extends \Doctrine\ORM\EntityManager class EntityManagerMock extends \Doctrine\ORM\EntityManager
{ {
private $_uowMock; private $_uowMock;
private $_proxyFactoryMock;
private $_idGenerators = array(); private $_idGenerators = array();
/** /**
...@@ -49,6 +51,16 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager ...@@ -49,6 +51,16 @@ class EntityManagerMock extends \Doctrine\ORM\EntityManager
$this->_uowMock = $uow; $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. * Mock factory method to create an EntityManager.
* *
......
...@@ -24,7 +24,6 @@ class AllTests ...@@ -24,7 +24,6 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest'); $suite->addTestSuite('Doctrine\Tests\ORM\CommitOrderCalculatorTest');
$suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest'); $suite->addTestSuite('Doctrine\Tests\ORM\QueryBuilderTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Proxy\ProxyClassGeneratorTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Proxy\ProxyClassGeneratorTest');
$suite->addTest(Query\AllTests::suite()); $suite->addTest(Query\AllTests::suite());
$suite->addTest(Hydration\AllTests::suite()); $suite->addTest(Hydration\AllTests::suite());
$suite->addTest(Entity\AllTests::suite()); $suite->addTest(Entity\AllTests::suite());
......
...@@ -36,6 +36,7 @@ class AllTests ...@@ -36,6 +36,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManySelfReferentialAssociationTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManySelfReferentialAssociationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ReferenceProxyTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\ReferenceProxyTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\LifecycleCallbackTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\LifecycleCallbackTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\StandardEntityPersisterTest');
$suite->addTest(Locking\AllTests::suite()); $suite->addTest(Locking\AllTests::suite());
......
...@@ -65,8 +65,21 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional ...@@ -65,8 +65,21 @@ class OneToOneBidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctional
$this->assertEquals('paypal', $customer->getCart()->getPayment()); $this->assertEquals('paypal', $customer->getCart()->getPayment());
} }
public function testLazyLoad() { public function testLazyLoadsObjectsOnTheOwningSide() {
$this->markTestSkipped(); $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->_createFixture();
$this->_em->getConfiguration()->setAllowPartialObjects(false); $this->_em->getConfiguration()->setAllowPartialObjects(false);
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer'); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCustomer');
......
...@@ -58,8 +58,7 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona ...@@ -58,8 +58,7 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
$this->assertEquals(1, $product->getShipping()->getDays()); $this->assertEquals(1, $product->getShipping()->getDays());
} }
public function testLazyLoad() { public function testLazyLoadsObjects() {
$this->markTestSkipped();
$this->_createFixture(); $this->_createFixture();
$this->_em->getConfiguration()->setAllowPartialObjects(false); $this->_em->getConfiguration()->setAllowPartialObjects(false);
$metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); $metadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct');
...@@ -73,6 +72,17 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona ...@@ -73,6 +72,17 @@ class OneToOneUnidirectionalAssociationTest extends \Doctrine\Tests\OrmFunctiona
$this->assertEquals(1, $product->getShipping()->getDays()); $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() protected function _createFixture()
{ {
$product = new ECommerceProduct; $product = new ECommerceProduct;
......
<?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; ...@@ -4,6 +4,8 @@ namespace Doctrine\Tests\ORM\Hydration;
use Doctrine\Tests\Mocks\HydratorMockStatement; use Doctrine\Tests\Mocks\HydratorMockStatement;
use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Mapping\AssociationMapping;
require_once __DIR__ . '/../../TestInit.php'; require_once __DIR__ . '/../../TestInit.php';
...@@ -98,6 +100,50 @@ class ObjectHydratorTest extends HydrationTestCase ...@@ -98,6 +100,50 @@ class ObjectHydratorTest extends HydrationTestCase
$this->assertEquals('Cool things II.', $result[3]->topic); $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 * select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u
* join u.phonenumbers p * join u.phonenumbers p
......
...@@ -14,7 +14,6 @@ require_once __DIR__ . '/../../TestInit.php'; ...@@ -14,7 +14,6 @@ require_once __DIR__ . '/../../TestInit.php';
class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
{ {
public function testGetMetadataForSingleClass() public function testGetMetadataForSingleClass()
{ {
$driverMock = new DriverMock(); $driverMock = new DriverMock();
...@@ -38,6 +37,11 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase ...@@ -38,6 +37,11 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true)); $cm1->mapField(array('fieldName' => 'id', 'type' => 'integer', 'id' => true));
// and a mapped association // and a mapped association
$cm1->mapOneToOne(array('fieldName' => 'other', 'targetEntity' => 'Other', 'mappedBy' => 'this')); $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 // and an id generator type
$cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO); $cm1->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
...@@ -49,13 +53,14 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase ...@@ -49,13 +53,14 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals(array(), $cm1->parentClasses); $this->assertEquals(array(), $cm1->parentClasses);
$this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm1->inheritanceType); $this->assertEquals(ClassMetadata::INHERITANCE_TYPE_NONE, $cm1->inheritanceType);
$this->assertTrue($cm1->hasField('name')); $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); $this->assertEquals(ClassMetadata::GENERATOR_TYPE_AUTO, $cm1->generatorType);
// Go // Go
$cm1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1'); $cm1 = $cmf->getMetadataFor('Doctrine\Tests\ORM\Mapping\TestEntity1');
$this->assertEquals(array(), $cm1->parentClasses); $this->assertEquals(array(), $cm1->parentClasses);
$this->assertEquals(array('other_id'), $cm1->joinColumnNames);
$this->assertTrue($cm1->hasField('name')); $this->assertTrue($cm1->hasField('name'));
$this->assertEquals(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $cm1->generatorType); $this->assertEquals(ClassMetadata::GENERATOR_TYPE_SEQUENCE, $cm1->generatorType);
} }
...@@ -93,4 +98,5 @@ class TestEntity1 ...@@ -93,4 +98,5 @@ class TestEntity1
private $id; private $id;
private $name; private $name;
private $other; private $other;
private $association;
} }
...@@ -62,11 +62,22 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase ...@@ -62,11 +62,22 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
public function testAllowsIdempotentCreationOfReferenceProxyClass() public function testAllowsIdempotentCreationOfReferenceProxyClass()
{ {
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $originalClassName = 'Doctrine\Tests\Models\ECommerce\ECommerceFeature';
$theSameProxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $proxyClass = $this->_generator->generateReferenceProxyClass($originalClassName);
$theSameProxyClass = $this->_generator->generateReferenceProxyClass($originalClassName);
$this->assertEquals($proxyClass, $theSameProxyClass); $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() public function testReferenceProxyRequiresPersisterInTheConstructor()
{ {
$proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $proxyClass = $this->_generator->generateReferenceProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
...@@ -140,22 +151,23 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase ...@@ -140,22 +151,23 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
$this->assertNotEquals($referenceProxyClass, $associationProxyClass); $this->assertNotEquals($referenceProxyClass, $associationProxyClass);
} }
public function testAssociationProxyRequiresEntityManagerAndAssociationAndOwnerInTheConstructor() public function testAssociationProxyRequiresEntityManagerAssociationOwnerAndForeignKeysInTheConstructor()
{ {
$proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
$product = new ECommerceProduct; $product = new ECommerceProduct;
$proxy = new $proxyClass($this->_emMock, $this->_getAssociationMock(), $product); $proxy = new $proxyClass($this->_emMock, $this->_getAssociationMock(), $product, array());
} }
public function testAssociationProxyDelegatesLoadingToTheAssociation() public function testAssociationProxyDelegatesLoadingToTheAssociation()
{ {
$proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
$product = new ECommerceProduct; $product = new ECommerceProduct;
$foreignKeys = array('customer_id' => 42);
$assoc = $this->_getAssociationMock(); $assoc = $this->_getAssociationMock();
$proxy = new $proxyClass($this->_emMock, $assoc, $product); $proxy = new $proxyClass($this->_emMock, $assoc, $product, $foreignKeys);
$assoc->expects($this->any()) $assoc->expects($this->any())
->method('load') ->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(); $proxy->getDescription();
} }
...@@ -163,7 +175,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase ...@@ -163,7 +175,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
{ {
$proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature'); $proxyClass = $this->_generator->generateAssociationProxyClass('Doctrine\Tests\Models\ECommerce\ECommerceFeature');
$assoc = $this->_getAssociationMock(); $assoc = $this->_getAssociationMock();
$proxy = new $proxyClass($this->_emMock, $assoc, null); $proxy = new $proxyClass($this->_emMock, $assoc, null, array());
$assoc->expects($this->once()) $assoc->expects($this->once())
->method('load'); ->method('load');
...@@ -171,7 +183,6 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase ...@@ -171,7 +183,6 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
$proxy->getDescription(); $proxy->getDescription();
} }
protected function _getAssociationMock() protected function _getAssociationMock()
{ {
$assoc = $this->getMock('Doctrine\ORM\Mapping\AssociationMapping', array('load'), array(), '', false, false, false); $assoc = $this->getMock('Doctrine\ORM\Mapping\AssociationMapping', array('load'), array(), '', false, false, false);
......
...@@ -36,7 +36,10 @@ class OrmTestCase extends DoctrineTestCase ...@@ -36,7 +36,10 @@ class OrmTestCase extends DoctrineTestCase
'password' => 'wayne' '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() private static function getSharedMetadataCacheImpl()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment