1
0
mirror of synced 2025-01-22 00:01:40 +03:00

[2.0] Started to implement proxy object handling. Introduced a configuration switch for partial objects (allowPartialObjects) that defaults to TRUE. Setting to FALSE enables proxying and lazy-loading.

This commit is contained in:
romanb 2009-05-13 15:19:27 +00:00
parent e0488ff8fc
commit ecd30bc242
38 changed files with 612 additions and 296 deletions

View File

@ -100,7 +100,7 @@ class ClassLoader
$class .= $this->_basePaths[$prefix] . DIRECTORY_SEPARATOR;
}
$class .= str_replace(array($this->_namespaceSeparator, '_'), DIRECTORY_SEPARATOR, $className)
$class .= str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $className)
. $this->_fileExtension;
if ($this->_checkFileExists) {

View File

@ -7,7 +7,7 @@ namespace Doctrine\DBAL\Types;
*
* @since 2.0
*/
class VarcharType extends Type
class StringType extends Type
{
/** @override */
public function getSqlDeclaration(array $fieldDeclaration, \Doctrine\DBAL\Platforms\AbstractPlatform $platform)
@ -24,6 +24,6 @@ class VarcharType extends Type
/** @override */
public function getName()
{
return 'Varchar';
return 'string';
}
}

View File

@ -13,7 +13,7 @@ abstract class Type
'int' => 'Doctrine\DBAL\Types\IntegerType',
'smallint' => 'Doctrine\DBAL\Types\SmallIntType',
'bigint' => 'Doctrine\DBAL\Types\BigIntType',
'varchar' => 'Doctrine\DBAL\Types\VarcharType',
'string' => 'Doctrine\DBAL\Types\StringType',
'text' => 'Doctrine\DBAL\Types\TextType',
'datetime' => 'Doctrine\DBAL\Types\DateTimeType',
'decimal' => 'Doctrine\DBAL\Types\DecimalType',

View File

@ -46,15 +46,52 @@ class Configuration extends \Doctrine\DBAL\Configuration
'metadataCacheImpl' => null,
'metadataDriverImpl' => new AnnotationDriver(),
'dqlClassAliasMap' => array(),
'cacheDir' => null
'cacheDir' => null,
'allowPartialObjects' => true
));
}
/**
* Gets a boolean flag that specifies whether partial objects are allowed.
*
* If partial objects are allowed, Doctrine will never use proxies or lazy loading
* and you always only get what you explicitly query for.
*
* @return boolean Whether partial objects are allowed.
*/
public function getAllowPartialObjects()
{
return $this->_attributes['allowPartialObjects'];
}
/**
* Sets a boolean flag that specifies whether partial objects are allowed.
*
* If partial objects are allowed, Doctrine will never use proxies or lazy loading
* and you always only get what you explicitly query for.
*
* @param boolean $allowed Whether partial objects are allowed.
*/
public function setAllowPartialObjects($allowed)
{
$this->_attributes['allowPartialObjects'] = $allowed;
}
/**
* Sets the directory where Doctrine writes any necessary cache files.
*
* @param string $dir
*/
public function setCacheDir($dir)
{
$this->_attributes['cacheDir'] = $dir;
}
/**
* Gets the directory where Doctrine writes any necessary cache files.
*
* @return string
*/
public function getCacheDir()
{
return $this->_attributes['cacheDir'];
@ -70,41 +107,81 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['dqlClassAliasMap'] = $map;
}
/**
* Sets the cache driver implementation that is used for metadata caching.
*
* @param object $driverImpl
*/
public function setMetadataDriverImpl($driverImpl)
{
$this->_attributes['metadataDriverImpl'] = $driverImpl;
}
/**
* Gets the cache driver implementation that is used for the mapping metadata.
*
* @return object
*/
public function getMetadataDriverImpl()
{
return $this->_attributes['metadataDriverImpl'];
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return object
*/
public function getResultCacheImpl()
{
return $this->_attributes['resultCacheImpl'];
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param object $cacheImpl
*/
public function setResultCacheImpl($cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for the query cache (SQL cache).
*
* @return object
*/
public function getQueryCacheImpl()
{
return $this->_attributes['queryCacheImpl'];
}
/**
* Sets the cache driver implementation that is used for the query cache (SQL cache).
*
* @param object $cacheImpl
*/
public function setQueryCacheImpl($cacheImpl)
{
$this->_attributes['queryCacheImpl'] = $cacheImpl;
}
/**
* Gets the cache driver implementation that is used for metadata caching.
*
* @return object
*/
public function getMetadataCacheImpl()
{
return $this->_attributes['metadataCacheImpl'];
}
/**
* Sets the cache driver implementation that is used for metadata caching.
*
* @param object $cacheImpl
*/
public function setMetadataCacheImpl($cacheImpl)
{
$this->_attributes['metadataCacheImpl'] = $cacheImpl;

View File

@ -26,9 +26,11 @@ namespace Doctrine\ORM;
* For that purpose he generates proxy class files on the fly as needed.
*
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class DynamicProxyGenerator
{
private static $_ns = 'Doctrine\Generated\Proxies\\';
private $_cacheDir = '/Users/robo/dev/php/tmp/gen/';
private $_em;
@ -36,78 +38,66 @@ class DynamicProxyGenerator
{
$this->_em = $em;
if ($cacheDir === null) {
$cacheDir = sys_get_tmp_dir();
$cacheDir = sys_get_temp_dir();
}
$this->_cacheDir = $cacheDir;
}
/**
*
* Gets a reference proxy instance.
*
* @param <type> $className
* @param <type> $identifier
* @return <type>
* @param string $className
* @param mixed $identifier
* @return object
*/
public function getProxy($className, $identifier)
public function getReferenceProxy($className, $identifier)
{
$proxyClassName = str_replace('\\', '_', $className) . 'Proxy';
$class = $this->_em->getClassMetadata($className);
$proxyClassName = str_replace('\\', '_', $className) . 'RProxy';
if ( ! class_exists($proxyClassName, false)) {
$this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $class);
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) {
$this->_generateProxyClass($className, $identifier, $proxyClassName, $fileName);
$this->_generateReferenceProxyClass($className, $identifier, $proxyClassName, $fileName);
}
require $fileName;
}
$proxyClassName = '\Doctrine\Generated\Proxies\\' . $proxyClassName;
return new $proxyClassName($this->_em, $this->_em->getClassMetadata($className), $identifier);
$proxyClassName = '\\' . self::$_ns . $proxyClassName;
return new $proxyClassName($this->_em, $class, $identifier);
}
/**
* Gets an association proxy instance.
*/
public function getAssociationProxy($owner, \Doctrine\ORM\Mapping\AssociationMapping $assoc)
{
$proxyClassName = str_replace('\\', '_', $assoc->getTargetEntityName()) . 'AProxy';
if ( ! class_exists($proxyClassName, false)) {
//die("$proxyClassName!");
$this->_em->getMetadataFactory()->setMetadataFor(self::$_ns . $proxyClassName, $this->_em->getClassMetadata($assoc->getTargetEntityName()));
$fileName = $this->_cacheDir . $proxyClassName . '.g.php';
if ( ! file_exists($fileName)) {
$this->_generateAssociationProxyClass($assoc->getTargetEntityName(), $proxyClassName, $fileName);
}
require $fileName;
}
$proxyClassName = '\\' . self::$_ns . $proxyClassName;
return new $proxyClassName($this->_em, $assoc, $owner);
}
/**
* Generates a proxy class.
*
* @param <type> $className
* @param <type> $id
* @param <type> $proxyClassName
* @param <type> $fileName
* @param string $className
* @param mixed $id
* @param string $proxyClassName
* @param string $fileName
*/
private function _generateProxyClass($className, $id, $proxyClassName, $fileName)
private function _generateReferenceProxyClass($className, $id, $proxyClassName, $fileName)
{
$class = $this->_em->getClassMetadata($className);
$file = self::$_proxyClassTemplate;
if (is_array($id) && count($id) > 1) {
// it's a composite key. keys = field names, values = values.
$values = array_values($id);
$keys = array_keys($id);
} else {
$values = is_array($id) ? array_values($id) : array($id);
$keys = $class->getIdentifierFieldNames();
}
$paramIndex = 1;
$identifierCondition = 'prx.' . $keys[0] . ' = ?' . $paramIndex++;
for ($i=1, $c=count($keys); $i < $c; ++$i) {
$identifierCondition .= ' AND prx.' . $keys[$i] . ' = ?' . $paramIndex++;
}
$parameters = 'array(';
$first = true;
foreach ($values as $value) {
if ($first) {
$first = false;
} else {
$parameters = ', ';
}
$parameters .= "'" . $value . "'";
}
$parameters .= ')';
$hydrationSetters = '';
foreach ($class->getReflectionProperties() as $name => $prop) {
if ( ! $class->hasAssociation($name)) {
$hydrationSetters .= '$this->_class->setValue($this, \'' . $name . '\', $scalar[0][\'prx_' . $name . '\']);' . PHP_EOL;
}
}
$methods = '';
foreach ($class->getReflectionClass()->getMethods() as $method) {
if ($method->isPublic() && ! $method->isFinal()) {
@ -147,12 +137,11 @@ class DynamicProxyGenerator
}
$placeholders = array(
'<proxyClassName>', '<className>', '<identifierCondition>',
'<parameters>', '<hydrationSetters>', '<methods>', '<sleepImpl>'
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>'
);
$replacements = array(
$proxyClassName, $className, $identifierCondition, $parameters,
$hydrationSetters, $methods, $sleepImpl
$proxyClassName, $className, $methods, $sleepImpl
);
$file = str_replace($placeholders, $replacements, $file);
@ -160,6 +149,70 @@ class DynamicProxyGenerator
file_put_contents($fileName, $file);
}
/**
* Generates a proxy class.
*
* @param string $className
* @param mixed $id
* @param string $proxyClassName
* @param string $fileName
*/
private function _generateAssociationProxyClass($className, $proxyClassName, $fileName)
{
$class = $this->_em->getClassMetadata($className);
$file = self::$_assocProxyClassTemplate;
$methods = '';
foreach ($class->getReflectionClass()->getMethods() as $method) {
if ($method->isPublic() && ! $method->isFinal()) {
$methods .= PHP_EOL . 'public function ' . $method->getName() . '(';
$firstParam = true;
$parameterString = '';
foreach ($method->getParameters() as $param) {
if ($firstParam) {
$firstParam = false;
} else {
$parameterString .= ', ';
}
$parameterString .= '$' . $param->getName();
}
$methods .= $parameterString . ') {' . PHP_EOL;
$methods .= '$this->_load();' . PHP_EOL;
$methods .= 'return parent::' . $method->getName() . '(' . $parameterString . ');';
$methods .= '}' . PHP_EOL;
}
}
$sleepImpl = '';
if ($class->getReflectionClass()->hasMethod('__sleep')) {
$sleepImpl .= 'return parent::__sleep();';
} else {
$sleepImpl .= 'return array(';
$first = true;
foreach ($class->getReflectionProperties() as $name => $prop) {
if ($first) {
$first = false;
} else {
$sleepImpl .= ', ';
}
$sleepImpl .= "'" . $name . "'";
}
$sleepImpl .= ');';
}
$placeholders = array(
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>'
);
$replacements = array(
$proxyClassName, $className, $methods, $sleepImpl
);
$file = str_replace($placeholders, $replacements, $file);
file_put_contents($fileName, $file);
}
/** Proxy class code template */
private static $_proxyClassTemplate =
'<?php
@ -176,8 +229,7 @@ namespace Doctrine\Generated\Proxies {
}
private function _load() {
if ( ! $this->_loaded) {
$scalar = $this->_em->createQuery(\'select prx from <className> prx where <identifierCondition>\')->execute(<parameters>, \Doctrine\ORM\Query::HYDRATE_SCALAR);
<hydrationSetters>
$this->_em->getUnitOfWork()->getEntityPersister($this->_class->getClassName())->load($this->_identifier, $this);
unset($this->_em);
unset($this->_class);
$this->_loaded = true;
@ -194,4 +246,39 @@ namespace Doctrine\Generated\Proxies {
}
}
}';
private static $_assocProxyClassTemplate =
'<?php
/** This class was generated by the Doctrine ORM. DO NOT EDIT THIS FILE. */
namespace Doctrine\Generated\Proxies {
class <proxyClassName> extends \<className> {
private $_em;
private $_assoc;
private $_owner;
private $_loaded = false;
public function __construct($em, $assoc, $owner) {
$this->_em = $em;
$this->_assoc = $assoc;
$this->_owner = $owner;
}
private function _load() {
if ( ! $this->_loaded) {
$this->_assoc->load($this->_owner, $this, $this->_em);
unset($this->_em);
unset($this->_owner);
unset($this->_assoc);
$this->_loaded = true;
}
}
<methods>
public function __sleep() {
if (!$this->_loaded) {
throw new RuntimeException("Not fully loaded proxy can not be serialized.");
}
<sleepImpl>
}
}
}';
}

View File

@ -146,6 +146,7 @@ class EntityManager
$this->_conn->getDatabasePlatform());
$this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
$this->_unitOfWork = new UnitOfWork($this);
$this->_proxyGenerator = new DynamicProxyGenerator($this, '/Users/robo/dev/php/tmp/gen/');
}
/**
@ -401,9 +402,6 @@ class EntityManager
public function refresh($entity)
{
throw DoctrineException::notImplemented();
/*$this->_mergeData($entity, $this->getRepository(get_class($entity))->find(
$entity->identifier(), Query::HYDRATE_ARRAY),
true);*/
}
/**
@ -445,7 +443,7 @@ class EntityManager
* Gets the repository for an entity class.
*
* @param string $entityName The name of the Entity.
* @return Doctrine\ORM\EntityRepository The repository.
* @return EntityRepository The repository.
*/
public function getRepository($entityName)
{
@ -458,7 +456,7 @@ class EntityManager
if ($customRepositoryClassName !== null) {
$repository = new $customRepositoryClassName($entityName, $metadata);
} else {
$repository = new \Doctrine\ORM\EntityRepository($this, $metadata);
$repository = new EntityRepository($this, $metadata);
}
$this->_repositories[$entityName] = $repository;
@ -547,23 +545,17 @@ class EntityManager
default:
throw DoctrineException::updateMe("No hydrator found for hydration mode '$hydrationMode'.");
}
}/* else if ($this->_hydrators[$hydrationMode] instanceof Closure) {
$this->_hydrators[$hydrationMode] = $this->_hydrators[$hydrationMode]($this);
}*/
}
return $this->_hydrators[$hydrationMode];
}
/**
* Sets a hydrator for a hydration mode.
*
* @param mixed $hydrationMode
* @param object $hydrator Either a hydrator instance or a Closure that creates a
* hydrator instance.
*
*/
/*public function setHydrator($hydrationMode, $hydrator)
public function getProxyGenerator()
{
$this->_hydrators[$hydrationMode] = $hydrator;
}*/
return $this->_proxyGenerator;
}
/**
* Factory method to create EntityManager instances.

View File

@ -63,8 +63,6 @@ class EntityRepository
/**
* Clears the repository, causing all managed entities to become detached.
*
* @return void
*/
public function clear()
{
@ -80,35 +78,17 @@ class EntityRepository
*/
public function find($id, $hydrationMode = null)
{
if (is_array($id) && count($id) > 1) {
// it's a composite key. keys = field names, values = values.
$values = array_values($id);
$keys = array_keys($id);
} else {
$values = is_array($id) ? array_values($id) : array($id);
$keys = $this->_classMetadata->getIdentifier();
}
// Check identity map first
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_classMetadata->getRootClassName())) {
return $entity; // Hit!
}
$dql = 'select e from ' . $this->_classMetadata->getClassName() . ' e where ';
$conditionDql = '';
$paramIndex = 1;
foreach ($keys as $key) {
if ($conditionDql != '') $conditionDql .= ' and ';
$conditionDql .= 'e.' . $key . ' = ?' . $paramIndex++;
}
$dql .= $conditionDql;
$q = $this->_em->createQuery($dql);
foreach ($values as $index => $value) {
$q->setParameter($index, $value);
if ( ! is_array($id) || count($id) <= 1) {
$value = is_array($id) ? array_values($id) : array($id);
$id = array_combine($this->_classMetadata->getIdentifier(), $value);
}
return $q->getSingleResult($hydrationMode);
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
}
/**
@ -150,43 +130,6 @@ class EntityRepository
return $hydrationMode === Doctrine::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst();
}
/**
* findBySql
* finds records with given SQL where clause
* returns a collection of records
*
* @param string $dql DQL after WHERE clause
* @param array $params query parameters
* @param int $hydrationMode Query::HYDRATE_ARRAY or Query::HYDRATE_RECORD
* @return Doctrine_Collection
*
* @todo This actually takes DQL, not SQL, but it requires column names
* instead of field names. This should be fixed to use raw SQL instead.
*/
public function findBySql($dql, array $params = array(), $hydrationMode = null)
{
return $this->_createQuery()->where($dql)->execute($params, $hydrationMode);
}
/**
* findByDql
* finds records with given DQL where clause
* returns a collection of records
*
* @param string $dql DQL after WHERE clause
* @param array $params query parameters
* @param int $hydrationMode Query::HYDRATE_ARRAY or Query::HYDRATE_RECORD
* @return Doctrine_Collection
*/
public function findByDql($dql, array $params = array(), $hydrationMode = null)
{
$query = new Query($this->_em);
$component = $this->getComponentName();
$dql = 'FROM ' . $component . ' WHERE ' . $dql;
return $query->query($dql, $params, $hydrationMode);
}
/**
* Adds support for magic finders.
* findByColumnName, findByRelationAlias
@ -211,7 +154,7 @@ class EntityRepository
if (isset($by)) {
if ( ! isset($arguments[0])) {
throw \Doctrine\Common\DoctrineException::updateMe('You must specify the value to findBy.');
throw DoctrineException::updateMe('You must specify the value to findBy.');
}
$fieldName = Doctrine::tableize($by);
@ -222,11 +165,11 @@ class EntityRepository
} else if ($this->_classMetadata->hasRelation($by)) {
$relation = $this->_classMetadata->getRelation($by);
if ($relation['type'] === Doctrine_Relation::MANY) {
throw \Doctrine\Common\DoctrineException::updateMe('Cannot findBy many relationship.');
throw DoctrineException::updateMe('Cannot findBy many relationship.');
}
return $this->$method($relation['local'], $arguments[0], $hydrationMode);
} else {
throw \Doctrine\Common\DoctrineException::updateMe('Cannot find by: ' . $by . '. Invalid field or relationship alias.');
throw DoctrineException::updateMe('Cannot find by: ' . $by . '. Invalid field or relationship alias.');
}
}
}

View File

@ -21,6 +21,7 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Types\Type;
use Doctrine\Common\DoctrineException;
use \PDO;
@ -184,7 +185,7 @@ abstract class AbstractHydrator
$classMetadata = $this->_lookupDeclaringClass($classMetadata, $fieldName);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false;
$cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName);
$cache[$key]['type'] = Type::getType($classMetadata->getTypeOfField($fieldName));
$cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName);
$cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key);
} else {
@ -196,17 +197,15 @@ abstract class AbstractHydrator
}
}
$fieldName = $cache[$key]['fieldName'];
if ($cache[$key]['isScalar']) {
$rowData['scalars'][$fieldName] = $value;
$rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue;
}
$dqlAlias = $cache[$key]['dqlAlias'];
if (isset($cache[$key]['isDiscriminator'])) {
$rowData[$dqlAlias][$fieldName] = $value;
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
continue;
}
@ -214,7 +213,7 @@ abstract class AbstractHydrator
$id[$dqlAlias] .= '|' . $value;
}
$rowData[$dqlAlias][$fieldName] = $cache[$key]['type']->convertToPHPValue($value);
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $cache[$key]['type']->convertToPHPValue($value);
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
$nonemptyComponents[$dqlAlias] = true;
@ -259,7 +258,7 @@ abstract class AbstractHydrator
//$fieldName = $classMetadata->getFieldNameForLowerColumnName($columnName);
$cache[$key]['fieldName'] = $fieldName;
$cache[$key]['isScalar'] = false;
$cache[$key]['type'] = $classMetadata->getTypeOfField($fieldName);
$cache[$key]['type'] = Type::getType($classMetadata->getTypeOfField($fieldName));
$cache[$key]['dqlAlias'] = $this->_resultSetMapping->getEntityAlias($key);
}
}

View File

@ -28,12 +28,12 @@ use Doctrine\Common\Collections\Collection;
/**
* The ObjectHydrator constructs an object graph out of an SQL result set.
*
* @author robo
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class ObjectHydrator extends AbstractHydrator
{
/* TOOD: Consider unifying _collections and _initializedRelations */
/* TODO: Consider unifying _collections and _initializedRelations */
/** Collections initialized by the hydrator */
private $_collections = array();
/** Memory for initialized relations */
@ -42,16 +42,19 @@ class ObjectHydrator extends AbstractHydrator
private $_classMetadatas = array();
private $_rootAliases = array();
private $_isSimpleQuery = false;
private $_allowPartialObjects = false;
private $_identifierMap = array();
private $_resultPointers = array();
private $_idTemplate = array();
private $_resultCounter = 0;
private $_discriminatorMap = array();
private $_fetchedAssociations = array();
/** @override */
protected function _prepare()
{
$this->_isSimpleQuery = $this->_resultSetMapping->getEntityResultCount() <= 1;
$this->_allowPartialObjects = $this->_em->getConfiguration()->getAllowPartialObjects();
$this->_identifierMap = array();
$this->_resultPointers = array();
$this->_idTemplate = array();
@ -64,10 +67,24 @@ class ObjectHydrator extends AbstractHydrator
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
$this->_discriminatorMap[$class->getClassName()][$class->getDiscriminatorValue()] = $class->getClassName();
foreach (array_merge($class->getParentClasses(), $class->getSubclasses()) as $className) {
$value = $this->_em->getClassMetadata($className)->getDiscriminatorValue();
$otherClass = $this->_em->getClassMetadata($className);
$value = $otherClass->getDiscriminatorValue();
$this->_classMetadatas[$className] = $otherClass;
$this->_discriminatorMap[$class->getClassName()][$value] = $className;
}
}
if ($this->_resultSetMapping->isRelation($dqlAlias)) {
$assoc = $this->_resultSetMapping->getRelation($dqlAlias);
$this->_fetchedAssociations[$assoc->getSourceEntityName()][$assoc->getSourceFieldName()] = true;
if ($mappedByField = $assoc->getMappedByFieldName()) {
$this->_fetchedAssociations[$assoc->getTargetEntityName()][$mappedByField] = true;
} else if ($inverseAssoc = $this->_em->getClassMetadata($assoc->getTargetEntityName())
->getInverseAssociationMapping($assoc->getSourceFieldName())) {
$this->_fetchedAssociations[$assoc->getTargetEntityName()][
$inverseAssoc->getSourceFieldName()
] = true;
}
}
}
}
@ -110,16 +127,15 @@ class ObjectHydrator extends AbstractHydrator
}
/**
* 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.
* 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.
*
* @param array $resultPointers The result pointers.
* @param Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @param boolean $oneToOne Whether it is a single-valued association or not.
*/
private function updateResultPointer(&$coll, $index, $dqlAlias/*, $oneToOne*/)
private function updateResultPointer(&$coll, $index, $dqlAlias)
{
if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
@ -169,7 +185,7 @@ class ObjectHydrator extends AbstractHydrator
$relation = $classMetadata->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
$coll = $this->getCollection($relatedClass->getClassName());
$coll = $this->getCollection($relatedClass);
$coll->setOwner($entity, $relation);
$classMetadata->getReflectionProperty($name)->setValue($entity, $coll);
@ -179,6 +195,14 @@ class ObjectHydrator extends AbstractHydrator
return $coll;
}
/**
*
* @param <type> $entity
* @param <type> $assocField
* @param <type> $indexField
* @return <type>
* @todo Consider inlining this method.
*/
private function isIndexKeyInUse($entity, $assocField, $indexField)
{
return $this->_classMetadatas[get_class($entity)]
@ -207,6 +231,30 @@ class ObjectHydrator extends AbstractHydrator
unset($data[$discrColumn]);
}
$entity = $this->_uow->createEntity($className, $data);
// Properly initialize any unfetched associations, if partial objects are not allowed.
if ( ! $this->_allowPartialObjects) {
foreach ($this->_classMetadatas[$className]->getAssociationMappings() as $field => $assoc) {
if ( ! isset($this->_fetchedAssociations[$className][$field])) {
if ($assoc->isOneToOne()) {
if ($assoc->isLazilyFetched()) {
// Inject proxy
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc);
$this->_classMetadatas[$className]->setFieldValue($entity, $field, $proxy);
} else {
//TODO: Schedule for eager fetching?
}
} else {
// Inject collection
$this->_classMetadatas[$className]->getReflectionProperty($field)
->setValue($entity, new PersistentCollection($this->_em,
$this->_em->getClassMetadata($assoc->getTargetEntityName())
));
}
}
}
}
return $entity;
}
@ -233,24 +281,21 @@ class ObjectHydrator extends AbstractHydrator
*/
private function setRelatedElement($entity1, $property, $entity2)
{
$oid = spl_object_hash($entity1);
$classMetadata1 = $this->_classMetadatas[get_class($entity1)];
$classMetadata1->getReflectionProperty($property)->setValue($entity1, $entity2);
$this->_uow->setOriginalEntityProperty($oid, $property, $entity2);
$this->_uow->setOriginalEntityProperty(spl_object_hash($entity1), $property, $entity2);
$relation = $classMetadata1->getAssociationMapping($property);
if ($relation->isOneToOne()) {
$targetClass = $this->_classMetadatas[$relation->getTargetEntityName()];
if ($relation->isOwningSide()) {
// If there is an inverse mapping on the target class its bidirectional
if ($targetClass->hasInverseAssociationMapping($property)) {
$oid2 = spl_object_hash($entity2);
$sourceProp = $targetClass->getInverseAssociationMapping($fieldName)->getSourceFieldName();
$targetClass->getReflectionProperty($sourceProp)->setValue($entity2, $entity1);
}
} else {
// For sure bidirectional, as there is no inverse side in unidirectional
$mappedByProp = $relation->getMappedByFieldName();
$targetClass->getReflectionProperty($mappedByProp)->setValue($entity2, $entity1);
$targetClass->getReflectionProperty($relation->getMappedByFieldName())->setValue($entity2, $entity1);
}
}
}
@ -284,7 +329,6 @@ class ObjectHydrator extends AbstractHydrator
$parent = $this->_resultSetMapping->getParentAlias($dqlAlias);
$relation = $this->_resultSetMapping->getRelation($dqlAlias);
$relationAlias = $relation->getSourceFieldName();
$path = $parent . '.' . $dqlAlias;
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
@ -305,10 +349,10 @@ class ObjectHydrator extends AbstractHydrator
if ( ! $relation->isOneToOne()) {
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($this->_initializedRelations[spl_object_hash($baseElement)][$relationAlias])) {
$this->initRelatedCollection($baseElement, $relationAlias)
->setHydrationFlag(true);
$this->initRelatedCollection($baseElement, $relationAlias)->setHydrationFlag(true);
}
$path = $parent . '.' . $dqlAlias;
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? $this->isIndexKeyInUse($baseElement, $relationAlias, $index) : false;
@ -318,17 +362,18 @@ class ObjectHydrator extends AbstractHydrator
// If it's a bi-directional many-to-many, also initialize the reverse collection.
if ($relation->isManyToMany()) {
if ($relation->isOwningSide()) {
$reverseFieldName = $this->_classMetadatas[get_class($element)]
->getInverseAssociationMapping($relationAlias)
->getSourceFieldName();
$this->initRelatedCollection($element, $reverseFieldName);
$reverseAssoc = $this->_classMetadatas[$entityName]
->getInverseAssociationMapping($relationAlias);
if ($reverseAssoc) {
$this->initRelatedCollection($element, $reverseAssoc->getSourceFieldName());
}
} else if ($mappedByField = $relation->getMappedByFieldName()) {
$this->initRelatedCollection($element, $mappedByField);
}
}
if ($field = $this->_getCustomIndexField($dqlAlias)) {
$indexValue = $this->_classMetadatas[get_class($element)]
$indexValue = $this->_classMetadatas[$entityName]
->getReflectionProperty($field)
->getValue($element);
$this->_classMetadatas[$parentClass]
@ -348,17 +393,15 @@ class ObjectHydrator extends AbstractHydrator
);
}
} else if ( ! $this->isFieldSet($baseElement, $relationAlias)) {
$coll = new PersistentCollection($this->_em, $entityName);
$coll = new PersistentCollection($this->_em, $this->_classMetadatas[$entityName]);
$this->_collections[] = $coll;
$this->setRelatedElement($baseElement, $relationAlias, $coll);
}
} else {
if ( ! isset($nonemptyComponents[$dqlAlias]) &&
! $this->isFieldSet($baseElement, $relationAlias)) {
if ( ! isset($nonemptyComponents[$dqlAlias]) && ! $this->isFieldSet($baseElement, $relationAlias)) {
$this->setRelatedElement($baseElement, $relationAlias, null);
} else if ( ! $this->isFieldSet($baseElement, $relationAlias)) {
$this->setRelatedElement($baseElement, $relationAlias,
$this->getEntity($data, $entityName));
$this->setRelatedElement($baseElement, $relationAlias, $this->getEntity($data, $entityName));
}
}

View File

@ -43,7 +43,8 @@ abstract class AssociationMapping
'none',
'save',
'delete',
'refresh'
'refresh',
'merge'
);
protected $_cascades = array();
@ -381,5 +382,5 @@ abstract class AssociationMapping
* @param <type> $entity
* @param <type> $entityManager
*/
abstract public function lazyLoadFor($entity, $entityManager);
/*abstract*/ public function load($entity, $em) {}
}

