diff --git a/lib/Doctrine/Common/ClassLoader.php b/lib/Doctrine/Common/ClassLoader.php index 334e33d12..718f1f21f 100644 --- a/lib/Doctrine/Common/ClassLoader.php +++ b/lib/Doctrine/Common/ClassLoader.php @@ -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) { diff --git a/lib/Doctrine/DBAL/Types/VarcharType.php b/lib/Doctrine/DBAL/Types/StringType.php similarity index 91% rename from lib/Doctrine/DBAL/Types/VarcharType.php rename to lib/Doctrine/DBAL/Types/StringType.php index 11f7b3be4..fd71a9ee9 100644 --- a/lib/Doctrine/DBAL/Types/VarcharType.php +++ b/lib/Doctrine/DBAL/Types/StringType.php @@ -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'; } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Types/Type.php b/lib/Doctrine/DBAL/Types/Type.php index ee98b4723..f3cf63d77 100644 --- a/lib/Doctrine/DBAL/Types/Type.php +++ b/lib/Doctrine/DBAL/Types/Type.php @@ -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', diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 45024ea45..3a22ddf36 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -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; diff --git a/lib/Doctrine/ORM/DynamicProxyGenerator.php b/lib/Doctrine/ORM/DynamicProxyGenerator.php index f40ded06c..126b98167 100644 --- a/lib/Doctrine/ORM/DynamicProxyGenerator.php +++ b/lib/Doctrine/ORM/DynamicProxyGenerator.php @@ -26,9 +26,11 @@ namespace Doctrine\ORM; * For that purpose he generates proxy class files on the fly as needed. * * @author Roman Borschel + * @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 $className - * @param $identifier - * @return + * @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 $className - * @param $id - * @param $proxyClassName - * @param $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( - '', '', '', - '', '', '', '' + '', '', + '', '' ); $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( + '', '', + '', '' + ); + $replacements = array( + $proxyClassName, $className, $methods, $sleepImpl + ); + + $file = str_replace($placeholders, $replacements, $file); + + file_put_contents($fileName, $file); + } + /** Proxy class code template */ private static $_proxyClassTemplate = '_loaded) { - $scalar = $this->_em->createQuery(\'select prx from prx where \')->execute(, \Doctrine\ORM\Query::HYDRATE_SCALAR); - + $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 = +' extends \ { + 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; + } + } + + + + public function __sleep() { + if (!$this->_loaded) { + throw new RuntimeException("Not fully loaded proxy can not be serialized."); + } + + } + } +}'; } diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 3341d5481..4b585ce6e 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -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. diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index ed7b2908a..04a6041f6 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -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.'); } } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 1cbd131a3..1e3aecd56 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -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); } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 7b95fa29d..49b35641f 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -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 * @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 $entity + * @param $assocField + * @param $indexField + * @return + * @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)); } } diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php index 5ab486c19..5e9e64d9a 100644 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ b/lib/Doctrine/ORM/Mapping/AssociationMapping.php @@ -43,7 +43,8 @@ abstract class AssociationMapping 'none', 'save', 'delete', - 'refresh' + 'refresh', + 'merge' ); protected $_cascades = array(); @@ -381,5 +382,5 @@ abstract class AssociationMapping * @param $entity * @param $entityManager */ - abstract public function lazyLoadFor($entity, $entityManager); + /*abstract*/ public function load($entity, $em) {} } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 9fc133748..61a8c67e2 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -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. diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 4532de41f..830efd39f 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -98,6 +98,16 @@ class ClassMetadataFactory } return $this->_loadedMetadata[$className]; } + + /** + * + * @param $className + * @param $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 diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 8fd51c925..431623164 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ 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); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index a68fb7e94..a48d40ecb 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -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; } /** diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php index 829ac46ef..483d04172 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php @@ -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; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 7967299c4..cc237ce1b 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -26,7 +26,7 @@ use Doctrine\ORM\PersistentCollection; /** * Persister for many-to-many collections. * - * @author robo + * @author Roman Borschel * @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; } /** diff --git a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php index 8d6e37deb..add05727b 100644 --- a/lib/Doctrine/ORM/Persisters/SingleTablePersister.php +++ b/lib/Doctrine/ORM/Persisters/SingleTablePersister.php @@ -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(); } } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 2f801c6b3..446fdd5e8 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -165,6 +165,11 @@ class ResultSetMapping return $this->_relationMap[$alias]; } + public function isRelation($alias) + { + return isset($this->_relationMap[$alias]); + } + /** * * @param $columnName diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index b726311fb..c27d6d058 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -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']; diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 8415bf2f8..e4b63cee6 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -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 */ /** diff --git a/lib/Doctrine/ORM/VirtualProxy.php b/lib/Doctrine/ORM/VirtualProxy.php index 42eb0afb2..db2b76de7 100644 --- a/lib/Doctrine/ORM/VirtualProxy.php +++ b/lib/Doctrine/ORM/VirtualProxy.php @@ -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; } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MySqlPlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MySqlPlatformTest.php index 5b9e7c89a..bce833c56 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MySqlPlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MySqlPlatformTest.php @@ -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 ) diff --git a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php index b8662cb51..de29305ae 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php @@ -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 ) diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php index 2f466c258..bb7ecbcf2 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SqlitePlatformTest.php @@ -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 ) ); diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php index dd967888f..f28781ae8 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsAddress.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddress.php @@ -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; + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsArticle.php b/tests/Doctrine/Tests/Models/CMS/CmsArticle.php index b2da30408..d86c51687 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsArticle.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsArticle.php @@ -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; /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsComment.php b/tests/Doctrine/Tests/Models/CMS/CmsComment.php index 1e5eef550..bcb2b9948 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsComment.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsComment.php @@ -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; /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsGroup.php b/tests/Doctrine/Tests/Models/CMS/CmsGroup.php index 2a1f7b472..2eedf3c17 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsGroup.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsGroup.php @@ -22,7 +22,7 @@ class CmsGroup */ public $id; /** - * @DoctrineColumn(type="varchar", length=50) + * @DoctrineColumn(type="string", length=50) */ public $name; /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsPhonenumber.php b/tests/Doctrine/Tests/Models/CMS/CmsPhonenumber.php index 5abca18b7..7054db7d9 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsPhonenumber.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsPhonenumber.php @@ -9,7 +9,7 @@ namespace Doctrine\Tests\Models\CMS; class CmsPhonenumber { /** - * @DoctrineColumn(type="varchar", length=50) + * @DoctrineColumn(type="string", length=50) * @DoctrineId */ public $phonenumber; diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index 9ce7901b6..19d068ebc 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -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; /** diff --git a/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php b/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php index 3d3fca0f8..e2a4551b8 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php @@ -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; } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/Company/CompanyManager.php b/tests/Doctrine/Tests/Models/Company/CompanyManager.php index 3514d95f6..03fcc4a09 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyManager.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyManager.php @@ -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; } \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/Forum/ForumCategory.php b/tests/Doctrine/Tests/Models/Forum/ForumCategory.php index b9be8d2ba..3213f3ca7 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumCategory.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumCategory.php @@ -18,7 +18,7 @@ class ForumCategory */ public $position; /** - * @DoctrineColumn(type="varchar", length=255) + * @DoctrineColumn(type="string", length=255) */ public $name; /** diff --git a/tests/Doctrine/Tests/Models/Forum/ForumEntry.php b/tests/Doctrine/Tests/Models/Forum/ForumEntry.php index 9cb33b670..3adcd6d08 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumEntry.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumEntry.php @@ -15,7 +15,7 @@ class ForumEntry */ public $id; /** - * @DoctrineColumn(type="varchar", length=50) + * @DoctrineColumn(type="string", length=50) */ public $topic; } diff --git a/tests/Doctrine/Tests/Models/Forum/ForumUser.php b/tests/Doctrine/Tests/Models/Forum/ForumUser.php index 9212b58ed..0edc4b20f 100644 --- a/tests/Doctrine/Tests/Models/Forum/ForumUser.php +++ b/tests/Doctrine/Tests/Models/Forum/ForumUser.php @@ -15,7 +15,7 @@ class ForumUser */ public $id; /** - * @DoctrineColumn(type="varchar", length=50) + * @DoctrineColumn(type="string", length=50) */ public $username; /** diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 09394a024..d08db2f69 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -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)); */ diff --git a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php index 017d1bd5c..cea67e5d8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SingleTableInheritanceTest.php @@ -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; /** diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index ce9b09d37..ededce96f 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -194,7 +194,7 @@ class NotifyChangedEntity implements \Doctrine\Common\NotifyPropertyChanged */ private $id; /** - * @DoctrineColumn(type="varchar") + * @DoctrineColumn(type="string") */ private $data;