[2.0] Implemented support for mapped superclasses. Fixed #2353.
This commit is contained in:
parent
772066156c
commit
e1f2b8abec
@ -302,8 +302,7 @@ class ArrayCollection implements Collection
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
// Note: Little "trick". Empty arrays evaluate to FALSE. No need to count().
|
||||
return ! (bool) $this->_elements;
|
||||
return ! $this->_elements;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\Common\DoctrineException;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
use Doctrine\ORM\Proxy\ProxyFactory;
|
||||
use Doctrine\ORM\Proxy\ProxyClassGenerator;
|
||||
use Doctrine\Common\EventManager,
|
||||
Doctrine\Common\DoctrineException,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Mapping\ClassMetadataFactory,
|
||||
Doctrine\ORM\Proxy\ProxyFactory,
|
||||
Doctrine\ORM\Proxy\ProxyClassGenerator;
|
||||
|
||||
/**
|
||||
* The EntityManager is the central access point to ORM functionality.
|
||||
@ -146,7 +146,6 @@ class EntityManager
|
||||
$this->_metadataFactory = new ClassMetadataFactory($this);
|
||||
$this->_metadataFactory->setCacheDriver($this->_config->getMetadataCacheImpl());
|
||||
$this->_unitOfWork = new UnitOfWork($this);
|
||||
//FIX: this should be in a factory
|
||||
$this->_proxyFactory = new ProxyFactory($this, new ProxyClassGenerator($this, $this->_config->getCacheDir()));
|
||||
}
|
||||
|
||||
@ -179,7 +178,7 @@ class EntityManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits a running transaction.
|
||||
* Commits a transaction on the underlying database connection.
|
||||
*
|
||||
* This causes a flush() of the EntityManager if the flush mode is set to
|
||||
* AUTO or COMMIT.
|
||||
@ -482,8 +481,7 @@ class EntityManager
|
||||
* Determines whether an entity instance is managed in this EntityManager.
|
||||
*
|
||||
* @param object $entity
|
||||
* @return boolean TRUE if this EntityManager currently manages the given entity
|
||||
* (and has it in the identity map), FALSE otherwise.
|
||||
* @return boolean TRUE if this EntityManager currently manages the given entity, FALSE otherwise.
|
||||
*/
|
||||
public function contains($entity)
|
||||
{
|
||||
@ -514,12 +512,12 @@ class EntityManager
|
||||
/**
|
||||
* Throws an exception if the EntityManager is closed or currently not active.
|
||||
*
|
||||
* @throws EntityManagerException If the EntityManager is closed or not active.
|
||||
* @throws EntityManagerException If the EntityManager is closed.
|
||||
*/
|
||||
private function _errorIfClosed()
|
||||
{
|
||||
if ($this->_closed) {
|
||||
throw EntityManagerException::notActiveOrClosed();
|
||||
throw EntityManagerException::closed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,19 +541,19 @@ class EntityManager
|
||||
if ( ! isset($this->_hydrators[$hydrationMode])) {
|
||||
switch ($hydrationMode) {
|
||||
case Query::HYDRATE_OBJECT:
|
||||
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this);
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\ObjectHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_ARRAY:
|
||||
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this);
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\ArrayHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SCALAR:
|
||||
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\ScalarHydrator($this);
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\ScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SINGLE_SCALAR:
|
||||
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\SingleScalarHydrator($this);
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\SingleScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_NONE:
|
||||
$this->_hydrators[$hydrationMode] = new \Doctrine\ORM\Internal\Hydration\NoneHydrator($this);
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\NoneHydrator($this);
|
||||
break;
|
||||
default:
|
||||
throw DoctrineException::updateMe("No hydrator found for hydration mode '$hydrationMode'.");
|
||||
|
@ -36,23 +36,28 @@ namespace Doctrine\ORM;
|
||||
*/
|
||||
class EntityRepository
|
||||
{
|
||||
protected $_entityName;
|
||||
protected $_em;
|
||||
protected $_classMetadata;
|
||||
private $_entityName;
|
||||
private $_em;
|
||||
private $_class;
|
||||
|
||||
public function __construct($em, \Doctrine\ORM\Mapping\ClassMetadata $classMetadata)
|
||||
/**
|
||||
* Initializes a new <tt>EntityRepository</tt>.
|
||||
*
|
||||
* @param EntityManager $em The EntityManager to use.
|
||||
* @param ClassMetadata $classMetadata The class descriptor.
|
||||
*/
|
||||
public function __construct($em, \Doctrine\ORM\Mapping\ClassMetadata $class)
|
||||
{
|
||||
$this->_entityName = $classMetadata->name;
|
||||
$this->_entityName = $class->name;
|
||||
$this->_em = $em;
|
||||
$this->_classMetadata = $classMetadata;
|
||||
$this->_class = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new Doctrine_Query object and adds the component name
|
||||
* of this table as the query 'from' part
|
||||
* Creates a new Doctrine_Query object and adds the component name
|
||||
* of this table as the query 'from' part.
|
||||
*
|
||||
* @param string Optional alias name for component aliasing.
|
||||
*
|
||||
* @return Doctrine_Query
|
||||
*/
|
||||
protected function _createQuery($alias = '')
|
||||
@ -68,26 +73,26 @@ class EntityRepository
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->_em->getUnitOfWork()->clearIdentitiesForEntity($this->_classMetadata->rootEntityName);
|
||||
$this->_em->clear($this->_class->rootEntityName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity by its primary key.
|
||||
* Finds an entity by its primary key / identifier.
|
||||
*
|
||||
* @param $id The identifier.
|
||||
* @param int $hydrationMode The hydration mode to use.
|
||||
* @return mixed Array or Doctrine_Entity or false if no result
|
||||
* @param $id The identifier.
|
||||
* @param int $hydrationMode The hydration mode to use.
|
||||
* @return mixed Array or Object or false if no result.
|
||||
*/
|
||||
public function find($id, $hydrationMode = null)
|
||||
{
|
||||
// Check identity map first
|
||||
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_classMetadata->rootEntityName)) {
|
||||
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
|
||||
return $entity; // Hit!
|
||||
}
|
||||
|
||||
if ( ! is_array($id) || count($id) <= 1) {
|
||||
$value = is_array($id) ? array_values($id) : array($id);
|
||||
$id = array_combine($this->_classMetadata->identifier, $value);
|
||||
$id = array_combine($this->_class->identifier, $value);
|
||||
}
|
||||
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
|
||||
@ -127,9 +132,10 @@ class EntityRepository
|
||||
*/
|
||||
protected function findOneBy($fieldName, $value, $hydrationMode = null)
|
||||
{
|
||||
$results = $this->_createQuery()->where($fieldName . ' = ?')->limit(1)->execute(
|
||||
array($value), $hydrationMode);
|
||||
return $hydrationMode === Doctrine::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst();
|
||||
$results = $this->_createQuery()->where($fieldName . ' = ?')
|
||||
->setMaxResults(1)
|
||||
->execute(array($value), $hydrationMode);
|
||||
return $hydrationMode === Query::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,10 +168,10 @@ class EntityRepository
|
||||
$fieldName = Doctrine::tableize($by);
|
||||
$hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
|
||||
|
||||
if ($this->_classMetadata->hasField($fieldName)) {
|
||||
if ($this->_class->hasField($fieldName)) {
|
||||
return $this->$method($fieldName, $arguments[0], $hydrationMode);
|
||||
} else if ($this->_classMetadata->hasRelation($by)) {
|
||||
$relation = $this->_classMetadata->getRelation($by);
|
||||
} else if ($this->_class->hasRelation($by)) {
|
||||
$relation = $this->_class->getRelation($by);
|
||||
if ($relation['type'] === Doctrine_Relation::MANY) {
|
||||
throw DoctrineException::updateMe('Cannot findBy many relationship.');
|
||||
}
|
||||
|
@ -42,12 +42,12 @@ class ArrayHydrator extends AbstractHydrator
|
||||
/** @override */
|
||||
protected function _prepare()
|
||||
{
|
||||
$this->_isSimpleQuery = $this->_rsm->getEntityResultCount() <= 1;
|
||||
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
|
||||
$this->_identifierMap = array();
|
||||
$this->_resultPointers = array();
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
foreach ($this->_rsm->getAliasMap() as $dqlAlias => $className) {
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_resultPointers[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
@ -89,8 +89,6 @@ class ArrayHydrator extends AbstractHydrator
|
||||
// It's a joined result
|
||||
|
||||
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
$relation = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relationAlias = $relation->getSourceFieldName();
|
||||
$path = $parent . '.' . $dqlAlias;
|
||||
|
||||
// Get a reference to the right element in the result tree.
|
||||
@ -105,6 +103,11 @@ class ArrayHydrator extends AbstractHydrator
|
||||
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
|
||||
continue;
|
||||
}
|
||||
|
||||
$relation = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relationAlias = $relation->sourceFieldName;
|
||||
//$relationAlias = $this->_rsm->relationMap[$dqlAlias];
|
||||
//$relation = $this->_ce[$parentClass]->associationMappings[$relationField];
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! $relation->isOneToOne()) {
|
||||
@ -113,9 +116,11 @@ class ArrayHydrator extends AbstractHydrator
|
||||
if ( ! isset($baseElement[$relationAlias])) {
|
||||
$baseElement[$relationAlias] = array();
|
||||
}
|
||||
|
||||
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
|
||||
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
|
||||
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
|
||||
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
$element = $data;
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
@ -176,7 +181,6 @@ class ArrayHydrator extends AbstractHydrator
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
}
|
||||
$this->updateResultPointer($result, $index, $dqlAlias, false);
|
||||
//unset($rowData[$rootAlias]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// Remember which associations are "fetch joined"
|
||||
if (isset($this->_rsm->relationMap[$dqlAlias])) {
|
||||
$assoc = $this->_rsm->relationMap[$dqlAlias];
|
||||
//$assoc = $class->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
|
||||
$this->_fetchedAssociations[$assoc->sourceEntityName][$assoc->sourceFieldName] = true;
|
||||
if ($assoc->mappedByFieldName) {
|
||||
$this->_fetchedAssociations[$assoc->targetEntityName][$assoc->mappedByFieldName] = true;
|
||||
@ -148,9 +149,9 @@ class ObjectHydrator extends AbstractHydrator
|
||||
end($coll);
|
||||
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
|
||||
} else if ($coll instanceof Collection) {
|
||||
if (count($coll) > 0) {
|
||||
//if ( ! $coll->isEmpty()) {
|
||||
$this->_resultPointers[$dqlAlias] = $coll->last();
|
||||
}
|
||||
//}
|
||||
} else {
|
||||
$this->_resultPointers[$dqlAlias] = $coll;
|
||||
}
|
||||
@ -301,8 +302,6 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// It's a joined result
|
||||
|
||||
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
$relation = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relationField = $relation->sourceFieldName;
|
||||
|
||||
// Get a reference to the right element in the result tree.
|
||||
// This element will get the associated element attached.
|
||||
@ -319,9 +318,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
$parentClass = get_class($baseElement);
|
||||
$oid = spl_object_hash($baseElement);
|
||||
$relation = $this->_rsm->relationMap[$dqlAlias];
|
||||
//$relationField = $this->_rsm->relationMap[$dqlAlias];
|
||||
//$relation = $this->_ce[$parentClass]->associationMappings[$relationField];
|
||||
$relationField = $relation->sourceFieldName;
|
||||
$reflField = $this->_ce[$parentClass]->reflFields[$relationField];
|
||||
$reflFieldValue = $reflField->getValue($baseElement);
|
||||
|
||||
|
||||
// Check the type of the relation (many or single-valued)
|
||||
if ( ! $relation->isOneToOne()) {
|
||||
// Collection-valued association
|
||||
@ -406,7 +409,6 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
} else {
|
||||
// Its a root result element
|
||||
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
|
||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
|
@ -24,8 +24,8 @@ namespace Doctrine\ORM\Mapping;
|
||||
use Doctrine\Common\DoctrineException;
|
||||
|
||||
/**
|
||||
* A <tt>ClassMetadata</tt> instance holds all the ORM metadata of an entity and
|
||||
* it's associations. It is the backbone of Doctrine's metadata mapping.
|
||||
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
|
||||
* of an entity and it's associations.
|
||||
*
|
||||
* Once populated, ClassMetadata instances are usually cached in a serialized form.
|
||||
*
|
||||
@ -142,6 +142,13 @@ final class ClassMetadata
|
||||
* @var string
|
||||
*/
|
||||
public $customRepositoryClassName;
|
||||
|
||||
/**
|
||||
* Whether this class describes the mapping of a mapped superclass.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $isMappedSuperclass = false;
|
||||
|
||||
/**
|
||||
* The names of the parent classes (ancestors).
|
||||
@ -1352,14 +1359,16 @@ final class ClassMetadata
|
||||
* @param string $owningClassName The name of the class that defined this mapping.
|
||||
* @todo Rename: addInheritedAssociationMapping
|
||||
*/
|
||||
public function addAssociationMapping(AssociationMapping $mapping, $owningClassName)
|
||||
public function addAssociationMapping(AssociationMapping $mapping, $owningClassName = null)
|
||||
{
|
||||
$sourceFieldName = $mapping->sourceFieldName;
|
||||
if (isset($this->associationMappings[$sourceFieldName])) {
|
||||
throw MappingException::duplicateFieldMapping();
|
||||
}
|
||||
$this->associationMappings[$sourceFieldName] = $mapping;
|
||||
$this->inheritedAssociationFields[$sourceFieldName] = $owningClassName;
|
||||
if ($owningClassName !== null) {
|
||||
$this->inheritedAssociationFields[$sourceFieldName] = $owningClassName;
|
||||
}
|
||||
$this->_registerMappingIfInverse($mapping);
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,9 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Common\DoctrineException;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\Common\DoctrineException,
|
||||
Doctrine\DBAL\Platforms\AbstractPlatform,
|
||||
Doctrine\ORM\Events;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
@ -89,7 +89,7 @@ class ClassMetadataFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metadata object for a class.
|
||||
* Gets the class metadata descriptor for a class.
|
||||
*
|
||||
* @param string $className The name of the class.
|
||||
* @return Doctrine\ORM\Mapping\ClassMetadata
|
||||
@ -112,6 +112,11 @@ class ClassMetadataFactory
|
||||
return $this->_loadedMetadata[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $className
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasMetadataFor($className)
|
||||
{
|
||||
return isset($this->_loadedMetadata[$className]);
|
||||
@ -156,11 +161,14 @@ class ClassMetadataFactory
|
||||
foreach ($parentClasses as $className) {
|
||||
if (isset($this->_loadedMetadata[$className])) {
|
||||
$parent = $this->_loadedMetadata[$className];
|
||||
array_unshift($visited, $className);
|
||||
if ( ! $parent->isMappedSuperclass) {
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $this->_newClassMetadataInstance($className);
|
||||
|
||||
if ($parent) {
|
||||
$class->setInheritanceType($parent->inheritanceType);
|
||||
$class->setDiscriminatorColumn($parent->discriminatorColumn);
|
||||
@ -176,7 +184,7 @@ class ClassMetadataFactory
|
||||
$this->_driver->loadMetadataForClass($className, $class);
|
||||
|
||||
// Verify & complete identifier mapping
|
||||
if ( ! $class->identifier) {
|
||||
if ( ! $class->identifier && ! $class->isMappedSuperclass) {
|
||||
throw MappingException::identifierRequired($className);
|
||||
}
|
||||
if ($parent) {
|
||||
@ -207,7 +215,10 @@ class ClassMetadataFactory
|
||||
$this->_loadedMetadata[$className] = $class;
|
||||
|
||||
$parent = $class;
|
||||
array_unshift($visited, $className);
|
||||
|
||||
if ( ! $class->isMappedSuperclass) {
|
||||
array_unshift($visited, $className);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,7 +242,7 @@ class ClassMetadataFactory
|
||||
private function _addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
|
||||
{
|
||||
foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
|
||||
if ( ! isset($mapping['inherited'])) {
|
||||
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
|
||||
$mapping['inherited'] = $parentClass->name;
|
||||
}
|
||||
$subClass->addFieldMapping($mapping);
|
||||
@ -253,9 +264,11 @@ class ClassMetadataFactory
|
||||
if (isset($parentClass->inheritedAssociationFields[$mapping->sourceFieldName])) {
|
||||
// parent class also inherited that one
|
||||
$subClass->addAssociationMapping($mapping, $parentClass->inheritedAssociationFields[$mapping->sourceFieldName]);
|
||||
} else {
|
||||
} else if ( ! $parentClass->isMappedSuperclass) {
|
||||
// parent class defined that one
|
||||
$subClass->addAssociationMapping($mapping, $parentClass->name);
|
||||
} else {
|
||||
$subClass->addAssociationMapping($mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,11 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\Common\DoctrineException;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\Common\DoctrineException,
|
||||
Doctrine\Common\Cache\ArrayCache,
|
||||
Doctrine\Common\Annotations\AnnotationReader,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Mapping\MappingException;
|
||||
|
||||
require __DIR__ . '/DoctrineAnnotations.php';
|
||||
|
||||
@ -60,13 +60,15 @@ class AnnotationDriver implements Driver
|
||||
|
||||
$classAnnotations = $this->_reader->getClassAnnotations($class);
|
||||
|
||||
// Evaluate DoctrineEntity annotation
|
||||
if ( ! isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
||||
throw DoctrineException::updateMe("$className is no entity.");
|
||||
// Evaluate Entity annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
||||
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
||||
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
||||
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
throw DoctrineException::updateMe("$className is no entity or mapped superclass.");
|
||||
}
|
||||
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
||||
|
||||
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
||||
|
||||
// Evaluate DoctrineTable annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) {
|
||||
|
@ -21,11 +21,14 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use \Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/* Annotations */
|
||||
|
||||
final class Entity extends \Doctrine\Common\Annotations\Annotation {
|
||||
public $repositoryClass;
|
||||
}
|
||||
final class MappedSuperclass extends Annotation {}
|
||||
final class InheritanceType extends \Doctrine\Common\Annotations\Annotation {}
|
||||
final class DiscriminatorColumn extends \Doctrine\Common\Annotations\Annotation {
|
||||
public $name;
|
||||
|
@ -35,7 +35,7 @@ final class NativeQuery extends AbstractQuery
|
||||
* Initializes a new instance of the <tt>NativeQuery</tt> class that is bound
|
||||
* to the given EntityManager.
|
||||
*
|
||||
* @param EntityManager $em
|
||||
* @param EntityManager $em The EntityManager to use.
|
||||
*/
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
|
@ -22,9 +22,8 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
/**
|
||||
* EntityManagerException
|
||||
* OptimisticLockException
|
||||
*
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
|
@ -25,11 +25,16 @@ namespace Doctrine\ORM\Query;
|
||||
* A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.
|
||||
*
|
||||
* IMPORTANT NOTE:
|
||||
* The properties of this class are only public for fast internal READ access.
|
||||
* The properties of this class are only public for fast internal READ access and to (drastically)
|
||||
* reduce the size of serialized instances for more effective caching due to better (un-)serialization
|
||||
* performance.
|
||||
*
|
||||
* Users should use the public methods.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @todo Do not store AssociationMappings in $relationMap. These bloat serialized instances
|
||||
* and in turn unserialize performance suffers which is important for most effective caching.
|
||||
*/
|
||||
class ResultSetMapping
|
||||
{
|
||||
@ -54,7 +59,7 @@ class ResultSetMapping
|
||||
/** Maps alias names to field names that should be used for indexing. */
|
||||
public $indexByMap = array();
|
||||
/** A list of columns that should be ignored/skipped during hydration. */
|
||||
public $ignoredColumns = array();
|
||||
//public $ignoredColumns = array();
|
||||
|
||||
/**
|
||||
*
|
||||
@ -318,19 +323,19 @@ class ResultSetMapping
|
||||
*
|
||||
* @param string $columnName
|
||||
*/
|
||||
public function addIgnoredColumn($columnName)
|
||||
/*public function addIgnoredColumn($columnName)
|
||||
{
|
||||
$this->ignoredColumns[$columnName] = true;
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $columnName
|
||||
* @return boolean
|
||||
*/
|
||||
public function isIgnoredColumn($columnName)
|
||||
/*public function isIgnoredColumn($columnName)
|
||||
{
|
||||
return isset($this->ignoredColumns[$columnName]);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,12 @@ class QueryBuilder
|
||||
const STATE_CLEAN = 1;
|
||||
|
||||
/**
|
||||
* @var EntityManager $em Instance of an EntityManager to use for query
|
||||
* @var EntityManager $em Instance of an EntityManager to use for query.
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
/**
|
||||
* @var array $dqlParts The array of DQL parts collected
|
||||
* @var array $dqlParts The array of DQL parts collected.
|
||||
*/
|
||||
private $_dqlParts = array(
|
||||
'select' => array(),
|
||||
@ -64,25 +64,30 @@ class QueryBuilder
|
||||
);
|
||||
|
||||
/**
|
||||
* @var integer $type The type of query this is. Can be select, update or delete
|
||||
* @var integer The type of query this is. Can be select, update or delete.
|
||||
*/
|
||||
private $_type = self::SELECT;
|
||||
|
||||
/**
|
||||
* @var integer $state The state of the query object. Can be dirty or clean.
|
||||
* @var integer The state of the query object. Can be dirty or clean.
|
||||
*/
|
||||
private $_state = self::STATE_CLEAN;
|
||||
|
||||
/**
|
||||
* @var string $dql The complete DQL string for this query
|
||||
* @var string The complete DQL string for this query.
|
||||
*/
|
||||
private $_dql;
|
||||
|
||||
/**
|
||||
* @var Query $q The Query instance used for this QueryBuilder
|
||||
* @var Query The Query instance used for this QueryBuilder.
|
||||
*/
|
||||
private $_q;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.
|
||||
*
|
||||
* @param EntityManager $entityManager The EntityManager to use.
|
||||
*/
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->_em = $entityManager;
|
||||
@ -399,12 +404,10 @@ class QueryBuilder
|
||||
*
|
||||
* BNF:
|
||||
*
|
||||
* UpdateStatement = UpdateClause [WhereClause] [OrderByClause] [LimitClause] [OffsetClause]
|
||||
* UpdateStatement = UpdateClause [WhereClause] [OrderByClause]
|
||||
* UpdateClause = "UPDATE" RangeVariableDeclaration "SET" UpdateItem {"," UpdateItem}
|
||||
* WhereClause = "WHERE" ConditionalExpression
|
||||
* OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
|
||||
* LimitClause = "LIMIT" integer
|
||||
* OffsetClause = "OFFSET" integer
|
||||
*
|
||||
* @return string $dql
|
||||
*/
|
||||
@ -422,15 +425,13 @@ class QueryBuilder
|
||||
*
|
||||
* BNF:
|
||||
*
|
||||
* SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] [LimitClause] [OffsetClause]
|
||||
* SelectStatement = [SelectClause] FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
|
||||
* SelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}
|
||||
* FromClause = "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}
|
||||
* WhereClause = "WHERE" ConditionalExpression
|
||||
* GroupByClause = "GROUP" "BY" GroupByItem {"," GroupByItem}
|
||||
* HavingClause = "HAVING" ConditionalExpression
|
||||
* OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
|
||||
* LimitClause = "LIMIT" integer
|
||||
* OffsetClause = "OFFSET" integer
|
||||
*
|
||||
* @return string $dql
|
||||
*/
|
||||
|
@ -252,11 +252,12 @@ class SchemaTool
|
||||
$joinTableColumns = array();
|
||||
$joinTableOptions = array();
|
||||
$joinTable = $mapping->getJoinTable();
|
||||
$constraint1 = array();
|
||||
$constraint1['tableName'] = $joinTable['name'];
|
||||
$constraint1['foreignTable'] = $class->getTableName();
|
||||
$constraint1['local'] = array();
|
||||
$constraint1['foreign'] = array();
|
||||
$constraint1 = array(
|
||||
'tableName' => $joinTable['name'],
|
||||
'foreignTable' => $class->getTableName(),
|
||||
'local' => array(),
|
||||
'foreign' => array()
|
||||
);
|
||||
foreach ($joinTable['joinColumns'] as $joinColumn) {
|
||||
$column = array();
|
||||
$column['primary'] = true;
|
||||
|
@ -1552,8 +1552,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ( ! $assocMapping->isCascadeRemove) {
|
||||
continue;
|
||||
}
|
||||
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]
|
||||
->getValue($entity);
|
||||
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]->getValue($entity);
|
||||
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->_doRemove($relatedEntity, $visited);
|
||||
@ -1607,16 +1606,6 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
$this->_orphanRemovals[spl_object_hash($entity)] = $entity;
|
||||
}
|
||||
|
||||
/*public function scheduleCollectionUpdate(PersistentCollection $coll)
|
||||
{
|
||||
$this->_collectionUpdates[] = $coll;
|
||||
}*/
|
||||
|
||||
/*public function isCollectionScheduledForUpdate(PersistentCollection $coll)
|
||||
{
|
||||
//...
|
||||
}*/
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
|
@ -38,6 +38,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ReferenceProxyTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\LifecycleCallbackTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\StandardEntityPersisterTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\MappedSuperclassTest');
|
||||
|
||||
$suite->addTest(Locking\AllTests::suite());
|
||||
|
||||
|
111
tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php
Normal file
111
tests/Doctrine/Tests/ORM/Functional/MappedSuperclassTest.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* MappedSuperclassTest
|
||||
*
|
||||
* @author robo
|
||||
*/
|
||||
class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\EntitySubClass'),
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
// Swallow all exceptions. We do not test the schema tool here.
|
||||
}
|
||||
}
|
||||
|
||||
public function testCRUD()
|
||||
{
|
||||
$e = new EntitySubClass;
|
||||
$e->setId(1);
|
||||
$e->setName('Roman');
|
||||
$e->setMapped1(42);
|
||||
$e->setMapped2('bar');
|
||||
|
||||
$this->_em->persist($e);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\EntitySubClass', 1);
|
||||
$this->assertEquals(1, $e2->getId());
|
||||
$this->assertEquals('Roman', $e2->getName());
|
||||
$this->assertNull($e2->getMappedRelated1());
|
||||
$this->assertEquals(42, $e2->getMapped1());
|
||||
$this->assertEquals('bar', $e2->getMapped2());
|
||||
}
|
||||
}
|
||||
|
||||
/** @MappedSuperclass */
|
||||
class MappedSuperclassBase {
|
||||
/** @Column(type="integer") */
|
||||
private $mapped1;
|
||||
/** @Column(type="string") */
|
||||
private $mapped2;
|
||||
/**
|
||||
* @OneToOne(targetEntity="MappedSuperclassRelated1")
|
||||
* @JoinColumn(name="related1_id", referencedColumnName="id")
|
||||
*/
|
||||
private $mappedRelated1;
|
||||
private $transient;
|
||||
|
||||
public function setMapped1($val) {
|
||||
$this->mapped1 = $val;
|
||||
}
|
||||
|
||||
public function getMapped1() {
|
||||
return $this->mapped1;
|
||||
}
|
||||
|
||||
public function setMapped2($val) {
|
||||
$this->mapped2 = $val;
|
||||
}
|
||||
|
||||
public function getMapped2() {
|
||||
return $this->mapped2;
|
||||
}
|
||||
|
||||
public function getMappedRelated1() {
|
||||
return $this->mappedRelated1;
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class MappedSuperclassRelated1 {
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class EntitySubClass extends MappedSuperclassBase {
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setId($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\YamlDriverTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataFactoryTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\ClassMetadataLoadEventTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Mapping\BasicInheritanceMappingTest');
|
||||
|
||||
return $suite;
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Mapping;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class BasicInheritanceMappingTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
private $_factory;
|
||||
|
||||
protected function setUp() {
|
||||
$this->_factory = new ClassMetadataFactory($this->_getTestEntityManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\Common\DoctrineException
|
||||
*/
|
||||
public function testGetMetadataForTransientClassThrowsException()
|
||||
{
|
||||
$this->_factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\TransientBaseClass');
|
||||
}
|
||||
|
||||
public function testGetMetadataForSubclassWithTransientBaseClass()
|
||||
{
|
||||
$class = $this->_factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\EntitySubClass');
|
||||
|
||||
$this->assertTrue(empty($class->subClasses));
|
||||
$this->assertTrue(empty($class->parentClasses));
|
||||
$this->assertTrue(isset($class->fieldMappings['id']));
|
||||
$this->assertTrue(isset($class->fieldMappings['name']));
|
||||
}
|
||||
|
||||
public function testGetMetadataForSubclassWithMappedSuperclass()
|
||||
{
|
||||
$class = $this->_factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\EntitySubClass2');
|
||||
|
||||
$this->assertTrue(empty($class->subClasses));
|
||||
$this->assertTrue(empty($class->parentClasses));
|
||||
|
||||
$this->assertTrue(isset($class->fieldMappings['mapped1']));
|
||||
$this->assertTrue(isset($class->fieldMappings['mapped2']));
|
||||
$this->assertTrue(isset($class->fieldMappings['id']));
|
||||
$this->assertTrue(isset($class->fieldMappings['name']));
|
||||
|
||||
$this->assertFalse(isset($class->fieldMappings['mapped1']['inherited']));
|
||||
$this->assertFalse(isset($class->fieldMappings['mapped2']['inherited']));
|
||||
$this->assertFalse(isset($class->fieldMappings['transient']));
|
||||
|
||||
$this->assertTrue(empty($class->inheritedAssociationFields));
|
||||
$this->assertTrue(isset($class->associationMappings['mappedRelated1']));
|
||||
}
|
||||
}
|
||||
|
||||
class TransientBaseClass {
|
||||
private $transient1;
|
||||
private $transient2;
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class EntitySubClass extends TransientBaseClass
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
}
|
||||
|
||||
/** @MappedSuperclass */
|
||||
class MappedSuperclassBase {
|
||||
/** @Column(type="integer") */
|
||||
private $mapped1;
|
||||
/** @Column(type="string") */
|
||||
private $mapped2;
|
||||
/**
|
||||
* @OneToOne(targetEntity="MappedSuperclassRelated1")
|
||||
* @JoinColumn(name="related1_id", referencedColumnName="id")
|
||||
*/
|
||||
private $mappedRelated1;
|
||||
private $transient;
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class EntitySubClass2 extends MappedSuperclassBase {
|
||||
/** @Id @Column(type="integer") */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user