View File

@ -421,6 +421,7 @@ final class ClassMetadata
}
/**
* Gets the change tracking policy used by this class.
*
* @return integer
*/
@ -430,6 +431,7 @@ final class ClassMetadata
}
/**
* Sets the change tracking policy used by this class.
*
* @param integer $policy
*/
@ -439,6 +441,7 @@ final class ClassMetadata
}
/**
* Whether the change tracking policy of this class is "deferred explicit".
*
* @return boolean
*/
@ -448,6 +451,7 @@ final class ClassMetadata
}
/**
* Whether the change tracking policy of this class is "deferred implicit".
*
* @return boolean
*/
@ -457,6 +461,7 @@ final class ClassMetadata
}
/**
* Whether the change tracking policy of this class is "notify".
*
* @return boolean
*/
@ -649,10 +654,8 @@ final class ClassMetadata
*/
public function getInverseAssociationMapping($mappedByFieldName)
{
if ( ! isset($this->_inverseMappings[$mappedByFieldName])) {
throw MappingException::mappingNotFound($mappedByFieldName);
}
return $this->_inverseMappings[$mappedByFieldName];
return isset($this->_inverseMappings[$mappedByFieldName]) ?
$this->_inverseMappings[$mappedByFieldName] : null;
}
/**
@ -741,9 +744,9 @@ final class ClassMetadata
throw MappingException::missingType();
}
if ( ! is_object($mapping['type'])) {
/*if ( ! is_object($mapping['type'])) {
$mapping['type'] = \Doctrine\DBAL\Types\Type::getType($mapping['type']);
}
}*/
// Complete fieldName and columnName mapping
if ( ! isset($mapping['columnName'])) {
@ -879,12 +882,12 @@ final class ClassMetadata
* @param string $field
* @param mixed $value
*/
public function setValue($entity, $field, $value)
/*public function setValue($entity, $field, $value)
{
if (isset($this->_reflectionProperties[$field])) {
$this->_reflectionProperties[$field]->setValue($entity, $value);
}
}
}*/
/**
* Extracts the identifier values of an entity of this class.
@ -913,6 +916,7 @@ final class ClassMetadata
*
* @param object $entity
* @param mixed $id
* @todo Rename to assignIdentifier()
*/
public function setIdentifierValues($entity, $id)
{
@ -924,6 +928,30 @@ final class ClassMetadata
$this->_reflectionProperties[$this->_identifier[0]]->setValue($entity, $id);
}
}
/**
* Sets the specified field to the specified value on the given entity.
*
* @param object $entity
* @param string $field
* @param mixed $value
*/
public function setFieldValue($entity, $field, $value)
{
$this->_reflectionProperties[$field]->setValue($entity, $value);
}
/**
* Sets the field mapped to the specified column to the specified value on the given entity.
*
* @param object $entity
* @param string $field
* @param mixed $value
*/
public function setColumnValue($entity, $column, $value)
{
$this->_reflectionProperties[$this->_fieldNames[$column]]->setValue($entity, $value);
}
/**
* Gets all field mappings.

View File

@ -98,6 +98,16 @@ class ClassMetadataFactory
}
return $this->_loadedMetadata[$className];
}
/**
*
* @param <type> $className
* @param <type> $class
*/
public function setMetadataFor($className, $class)
{
$this->_loadedMetadata[$className] = $class;
}
/**
* Loads the metadata of the class in question and all it's ancestors whose metadata

View File

@ -16,7 +16,7 @@
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.org>.
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Mapping;
@ -140,27 +140,37 @@ class OneToOneMapping extends AssociationMapping
/**
* {@inheritdoc}
*
* @param Doctrine\ORM\Entity $entity
* @return void
* @param object $owningEntity
* @param object $targetEntity
* @param EntityManager $em
*/
public function lazyLoadFor($entity, $entityManager)
public function load($owningEntity, $targetEntity, $em)
{
$sourceClass = $entityManager->getClassMetadata($this->_sourceEntityName);
$targetClass = $entityManager->getClassMetadata($this->_targetEntityName);
$dql = 'SELECT t FROM ' . $targetClass->getClassName() . ' t WHERE ';
$params = array();
foreach ($this->_sourceToTargetKeyFields as $sourceKeyField => $targetKeyField) {
if ($params) $dql .= " AND ";
$dql .= "t.$targetKeyField = ?";
$params[] = $sourceClass->getReflectionProperty($sourceKeyField)->getValue($entity);
}
$sourceClass = $em->getClassMetadata($this->_sourceEntityName);
$targetClass = $em->getClassMetadata($this->_targetEntityName);
$otherEntity = $entityManager->query($dql, $params)->getFirst();
if ( ! $otherEntity) {
$otherEntity = null;
$conditions = array();
if ($this->_isOwningSide) {
foreach ($this->_sourceToTargetKeyColumns as $sourceKeyColumn => $targetKeyColumn) {
$conditions[$targetKeyColumn] = $sourceClass->getReflectionProperty(
$sourceClass->getFieldName($sourceKeyColumn))->getValue($owningEntity);
}
if ($targetClass->hasInverseAssociation($this->_sourceFieldName)) {
$targetClass->setFieldValue(
$targetEntity,
$targetClass->getInverseAssociationMapping($this->_sourceFieldName)->getSourceFieldName(),
$owningEntity);
}
} else {
$owningAssoc = $em->getClassMetadata($this->_targetEntityName)->getAssociationMapping($this->_mappedByFieldName);
foreach ($owningAssoc->getTargetToSourceKeyColumns() as $targetKeyColumn => $sourceKeyColumn) {
$conditions[$sourceKeyColumn] = $sourceClass->getReflectionProperty(
$sourceClass->getFieldName($targetKeyColumn))->getValue($owningEntity);
}
$targetClass->setFieldValue($targetEntity, $this->_mappedByFieldName, $owningEntity);
}
$sourceClass->getReflectionProperty($this->_sourceFieldName)->setValue($entity, $otherEntity);
$em->getUnitOfWork()->getEntityPersister($this->_targetEntityName)->load($conditions, $targetEntity);
}
}

View File

@ -120,18 +120,12 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
/**
* Creates a new persistent collection.
*/
public function __construct(EntityManager $em, $type, array $data = array(), $keyField = null)
public function __construct(EntityManager $em, $class, array $data = array())
{
parent::__construct($data);
$this->_type = $type;
$this->_type = $class->getClassName();
$this->_em = $em;
$this->_ownerClass = $em->getClassMetadata($type);
if ($keyField !== null) {
if ( ! $this->_ownerClass->hasField($keyField)) {
throw DoctrineException::updateMe("Invalid field '$keyField' can't be used as key.");
}
$this->_keyField = $keyField;
}
$this->_ownerClass = $class;
}
/**

View File

@ -21,7 +21,9 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata;
/**
@ -40,7 +42,7 @@ abstract class AbstractEntityPersister
*
* @var Doctrine\ORM\Mapping\ClassMetadata
*/
protected $_classMetadata;
protected $_class;
/**
* The name of the entity the persister is used for.
@ -75,12 +77,12 @@ abstract class AbstractEntityPersister
* that uses the given EntityManager and persists instances of the class described
* by the given class metadata descriptor.
*/
public function __construct(EntityManager $em, ClassMetadata $classMetadata)
public function __construct(EntityManager $em, ClassMetadata $class)
{
$this->_em = $em;
$this->_entityName = $classMetadata->getClassName();
$this->_entityName = $class->getClassName();
$this->_conn = $em->getConnection();
$this->_classMetadata = $classMetadata;
$this->_class = $class;
}
/**
@ -94,8 +96,8 @@ abstract class AbstractEntityPersister
{
$insertData = array();
$this->_prepareData($entity, $insertData, true);
$this->_conn->insert($this->_classMetadata->getTableName(), $insertData);
$idGen = $this->_classMetadata->getIdGenerator();
$this->_conn->insert($this->_class->getTableName(), $insertData);
$idGen = $this->_class->getIdGenerator();
if ($idGen->isPostInsertGenerator()) {
return $idGen->generate($this->_em, $entity);
}
@ -119,8 +121,8 @@ abstract class AbstractEntityPersister
*/
public function executeInserts()
{
//$tableName = $this->_classMetadata->getTableName();
$stmt = $this->_conn->prepare($this->_classMetadata->getInsertSql());
//$tableName = $this->_class->getTableName();
$stmt = $this->_conn->prepare($this->_class->getInsertSql());
foreach ($this->_queuedInserts as $insertData) {
$stmt->execute(array_values($insertData));
}
@ -136,9 +138,9 @@ abstract class AbstractEntityPersister
{
$updateData = array();
$this->_prepareData($entity, $updateData);
$id = array_combine($this->_classMetadata->getIdentifierFieldNames(),
$id = array_combine($this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity));
$this->_conn->update($this->_classMetadata->getTableName(), $updateData, $id);
$this->_conn->update($this->_class->getTableName(), $updateData, $id);
}
/**
@ -149,10 +151,10 @@ abstract class AbstractEntityPersister
public function delete($entity)
{
$id = array_combine(
$this->_classMetadata->getIdentifierFieldNames(),
$this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
);
$this->_conn->delete($this->_classMetadata->getTableName(), $id);
$this->_conn->delete($this->_class->getTableName(), $id);
}
/**
@ -182,7 +184,7 @@ abstract class AbstractEntityPersister
*/
public function getClassMetadata()
{
return $this->_classMetadata;
return $this->_class;
}
/**
@ -221,12 +223,11 @@ abstract class AbstractEntityPersister
foreach ($this->_em->getUnitOfWork()->getEntityChangeSet($entity) as $field => $change) {
$oldVal = $change[0];
$newVal = $change[1];
$type = $this->_classMetadata->getTypeOfField($field);
$columnName = $this->_classMetadata->getColumnName($field);
if ($this->_classMetadata->hasAssociation($field)) {
$assocMapping = $this->_classMetadata->getAssociationMapping($field);
$columnName = $this->_class->getColumnName($field);
if ($this->_class->hasAssociation($field)) {
$assocMapping = $this->_class->getAssociationMapping($field);
if ( ! $assocMapping->isOneToOne() || $assocMapping->isInverseSide()) {
continue;
}
@ -242,8 +243,94 @@ abstract class AbstractEntityPersister
} else if ($newVal === null) {
$result[$columnName] = null;
} else {
$result[$columnName] = $type->convertToDatabaseValue($newVal, $this->_conn->getDatabasePlatform());
$result[$columnName] = Type::getType($this->_class->getTypeOfField($field))
->convertToDatabaseValue($newVal, $this->_conn->getDatabasePlatform());
}
}
}
/**
* Loads an entity by a list of field criteria.
*
* @param array $criteria The criteria by which to load the entity.
* @param object $entity The entity to load the data into. If not specified,
* a new entity is created.
*/
public function load(array $criteria, $entity = null)
{
$stmt = $this->_conn->prepare($this->_getSelectSingleEntitySql($criteria));
$stmt->execute(array_values($criteria));
$data = array();
foreach ($stmt->fetch(\PDO::FETCH_ASSOC) as $column => $value) {
$fieldName = $this->_class->getFieldNameForLowerColumnName($column);
$data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName))
->convertToPHPValue($value);
}
$stmt->closeCursor();
if ($entity === null) {
$entity = $this->_em->getUnitOfWork()->createEntity($this->_entityName, $data);
} else {
foreach ($data as $field => $value) {
$this->_class->setFieldValue($entity, $field, $value);
}
$id = array();
if ($this->_class->isIdentifierComposite()) {
$identifierFieldNames = $this->_class->getIdentifier();
foreach ($identifierFieldNames as $fieldName) {
$id[] = $data[$fieldName];
}
} else {
$id = array($data[$this->_class->getSingleIdentifierFieldName()]);
}
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
}
if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) {
foreach ($this->_class->getAssociationMappings() as $field => $assoc) {
if ($assoc->isOneToOne()) {
if ($assoc->isLazilyFetched()) {
// Inject proxy
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($entity, $assoc);
$this->_class->setFieldValue($entity, $field, $proxy);
} else {
//TODO: Eager fetch?
}
} else {
// Inject collection
$this->_class->getReflectionProperty($field)
->setValue($entity, new PersistentCollection($this->_em,
$this->_em->getClassMetadata($assoc->getTargetEntityName())
));
}
}
}
return $entity;
}
/**
* Gets the SELECT SQL to select a single entity by a set of field criteria.
*
* @param array $criteria
* @return string
*/
protected function _getSelectSingleEntitySql(array $criteria)
{
$columnList = '';
$columnNames = $this->_class->getColumnNames();
foreach ($columnNames as $column) {
if ($columnList != '') $columnList .= ', ';
$columnList .= $column;
}
$conditionSql = '';
foreach ($criteria as $field => $value) {
if ($conditionSql != '') $conditionSql .= ' AND ';
$conditionSql .= $this->_class->getColumnName($field) . ' = ?';
}
return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName()
. ' WHERE ' . $conditionSql;
}
}

View File

@ -26,7 +26,7 @@ use Doctrine\ORM\PersistentCollection;
/**
* Persister for many-to-many collections.
*
* @author robo
* @author Roman Borschel <roman@code-factory.org>
* @since 2.0
*/
class ManyToManyPersister extends AbstractCollectionPersister
@ -41,7 +41,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$columns = $mapping->getJoinTableColumns();
return "DELETE FROM {$joinTable['name']} WHERE " . implode(' = ? AND ', $columns) . ' = ?';
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
}
/**
@ -77,8 +77,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
$mapping = $coll->getMapping();
$joinTable = $mapping->getJoinTable();
$columns = $mapping->getJoinTableColumns();
return "INSERT INTO {$joinTable['name']} (" . implode(', ', $columns) . ")"
. " VALUES (" . implode(', ', array_fill(0, count($columns), '?')) . ')';
return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')'
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
}
/**
@ -88,7 +88,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/
protected function _getInsertRowSqlParameters(PersistentCollection $coll, $element)
{
//FIXME: This is still problematic for composite keys because we silently
// FIXME: This is still problematic for composite keys because we silently
// rely on a specific ordering of the columns.
$params = array_merge(
$this->_uow->getEntityIdentifier($coll->getOwner()),
@ -112,7 +112,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
if ($whereClause !== '') $whereClause .= ' AND ';
$whereClause .= "$relationColumn = ?";
}
return "DELETE FROM {$joinTable['name']} WHERE $whereClause";
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
}
/**

View File

@ -33,19 +33,14 @@ namespace Doctrine\ORM\Persisters;
*/
class SingleTablePersister extends AbstractEntityPersister
{
public function insert($entity)
{
return parent::insert($entity);
}
/** @override */
protected function _prepareData($entity, array &$result, $isInsert = false)
{
parent::_prepareData($entity, $result, $isInsert);
// Populate the discriminator column
if ($isInsert) {
$discColumn = $this->_classMetadata->getDiscriminatorColumn();
$result[$discColumn['name']] = $this->_classMetadata->getDiscriminatorValue();
$discColumn = $this->_class->getDiscriminatorColumn();
$result[$discColumn['name']] = $this->_class->getDiscriminatorValue();
}
}
}

View File

@ -165,6 +165,11 @@ class ResultSetMapping
return $this->_relationMap[$alias];
}
public function isRelation($alias)
{
return isset($this->_relationMap[$alias]);
}
/**
*
* @param <type> $columnName

View File

@ -158,7 +158,7 @@ class SchemaTool
foreach ($class->getFieldMappings() as $fieldName => $mapping) {
$column = array();
$column['name'] = $mapping['columnName'];
$column['type'] = $mapping['type'];
$column['type'] = Type::getType($mapping['type']);
$column['length'] = $mapping['length'];
$column['notnull'] = ! $mapping['nullable'];
if ($class->isIdentifier($fieldName)) {
@ -187,7 +187,7 @@ class SchemaTool
foreach ($mapping->getJoinColumns() as $joinColumn) {
$column = array();
$column['name'] = $joinColumn['name'];
$column['type'] = $foreignClass->getTypeOfColumn($joinColumn['referencedColumnName']);
$column['type'] = Type::getType($foreignClass->getTypeOfColumn($joinColumn['referencedColumnName']));
$columns[$joinColumn['name']] = $column;
$constraint['local'][] = $joinColumn['name'];
$constraint['foreign'][] = $joinColumn['referencedColumnName'];
@ -211,7 +211,7 @@ class SchemaTool
$column['primary'] = true;
$joinTableOptions['primary'][] = $joinColumn['name'];
$column['name'] = $joinColumn['name'];
$column['type'] = $class->getTypeOfColumn($joinColumn['referencedColumnName']);
$column['type'] = Type::getType($class->getTypeOfColumn($joinColumn['referencedColumnName']));
$joinTableColumns[$joinColumn['name']] = $column;
$constraint1['local'][] = $joinColumn['name'];
$constraint1['foreign'][] = $joinColumn['referencedColumnName'];
@ -228,8 +228,8 @@ class SchemaTool
$column['primary'] = true;
$joinTableOptions['primary'][] = $inverseJoinColumn['name'];
$column['name'] = $inverseJoinColumn['name'];
$column['type'] = $this->_em->getClassMetadata($mapping->getTargetEntityName())
->getTypeOfColumn($inverseJoinColumn['referencedColumnName']);
$column['type'] = Type::getType($this->_em->getClassMetadata($mapping->getTargetEntityName())
->getTypeOfColumn($inverseJoinColumn['referencedColumnName']));
$joinTableColumns[$inverseJoinColumn['name']] = $column;
$constraint2['local'][] = $inverseJoinColumn['name'];
$constraint2['foreign'][] = $inverseJoinColumn['referencedColumnName'];

View File

@ -400,7 +400,7 @@ class UnitOfWork implements PropertyChangedListener
$assoc = $class->getAssociationMapping($name);
//echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL;
// Inject PersistentCollection
$coll = new PersistentCollection($this->_em, $assoc->getTargetEntityName(),
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->getTargetEntityName()),
$actualData[$name] ? $actualData[$name] : array());
$coll->setOwner($entity, $assoc);
if ( ! $coll->isEmpty()) {
@ -1298,6 +1298,7 @@ class UnitOfWork implements PropertyChangedListener
}
/**
* INTERNAL:
* Creates an entity. Used for reconstitution of entities during hydration.
*
* @param string $className The name of the entity class.
@ -1328,13 +1329,6 @@ class UnitOfWork implements PropertyChangedListener
} else {
$entity = new $className;
$oid = spl_object_hash($entity);
/*if ($class->hasLazySingleValuedAssociations()) {
foreach ($class->getLazyAssociations() as $lazyAssoc) {
// Inject VirtualProxy
$prop = $class->getReflectionProperty($lazyAssoc->getSourceFieldName());
$prop->setValue($entity, new \Doctrine\ORM\VirtualProxy($entity, $lazyAssoc, $prop));
}
}*/
$this->_entityIdentifiers[$oid] = $id;
$this->_entityStates[$oid] = self::STATE_MANAGED;
$this->_originalEntityData[$oid] = $data;
@ -1344,7 +1338,9 @@ class UnitOfWork implements PropertyChangedListener
if ($overrideLocalChanges) {
foreach ($data as $field => $value) {
$class->setValue($entity, $field, $value);
if ($class->hasField($field)) {
$class->setFieldValue($entity, $field, $value);
}
}
} else {
foreach ($data as $field => $value) {
@ -1512,6 +1508,23 @@ class UnitOfWork implements PropertyChangedListener
return $this->_collectionPersisters[$type];
}
/**
* INTERNAL:
* Registers an entity as managed.
*
* @param object $entity The entity.
* @param array $id The identifier values.
* @param array $data The original entity data.
*/
public function registerManaged($entity, $id, $data)
{
$oid = spl_object_hash($entity);
$this->_entityIdentifiers[$oid] = $id;
$this->_entityStates[$oid] = self::STATE_MANAGED;
$this->_originalEntityData[$oid] = $data;
$this->addToIdentityMap($entity);
}
/* PropertyChangedListener implementation */
/**

View File

@ -53,7 +53,7 @@ class VirtualProxy
private function _load()
{
$realInstance = $tis->_assoc->lazyLoadFor($this->_owner);
$realInstance = $this->_assoc->lazyLoadFor($this->_owner);
$this->_refProp->setValue($this->_owner, $realInstance);
return $realInstance;
}

View File

@ -26,7 +26,7 @@ class MySqlPlatformTest extends \Doctrine\Tests\DbalTestCase
'notnull' => true
),
'test' => array(
'type' => Type::getType('varchar'),
'type' => Type::getType('string'),
'length' => 255,
'notnull' => true
)

View File

@ -26,7 +26,7 @@ class PostgreSqlPlatformTest extends \Doctrine\Tests\DbalTestCase
'notnull' => true
),
'test' => array(
'type' => Type::getType('varchar'),
'type' => Type::getType('string'),
'length' => 255,
'notnull' => true
)

View File

@ -25,7 +25,7 @@ class SqlitePlatformTest extends \Doctrine\Tests\DbalTestCase
'notnull' => true
),
'test' => array(
'type' => new \Doctrine\DBAL\Types\VarcharType,
'type' => new \Doctrine\DBAL\Types\StringType,
'length' => 255
)
);

View File

@ -19,17 +19,17 @@ class CmsAddress
public $id;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $country;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $zip;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $city;
@ -38,4 +38,20 @@ class CmsAddress
* @DoctrineJoinColumn(name="user_id", referencedColumnName="id")
*/
public $user;
public function getId() {
return $this->id;
}
public function getCountry() {
return $this->country;
}
public function getZipCode() {
return $this->zip;
}
public function getCity() {
return $this->city;
}
}

View File

@ -15,11 +15,11 @@ class CmsArticle
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=255)
* @DoctrineColumn(type="string", length=255)
*/
public $topic;
/**
* @DoctrineColumn(type="varchar")
* @DoctrineColumn(type="string")
*/
public $text;
/**

View File

@ -15,11 +15,11 @@ class CmsComment
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=255)
* @DoctrineColumn(type="string", length=255)
*/
public $topic;
/**
* @DoctrineColumn(type="varchar")
* @DoctrineColumn(type="string")
*/
public $text;
/**

View File

@ -22,7 +22,7 @@ class CmsGroup
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $name;
/**

View File

@ -9,7 +9,7 @@ namespace Doctrine\Tests\Models\CMS;
class CmsPhonenumber
{
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
* @DoctrineId
*/
public $phonenumber;

View File

@ -15,15 +15,15 @@ class CmsUser
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $status;
/**
* @DoctrineColumn(type="varchar", length=255)
* @DoctrineColumn(type="string", length=255)
*/
public $username;
/**
* @DoctrineColumn(type="varchar", length=255)
* @DoctrineColumn(type="string", length=255)
*/
public $name;
/**

View File

@ -6,7 +6,7 @@ namespace Doctrine\Tests\Models\Company;
* @DoctrineEntity
* @DoctrineTable(name="company_employee")
* @DoctrineInheritanceType("joined")
* @DoctrineDiscriminatorColumn(name="dtype", type="varchar", length=20)
* @DoctrineDiscriminatorColumn(name="dtype", type="string", length=20)
* @DoctrineDiscriminatorMap({
"emp" = "Doctrine\Tests\Models\Company\CompanyEmployee",
"man" = "Doctrine\Tests\Models\Company\CompanyManager"})
@ -27,7 +27,7 @@ class CompanyEmployee
public $salary;
/**
* @DoctrineColumn(type="varchar", length=255)
* @DoctrineColumn(type="string", length=255)
*/
public $department;
}

View File

@ -8,7 +8,7 @@ namespace Doctrine\Tests\Models\Company;
class CompanyManager extends CompanyEmployee
{
/*
* @DoctrineColumn(type="varchar", length="255")
* @DoctrineColumn(type="string", length="255")
*/
public $title;
}

View File

@ -18,7 +18,7 @@ class ForumCategory
*/
public $position;
/**
* @DoctrineColumn(type="varchar", length=255)
* @DoctrineColumn(type="string", length=255)
*/
public $name;
/**

View File

@ -15,7 +15,7 @@ class ForumEntry
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $topic;
}

View File

@ -15,7 +15,7 @@ class ForumUser
*/
public $id;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
public $username;
/**

View File

@ -202,8 +202,8 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('Guilherme', $users[0]->name);
$this->assertEquals('gblanco', $users[0]->username);
$this->assertEquals('developer', $users[0]->status);
$this->assertNull($users[0]->phonenumbers);
$this->assertNull($users[0]->articles);
//$this->assertNull($users[0]->phonenumbers);
//$this->assertNull($users[0]->articles);
$usersArray = $query->getResultArray();
@ -257,7 +257,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals('developer', $users[0]->status);
$this->assertTrue($users[0]->phonenumbers instanceof \Doctrine\ORM\PersistentCollection);
$this->assertEquals(0, $users[0]->phonenumbers->count());
$this->assertNull($users[0]->articles);
//$this->assertNull($users[0]->articles);
}
public function testBasicManyToManyJoin()
@ -307,11 +307,27 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u inner join u.groups g");
$this->assertEquals(0, count($query->getResultList()));
/* RB: TEST
\Doctrine\ORM\DynamicProxyGenerator::configure($this->_em);
$proxy = \Doctrine\ORM\DynamicProxyGenerator::getReferenceProxy('Doctrine\Tests\Models\CMS\CmsUser', 1);
echo $proxy->getId();
var_dump(serialize($proxy));
/* RB: TEST */
/*
$address = new CmsAddress;
$address->country = 'Germany';
$address->zip = '103040';
$address->city = 'Berlin';
$address->user = $user;
$this->_em->save($address);
$this->_em->clear();
$proxy = $this->_em->getProxyGenerator()->getAssociationProxy($user, $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('address'));
var_dump($proxy->getId());
//var_dump(get_class($proxy));
var_dump(get_class($proxy->user));
//var_dump($proxy);
//$proxy = $this->_em->getProxyGenerator()->getReferenceProxy('Doctrine\Tests\Models\CMS\CmsUser', 1);
//echo $proxy->getId();
//var_dump(serialize($proxy));
*/

View File

@ -94,7 +94,7 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
/**
* @DoctrineEntity
* @DoctrineInheritanceType("singleTable")
* @DoctrineDiscriminatorColumn(name="discr", type="varchar")
* @DoctrineDiscriminatorColumn(name="discr", type="string")
* @DoctrineSubClasses({"Doctrine\Tests\ORM\Functional\ChildEntity"})
* @DoctrineDiscriminatorValue("parent")
*/
@ -107,7 +107,7 @@ class ParentEntity {
private $id;
/**
* @DoctrineColumn(type="varchar")
* @DoctrineColumn(type="string")
*/
private $data;
@ -168,7 +168,7 @@ class RelatedEntity {
*/
private $id;
/**
* @DoctrineColumn(type="varchar", length=50)
* @DoctrineColumn(type="string", length=50)
*/
private $name;
/**

View File

@ -194,7 +194,7 @@ class NotifyChangedEntity implements \Doctrine\Common\NotifyPropertyChanged
*/
private $id;
/**
* @DoctrineColumn(type="varchar")
* @DoctrineColumn(type="string")
*/
private $data;