Merge remote-tracking branch 'doctrine/master' into shesek-patch-1
This commit is contained in:
commit
fea855004c
@ -1,6 +1,6 @@
|
||||
# Doctrine 2 ORM
|
||||
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.0+ that provides transparent persistence
|
||||
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.3.2+ that provides transparent persistence
|
||||
for PHP objects. It sits on top of a powerful database abstraction layer (DBAL). One of its key features
|
||||
is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL),
|
||||
inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility
|
||||
|
@ -1,3 +1,30 @@
|
||||
# EntityManager#getPartialReference() creates read-only entity
|
||||
|
||||
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they
|
||||
haven't been in the identity map before. This means objects of this kind never lead to changes
|
||||
in the UnitOfWork.
|
||||
|
||||
# Fields omitted in a partial DQL query or a native query are never updated
|
||||
|
||||
Fields of an entity that are not returned from a partial DQL Query or native SQL query
|
||||
will never be updated through an UPDATE statement.
|
||||
|
||||
# Removed support for onUpdate in @JoinColumn
|
||||
|
||||
The onUpdate foreign key handling makes absolutly no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.
|
||||
|
||||
# Changes in Annotation Handling
|
||||
|
||||
There have been some changes to the annotation handling in Common 2.2 again, that affect how people with old configurations
|
||||
from 2.0 have to configure the annotation driver if they don't use `Configuration::newDefaultAnnotationDriver()`:
|
||||
|
||||
// Register the ORM Annotations in the AnnotationRegistry
|
||||
AnnotationRegistry::registerFile('path/to/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
|
||||
|
||||
$reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader();
|
||||
$reader->addNamespace('Doctrine\ORM\Mapping');
|
||||
$reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache());
|
||||
|
||||
$driver = new AnnotationDriver($reader, (array)$paths);
|
||||
|
||||
$config->setMetadataDriverImpl($driver);
|
@ -8,6 +8,7 @@ report.dir=reports
|
||||
log.archive.dir=logs
|
||||
project.pirum_dir=
|
||||
project.download_dir=
|
||||
project.xsd_dir=
|
||||
test.phpunit_configuration_file=
|
||||
test.phpunit_generate_coverage=0
|
||||
test.pmd_reports=0
|
||||
|
@ -223,7 +223,12 @@
|
||||
|
||||
<target name="distribute-download">
|
||||
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
|
||||
<copy file="${dist.dir}/doctrine-orm-${version}.phar" todir="${project.download_dir}" />
|
||||
<!--<copy file="${dist.dir}/doctrine-orm-${version}.phar" todir="${project.download_dir}" />-->
|
||||
</target>
|
||||
|
||||
<target name="distribute-xsd">
|
||||
<php expression="substr('${version}', 0, 3)" returnProperty="minorVersion" /><!-- not too robust -->
|
||||
<copy file="${project.basedir}/doctrine-mapping.xsd" tofile="${project.xsd_dir}/doctrine-mapping-${minorVersion}.xsd" />
|
||||
</target>
|
||||
|
||||
<target name="update-dev-version">
|
||||
@ -235,7 +240,7 @@
|
||||
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
|
||||
</target>
|
||||
|
||||
<target name="release" depends="git-tag,build-packages,package-phar,distribute-download,pirum-release,update-dev-version" />
|
||||
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version,distribute-xsd" />
|
||||
|
||||
<!--
|
||||
Builds distributable PEAR packages for the Symfony Dependencies
|
||||
|
20
composer.json
Normal file
20
composer.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "doctrine/orm",
|
||||
"type": "library",
|
||||
"description": "Object-Relational-Mapper for PHP",
|
||||
"keywords": ["orm", "database"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"license": "LGPL",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
|
||||
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
|
||||
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"ext-pdo": "*",
|
||||
"doctrine/common": "master-dev",
|
||||
"doctrine/dbal": "master-dev"
|
||||
}
|
||||
}
|
@ -150,7 +150,7 @@
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="CASCADE"/>
|
||||
<xs:enumeration value="RESTRICT"/>
|
||||
<xs:enumeration value="SET_NULL"/>
|
||||
<xs:enumeration value="SET NULL"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
@ -163,6 +163,16 @@ abstract class AbstractQuery
|
||||
return $this->_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defined parameter types.
|
||||
*
|
||||
* @return array The defined query parameter types.
|
||||
*/
|
||||
public function getParameterTypes()
|
||||
{
|
||||
return $this->_paramTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parameter.
|
||||
*
|
||||
@ -174,6 +184,17 @@ abstract class AbstractQuery
|
||||
return isset($this->_params[$key]) ? $this->_params[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a query parameter type.
|
||||
*
|
||||
* @param mixed $key The key (index or name) of the bound parameter.
|
||||
* @return mixed The parameter type of the bound parameter.
|
||||
*/
|
||||
public function getParameterType($key)
|
||||
{
|
||||
return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL query that corresponds to this query object.
|
||||
* The returned SQL syntax depends on the connection driver that is used
|
||||
|
@ -516,4 +516,32 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
}
|
||||
return $this->_attributes['classMetadataFactoryName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default repository class.
|
||||
*
|
||||
* @since 2.2
|
||||
* @param string $className
|
||||
* @throws ORMException If not is a Doctrine\ORM\EntityRepository
|
||||
*/
|
||||
public function setDefaultRepositoryClassName($className)
|
||||
{
|
||||
if ($className != "Doctrine\ORM\EntityRepository" &&
|
||||
!is_subclass_of($className, 'Doctrine\ORM\EntityRepository')){
|
||||
throw ORMException::invalidEntityRepository($className);
|
||||
}
|
||||
$this->_attributes['defaultRepositoryClassName'] = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default repository class.
|
||||
*
|
||||
* @since 2.2
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultRepositoryClassName()
|
||||
{
|
||||
return isset($this->_attributes['defaultRepositoryClassName']) ?
|
||||
$this->_attributes['defaultRepositoryClassName'] : 'Doctrine\ORM\EntityRepository';
|
||||
}
|
||||
}
|
@ -413,6 +413,7 @@ class EntityManager implements ObjectManager
|
||||
$entity = $class->newInstance();
|
||||
$class->setIdentifierValues($entity, $identifier);
|
||||
$this->unitOfWork->registerManaged($entity, $identifier, array());
|
||||
$this->unitOfWork->markReadOnly($entity);
|
||||
|
||||
return $entity;
|
||||
}
|
||||
@ -421,16 +422,11 @@ class EntityManager implements ObjectManager
|
||||
* Clears the EntityManager. All entities that are currently managed
|
||||
* by this EntityManager become detached.
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param string $entityName if given, only entities of this type will get detached
|
||||
*/
|
||||
public function clear($entityName = null)
|
||||
{
|
||||
if ($entityName === null) {
|
||||
$this->unitOfWork->clear();
|
||||
} else {
|
||||
//TODO
|
||||
throw new ORMException("EntityManager#clear(\$entityName) not yet implemented.");
|
||||
}
|
||||
$this->unitOfWork->clear($entityName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -576,7 +572,8 @@ class EntityManager implements ObjectManager
|
||||
if ($customRepositoryClassName !== null) {
|
||||
$repository = new $customRepositoryClassName($this, $metadata);
|
||||
} else {
|
||||
$repository = new EntityRepository($this, $metadata);
|
||||
$repositoryClass = $this->config->getDefaultRepositoryClassName();
|
||||
$repository = new $repositoryClass($this, $metadata);
|
||||
}
|
||||
|
||||
$this->repositories[$entityName] = $repository;
|
||||
@ -712,6 +709,18 @@ class EntityManager implements ObjectManager
|
||||
return $this->proxyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to initialize a lazy loading proxy or persistent collection.
|
||||
*
|
||||
* This method is a no-op for other objects
|
||||
*
|
||||
* @param object $obj
|
||||
*/
|
||||
public function initializeObject($obj)
|
||||
{
|
||||
$this->unitOfWork->initializeObject($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create EntityManager instances.
|
||||
*
|
||||
|
@ -178,7 +178,7 @@ class EntityRepository implements ObjectRepository
|
||||
*/
|
||||
public function findOneBy(array $criteria)
|
||||
{
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria);
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria, null, null, array(), 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
110
lib/Doctrine/ORM/Event/EntityEventDelegator.php
Normal file
110
lib/Doctrine/ORM/Event/EntityEventDelegator.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Event;
|
||||
|
||||
use \Doctrine\Common\EventSubscriber;
|
||||
use \LogicException;
|
||||
|
||||
/**
|
||||
* Delegate events only for certain entities they are registered for.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.2
|
||||
*/
|
||||
class EntityEventDelegator implements EventSubscriber
|
||||
{
|
||||
/**
|
||||
* Keeps track of all the event listeners.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $listeners = array();
|
||||
|
||||
/**
|
||||
* If frozen no new event listeners can be added.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $frozen = false;
|
||||
|
||||
/**
|
||||
* Adds an event listener that listens on the specified events.
|
||||
*
|
||||
* @param string|array $events The event(s) to listen on.
|
||||
* @param string|array $entities The entities to trigger this listener for
|
||||
* @param object $listener The listener object.
|
||||
*/
|
||||
public function addEventListener($events, $entities, $listener)
|
||||
{
|
||||
if ($this->frozen) {
|
||||
throw new LogicException("Cannot add event listeners after EntityEventDelegator::getSubscribedEvents() " .
|
||||
"is called once. This happens when you register the delegator with the event manager.");
|
||||
}
|
||||
|
||||
// Picks the hash code related to that listener
|
||||
$hash = spl_object_hash($listener);
|
||||
|
||||
foreach ((array) $events as $event) {
|
||||
// Overrides listener if a previous one was associated already
|
||||
// Prevents duplicate listeners on same event (same instance only)
|
||||
$this->listeners[$event][$hash] = array('listener' => $listener, 'entities' => array_flip((array)$entities));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an EventSubscriber. The subscriber is asked for all the events he is
|
||||
* interested in and added as a listener for these events.
|
||||
*
|
||||
* @param Doctrine\Common\EventSubscriber $subscriber The subscriber.
|
||||
*/
|
||||
public function addEventSubscriber(EventSubscriber $subscriber, $entities)
|
||||
{
|
||||
$this->addEventListener($subscriber->getSubscribedEvents(), $entities, $subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of events this subscriber wants to listen to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
$this->frozen = true;
|
||||
return array_keys($this->listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate the event to an appropriate listener
|
||||
*
|
||||
* @param $eventName
|
||||
* @param $event
|
||||
* @return void
|
||||
*/
|
||||
public function __call($eventName, $args)
|
||||
{
|
||||
$event = $args[0];
|
||||
foreach ($this->listeners[$eventName] AS $listenerData) {
|
||||
$class = get_class($event->getEntity());
|
||||
if (isset($listenerData['entities'][$class])) {
|
||||
$listenerData['listener']->$eventName($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -36,12 +36,18 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $entityClass;
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\EntityManager $em
|
||||
*/
|
||||
public function __construct($em)
|
||||
public function __construct($em, $entityClass = null)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->entityClass = $entityClass;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,4 +57,24 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs
|
||||
{
|
||||
return $this->em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the entity class that is cleared, or empty if all are cleared.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEntityClass()
|
||||
{
|
||||
return $this->entityClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if event clears all entities.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clearsAllEntities()
|
||||
{
|
||||
return $this->entityClass === null;
|
||||
}
|
||||
}
|
@ -90,7 +90,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
|
||||
if (!isset($this->_entityChangeSet[$field])) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Field '".$field."' is not a valid field of the entity ".
|
||||
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs."
|
||||
"'".get_class($this->getEntity())."' in PreUpdateEventArgs."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
$identifier[$idField] = $value;
|
||||
}
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -78,7 +78,7 @@ class AssignedGenerator extends AbstractIdGenerator
|
||||
$identifier[$idField] = $value;
|
||||
}
|
||||
} else {
|
||||
throw ORMException::entityMissingAssignedId($entity);
|
||||
throw ORMException::entityMissingAssignedIdForField($entity, $idField);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,11 @@ abstract class AbstractHydrator
|
||||
* field names during this procedure as well as any necessary conversions on
|
||||
* the values applied.
|
||||
*
|
||||
* @param array $data SQL Result Row
|
||||
* @param array &$cache Cache for column to field result information
|
||||
* @param array &$id Dql-Alias => ID-Hash
|
||||
* @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value?
|
||||
*
|
||||
* @return array An array with all the fields (name => value) of the data row,
|
||||
* grouped by their component alias.
|
||||
*/
|
||||
|
@ -92,6 +92,11 @@ class ArrayHydrator extends AbstractHydrator
|
||||
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
|
||||
$path = $parent . '.' . $dqlAlias;
|
||||
|
||||
// missing parent data, skipping as RIGHT JOIN hydration is not supported.
|
||||
if ( ! isset($nonemptyComponents[$parent]) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get a reference to the right element in the result tree.
|
||||
// This element will get the associated element attached.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
|
||||
@ -155,6 +160,17 @@ class ArrayHydrator extends AbstractHydrator
|
||||
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root
|
||||
|
||||
// if this row has a NULL value for the root result id then make it a null result.
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array(0 => null);
|
||||
} else {
|
||||
$result[] = null;
|
||||
}
|
||||
++$this->_resultCounter;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for an existing element
|
||||
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $rowData[$dqlAlias];
|
||||
|
@ -302,6 +302,12 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// seen for this parent-child relationship
|
||||
$path = $parentAlias . '.' . $dqlAlias;
|
||||
|
||||
// We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
|
||||
if (!isset($nonemptyComponents[$parentAlias])) {
|
||||
// TODO: Add special case code where we hydrate the right join objects into identity map at least
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get a reference to the parent object to which the joined element belongs.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
|
||||
$first = reset($this->_resultPointers);
|
||||
@ -408,6 +414,18 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// PATH C: Its a root result element
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
|
||||
// if this row has a NULL value for the root result id then make it a null result.
|
||||
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$result[] = array(0 => null);
|
||||
} else {
|
||||
$result[] = null;
|
||||
}
|
||||
++$this->_resultCounter;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for existing result from the iterations before
|
||||
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
|
167
lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php
Normal file
167
lib/Doctrine/ORM/Mapping/Builder/AssociationBuilder.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
class AssociationBuilder
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadataBuilder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $mapping;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $joinColumns;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @param ClassMetadataBuilder $builder
|
||||
* @param array $mapping
|
||||
*/
|
||||
public function __construct(ClassMetadataBuilder $builder, array $mapping, $type)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
$this->mapping = $mapping;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function mappedBy($fieldName)
|
||||
{
|
||||
$this->mapping['mappedBy'] = $fieldName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function inversedBy($fieldName)
|
||||
{
|
||||
$this->mapping['inversedBy'] = $fieldName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cascadeAll()
|
||||
{
|
||||
$this->mapping['cascade'] = array("ALL");
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cascadePersist()
|
||||
{
|
||||
$this->mapping['cascade'][] = "persist";
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cascadeRemove()
|
||||
{
|
||||
$this->mapping['cascade'][] = "remove";
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cascadeMerge()
|
||||
{
|
||||
$this->mapping['cascade'][] = "merge";
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cascadeDetach()
|
||||
{
|
||||
$this->mapping['cascade'][] = "detach";
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function cascadeRefresh()
|
||||
{
|
||||
$this->mapping['cascade'][] = "refresh";
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fetchExtraLazy()
|
||||
{
|
||||
$this->mapping['fetch'] = ClassMetadata::FETCH_EXTRA_LAZY;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fetchEager()
|
||||
{
|
||||
$this->mapping['fetch'] = ClassMetadata::FETCH_EAGER;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fetchLazy()
|
||||
{
|
||||
$this->mapping['fetch'] = ClassMetadata::FETCH_LAZY;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Join Columns
|
||||
*
|
||||
* @param string $columnName
|
||||
* @param string $referencedColumnName
|
||||
* @param bool $nullable
|
||||
* @param bool $unique
|
||||
* @param string $onDelete
|
||||
* @param string $columnDef
|
||||
*/
|
||||
public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
|
||||
{
|
||||
$this->joinColumns[] = array(
|
||||
'name' => $columnName,
|
||||
'referencedColumnName' => $referencedColumnName,
|
||||
'nullable' => $nullable,
|
||||
'unique' => $unique,
|
||||
'onDelete' => $onDelete,
|
||||
'columnDefinition' => $columnDef,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$mapping = $this->mapping;
|
||||
if ($this->joinColumns) {
|
||||
$mapping['joinColumns'] = $this->joinColumns;
|
||||
}
|
||||
$cm = $this->builder->getClassMetadata();
|
||||
if ($this->type == ClassMetadata::MANY_TO_ONE) {
|
||||
$cm->mapManyToOne($mapping);
|
||||
} else if ($this->type == ClassMetadata::ONE_TO_ONE) {
|
||||
$cm->mapOneToOne($mapping);
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Type should be a ToOne Assocation here");
|
||||
}
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
407
lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php
Normal file
407
lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php
Normal file
@ -0,0 +1,407 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* Builder Object for ClassMetadata
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.2
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class ClassMetadataBuilder
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadata
|
||||
*/
|
||||
private $cm;
|
||||
|
||||
/**
|
||||
* @param ClassMetadata $cm
|
||||
*/
|
||||
public function __construct(ClassMetadata $cm)
|
||||
{
|
||||
$this->cm = $cm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMetadata
|
||||
*/
|
||||
public function getClassMetadata()
|
||||
{
|
||||
return $this->cm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the class as mapped superclass.
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setMappedSuperClass()
|
||||
{
|
||||
$this->cm->isMappedSuperclass = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set custom Repository class name
|
||||
*
|
||||
* @param string $repositoryClassName
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setCustomRepositoryClass($repositoryClassName)
|
||||
{
|
||||
$this->cm->setCustomRepositoryClass($repositoryClassName);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark class read only
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setReadOnly()
|
||||
{
|
||||
$this->cm->markReadOnly();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table name
|
||||
*
|
||||
* @param string $name
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setTable($name)
|
||||
{
|
||||
$this->cm->setPrimaryTable(array('name' => $name));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Index
|
||||
*
|
||||
* @param array $columns
|
||||
* @param string $name
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addIndex(array $columns, $name)
|
||||
{
|
||||
if (!isset($this->cm->table['indexes'])) {
|
||||
$this->cm->table['indexes'] = array();
|
||||
}
|
||||
$this->cm->table['indexes'][$name] = array('columns' => $columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Unique Constraint
|
||||
*
|
||||
* @param array $columns
|
||||
* @param string $name
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addUniqueConstraint(array $columns, $name)
|
||||
{
|
||||
if (!isset($this->cm->table['uniqueConstraints'])) {
|
||||
$this->cm->table['uniqueConstraints'] = array();
|
||||
}
|
||||
$this->cm->table['uniqueConstraints'][$name] = array('columns' => $columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add named query
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $dqlQuery
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addNamedQuery($name, $dqlQuery)
|
||||
{
|
||||
$this->cm->addNamedQuery(array(
|
||||
'name' => $name,
|
||||
'query' => $dqlQuery,
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class as root of a joined table inheritance hierachy.
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setJoinedTableInheritance()
|
||||
{
|
||||
$this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_JOINED);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class as root of a single table inheritance hierachy.
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setSingleTableInheritance()
|
||||
{
|
||||
$this->cm->setInheritanceType(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the discriminator column details.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
*/
|
||||
public function setDiscriminatorColumn($name, $type = 'string', $length = 255)
|
||||
{
|
||||
$this->cm->setDiscriminatorColumn(array(
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'length' => $length,
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subclass to this inheritance hierachy.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $class
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addDiscriminatorMapClass($name, $class)
|
||||
{
|
||||
$this->cm->addDiscriminatorMapClass($name, $class);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set deferred explicit change tracking policy.
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setChangeTrackingPolicyDeferredExplicit()
|
||||
{
|
||||
$this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set notify change tracking policy.
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function setChangeTrackingPolicyNotify()
|
||||
{
|
||||
$this->cm->setChangeTrackingPolicy(ClassMetadata::CHANGETRACKING_NOTIFY);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add lifecycle event
|
||||
*
|
||||
* @param string $methodName
|
||||
* @param string $event
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addLifecycleEvent($methodName, $event)
|
||||
{
|
||||
$this->cm->addLifecycleCallback($methodName, $event);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @param array $mapping
|
||||
*/
|
||||
public function addField($name, $type, array $mapping = array())
|
||||
{
|
||||
$mapping['fieldName'] = $name;
|
||||
$mapping['type'] = $type;
|
||||
$this->cm->mapField($mapping);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a field builder.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function createField($name, $type)
|
||||
{
|
||||
return new FieldBuilder($this, array('fieldName' => $name, 'type' => $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a simple many to one association, optionally with the inversed by field.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @param string|null $inversedBy
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addManyToOne($name, $targetEntity, $inversedBy = null)
|
||||
{
|
||||
$builder = $this->createManyToOne($name, $targetEntity);
|
||||
if ($inversedBy) {
|
||||
$builder->setInversedBy($inversedBy);
|
||||
}
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ManyToOne Assocation Builder.
|
||||
*
|
||||
* Note: This method does not add the association, you have to call build() on the AssociationBuilder.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @return AssociationBuilder
|
||||
*/
|
||||
public function createManyToOne($name, $targetEntity)
|
||||
{
|
||||
return new AssociationBuilder($this, array('fieldName' => $name, 'targetEntity' => $targetEntity), ClassMetadata::MANY_TO_ONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create OneToOne Assocation Builder
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @return AssociationBuilder
|
||||
*/
|
||||
public function createOneToOne($name, $targetEntity)
|
||||
{
|
||||
return new AssociationBuilder($this, array('fieldName' => $name, 'targetEntity' => $targetEntity), ClassMetadata::ONE_TO_ONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add simple inverse one-to-one assocation.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @param string $mappedBy
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addInverseOneToOne($name, $targetEntity, $mappedBy)
|
||||
{
|
||||
$builder = $this->createOneToOne($name, $targetEntity);
|
||||
$builder->setMappedBy($mappedBy);
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add simple owning one-to-one assocation.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @param string $inversedBy
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addOwningOneToOne($name, $targetEntity, $inversedBy = null)
|
||||
{
|
||||
$builder = $this->createOneToOne($name, $targetEntity);
|
||||
if ($inversedBy) {
|
||||
$builder->setInversedBy($inversedBy);
|
||||
}
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ManyToMany Assocation Builder
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @return ManyToManyAssociationBuilder
|
||||
*/
|
||||
public function createManyToMany($name, $targetEntity)
|
||||
{
|
||||
return new ManyToManyAssociationBuilder($this, array('fieldName' => $name, 'targetEntity' => $targetEntity), ClassMetadata::MANY_TO_MANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a simple owning many to many assocation.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @param string|null $inversedBy
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addOwningManyToMany($name, $targetEntity, $inversedBy = null)
|
||||
{
|
||||
$builder = $this->createManyToMany($name, $targetEntity);
|
||||
if ($inversedBy) {
|
||||
$builder->setInversedBy($inversedBy);
|
||||
}
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a simple inverse many to many assocation.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @param string $mappedBy
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addInverseManyToMany($name, $targetEntity, $mappedBy)
|
||||
{
|
||||
$builder = $this->createManyToMany($name, $targetEntity);
|
||||
$builder->setMappedBy($mappedBy);
|
||||
return $builder->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a one to many assocation builder
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @return OneToManyAssociationBuilder
|
||||
*/
|
||||
public function createOneToMany($name, $targetEntity)
|
||||
{
|
||||
return new OneToManyAssociationBuilder($this, array('fieldName' => $name, 'targetEntity' => $targetEntity), ClassMetadata::ONE_TO_MANY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add simple OneToMany assocation.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $targetEntity
|
||||
* @param string $mappedBy
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function addOneToMany($name, $targetEntity, $mappedBy)
|
||||
{
|
||||
$builder = $this->createOneToMany($name, $targetEntity);
|
||||
$builder->setMappedBy($mappedBy);
|
||||
return $builder->build();
|
||||
}
|
||||
}
|
223
lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php
Normal file
223
lib/Doctrine/ORM/Mapping/Builder/FieldBuilder.php
Normal file
@ -0,0 +1,223 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
/**
|
||||
* Field Builder
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.2
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class FieldBuilder
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadataBuilder
|
||||
*/
|
||||
private $builder;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $mapping;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $generatedValue;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $sequenceDef;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ClassMetadataBuilder $builder
|
||||
* @param array $mapping
|
||||
*/
|
||||
public function __construct(ClassMetadataBuilder $builder, array $mapping)
|
||||
{
|
||||
$this->builder = $builder;
|
||||
$this->mapping = $mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set length.
|
||||
*
|
||||
* @param int $length
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function length($length)
|
||||
{
|
||||
$this->mapping['length'] = $length;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set nullable
|
||||
*
|
||||
* @param bool
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function nullable($flag = true)
|
||||
{
|
||||
$this->mapping['nullable'] = (bool)$flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Unique
|
||||
*
|
||||
* @param bool
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function unique($flag = true)
|
||||
{
|
||||
$this->mapping['unique'] = (bool)$flag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set column name
|
||||
*
|
||||
* @param string $name
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function columnName($name)
|
||||
{
|
||||
$this->mapping['columnName'] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Precision
|
||||
*
|
||||
* @param int $p
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function precision($p)
|
||||
{
|
||||
$this->mapping['precision'] = $p;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scale.
|
||||
*
|
||||
* @param int $s
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function scale($s)
|
||||
{
|
||||
$this->mapping['scale'] = $s;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field as primary key.
|
||||
*
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function isPrimaryKey()
|
||||
{
|
||||
$this->mapping['id'] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $strategy
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function generatedValue($strategy = 'AUTO')
|
||||
{
|
||||
$this->generatedValue = $strategy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field versioned
|
||||
*
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function isVersionField()
|
||||
{
|
||||
$this->version = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Sequence Generator
|
||||
*
|
||||
* @param string $sequenceName
|
||||
* @param int $allocationSize
|
||||
* @param int $initialValue
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1)
|
||||
{
|
||||
$this->sequenceDef = array(
|
||||
'sequenceName' => $sequenceName,
|
||||
'allocationSize' => $allocationSize,
|
||||
'initialValue' => $initialValue,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set column definition.
|
||||
*
|
||||
* @param string $def
|
||||
* @return FieldBuilder
|
||||
*/
|
||||
public function columnDefinition($def)
|
||||
{
|
||||
$this->mapping['columnDefinition'] = $def;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize this field and attach it to the ClassMetadata.
|
||||
*
|
||||
* Without this call a FieldBuilder has no effect on the ClassMetadata.
|
||||
*
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$cm = $this->builder->getClassMetadata();
|
||||
if ($this->generatedValue) {
|
||||
$cm->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' . $this->generatedValue));
|
||||
}
|
||||
if ($this->version) {
|
||||
$cm->setVersionMapping($this->mapping);
|
||||
}
|
||||
$cm->mapField($this->mapping);
|
||||
if ($this->sequenceDef) {
|
||||
$cm->setSequenceGeneratorDefinition($this->sequenceDef);
|
||||
}
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
/**
|
||||
* ManyToMany Association Builder
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
|
||||
{
|
||||
private $joinTableName;
|
||||
|
||||
private $inverseJoinColumns = array();
|
||||
|
||||
public function setJoinTable($name)
|
||||
{
|
||||
$this->joinTableName = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Inverse Join Columns
|
||||
*
|
||||
* @param string $columnName
|
||||
* @param string $referencedColumnName
|
||||
* @param bool $nullable
|
||||
* @param bool $unique
|
||||
* @param string $onDelete
|
||||
* @param string $columnDef
|
||||
*/
|
||||
public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null)
|
||||
{
|
||||
$this->inverseJoinColumns[] = array(
|
||||
'name' => $columnName,
|
||||
'referencedColumnName' => $referencedColumnName,
|
||||
'nullable' => $nullable,
|
||||
'unique' => $unique,
|
||||
'onDelete' => $onDelete,
|
||||
'columnDefinition' => $columnDef,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$mapping = $this->mapping;
|
||||
$mapping['joinTable'] = array();
|
||||
if ($this->joinColumns) {
|
||||
$mapping['joinTable']['joinColumns'] = $this->joinColumns;
|
||||
}
|
||||
if ($this->inverseJoinColumns) {
|
||||
$mapping['joinTable']['inverseJoinColumns'] = $this->inverseJoinColumns;
|
||||
}
|
||||
if ($this->joinTableName) {
|
||||
$mapping['joinTable']['name'] = $this->joinTableName;
|
||||
}
|
||||
$cm = $this->builder->getClassMetadata();
|
||||
$cm->mapManyToMany($mapping);
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Builder;
|
||||
|
||||
/**
|
||||
* OneToMany Association Builder
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.com
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class OneToManyAssociationBuilder extends AssociationBuilder
|
||||
{
|
||||
/**
|
||||
* @param array $fieldNames
|
||||
* @return OneToManyAssociationBuilder
|
||||
*/
|
||||
public function setOrderBy(array $fieldNames)
|
||||
{
|
||||
$this->mapping['orderBy'] = $fieldNames;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setIndexBy($fieldName)
|
||||
{
|
||||
$this->mapping['indexBy'] = $fieldName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassMetadataBuilder
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$mapping = $this->mapping;
|
||||
if ($this->joinColumns) {
|
||||
$mapping['joinColumns'] = $this->joinColumns;
|
||||
}
|
||||
$cm = $this->builder->getClassMetadata();
|
||||
$cm->mapOneToMany($mapping);
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
@ -343,4 +343,17 @@ class ClassMetadata extends ClassMetadataInfo
|
||||
}
|
||||
return clone $this->_prototype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $callback
|
||||
* @param string $event
|
||||
*/
|
||||
public function addLifecycleCallback($callback, $event)
|
||||
{
|
||||
if ( !$this->reflClass->hasMethod($callback) ||
|
||||
($this->reflClass->getMethod($callback)->getModifiers() & \ReflectionMethod::IS_PUBLIC) == 0) {
|
||||
throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callback);
|
||||
}
|
||||
return parent::addLifecycleCallback($callback, $event);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
private $targetPlatform;
|
||||
|
||||
/**
|
||||
* @var Driver\Driver
|
||||
* @var \Doctrine\ORM\Mapping\Driver\Driver
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
@ -274,6 +274,9 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
$class->setDiscriminatorMap($parent->discriminatorMap);
|
||||
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
|
||||
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
|
||||
if ($parent->isMappedSuperclass) {
|
||||
$class->setCustomRepositoryClass($parent->customRepositoryClassName);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke driver
|
||||
@ -306,6 +309,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
$class->setPrimaryTable($parent->table);
|
||||
}
|
||||
|
||||
if ($parent && $parent->containsForeignIdentifier) {
|
||||
$class->containsForeignIdentifier = true;
|
||||
}
|
||||
|
||||
$class->setParentClasses($visited);
|
||||
|
||||
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
||||
@ -448,7 +455,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
// <table>_<column>_seq in PostgreSQL for SERIAL columns.
|
||||
// Not pretty but necessary and the simplest solution that currently works.
|
||||
$seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
|
||||
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
|
||||
$class->getTableName() . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
|
||||
null;
|
||||
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
|
||||
break;
|
||||
@ -478,4 +485,15 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
throw new ORMException("Unknown generator type: " . $class->generatorType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this class is mapped by this EntityManager + ClassMetadata configuration
|
||||
*
|
||||
* @param $class
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransient($class)
|
||||
{
|
||||
return $this->driver->isTransient($class);
|
||||
}
|
||||
}
|
||||
|
@ -774,11 +774,15 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
// If targetEntity is unqualified, assume it is in the same namespace as
|
||||
// the sourceEntity.
|
||||
$mapping['sourceEntity'] = $this->name;
|
||||
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
|
||||
&& strlen($this->namespace) > 0) {
|
||||
|
||||
if (isset($mapping['targetEntity'])) {
|
||||
if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
|
||||
$mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
|
||||
}
|
||||
|
||||
$mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
|
||||
}
|
||||
|
||||
// Complete id mapping
|
||||
if (isset($mapping['id']) && $mapping['id'] === true) {
|
||||
if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
|
||||
@ -904,9 +908,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
|
||||
}
|
||||
|
||||
//TODO: if orphanRemoval, cascade=remove is implicit!
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
|
||||
(bool) $mapping['orphanRemoval'] : false;
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
|
||||
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
|
||||
|
||||
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
|
||||
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
|
||||
@ -931,9 +934,8 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
|
||||
}
|
||||
|
||||
//TODO: if orphanRemoval, cascade=remove is implicit!
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ?
|
||||
(bool) $mapping['orphanRemoval'] : false;
|
||||
$mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
|
||||
$mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
|
||||
|
||||
if (isset($mapping['orderBy'])) {
|
||||
if ( ! is_array($mapping['orderBy'])) {
|
||||
@ -1268,7 +1270,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
public function getTemporaryIdTableName()
|
||||
{
|
||||
// replace dots with underscores because PostgreSQL creates temporary tables in a special schema
|
||||
return str_replace('.', '_', $this->table['name'] . '_id_tmp');
|
||||
return str_replace('.', '_', $this->getTableName() . '_id_tmp');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1367,9 +1369,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->table['name'] = $table['name'];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($table['indexes'])) {
|
||||
$this->table['indexes'] = $table['indexes'];
|
||||
}
|
||||
|
||||
if (isset($table['uniqueConstraints'])) {
|
||||
$this->table['uniqueConstraints'] = $table['uniqueConstraints'];
|
||||
}
|
||||
@ -1564,9 +1568,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
/**
|
||||
* Adds a lifecycle callback for entities of this class.
|
||||
*
|
||||
* Note: If the same callback is registered more than once, the old one
|
||||
* will be overridden.
|
||||
*
|
||||
* @param string $callback
|
||||
* @param string $event
|
||||
*/
|
||||
@ -1625,13 +1626,27 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
public function setDiscriminatorMap(array $map)
|
||||
{
|
||||
foreach ($map as $value => $className) {
|
||||
if (strpos($className, '\\') === false && strlen($this->namespace)) {
|
||||
$this->addDiscriminatorMapClass($value, $className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one entry of the discriminator map with a new class and corresponding name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $className
|
||||
*/
|
||||
public function addDiscriminatorMapClass($name, $className)
|
||||
{
|
||||
if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
|
||||
$className = $this->namespace . '\\' . $className;
|
||||
}
|
||||
|
||||
$className = ltrim($className, '\\');
|
||||
$this->discriminatorMap[$value] = $className;
|
||||
$this->discriminatorMap[$name] = $className;
|
||||
|
||||
if ($this->name == $className) {
|
||||
$this->discriminatorValue = $value;
|
||||
$this->discriminatorValue = $name;
|
||||
} else {
|
||||
if ( ! class_exists($className)) {
|
||||
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
|
||||
@ -1641,7 +1656,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the class has a named query with the given query name.
|
||||
@ -1876,6 +1890,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
if ( ! isset($this->associationMappings[$assocName])) {
|
||||
throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
|
||||
}
|
||||
|
||||
return $this->associationMappings[$assocName]['targetEntity'];
|
||||
}
|
||||
|
||||
@ -1899,9 +1914,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getQuotedColumnName($field, $platform)
|
||||
{
|
||||
return isset($this->fieldMappings[$field]['quoted']) ?
|
||||
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
|
||||
$this->fieldMappings[$field]['columnName'];
|
||||
return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1913,9 +1926,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getQuotedTableName($platform)
|
||||
{
|
||||
return isset($this->table['quoted']) ?
|
||||
$platform->quoteIdentifier($this->table['name']) :
|
||||
$this->table['name'];
|
||||
return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1926,8 +1937,6 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function getQuotedJoinTableName(array $assoc, $platform)
|
||||
{
|
||||
return isset($assoc['joinTable']['quoted'])
|
||||
? $platform->quoteIdentifier($assoc['joinTable']['name'])
|
||||
: $assoc['joinTable']['name'];
|
||||
return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
|
||||
}
|
||||
}
|
||||
|
@ -147,12 +147,15 @@ class AnnotationDriver implements Driver
|
||||
// Evaluate Entity annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
|
||||
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
|
||||
if ($entityAnnot->repositoryClass !== null) {
|
||||
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
|
||||
|
||||
}
|
||||
if ($entityAnnot->readOnly) {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
|
||||
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
|
||||
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
||||
|
@ -19,7 +19,8 @@
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
interface Annotation {}
|
||||
|
||||
|
||||
/* Annotations */
|
||||
|
||||
@ -27,8 +28,10 @@ use Doctrine\Common\Annotations\Annotation;
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class Entity extends Annotation {
|
||||
final class Entity implements Annotation {
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
/** @var boolean */
|
||||
public $readOnly = false;
|
||||
}
|
||||
|
||||
@ -36,42 +39,56 @@ final class Entity extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class MappedSuperclass extends Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class InheritanceType extends Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorColumn extends Annotation {
|
||||
public $name;
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
public $type;
|
||||
public $length;
|
||||
final class MappedSuperclass implements Annotation {
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorMap extends Annotation {}
|
||||
final class InheritanceType implements Annotation {
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorColumn implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $type;
|
||||
/** @var integer */
|
||||
public $length;
|
||||
/** @var mixed */
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorMap implements Annotation {
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Id extends Annotation {}
|
||||
final class Id implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class GeneratedValue extends Annotation {
|
||||
final class GeneratedValue implements Annotation {
|
||||
/** @var string */
|
||||
public $strategy = 'AUTO';
|
||||
}
|
||||
|
||||
@ -79,43 +96,60 @@ final class GeneratedValue extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Version extends Annotation {}
|
||||
final class Version implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY","ANNOTATION"})
|
||||
*/
|
||||
final class JoinColumn extends Annotation {
|
||||
final class JoinColumn implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
/** @var string */
|
||||
public $referencedColumnName = 'id';
|
||||
/** @var boolean */
|
||||
public $unique = false;
|
||||
/** @var boolean */
|
||||
public $nullable = true;
|
||||
/** @var mixed */
|
||||
public $onDelete;
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
/** @var string */
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class JoinColumns extends Annotation {}
|
||||
final class JoinColumns implements Annotation {
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Column extends Annotation {
|
||||
public $type = 'string';
|
||||
public $length;
|
||||
// The precision for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
public $precision = 0;
|
||||
// The scale for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
public $scale = 0;
|
||||
public $unique = false;
|
||||
public $nullable = false;
|
||||
final class Column implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var mixed */
|
||||
public $type = 'string';
|
||||
/** @var integer */
|
||||
public $length;
|
||||
/** @var integer */
|
||||
public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
/** @var integer */
|
||||
public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
/** @var boolean */
|
||||
public $unique = false;
|
||||
/** @var boolean */
|
||||
public $nullable = false;
|
||||
/** @var array */
|
||||
public $options = array();
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
}
|
||||
|
||||
@ -123,12 +157,18 @@ final class Column extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OneToOne extends Annotation {
|
||||
final class OneToOne implements Annotation {
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var boolean */
|
||||
public $orphanRemoval = false;
|
||||
}
|
||||
|
||||
@ -136,12 +176,18 @@ final class OneToOne extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OneToMany extends Annotation {
|
||||
final class OneToMany implements Annotation {
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var boolean */
|
||||
public $orphanRemoval = false;
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
}
|
||||
|
||||
@ -149,10 +195,14 @@ final class OneToMany extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class ManyToOne extends Annotation {
|
||||
final class ManyToOne implements Annotation {
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
}
|
||||
|
||||
@ -160,12 +210,18 @@ final class ManyToOne extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class ManyToMany extends Annotation {
|
||||
final class ManyToMany implements Annotation {
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
}
|
||||
|
||||
@ -174,7 +230,8 @@ final class ManyToMany extends Annotation {
|
||||
* @Target("ALL")
|
||||
* @todo check available targets
|
||||
*/
|
||||
final class ElementCollection extends Annotation {
|
||||
final class ElementCollection implements Annotation {
|
||||
/** @var string */
|
||||
public $tableName;
|
||||
}
|
||||
|
||||
@ -182,10 +239,14 @@ final class ElementCollection extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class Table extends Annotation {
|
||||
final class Table implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $schema;
|
||||
/** @var array<Doctrine\ORM\Mapping\Index> */
|
||||
public $indexes;
|
||||
/** @var array<Doctrine\ORM\Mapping\UniqueConstraint> */
|
||||
public $uniqueConstraints;
|
||||
}
|
||||
|
||||
@ -193,8 +254,10 @@ final class Table extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class UniqueConstraint extends Annotation {
|
||||
final class UniqueConstraint implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
}
|
||||
|
||||
@ -202,8 +265,10 @@ final class UniqueConstraint extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class Index extends Annotation {
|
||||
final class Index implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
}
|
||||
|
||||
@ -211,10 +276,14 @@ final class Index extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class JoinTable extends Annotation {
|
||||
final class JoinTable implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $schema;
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $joinColumns = array();
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $inverseJoinColumns = array();
|
||||
}
|
||||
|
||||
@ -222,9 +291,12 @@ final class JoinTable extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class SequenceGenerator extends Annotation {
|
||||
final class SequenceGenerator implements Annotation {
|
||||
/** @var string */
|
||||
public $sequenceName;
|
||||
/** @var integer */
|
||||
public $allocationSize = 1;
|
||||
/** @var integer */
|
||||
public $initialValue = 1;
|
||||
}
|
||||
|
||||
@ -232,26 +304,37 @@ final class SequenceGenerator extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class ChangeTrackingPolicy extends Annotation {}
|
||||
final class ChangeTrackingPolicy implements Annotation {
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OrderBy extends Annotation {}
|
||||
final class OrderBy implements Annotation {
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class NamedQueries extends Annotation {}
|
||||
final class NamedQueries implements Annotation {
|
||||
/** @var array<Doctrine\ORM\Mapping\NamedQuery> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class NamedQuery extends Annotation {
|
||||
final class NamedQuery implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $query;
|
||||
}
|
||||
|
||||
@ -261,46 +344,46 @@ final class NamedQuery extends Annotation {
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class HasLifecycleCallbacks extends Annotation {}
|
||||
final class HasLifecycleCallbacks implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PrePersist extends Annotation {}
|
||||
final class PrePersist implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostPersist extends Annotation {}
|
||||
final class PostPersist implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreUpdate extends Annotation {}
|
||||
final class PreUpdate implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostUpdate extends Annotation {}
|
||||
final class PostUpdate implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreRemove extends Annotation {}
|
||||
final class PreRemove implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostRemove extends Annotation {}
|
||||
final class PostRemove implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostLoad extends Annotation {}
|
||||
final class PostLoad implements Annotation {}
|
||||
|
176
lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php
Normal file
176
lib/Doctrine/ORM/Mapping/Driver/SimplifiedXmlDriver.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
|
||||
/**
|
||||
* XmlDriver that additionally looks for mapping information in a global file.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @license MIT
|
||||
*/
|
||||
class SimplifiedXmlDriver extends XmlDriver
|
||||
{
|
||||
protected $_prefixes = array();
|
||||
protected $_globalBasename;
|
||||
protected $_classCache;
|
||||
protected $_fileExtension = '.orm.xml';
|
||||
|
||||
public function __construct($prefixes)
|
||||
{
|
||||
$this->addNamespacePrefixes($prefixes);
|
||||
}
|
||||
|
||||
public function setGlobalBasename($file)
|
||||
{
|
||||
$this->_globalBasename = $file;
|
||||
}
|
||||
|
||||
public function getGlobalBasename()
|
||||
{
|
||||
return $this->_globalBasename;
|
||||
}
|
||||
|
||||
public function addNamespacePrefixes($prefixes)
|
||||
{
|
||||
$this->_prefixes = array_merge($this->_prefixes, $prefixes);
|
||||
$this->addPaths(array_flip($prefixes));
|
||||
}
|
||||
|
||||
public function getNamespacePrefixes()
|
||||
{
|
||||
return $this->_prefixes;
|
||||
}
|
||||
|
||||
public function isTransient($className)
|
||||
{
|
||||
if (null === $this->_classCache) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// The mapping is defined in the global mapping file
|
||||
if (isset($this->_classCache[$className])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->_findMappingFile($className);
|
||||
|
||||
return false;
|
||||
} catch (MappingException $e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllClassNames()
|
||||
{
|
||||
if (null === $this->_classCache) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
|
||||
if ($this->_paths) {
|
||||
foreach ((array) $this->_paths as $path) {
|
||||
if (!is_dir($path)) {
|
||||
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($path),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$fileName = $file->getBasename($this->_fileExtension);
|
||||
|
||||
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: All files found here means classes are not transient!
|
||||
if (isset($this->_prefixes[$path])) {
|
||||
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
|
||||
} else {
|
||||
$classes[] = str_replace('.', '\\', $fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($classes, array_keys($this->_classCache));
|
||||
}
|
||||
|
||||
public function getElement($className)
|
||||
{
|
||||
if (null === $this->_classCache) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if (!isset($this->_classCache[$className])) {
|
||||
$this->_classCache[$className] = parent::getElement($className);
|
||||
}
|
||||
|
||||
return $this->_classCache[$className];
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
$this->_classCache = array();
|
||||
if (null !== $this->_globalBasename) {
|
||||
foreach ($this->_paths as $path) {
|
||||
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
|
||||
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _findMappingFile($className)
|
||||
{
|
||||
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
|
||||
foreach ($this->_paths as $path) {
|
||||
if (!isset($this->_prefixes[$path])) {
|
||||
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
|
||||
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$prefix = $this->_prefixes[$path];
|
||||
|
||||
if (0 !== strpos($className, $prefix.'\\')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
|
||||
if (is_file($filename)) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
throw MappingException::mappingFileNotFound($className, $filename);
|
||||
}
|
||||
|
||||
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension);
|
||||
}
|
||||
}
|
182
lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php
Normal file
182
lib/Doctrine/ORM/Mapping/Driver/SimplifiedYamlDriver.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping\Driver;
|
||||
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
|
||||
/**
|
||||
* YamlDriver that additionally looks for mapping information in a global file.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @license MIT
|
||||
*/
|
||||
class SimplifiedYamlDriver extends YamlDriver
|
||||
{
|
||||
protected $_prefixes = array();
|
||||
protected $_globalBasename;
|
||||
protected $_classCache;
|
||||
protected $_fileExtension = '.orm.yml';
|
||||
|
||||
public function __construct($prefixes)
|
||||
{
|
||||
$this->addNamespacePrefixes($prefixes);
|
||||
}
|
||||
|
||||
public function setGlobalBasename($file)
|
||||
{
|
||||
$this->_globalBasename = $file;
|
||||
}
|
||||
|
||||
public function getGlobalBasename()
|
||||
{
|
||||
return $this->_globalBasename;
|
||||
}
|
||||
|
||||
public function addNamespacePrefixes($prefixes)
|
||||
{
|
||||
$this->_prefixes = array_merge($this->_prefixes, $prefixes);
|
||||
$this->addPaths(array_flip($prefixes));
|
||||
}
|
||||
|
||||
public function addNamespacePrefix($prefix, $path)
|
||||
{
|
||||
$this->_prefixes[$path] = $prefix;
|
||||
}
|
||||
|
||||
|
||||
public function getNamespacePrefixes()
|
||||
{
|
||||
return $this->_prefixes;
|
||||
}
|
||||
|
||||
public function isTransient($className)
|
||||
{
|
||||
if (null === $this->_classCache) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// The mapping is defined in the global mapping file
|
||||
if (isset($this->_classCache[$className])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->_findMappingFile($className);
|
||||
|
||||
return false;
|
||||
} catch (MappingException $e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllClassNames()
|
||||
{
|
||||
if (null === $this->_classCache) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$classes = array();
|
||||
|
||||
if ($this->_paths) {
|
||||
foreach ((array) $this->_paths as $path) {
|
||||
if (!is_dir($path)) {
|
||||
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
|
||||
}
|
||||
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($path),
|
||||
\RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
$fileName = $file->getBasename($this->_fileExtension);
|
||||
|
||||
if ($fileName == $file->getBasename() || $fileName == $this->_globalBasename) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: All files found here means classes are not transient!
|
||||
if (isset($this->_prefixes[$path])) {
|
||||
$classes[] = $this->_prefixes[$path].'\\'.str_replace('.', '\\', $fileName);
|
||||
} else {
|
||||
$classes[] = str_replace('.', '\\', $fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($classes, array_keys($this->_classCache));
|
||||
}
|
||||
|
||||
public function getElement($className)
|
||||
{
|
||||
if (null === $this->_classCache) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if (!isset($this->_classCache[$className])) {
|
||||
$this->_classCache[$className] = parent::getElement($className);
|
||||
}
|
||||
|
||||
return $this->_classCache[$className];
|
||||
}
|
||||
|
||||
protected function initialize()
|
||||
{
|
||||
$this->_classCache = array();
|
||||
if (null !== $this->_globalBasename) {
|
||||
foreach ($this->_paths as $path) {
|
||||
if (is_file($file = $path.'/'.$this->_globalBasename.$this->_fileExtension)) {
|
||||
$this->_classCache = array_merge($this->_classCache, $this->_loadMappingFile($file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function _findMappingFile($className)
|
||||
{
|
||||
$defaultFileName = str_replace('\\', '.', $className).$this->_fileExtension;
|
||||
foreach ($this->_paths as $path) {
|
||||
if (!isset($this->_prefixes[$path])) {
|
||||
if (is_file($path.DIRECTORY_SEPARATOR.$defaultFileName)) {
|
||||
return $path.DIRECTORY_SEPARATOR.$defaultFileName;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$prefix = $this->_prefixes[$path];
|
||||
|
||||
if (0 !== strpos($className, $prefix.'\\')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filename = $path.'/'.strtr(substr($className, strlen($prefix)+1), '\\', '.').$this->_fileExtension;
|
||||
if (is_file($filename)) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
throw MappingException::mappingFileNotFound($className, $filename);
|
||||
}
|
||||
|
||||
throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1).$this->_fileExtension);
|
||||
}
|
||||
}
|
@ -52,13 +52,16 @@ class XmlDriver extends AbstractFileDriver
|
||||
$xmlRoot = $this->getElement($className);
|
||||
|
||||
if ($xmlRoot->getName() == 'entity') {
|
||||
$metadata->setCustomRepositoryClass(
|
||||
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
|
||||
);
|
||||
if (isset($xmlRoot['repository-class'])) {
|
||||
$metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']);
|
||||
}
|
||||
if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} else if ($xmlRoot->getName() == 'mapped-superclass') {
|
||||
$metadata->setCustomRepositoryClass(
|
||||
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
|
||||
);
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
||||
|
@ -46,13 +46,16 @@ class YamlDriver extends AbstractFileDriver
|
||||
$element = $this->getElement($className);
|
||||
|
||||
if ($element['type'] == 'entity') {
|
||||
$metadata->setCustomRepositoryClass(
|
||||
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
|
||||
);
|
||||
if (isset($element['repositoryClass'])) {
|
||||
$metadata->setCustomRepositoryClass($element['repositoryClass']);
|
||||
}
|
||||
if (isset($element['readOnly']) && $element['readOnly'] == true) {
|
||||
$metadata->markReadOnly();
|
||||
}
|
||||
} else if ($element['type'] == 'mappedSuperclass') {
|
||||
$metadata->setCustomRepositoryClass(
|
||||
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
|
||||
);
|
||||
$metadata->isMappedSuperclass = true;
|
||||
} else {
|
||||
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
|
||||
|
@ -293,4 +293,9 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
"to avoid this exception from occuring."
|
||||
);
|
||||
}
|
||||
|
||||
public static function lifecycleCallbackMethodNotFound($className, $methodName)
|
||||
{
|
||||
return new self("Entity '" . $className . "' has no method '" . $methodName . "' to be registered as lifecycle callback.");
|
||||
}
|
||||
}
|
@ -46,15 +46,14 @@ class ORMException extends Exception
|
||||
);
|
||||
}
|
||||
|
||||
public static function entityMissingAssignedId($entity)
|
||||
public static function entityMissingAssignedIdForField($entity, $field)
|
||||
{
|
||||
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " .
|
||||
return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " .
|
||||
"The identifier generation strategy for this entity requires the ID field to be populated before ".
|
||||
"EntityManager#persist() is called. If you want automatically generated identifiers instead " .
|
||||
"you need to adjust the metadata mapping accordingly."
|
||||
);
|
||||
}
|
||||
|
||||
public static function unrecognizedField($field)
|
||||
{
|
||||
return new self("Unrecognized field: $field");
|
||||
@ -130,4 +129,10 @@ class ORMException extends Exception
|
||||
"Unknown Entity namespace alias '$entityNamespaceAlias'."
|
||||
);
|
||||
}
|
||||
|
||||
public static function invalidEntityRepository($className)
|
||||
{
|
||||
return new self("Invalid repository class '".$className."'. ".
|
||||
"it must be a Doctrine\ORM\EntityRepository.");
|
||||
}
|
||||
}
|
||||
|
@ -65,9 +65,11 @@ abstract class AbstractCollectionPersister
|
||||
public function delete(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
|
||||
if ( ! $mapping['isOwningSide']) {
|
||||
return; // ignore inverse side
|
||||
}
|
||||
|
||||
$sql = $this->_getDeleteSQL($coll);
|
||||
$this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll));
|
||||
}
|
||||
@ -96,9 +98,11 @@ abstract class AbstractCollectionPersister
|
||||
public function update(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
|
||||
if ( ! $mapping['isOwningSide']) {
|
||||
return; // ignore inverse side
|
||||
}
|
||||
|
||||
$this->deleteRows($coll);
|
||||
//$this->updateRows($coll);
|
||||
$this->insertRows($coll);
|
||||
@ -108,6 +112,7 @@ abstract class AbstractCollectionPersister
|
||||
{
|
||||
$deleteDiff = $coll->getDeleteDiff();
|
||||
$sql = $this->_getDeleteRowSQL($coll);
|
||||
|
||||
foreach ($deleteDiff as $element) {
|
||||
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
|
||||
}
|
||||
@ -120,6 +125,7 @@ abstract class AbstractCollectionPersister
|
||||
{
|
||||
$insertDiff = $coll->getInsertDiff();
|
||||
$sql = $this->_getInsertRowSQL($coll);
|
||||
|
||||
foreach ($insertDiff as $element) {
|
||||
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
|
||||
}
|
||||
|
@ -39,10 +39,12 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
protected function _prepareInsertData($entity)
|
||||
{
|
||||
$data = parent::_prepareInsertData($entity);
|
||||
|
||||
// Populate the discriminator column
|
||||
$discColumn = $this->_class->discriminatorColumn;
|
||||
$this->_columnTypes[$discColumn['name']] = $discColumn['type'];
|
||||
$data[$this->_getDiscriminatorColumnTableName()][$discColumn['name']] = $this->_class->discriminatorValue;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@ -63,7 +65,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
|
||||
|
||||
return "$sql AS $columnAlias";
|
||||
return $sql . ' AS ' . $columnAlias;
|
||||
}
|
||||
|
||||
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
|
||||
@ -72,6 +74,6 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
|
||||
|
||||
return $tableAlias . ".$joinColumnName AS $columnAlias";
|
||||
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
|
||||
}
|
||||
}
|
@ -222,13 +222,14 @@ class BasicEntityPersister
|
||||
$isPostInsertId = $idGen->isPostInsertGenerator();
|
||||
|
||||
$stmt = $this->_conn->prepare($this->_getInsertSQL());
|
||||
$tableName = $this->_class->table['name'];
|
||||
$tableName = $this->_class->getTableName();
|
||||
|
||||
foreach ($this->_queuedInserts as $entity) {
|
||||
$insertData = $this->_prepareInsertData($entity);
|
||||
|
||||
if (isset($insertData[$tableName])) {
|
||||
$paramIndex = 1;
|
||||
|
||||
foreach ($insertData[$tableName] as $column => $value) {
|
||||
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]);
|
||||
}
|
||||
@ -279,10 +280,13 @@ class BasicEntityPersister
|
||||
{
|
||||
$versionField = $versionedClass->versionField;
|
||||
$identifier = $versionedClass->getIdentifierColumnNames();
|
||||
$versionFieldColumnName = $versionedClass->getColumnName($versionField);
|
||||
|
||||
$versionFieldColumnName = $versionedClass->getQuotedColumnName($versionField, $this->_platform);
|
||||
|
||||
//FIXME: Order with composite keys might not be correct
|
||||
$sql = "SELECT " . $versionFieldColumnName . " FROM " . $versionedClass->getQuotedTableName($this->_platform)
|
||||
. " WHERE " . implode(' = ? AND ', $identifier) . " = ?";
|
||||
$sql = 'SELECT ' . $versionFieldColumnName
|
||||
. ' FROM ' . $versionedClass->getQuotedTableName($this->_platform)
|
||||
. ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';
|
||||
$value = $this->_conn->fetchColumn($sql, array_values((array)$id));
|
||||
|
||||
return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform);
|
||||
@ -305,7 +309,8 @@ class BasicEntityPersister
|
||||
public function update($entity)
|
||||
{
|
||||
$updateData = $this->_prepareUpdateData($entity);
|
||||
$tableName = $this->_class->table['name'];
|
||||
$tableName = $this->_class->getTableName();
|
||||
|
||||
if (isset($updateData[$tableName]) && $updateData[$tableName]) {
|
||||
$this->_updateTable(
|
||||
$entity, $this->_class->getQuotedTableName($this->_platform),
|
||||
@ -333,17 +338,17 @@ class BasicEntityPersister
|
||||
$set = $params = $types = array();
|
||||
|
||||
foreach ($updateData as $columnName => $value) {
|
||||
if (isset($this->_class->fieldNames[$columnName])) {
|
||||
$set[] = $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?';
|
||||
} else {
|
||||
$set[] = $columnName . ' = ?';
|
||||
}
|
||||
$set[] = (isset($this->_class->fieldNames[$columnName]))
|
||||
? $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?'
|
||||
: $columnName . ' = ?';
|
||||
|
||||
$params[] = $value;
|
||||
$types[] = $this->_columnTypes[$columnName];
|
||||
}
|
||||
|
||||
$where = array();
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
|
||||
|
||||
foreach ($this->_class->identifier as $idField) {
|
||||
if (isset($this->_class->associationMappings[$idField])) {
|
||||
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
|
||||
@ -361,17 +366,20 @@ class BasicEntityPersister
|
||||
$versionField = $this->_class->versionField;
|
||||
$versionFieldType = $this->_class->fieldMappings[$versionField]['type'];
|
||||
$versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform);
|
||||
|
||||
if ($versionFieldType == Type::INTEGER) {
|
||||
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
|
||||
} else if ($versionFieldType == Type::DATETIME) {
|
||||
$set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
|
||||
}
|
||||
|
||||
$where[] = $versionColumn;
|
||||
$params[] = $this->_class->reflFields[$versionField]->getValue($entity);
|
||||
$types[] = $this->_class->fieldMappings[$versionField]['type'];
|
||||
}
|
||||
|
||||
$sql = "UPDATE $quotedTableName SET " . implode(', ', $set)
|
||||
$sql = 'UPDATE ' . $quotedTableName
|
||||
. ' SET ' . implode(', ', $set)
|
||||
. ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
|
||||
|
||||
$result = $this->_conn->executeUpdate($sql, $params, $types);
|
||||
@ -398,21 +406,29 @@ class BasicEntityPersister
|
||||
$relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$mapping = $relatedClass->associationMappings[$mapping['mappedBy']];
|
||||
$keys = array_keys($mapping['relationToTargetKeyColumns']);
|
||||
|
||||
if ($selfReferential) {
|
||||
$otherKeys = array_keys($mapping['relationToSourceKeyColumns']);
|
||||
}
|
||||
} else {
|
||||
$keys = array_keys($mapping['relationToSourceKeyColumns']);
|
||||
|
||||
if ($selfReferential) {
|
||||
$otherKeys = array_keys($mapping['relationToTargetKeyColumns']);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset($mapping['isOnDeleteCascade'])) {
|
||||
$this->_conn->delete($mapping['joinTable']['name'], array_combine($keys, $identifier));
|
||||
$this->_conn->delete(
|
||||
$this->_class->getQuotedJoinTableName($mapping, $this->_platform),
|
||||
array_combine($keys, $identifier)
|
||||
);
|
||||
|
||||
if ($selfReferential) {
|
||||
$this->_conn->delete($mapping['joinTable']['name'], array_combine($otherKeys, $identifier));
|
||||
$this->_conn->delete(
|
||||
$this->_class->getQuotedJoinTableName($mapping, $this->_platform),
|
||||
array_combine($otherKeys, $identifier)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,7 +479,7 @@ class BasicEntityPersister
|
||||
$result = array();
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
|
||||
if ($versioned = $this->_class->isVersioned) {
|
||||
if (($versioned = $this->_class->isVersioned) != false) {
|
||||
$versionField = $this->_class->versionField;
|
||||
}
|
||||
|
||||
@ -477,6 +493,7 @@ class BasicEntityPersister
|
||||
|
||||
if (isset($this->_class->associationMappings[$field])) {
|
||||
$assoc = $this->_class->associationMappings[$field];
|
||||
|
||||
// Only owning side of x-1 associations can have a FK column.
|
||||
if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
|
||||
continue;
|
||||
@ -484,6 +501,7 @@ class BasicEntityPersister
|
||||
|
||||
if ($newVal !== null) {
|
||||
$oid = spl_object_hash($newVal);
|
||||
|
||||
if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
|
||||
// The associated entity $newVal is not yet persisted, so we must
|
||||
// set $newVal = null, in order to insert a null value and schedule an
|
||||
@ -510,6 +528,7 @@ class BasicEntityPersister
|
||||
} else {
|
||||
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
|
||||
}
|
||||
|
||||
$this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn);
|
||||
}
|
||||
} else {
|
||||
@ -518,6 +537,7 @@ class BasicEntityPersister
|
||||
$result[$this->getOwningTable($field)][$columnName] = $newVal;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -548,7 +568,7 @@ class BasicEntityPersister
|
||||
*/
|
||||
public function getOwningTable($fieldName)
|
||||
{
|
||||
return $this->_class->table['name'];
|
||||
return $this->_class->getTableName();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -560,12 +580,13 @@ class BasicEntityPersister
|
||||
* @param $assoc The association that connects the entity to load to another entity, if any.
|
||||
* @param array $hints Hints for entity creation.
|
||||
* @param int $lockMode
|
||||
* @param int $limit Limit number of results
|
||||
* @return object The loaded and managed entity instance or NULL if the entity can not be found.
|
||||
* @todo Check identity map? loadById method? Try to guess whether $criteria is the id?
|
||||
*/
|
||||
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0)
|
||||
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null)
|
||||
{
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode);
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
|
||||
@ -574,12 +595,9 @@ class BasicEntityPersister
|
||||
$hints[Query::HINT_REFRESH_ENTITY] = $entity;
|
||||
}
|
||||
|
||||
if ($this->_selectJoinSql) {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
} else {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
|
||||
}
|
||||
$hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
||||
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
|
||||
|
||||
return $entities ? $entities[0] : null;
|
||||
}
|
||||
|
||||
@ -596,7 +614,7 @@ class BasicEntityPersister
|
||||
*/
|
||||
public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array())
|
||||
{
|
||||
if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) {
|
||||
if (($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) != false) {
|
||||
return $foundEntity;
|
||||
}
|
||||
|
||||
@ -608,14 +626,17 @@ class BasicEntityPersister
|
||||
// Mark inverse side as fetched in the hints, otherwise the UoW would
|
||||
// try to load it in a separate query (remember: to-one inverse sides can not be lazy).
|
||||
$hints = array();
|
||||
|
||||
if ($isInverseSingleValued) {
|
||||
$hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true;
|
||||
|
||||
if ($targetClass->subClasses) {
|
||||
foreach ($targetClass->subClasses as $targetSubclassName) {
|
||||
$hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cascade read-only status
|
||||
if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) {
|
||||
$hints[Query::HINT_READ_ONLY] = true;
|
||||
@ -631,19 +652,21 @@ class BasicEntityPersister
|
||||
} else {
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
$owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']);
|
||||
|
||||
// TRICKY: since the association is specular source and target are flipped
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
|
||||
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
// unset the old value and set the new sql aliased value here. By definition
|
||||
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
|
||||
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
|
||||
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
unset($identifier[$targetKeyColumn]);
|
||||
} else {
|
||||
if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
throw MappingException::joinColumnMustPointToMappedField(
|
||||
$sourceClass->name, $sourceKeyColumn
|
||||
);
|
||||
}
|
||||
|
||||
// unset the old value and set the new sql aliased value here. By definition
|
||||
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
|
||||
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
|
||||
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
|
||||
unset($identifier[$targetKeyColumn]);
|
||||
}
|
||||
|
||||
$targetEntity = $this->load($identifier, null, $assoc);
|
||||
@ -675,7 +698,9 @@ class BasicEntityPersister
|
||||
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
|
||||
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||
}
|
||||
|
||||
$evm = $this->_em->getEventManager();
|
||||
|
||||
if ($evm->hasListeners(Events::postLoad)) {
|
||||
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
|
||||
}
|
||||
@ -697,11 +722,8 @@ class BasicEntityPersister
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
|
||||
if ($this->_selectJoinSql) {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
} else {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
|
||||
}
|
||||
$hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
|
||||
|
||||
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
|
||||
}
|
||||
|
||||
@ -717,6 +739,7 @@ class BasicEntityPersister
|
||||
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
|
||||
|
||||
return $this->loadArrayFromStatement($assoc, $stmt);
|
||||
}
|
||||
|
||||
@ -725,6 +748,7 @@ class BasicEntityPersister
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param Doctrine\DBAL\Statement $stmt
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function loadArrayFromStatement($assoc, $stmt)
|
||||
@ -739,6 +763,7 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
|
||||
return $hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
}
|
||||
|
||||
@ -748,6 +773,8 @@ class BasicEntityPersister
|
||||
* @param array $assoc
|
||||
* @param Doctrine\DBAL\Statement $stmt
|
||||
* @param PersistentCollection $coll
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function loadCollectionFromStatement($assoc, $stmt, $coll)
|
||||
{
|
||||
@ -761,7 +788,8 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
$hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
|
||||
return $hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -777,6 +805,7 @@ class BasicEntityPersister
|
||||
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
{
|
||||
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
|
||||
|
||||
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
|
||||
}
|
||||
|
||||
@ -784,15 +813,18 @@ class BasicEntityPersister
|
||||
{
|
||||
$criteria = array();
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
|
||||
if ($assoc['isOwningSide']) {
|
||||
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform);
|
||||
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
|
||||
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
|
||||
|
||||
if (isset($sourceClass->associationMappings[$field])) {
|
||||
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
|
||||
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
|
||||
}
|
||||
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
|
||||
@ -807,15 +839,18 @@ class BasicEntityPersister
|
||||
} else {
|
||||
$owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']];
|
||||
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform);
|
||||
|
||||
// TRICKY: since the association is inverted source and target are flipped
|
||||
foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
|
||||
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
|
||||
|
||||
if (isset($sourceClass->associationMappings[$field])) {
|
||||
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
|
||||
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
|
||||
}
|
||||
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
|
||||
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
|
||||
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
@ -829,6 +864,7 @@ class BasicEntityPersister
|
||||
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
}
|
||||
|
||||
@ -847,15 +883,14 @@ class BasicEntityPersister
|
||||
*/
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null)
|
||||
{
|
||||
$joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
|
||||
$this->_getSelectManyToManyJoinSQL($assoc) : '';
|
||||
|
||||
$joinSql = ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : '';
|
||||
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
|
||||
|
||||
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
|
||||
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
|
||||
|
||||
$lockSql = '';
|
||||
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ) {
|
||||
$lockSql = ' ' . $this->_platform->getReadLockSql();
|
||||
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
|
||||
@ -882,6 +917,7 @@ class BasicEntityPersister
|
||||
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
|
||||
{
|
||||
$orderBySql = '';
|
||||
|
||||
foreach ($orderBy as $fieldName => $orientation) {
|
||||
if ( ! isset($this->_class->fieldMappings[$fieldName])) {
|
||||
throw ORMException::unrecognizedField($fieldName);
|
||||
@ -892,6 +928,7 @@ class BasicEntityPersister
|
||||
: $baseTableAlias;
|
||||
|
||||
$columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform);
|
||||
|
||||
$orderBySql .= $orderBySql ? ', ' : ' ORDER BY ';
|
||||
$orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation;
|
||||
}
|
||||
@ -924,20 +961,25 @@ class BasicEntityPersister
|
||||
// Add regular columns to select list
|
||||
foreach ($this->_class->fieldNames as $field) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnList .= $this->_getSelectColumnSQL($field, $this->_class);
|
||||
}
|
||||
|
||||
$this->_selectJoinSql = '';
|
||||
$eagerAliasCounter = 0;
|
||||
|
||||
foreach ($this->_class->associationMappings as $assocField => $assoc) {
|
||||
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
|
||||
|
||||
if ($assocColumnSQL) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnList .= $assocColumnSQL;
|
||||
}
|
||||
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
continue; // now this is why you shouldn't use inheritance
|
||||
}
|
||||
@ -947,41 +989,48 @@ class BasicEntityPersister
|
||||
|
||||
foreach ($eagerEntity->fieldNames AS $field) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
|
||||
}
|
||||
|
||||
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
|
||||
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
|
||||
|
||||
if ($assoc2ColumnSQL) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $assoc2ColumnSQL;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
|
||||
$first = true;
|
||||
|
||||
if ($assoc['isOwningSide']) {
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
if ( ! $first) {
|
||||
$this->_selectJoinSql .= ' AND ';
|
||||
}
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.'.$sourceCol.' = ' .
|
||||
$this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.'.$targetCol.' ';
|
||||
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = '
|
||||
. $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol . ' ';
|
||||
$first = false;
|
||||
}
|
||||
} else {
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
|
||||
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON ';
|
||||
|
||||
foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
if ( ! $first) {
|
||||
$this->_selectJoinSql .= ' AND ';
|
||||
}
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.'.$sourceCol.' = ' .
|
||||
$this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
|
||||
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
|
||||
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
@ -993,19 +1042,32 @@ class BasicEntityPersister
|
||||
return $this->_selectColumnListSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL join fragment used when selecting entities from an association.
|
||||
*
|
||||
* @param string $field
|
||||
* @param array $assoc
|
||||
* @param ClassMetadata $class
|
||||
* @param string $alias
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnList = '';
|
||||
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
|
||||
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias";
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
|
||||
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
|
||||
. '.' . $srcColumn . ' AS ' . $resultColumnName;
|
||||
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
|
||||
}
|
||||
}
|
||||
|
||||
return $columnList;
|
||||
}
|
||||
|
||||
@ -1027,8 +1089,8 @@ class BasicEntityPersister
|
||||
}
|
||||
|
||||
$joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform);
|
||||
|
||||
$joinSql = '';
|
||||
|
||||
foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
|
||||
if ($joinSql != '') $joinSql .= ' AND ';
|
||||
|
||||
@ -1038,12 +1100,11 @@ class BasicEntityPersister
|
||||
$quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$joinSql .= $this->_getSQLTableAlias($this->_class->name) .
|
||||
'.' . $quotedColumn . ' = '
|
||||
$joinSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $quotedColumn . ' = '
|
||||
. $joinTableName . '.' . $joinTableColumn;
|
||||
}
|
||||
|
||||
return " INNER JOIN $joinTableName ON $joinSql";
|
||||
return ' INNER JOIN ' . $joinTableName . ' ON ' . $joinSql;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1056,6 +1117,7 @@ class BasicEntityPersister
|
||||
if ($this->_insertSql === null) {
|
||||
$insertSql = '';
|
||||
$columns = $this->_getInsertColumnList();
|
||||
|
||||
if (empty($columns)) {
|
||||
$insertSql = $this->_platform->getEmptyIdentityInsertSQL(
|
||||
$this->_class->getQuotedTableName($this->_platform),
|
||||
@ -1066,11 +1128,12 @@ class BasicEntityPersister
|
||||
$values = array_fill(0, count($columns), '?');
|
||||
|
||||
$insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform)
|
||||
. ' (' . implode(', ', $columns) . ') '
|
||||
. 'VALUES (' . implode(', ', $values) . ')';
|
||||
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')';
|
||||
}
|
||||
|
||||
$this->_insertSql = $insertSql;
|
||||
}
|
||||
|
||||
return $this->_insertSql;
|
||||
}
|
||||
|
||||
@ -1085,19 +1148,21 @@ class BasicEntityPersister
|
||||
protected function _getInsertColumnList()
|
||||
{
|
||||
$columns = array();
|
||||
|
||||
foreach ($this->_class->reflFields as $name => $field) {
|
||||
if ($this->_class->isVersioned && $this->_class->versionField == $name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($this->_class->associationMappings[$name])) {
|
||||
$assoc = $this->_class->associationMappings[$name];
|
||||
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) {
|
||||
$columns[] = $sourceCol;
|
||||
}
|
||||
}
|
||||
} else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY ||
|
||||
$this->_class->identifier[0] != $name) {
|
||||
} else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->_class->identifier[0] != $name) {
|
||||
$columns[] = $this->_class->getQuotedColumnName($name, $this->_platform);
|
||||
}
|
||||
}
|
||||
@ -1116,11 +1181,13 @@ class BasicEntityPersister
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
|
||||
. '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
|
||||
|
||||
return "$sql AS $columnAlias";
|
||||
return $sql . ' AS ' . $columnAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1139,9 +1206,11 @@ class BasicEntityPersister
|
||||
if (isset($this->_sqlTableAliases[$className])) {
|
||||
return $this->_sqlTableAliases[$className];
|
||||
}
|
||||
|
||||
$tableAlias = 't' . $this->_sqlAliasCounter++;
|
||||
|
||||
$this->_sqlTableAliases[$className] = $tableAlias;
|
||||
|
||||
return $tableAlias;
|
||||
}
|
||||
|
||||
@ -1165,7 +1234,9 @@ class BasicEntityPersister
|
||||
$sql = 'SELECT 1 '
|
||||
. $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode)
|
||||
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql;
|
||||
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
}
|
||||
|
||||
@ -1194,28 +1265,26 @@ class BasicEntityPersister
|
||||
protected function _getSelectConditionSQL(array $criteria, $assoc = null)
|
||||
{
|
||||
$conditionSql = '';
|
||||
|
||||
foreach ($criteria as $field => $value) {
|
||||
$conditionSql .= $conditionSql ? ' AND ' : '';
|
||||
|
||||
if (isset($this->_class->columnNames[$field])) {
|
||||
if (isset($this->_class->fieldMappings[$field]['inherited'])) {
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->fieldMappings[$field]['inherited']) . '.';
|
||||
} else {
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
|
||||
}
|
||||
$conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
|
||||
$className = (isset($this->_class->fieldMappings[$field]['inherited']))
|
||||
? $this->_class->fieldMappings[$field]['inherited']
|
||||
: $this->_class->name;
|
||||
|
||||
$conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform);
|
||||
} else if (isset($this->_class->associationMappings[$field])) {
|
||||
if ( ! $this->_class->associationMappings[$field]['isOwningSide']) {
|
||||
throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field);
|
||||
}
|
||||
|
||||
if (isset($this->_class->associationMappings[$field]['inherited'])) {
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->associationMappings[$field]['inherited']) . '.';
|
||||
} else {
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
|
||||
}
|
||||
$className = (isset($this->_class->associationMappings[$field]['inherited']))
|
||||
? $this->_class->associationMappings[$field]['inherited']
|
||||
: $this->_class->name;
|
||||
|
||||
$conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
|
||||
$conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
|
||||
} else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
|
||||
// very careless developers could potentially open up this normally hidden api for userland attacks,
|
||||
// therefore checking for spaces and function calls which are not allowed.
|
||||
@ -1243,6 +1312,7 @@ class BasicEntityPersister
|
||||
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
|
||||
|
||||
return $this->loadArrayFromStatement($assoc, $stmt);
|
||||
}
|
||||
|
||||
@ -1258,7 +1328,8 @@ class BasicEntityPersister
|
||||
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
{
|
||||
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
|
||||
$this->loadCollectionFromStatement($assoc, $stmt, $coll);
|
||||
|
||||
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1276,18 +1347,18 @@ class BasicEntityPersister
|
||||
$owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']];
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
|
||||
$tableAlias = isset($owningAssoc['inherited']) ?
|
||||
$this->_getSQLTableAlias($owningAssoc['inherited'])
|
||||
: $this->_getSQLTableAlias($this->_class->name);
|
||||
$tableAlias = $this->_getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->_class->name);
|
||||
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$field = $sourceClass->getFieldForColumn($sourceKeyColumn);
|
||||
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
|
||||
|
||||
if (isset($sourceClass->associationMappings[$field])) {
|
||||
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
|
||||
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
|
||||
}
|
||||
|
||||
$criteria[$tableAlias . "." . $targetKeyColumn] = $value;
|
||||
} else {
|
||||
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
@ -1416,14 +1487,17 @@ class BasicEntityPersister
|
||||
public function exists($entity, array $extraConditions = array())
|
||||
{
|
||||
$criteria = $this->_class->getIdentifierValues($entity);
|
||||
|
||||
if ($extraConditions) {
|
||||
$criteria = array_merge($criteria, $extraConditions);
|
||||
}
|
||||
|
||||
$sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform)
|
||||
. ' ' . $this->_getSQLTableAlias($this->_class->name)
|
||||
$sql = 'SELECT 1'
|
||||
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name)
|
||||
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
|
||||
|
||||
return (bool) $this->_conn->fetchColumn($sql, array_values($criteria));
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
|
||||
return (bool) $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace Doctrine\ORM\Persisters;
|
||||
use Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\Query\ResultSetMapping;
|
||||
|
||||
/**
|
||||
@ -55,11 +56,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
*/
|
||||
protected function _getDiscriminatorColumnTableName()
|
||||
{
|
||||
if ($this->_class->name == $this->_class->rootEntityName) {
|
||||
return $this->_class->table['name'];
|
||||
} else {
|
||||
return $this->_em->getClassMetadata($this->_class->rootEntityName)->table['name'];
|
||||
}
|
||||
$class = ($this->_class->name !== $this->_class->rootEntityName)
|
||||
? $this->_em->getClassMetadata($this->_class->rootEntityName)
|
||||
: $this->_class;
|
||||
|
||||
return $class->getTableName();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,8 +73,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
{
|
||||
if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) {
|
||||
$definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited'];
|
||||
|
||||
return $this->_em->getClassMetadata($definingClassName);
|
||||
}
|
||||
|
||||
return $this->_class;
|
||||
}
|
||||
|
||||
@ -86,7 +89,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
*/
|
||||
public function getOwningTable($fieldName)
|
||||
{
|
||||
if (!isset($this->_owningTableMap[$fieldName])) {
|
||||
if (isset($this->_owningTableMap[$fieldName])) {
|
||||
return $this->_owningTableMap[$fieldName];
|
||||
}
|
||||
|
||||
if (isset($this->_class->associationMappings[$fieldName]['inherited'])) {
|
||||
$cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']);
|
||||
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
|
||||
@ -94,11 +100,13 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
} else {
|
||||
$cm = $this->_class;
|
||||
}
|
||||
$this->_owningTableMap[$fieldName] = $cm->table['name'];
|
||||
$this->_quotedTableMap[$cm->table['name']] = $cm->getQuotedTableName($this->_platform);
|
||||
}
|
||||
|
||||
return $this->_owningTableMap[$fieldName];
|
||||
$tableName = $cm->getTableName();
|
||||
|
||||
$this->_owningTableMap[$fieldName] = $tableName;
|
||||
$this->_quotedTableMap[$tableName] = $cm->getQuotedTableName($this->_platform);
|
||||
|
||||
return $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,20 +123,22 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
$isPostInsertId = $idGen->isPostInsertGenerator();
|
||||
|
||||
// Prepare statement for the root table
|
||||
$rootClass = $this->_class->name == $this->_class->rootEntityName ?
|
||||
$this->_class : $this->_em->getClassMetadata($this->_class->rootEntityName);
|
||||
$rootClass = ($this->_class->name !== $this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName) : $this->_class;
|
||||
$rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name);
|
||||
$rootTableName = $rootClass->table['name'];
|
||||
$rootTableName = $rootClass->getTableName();
|
||||
$rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL());
|
||||
|
||||
// Prepare statements for sub tables.
|
||||
$subTableStmts = array();
|
||||
|
||||
if ($rootClass !== $this->_class) {
|
||||
$subTableStmts[$this->_class->table['name']] = $this->_conn->prepare($this->_getInsertSQL());
|
||||
$subTableStmts[$this->_class->getTableName()] = $this->_conn->prepare($this->_getInsertSQL());
|
||||
}
|
||||
|
||||
foreach ($this->_class->parentClasses as $parentClassName) {
|
||||
$parentClass = $this->_em->getClassMetadata($parentClassName);
|
||||
$parentTableName = $parentClass->table['name'];
|
||||
$parentTableName = $parentClass->getTableName();
|
||||
|
||||
if ($parentClass !== $rootClass) {
|
||||
$parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName);
|
||||
$subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL());
|
||||
@ -143,9 +153,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
|
||||
// Execute insert on root table
|
||||
$paramIndex = 1;
|
||||
|
||||
foreach ($insertData[$rootTableName] as $columnName => $value) {
|
||||
$rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
|
||||
}
|
||||
|
||||
$rootTableStmt->execute();
|
||||
|
||||
if ($isPostInsertId) {
|
||||
@ -160,17 +172,23 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
foreach ($subTableStmts as $tableName => $stmt) {
|
||||
$data = isset($insertData[$tableName]) ? $insertData[$tableName] : array();
|
||||
$paramIndex = 1;
|
||||
foreach ((array) $id as $idVal) {
|
||||
$stmt->bindValue($paramIndex++, $idVal);
|
||||
|
||||
foreach ((array) $id as $idName => $idVal) {
|
||||
$type = isset($this->_columnTypes[$idName]) ? $this->_columnTypes[$idName] : Type::STRING;
|
||||
|
||||
$stmt->bindValue($paramIndex++, $idVal, $type);
|
||||
}
|
||||
|
||||
foreach ($data as $columnName => $value) {
|
||||
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
|
||||
$rootTableStmt->closeCursor();
|
||||
|
||||
foreach ($subTableStmts as $stmt) {
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
@ -191,15 +209,18 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
{
|
||||
$updateData = $this->_prepareUpdateData($entity);
|
||||
|
||||
if ($isVersioned = $this->_class->isVersioned) {
|
||||
if (($isVersioned = $this->_class->isVersioned) != false) {
|
||||
$versionedClass = $this->_getVersionedClassMetadata();
|
||||
$versionedTable = $versionedClass->table['name'];
|
||||
$versionedTable = $versionedClass->getTableName();
|
||||
}
|
||||
|
||||
if ($updateData) {
|
||||
foreach ($updateData as $tableName => $data) {
|
||||
$this->_updateTable($entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName);
|
||||
$this->_updateTable(
|
||||
$entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the table with the version column is updated even if no columns on that
|
||||
// table were affected.
|
||||
if ($isVersioned && ! isset($updateData[$versionedTable])) {
|
||||
@ -224,13 +245,17 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
// If the database platform supports FKs, just
|
||||
// delete the row from the root table. Cascades do the rest.
|
||||
if ($this->_platform->supportsForeignKeyConstraints()) {
|
||||
$this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName)
|
||||
->getQuotedTableName($this->_platform), $id);
|
||||
$this->_conn->delete(
|
||||
$this->_em->getClassMetadata($this->_class->rootEntityName)->getQuotedTableName($this->_platform), $id
|
||||
);
|
||||
} else {
|
||||
// Delete from all tables individually, starting from this class' table up to the root table.
|
||||
$this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id);
|
||||
|
||||
foreach ($this->_class->parentClasses as $parentClass) {
|
||||
$this->_conn->delete($this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id);
|
||||
$this->_conn->delete(
|
||||
$this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,23 +276,27 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
|
||||
// Add regular columns
|
||||
$columnList = '';
|
||||
|
||||
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
|
||||
if ($columnList != '') $columnList .= ', ';
|
||||
$columnList .= $this->_getSelectColumnSQL($fieldName,
|
||||
isset($mapping['inherited']) ?
|
||||
$this->_em->getClassMetadata($mapping['inherited']) :
|
||||
$this->_class);
|
||||
|
||||
$columnList .= $this->_getSelectColumnSQL(
|
||||
$fieldName,
|
||||
isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class
|
||||
);
|
||||
}
|
||||
|
||||
// Add foreign key columns
|
||||
foreach ($this->_class->associationMappings as $assoc2) {
|
||||
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) {
|
||||
$tableAlias = isset($assoc2['inherited']) ?
|
||||
$this->_getSQLTableAlias($assoc2['inherited'])
|
||||
: $baseTableAlias;
|
||||
$tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias;
|
||||
|
||||
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList != '') $columnList .= ', ';
|
||||
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
|
||||
|
||||
$columnList .= $this->getSelectJoinColumnSQL(
|
||||
$tableAlias,
|
||||
$srcColumn,
|
||||
isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name
|
||||
);
|
||||
}
|
||||
@ -276,27 +305,27 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
|
||||
// Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult).
|
||||
$discrColumn = $this->_class->discriminatorColumn['name'];
|
||||
if ($this->_class->rootEntityName == $this->_class->name) {
|
||||
$columnList .= ", $baseTableAlias.$discrColumn";
|
||||
} else {
|
||||
$columnList .= ', ' . $this->_getSQLTableAlias($this->_class->rootEntityName)
|
||||
. ".$discrColumn";
|
||||
}
|
||||
$tableAlias = ($this->_class->rootEntityName == $this->_class->name) ? $baseTableAlias : $this->_getSQLTableAlias($this->_class->rootEntityName);
|
||||
$columnList .= ', ' . $tableAlias . '.' . $discrColumn;
|
||||
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
|
||||
|
||||
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
|
||||
}
|
||||
|
||||
// INNER JOIN parent tables
|
||||
$joinSql = '';
|
||||
|
||||
foreach ($this->_class->parentClasses as $parentClassName) {
|
||||
$parentClass = $this->_em->getClassMetadata($parentClassName);
|
||||
$tableAlias = $this->_getSQLTableAlias($parentClassName);
|
||||
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($idColumns as $idColumn) {
|
||||
if ($first) $first = false; else $joinSql .= ' AND ';
|
||||
|
||||
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
}
|
||||
}
|
||||
@ -309,19 +338,20 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
if ($this->_selectColumnListSql === null) {
|
||||
// Add subclass columns
|
||||
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
||||
if (isset($mapping['inherited'])) {
|
||||
continue;
|
||||
}
|
||||
if (isset($mapping['inherited'])) continue;
|
||||
|
||||
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
|
||||
}
|
||||
|
||||
// Add join columns (foreign keys)
|
||||
foreach ($subClass->associationMappings as $assoc2) {
|
||||
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE
|
||||
&& ! isset($assoc2['inherited'])) {
|
||||
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) {
|
||||
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList != '') $columnList .= ', ';
|
||||
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
|
||||
|
||||
$columnList .= $this->getSelectJoinColumnSQL(
|
||||
$tableAlias,
|
||||
$srcColumn,
|
||||
isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name
|
||||
);
|
||||
}
|
||||
@ -332,14 +362,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
// Add LEFT JOIN
|
||||
$joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($idColumns as $idColumn) {
|
||||
if ($first) $first = false; else $joinSql .= ' AND ';
|
||||
|
||||
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
}
|
||||
}
|
||||
|
||||
$joinSql .= $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
|
||||
$this->_getSelectManyToManyJoinSQL($assoc) : '';
|
||||
$joinSql .= ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : '';
|
||||
|
||||
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
|
||||
|
||||
@ -351,6 +382,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
$lockSql = '';
|
||||
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ) {
|
||||
$lockSql = ' ' . $this->_platform->getReadLockSql();
|
||||
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
|
||||
@ -376,13 +408,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
|
||||
// INNER JOIN parent tables
|
||||
$joinSql = '';
|
||||
|
||||
foreach ($this->_class->parentClasses as $parentClassName) {
|
||||
$parentClass = $this->_em->getClassMetadata($parentClassName);
|
||||
$tableAlias = $this->_getSQLTableAlias($parentClassName);
|
||||
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($idColumns as $idColumn) {
|
||||
if ($first) $first = false; else $joinSql .= ' AND ';
|
||||
|
||||
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +40,10 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
protected function _getDeleteRowSQL(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$columns = $mapping['joinTableColumns'];
|
||||
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?';
|
||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||
|
||||
return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . implode(' = ? AND ', $mapping['joinTableColumns']) . ' = ?';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,10 +76,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
protected function _getInsertRowSQL(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$columns = $mapping['joinTableColumns'];
|
||||
return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')'
|
||||
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
|
||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||
|
||||
return 'INSERT INTO ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,13 +152,18 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
protected function _getDeleteSQL(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$whereClause = '';
|
||||
|
||||
foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) {
|
||||
if ($whereClause !== '') $whereClause .= ' AND ';
|
||||
$whereClause .= "$relationColumn = ?";
|
||||
|
||||
$whereClause .= $relationColumn . ' = ?';
|
||||
}
|
||||
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . $whereClause;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,8 +178,10 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
if (count($mapping['relationToSourceKeyColumns']) > 1) {
|
||||
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
|
||||
|
||||
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
|
||||
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
|
||||
}
|
||||
@ -194,30 +203,31 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
if ($mapping['isOwningSide']) {
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$joinColumns = $mapping['relationToSourceKeyColumns'];
|
||||
} else {
|
||||
$mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']];
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$joinColumns = $mapping['relationToTargetKeyColumns'];
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($joinColumns[$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($class->containsForeignIdentifier) {
|
||||
$params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||
$params[] = ($class->containsForeignIdentifier)
|
||||
? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]
|
||||
: $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = 'SELECT count(*) FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
$sql = 'SELECT COUNT(*)'
|
||||
. ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . $whereClause;
|
||||
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
@ -231,9 +241,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
return $this->_em->getUnitOfWork()
|
||||
->getEntityPersister($mapping['targetEntity'])
|
||||
->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,35 +274,36 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$sourceId = $uow->getEntityIdentifier($coll->getOwner());
|
||||
$targetId = $uow->getEntityIdentifier($element);
|
||||
}
|
||||
$joinTable = $mapping['joinTable'];
|
||||
|
||||
$whereClause = '';
|
||||
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
$whereClause .= $joinTableColumn . ' = ?';
|
||||
|
||||
$params[] = ($targetClass->containsForeignIdentifier)
|
||||
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
||||
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
$whereClause .= $joinTableColumn . ' = ?';
|
||||
|
||||
$params[] = ($sourceClass->containsForeignIdentifier)
|
||||
? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
|
||||
: $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
$sql = 'SELECT 1'
|
||||
. ' FROM ' . $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . $whereClause;
|
||||
|
||||
return (bool) $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
@ -35,38 +35,49 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
/** {@inheritdoc} */
|
||||
protected function _getDiscriminatorColumnTableName()
|
||||
{
|
||||
return $this->_class->table['name'];
|
||||
return $this->_class->getTableName();
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
protected function _getSelectColumnListSQL()
|
||||
{
|
||||
if ($this->_selectColumnListSql !== null) {
|
||||
return $this->_selectColumnListSql;
|
||||
}
|
||||
|
||||
$columnList = parent::_getSelectColumnListSQL();
|
||||
|
||||
// Append discriminator column
|
||||
$discrColumn = $this->_class->discriminatorColumn['name'];
|
||||
$columnList .= ", $discrColumn";
|
||||
$columnList .= ', ' . $discrColumn;
|
||||
|
||||
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
|
||||
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
|
||||
|
||||
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
|
||||
|
||||
// Append subclass columns
|
||||
foreach ($this->_class->subClasses as $subClassName) {
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
|
||||
// Regular columns
|
||||
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
||||
if ( ! isset($mapping['inherited'])) {
|
||||
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Foreign key columns
|
||||
foreach ($subClass->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList != '') $columnList .= ', ';
|
||||
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
|
||||
|
||||
$columnList .= $this->getSelectJoinColumnSQL(
|
||||
$tableAlias,
|
||||
$srcColumn,
|
||||
isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name
|
||||
);
|
||||
}
|
||||
@ -74,13 +85,15 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
}
|
||||
|
||||
return $columnList;
|
||||
$this->_selectColumnListSql = $columnList;
|
||||
return $this->_selectColumnListSql;
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
protected function _getInsertColumnList()
|
||||
{
|
||||
$columns = parent::_getInsertColumnList();
|
||||
|
||||
// Add discriminator column to the INSERT SQL
|
||||
$columns[] = $this->_class->discriminatorColumn['name'];
|
||||
|
||||
@ -100,17 +113,20 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
|
||||
// Append discriminator condition
|
||||
if ($conditionSql) $conditionSql .= ' AND ';
|
||||
|
||||
$values = array();
|
||||
|
||||
if ($this->_class->discriminatorValue !== null) { // discriminators can be 0
|
||||
$values[] = $this->_conn->quote($this->_class->discriminatorValue);
|
||||
}
|
||||
|
||||
$discrValues = array_flip($this->_class->discriminatorMap);
|
||||
|
||||
foreach ($this->_class->subClasses as $subclassName) {
|
||||
$values[] = $this->_conn->quote($discrValues[$subclassName]);
|
||||
}
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'
|
||||
. $this->_class->discriminatorColumn['name']
|
||||
|
||||
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $this->_class->discriminatorColumn['name']
|
||||
. ' IN (' . implode(', ', $values) . ')';
|
||||
|
||||
return $conditionSql;
|
||||
|
@ -209,6 +209,11 @@ class ProxyFactory
|
||||
|
||||
$methods .= $parameterString . ')';
|
||||
$methods .= "\n" . ' {' . "\n";
|
||||
if ($this->isShortIdentifierGetter($method, $class)) {
|
||||
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
|
||||
$methods .= ' return $this->_identifier["' . lcfirst(substr($method->getName(), 3)) . '"];' . "\n";
|
||||
$methods .= ' }' . "\n";
|
||||
}
|
||||
$methods .= ' $this->__load();' . "\n";
|
||||
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
|
||||
$methods .= "\n" . ' }' . "\n";
|
||||
@ -218,6 +223,21 @@ class ProxyFactory
|
||||
return $methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionMethod $method
|
||||
* @param ClassMetadata $class
|
||||
* @return bool
|
||||
*/
|
||||
private function isShortIdentifierGetter($method, $class)
|
||||
{
|
||||
return (
|
||||
$method->getNumberOfParameters() == 0 &&
|
||||
substr($method->getName(), 0, 3) == "get" &&
|
||||
in_array(lcfirst(substr($method->getName(), 3)), $class->identifier, true) &&
|
||||
(($method->getEndLine() - $method->getStartLine()) <= 4)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the code for the __sleep method for a proxy class.
|
||||
*
|
||||
|
@ -206,6 +206,7 @@ final class Query extends AbstractQuery
|
||||
if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) {
|
||||
$hash = $this->_getQueryCacheId();
|
||||
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
|
||||
|
||||
if ($cached === false) {
|
||||
// Cache miss.
|
||||
$parser = new Parser($this);
|
||||
@ -219,6 +220,7 @@ final class Query extends AbstractQuery
|
||||
$parser = new Parser($this);
|
||||
$this->_parserResult = $parser->parse();
|
||||
}
|
||||
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
|
||||
return $this->_parserResult;
|
||||
@ -238,36 +240,42 @@ final class Query extends AbstractQuery
|
||||
throw QueryException::invalidParameterNumber();
|
||||
}
|
||||
|
||||
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
|
||||
|
||||
if ($this->_resultSetMapping === null) {
|
||||
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
|
||||
}
|
||||
|
||||
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes query parameter mappings
|
||||
*
|
||||
* @param array $paramMappings
|
||||
* @return array
|
||||
*/
|
||||
private function processParameterMappings($paramMappings)
|
||||
{
|
||||
$sqlParams = $types = array();
|
||||
|
||||
foreach ($this->_params as $key => $value) {
|
||||
if ( ! isset($paramMappings[$key])) {
|
||||
throw QueryException::unknownParameter($key);
|
||||
}
|
||||
|
||||
if (isset($this->_paramTypes[$key])) {
|
||||
foreach ($paramMappings[$key] as $position) {
|
||||
$types[$position] = $this->_paramTypes[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
|
||||
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
|
||||
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
|
||||
} else {
|
||||
$class = $this->_em->getClassMetadata(get_class($value));
|
||||
$idValues = $class->getIdentifierValues($value);
|
||||
}
|
||||
$sqlPositions = $paramMappings[$key];
|
||||
$cSqlPos = count($sqlPositions);
|
||||
$cIdValues = count($idValues);
|
||||
$idValues = array_values($idValues);
|
||||
for ($i = 0; $i < $cSqlPos; $i++) {
|
||||
$sqlParams[$sqlPositions[$i]] = $idValues[ ($i % $cIdValues) ];
|
||||
}
|
||||
} else {
|
||||
foreach ($paramMappings[$key] as $position) {
|
||||
$sqlParams[$position] = $value;
|
||||
}
|
||||
$value = array_values($this->processParameterValue($value));
|
||||
$countValue = count($value);
|
||||
|
||||
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
|
||||
$sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,11 +284,40 @@ final class Query extends AbstractQuery
|
||||
$sqlParams = array_values($sqlParams);
|
||||
}
|
||||
|
||||
if ($this->_resultSetMapping === null) {
|
||||
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
|
||||
return array($sqlParams, $types);
|
||||
}
|
||||
|
||||
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
||||
/**
|
||||
* Process an individual parameter value
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
private function processParameterValue($value)
|
||||
{
|
||||
switch (true) {
|
||||
case is_array($value):
|
||||
for ($i = 0, $l = count($value); $i < $l; $i++) {
|
||||
$paramValue = $this->processParameterValue($value[$i]);
|
||||
|
||||
// TODO: What about Entities that have composite primary key?
|
||||
$value[$i] = is_array($paramValue) ? $paramValue[key($paramValue)] : $paramValue;
|
||||
}
|
||||
|
||||
return array($value);
|
||||
|
||||
case is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value)):
|
||||
if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) {
|
||||
return array_values($this->_em->getUnitOfWork()->getEntityIdentifier($value));
|
||||
}
|
||||
|
||||
$class = $this->_em->getClassMetadata(get_class($value));
|
||||
|
||||
return array_values($class->getIdentifierValues($value));
|
||||
|
||||
default:
|
||||
return array($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
68
lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php
Normal file
68
lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Lexer;
|
||||
|
||||
/**
|
||||
* "IDENTITY" "(" SingleValuedAssociationPathExpression ")"
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.2
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class IdentityFunction extends FunctionNode
|
||||
{
|
||||
public $pathExpression;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
|
||||
$dqlAlias = $this->pathExpression->identificationVariable;
|
||||
$assocField = $this->pathExpression->field;
|
||||
|
||||
$qComp = $sqlWalker->getQueryComponent($dqlAlias);
|
||||
$class = $qComp['metadata'];
|
||||
$assoc = $class->associationMappings[$assocField];
|
||||
|
||||
$tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
return $tableAlias . '.' . reset($assoc['targetToSourceKeyColumns']);;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
$parser->match(Lexer::T_IDENTIFIER);
|
||||
$parser->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$this->pathExpression = $parser->SingleValuedAssociationPathExpression();
|
||||
|
||||
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ class SizeFunction extends FunctionNode
|
||||
|
||||
if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) {
|
||||
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
|
||||
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName());
|
||||
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
$sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE ';
|
||||
|
||||
@ -77,7 +77,7 @@ class SizeFunction extends FunctionNode
|
||||
|
||||
// SQL table aliases
|
||||
$joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
// join to target table
|
||||
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE ';
|
||||
|
@ -23,7 +23,7 @@ namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
/**
|
||||
* SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression |
|
||||
* (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable]
|
||||
* (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable]
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
@ -37,11 +37,13 @@ class SelectExpression extends Node
|
||||
{
|
||||
public $expression;
|
||||
public $fieldIdentificationVariable;
|
||||
public $hiddenAliasResultVariable;
|
||||
|
||||
public function __construct($expression, $fieldIdentificationVariable)
|
||||
public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false)
|
||||
{
|
||||
$this->expression = $expression;
|
||||
$this->fieldIdentificationVariable = $fieldIdentificationVariable;
|
||||
$this->hiddenAliasResultVariable = $hiddenAliasResultVariable;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
|
@ -63,7 +63,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
$idColumnList = implode(', ', $idColumnNames);
|
||||
|
||||
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
|
||||
$sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $primaryDqlAlias);
|
||||
$sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias);
|
||||
|
||||
$this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
|
||||
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
|
||||
@ -98,7 +98,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
|
||||
}
|
||||
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
|
||||
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
|
||||
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
|
||||
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,7 +64,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
$idColumnList = implode(', ', $idColumnNames);
|
||||
|
||||
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
|
||||
$sqlWalker->setSQLTableAlias($primaryClass->table['name'], 't0', $updateClause->aliasIdentificationVariable);
|
||||
$sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable);
|
||||
|
||||
$this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
|
||||
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
|
||||
@ -106,7 +106,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
//FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage.
|
||||
if ($newValue instanceof AST\InputParameter) {
|
||||
$paramKey = $newValue->name;
|
||||
$this->_sqlParameters[$i][] = $sqlWalker->getQuery()->getParameter($paramKey);
|
||||
$this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey);
|
||||
$this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey);
|
||||
|
||||
++$this->_numParametersInUpdateClause;
|
||||
}
|
||||
@ -136,7 +137,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
|
||||
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
|
||||
|
||||
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable;
|
||||
$this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,11 +155,23 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
|
||||
$conn->executeUpdate($this->_createTempTableSql);
|
||||
|
||||
// Insert identifiers. Parameters from the update clause are cut off.
|
||||
$numUpdated = $conn->executeUpdate($this->_insertSql, array_slice($params, $this->_numParametersInUpdateClause), $types);
|
||||
$numUpdated = $conn->executeUpdate(
|
||||
$this->_insertSql,
|
||||
array_slice($params, $this->_numParametersInUpdateClause),
|
||||
array_slice($types, $this->_numParametersInUpdateClause)
|
||||
);
|
||||
|
||||
// Execute UPDATE statements
|
||||
for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) {
|
||||
$conn->executeUpdate($this->_sqlStatements[$i], isset($this->_sqlParameters[$i]) ? $this->_sqlParameters[$i] : array());
|
||||
$parameters = array();
|
||||
$types = array();
|
||||
|
||||
if (isset($this->_sqlParameters[$i])) {
|
||||
$parameters = isset($this->_sqlParameters[$i]['parameters']) ? $this->_sqlParameters[$i]['parameters'] : array();
|
||||
$types = isset($this->_sqlParameters[$i]['types']) ? $this->_sqlParameters[$i]['types'] : array();
|
||||
}
|
||||
|
||||
$conn->executeUpdate($this->_sqlStatements[$i], $parameters, $types);
|
||||
}
|
||||
|
||||
// Drop temporary table
|
||||
|
@ -51,6 +51,8 @@ abstract class Base
|
||||
foreach ((array) $args as $arg) {
|
||||
$this->add($arg);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function add($arg)
|
||||
@ -67,6 +69,8 @@ abstract class Base
|
||||
|
||||
$this->_parts[] = $arg;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function count()
|
||||
|
@ -34,27 +34,55 @@ namespace Doctrine\ORM\Query\Expr;
|
||||
*/
|
||||
class From
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $_from;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $_alias;
|
||||
|
||||
public function __construct($from, $alias)
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $_indexBy;
|
||||
|
||||
/**
|
||||
* @param string $from The class name.
|
||||
* @param string $alias The alias of the class.
|
||||
* @param string $indexBy The index for the from.
|
||||
*/
|
||||
public function __construct($from, $alias, $indexBy = null)
|
||||
{
|
||||
$this->_from = $from;
|
||||
$this->_alias = $alias;
|
||||
$this->_indexBy = $indexBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->_from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->_alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->_from . ' ' . $this->_alias;
|
||||
return $this->_from . ' ' . $this->_alias .
|
||||
($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : '');
|
||||
}
|
||||
}
|
@ -76,39 +76,40 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
const T_FROM = 122;
|
||||
const T_GROUP = 123;
|
||||
const T_HAVING = 124;
|
||||
const T_IN = 125;
|
||||
const T_INDEX = 126;
|
||||
const T_INNER = 127;
|
||||
const T_INSTANCE = 128;
|
||||
const T_IS = 129;
|
||||
const T_JOIN = 130;
|
||||
const T_LEADING = 131;
|
||||
const T_LEFT = 132;
|
||||
const T_LIKE = 133;
|
||||
const T_MAX = 134;
|
||||
const T_MEMBER = 135;
|
||||
const T_MIN = 136;
|
||||
const T_NOT = 137;
|
||||
const T_NULL = 138;
|
||||
const T_NULLIF = 139;
|
||||
const T_OF = 140;
|
||||
const T_OR = 141;
|
||||
const T_ORDER = 142;
|
||||
const T_OUTER = 143;
|
||||
const T_SELECT = 144;
|
||||
const T_SET = 145;
|
||||
const T_SIZE = 146;
|
||||
const T_SOME = 147;
|
||||
const T_SUM = 148;
|
||||
const T_THEN = 149;
|
||||
const T_TRAILING = 150;
|
||||
const T_TRUE = 151;
|
||||
const T_UPDATE = 152;
|
||||
const T_WHEN = 153;
|
||||
const T_WHERE = 154;
|
||||
const T_WITH = 155;
|
||||
const T_PARTIAL = 156;
|
||||
const T_MOD = 157;
|
||||
const T_HIDDEN = 125;
|
||||
const T_IN = 126;
|
||||
const T_INDEX = 127;
|
||||
const T_INNER = 128;
|
||||
const T_INSTANCE = 129;
|
||||
const T_IS = 130;
|
||||
const T_JOIN = 131;
|
||||
const T_LEADING = 132;
|
||||
const T_LEFT = 133;
|
||||
const T_LIKE = 134;
|
||||
const T_MAX = 135;
|
||||
const T_MEMBER = 136;
|
||||
const T_MIN = 137;
|
||||
const T_NOT = 138;
|
||||
const T_NULL = 139;
|
||||
const T_NULLIF = 140;
|
||||
const T_OF = 141;
|
||||
const T_OR = 142;
|
||||
const T_ORDER = 143;
|
||||
const T_OUTER = 144;
|
||||
const T_SELECT = 145;
|
||||
const T_SET = 146;
|
||||
const T_SIZE = 147;
|
||||
const T_SOME = 148;
|
||||
const T_SUM = 149;
|
||||
const T_THEN = 150;
|
||||
const T_TRAILING = 151;
|
||||
const T_TRUE = 152;
|
||||
const T_UPDATE = 153;
|
||||
const T_WHEN = 154;
|
||||
const T_WHERE = 155;
|
||||
const T_WITH = 156;
|
||||
const T_PARTIAL = 157;
|
||||
const T_MOD = 158;
|
||||
|
||||
/**
|
||||
* Creates a new query scanner object.
|
||||
|
@ -40,7 +40,8 @@ class Parser
|
||||
'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction',
|
||||
'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction',
|
||||
'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction',
|
||||
'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction'
|
||||
'upper' => 'Doctrine\ORM\Query\AST\Functions\UpperFunction',
|
||||
'identity' => 'Doctrine\ORM\Query\AST\Functions\IdentityFunction',
|
||||
);
|
||||
|
||||
/** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
|
||||
@ -1327,7 +1328,10 @@ class Parser
|
||||
// We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
|
||||
$glimpse = $this->_lexer->glimpse();
|
||||
|
||||
if ($glimpse['type'] != Lexer::T_DOT) {
|
||||
if ($glimpse['type'] == Lexer::T_DOT) {
|
||||
return $this->SingleValuedPathExpression();
|
||||
}
|
||||
|
||||
$token = $this->_lexer->lookahead;
|
||||
$identVariable = $this->IdentificationVariable();
|
||||
|
||||
@ -1338,9 +1342,6 @@ class Parser
|
||||
return $identVariable;
|
||||
}
|
||||
|
||||
return $this->SingleValuedPathExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* OrderByItem ::= (ResultVariable | SingleValuedPathExpression) ["ASC" | "DESC"]
|
||||
*
|
||||
@ -1353,12 +1354,9 @@ class Parser
|
||||
// We need to check if we are in a ResultVariable or StateFieldPathExpression
|
||||
$glimpse = $this->_lexer->glimpse();
|
||||
|
||||
if ($glimpse['type'] != Lexer::T_DOT) {
|
||||
$token = $this->_lexer->lookahead;
|
||||
$expr = $this->ResultVariable();
|
||||
} else {
|
||||
$expr = $this->SingleValuedPathExpression();
|
||||
}
|
||||
$expr = ($glimpse['type'] != Lexer::T_DOT)
|
||||
? $this->ResultVariable()
|
||||
: $this->SingleValuedPathExpression();
|
||||
|
||||
$item = new AST\OrderByItem($expr);
|
||||
|
||||
@ -1695,6 +1693,7 @@ class Parser
|
||||
return $this->CoalesceExpression();
|
||||
|
||||
case Lexer::T_CASE:
|
||||
$this->_lexer->resetPeek();
|
||||
$peek = $this->_lexer->peek();
|
||||
|
||||
return ($peek['type'] === Lexer::T_WHEN)
|
||||
@ -1829,7 +1828,7 @@ class Parser
|
||||
/**
|
||||
* SelectExpression ::=
|
||||
* IdentificationVariable | StateFieldPathExpression |
|
||||
* (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]
|
||||
* (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
*
|
||||
* @return Doctrine\ORM\Query\AST\SelectExpression
|
||||
*/
|
||||
@ -1837,6 +1836,7 @@ class Parser
|
||||
{
|
||||
$expression = null;
|
||||
$identVariable = null;
|
||||
$hiddenAliasResultVariable = false;
|
||||
$fieldAliasIdentificationVariable = null;
|
||||
$peek = $this->_lexer->glimpse();
|
||||
|
||||
@ -1899,6 +1899,12 @@ class Parser
|
||||
$this->match(Lexer::T_AS);
|
||||
}
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_HIDDEN)) {
|
||||
$this->match(Lexer::T_HIDDEN);
|
||||
|
||||
$hiddenAliasResultVariable = true;
|
||||
}
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
$token = $this->_lexer->lookahead;
|
||||
$fieldAliasIdentificationVariable = $this->AliasResultVariable();
|
||||
@ -1912,10 +1918,12 @@ class Parser
|
||||
}
|
||||
}
|
||||
|
||||
$expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable);
|
||||
$expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable, $hiddenAliasResultVariable);
|
||||
|
||||
if ( ! $supportsAlias) {
|
||||
$this->_identVariableExpressions[$identVariable] = $expr;
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
@ -2381,7 +2389,7 @@ class Parser
|
||||
/**
|
||||
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
|
||||
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
|
||||
* | FunctionsReturningDatetime | IdentificationVariable | CaseExpression
|
||||
* | FunctionsReturningDatetime | IdentificationVariable | ResultVariable | CaseExpression
|
||||
*/
|
||||
public function ArithmeticPrimary()
|
||||
{
|
||||
@ -2411,6 +2419,10 @@ class Parser
|
||||
return $this->SingleValuedPathExpression();
|
||||
}
|
||||
|
||||
if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
|
||||
return $this->ResultVariable();
|
||||
}
|
||||
|
||||
return $this->StateFieldPathExpression();
|
||||
|
||||
case Lexer::T_INPUT_PARAMETER:
|
||||
|
@ -244,24 +244,23 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
$baseTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
// INNER JOIN parent class tables
|
||||
foreach ($class->parentClasses as $parentClassName) {
|
||||
$parentClass = $this->_em->getClassMetadata($parentClassName);
|
||||
$tableAlias = $this->getSQLTableAlias($parentClass->table['name'], $dqlAlias);
|
||||
$tableAlias = $this->getSQLTableAlias($parentClass->getTableName(), $dqlAlias);
|
||||
|
||||
// If this is a joined association we must use left joins to preserve the correct result.
|
||||
$sql .= isset($this->_queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
|
||||
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform)
|
||||
. ' ' . $tableAlias . ' ON ';
|
||||
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($class->identifier as $idField) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
|
||||
$sql .= $baseTableAlias . '.' . $columnName
|
||||
. ' = '
|
||||
. $tableAlias . '.' . $columnName;
|
||||
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,17 +268,15 @@ class SqlWalker implements TreeWalker
|
||||
if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
|
||||
foreach ($class->subClasses as $subClassName) {
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
$tableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias);
|
||||
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform)
|
||||
. ' ' . $tableAlias . ' ON ';
|
||||
$tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
|
||||
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($class->identifier as $idField) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$columnName = $class->getQuotedColumnName($idField, $this->_platform);
|
||||
$sql .= $baseTableAlias . '.' . $columnName
|
||||
. ' = '
|
||||
. $tableAlias . '.' . $columnName;
|
||||
$sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -290,24 +287,26 @@ class SqlWalker implements TreeWalker
|
||||
private function _generateOrderedCollectionOrderByItems()
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
foreach ($this->_selectedClasses AS $dqlAlias => $class) {
|
||||
$qComp = $this->_queryComponents[$dqlAlias];
|
||||
|
||||
if (isset($qComp['relation']['orderBy'])) {
|
||||
foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) {
|
||||
if ($qComp['metadata']->isInheritanceTypeJoined()) {
|
||||
$tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName);
|
||||
} else {
|
||||
$tableName = $qComp['metadata']->table['name'];
|
||||
}
|
||||
$tableName = ($qComp['metadata']->isInheritanceTypeJoined())
|
||||
? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
|
||||
: $qComp['metadata']->getTableName();
|
||||
|
||||
if ($sql != '') {
|
||||
$sql .= ', ';
|
||||
}
|
||||
$sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.' .
|
||||
$qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . " $orientation";
|
||||
|
||||
$sql .= $this->getSQLTableAlias($tableName, $dqlAlias) . '.'
|
||||
. $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform) . ' ' . $orientation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
@ -328,6 +327,7 @@ class SqlWalker implements TreeWalker
|
||||
if ($class->isInheritanceTypeSingleTable()) {
|
||||
$conn = $this->_em->getConnection();
|
||||
$values = array();
|
||||
|
||||
if ($class->discriminatorValue !== null) { // discrimnators can be 0
|
||||
$values[] = $conn->quote($class->discriminatorValue);
|
||||
}
|
||||
@ -342,7 +342,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
$sql .= ($sql != '' ? ' AND ' : '')
|
||||
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '')
|
||||
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
|
||||
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
|
||||
}
|
||||
}
|
||||
@ -369,16 +369,15 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= ' ORDER BY ' . $orderBySql;
|
||||
}
|
||||
|
||||
|
||||
$sql = $this->_platform->modifyLimitQuery(
|
||||
$sql, $this->_query->getMaxResults(), $this->_query->getFirstResult()
|
||||
);
|
||||
|
||||
if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) {
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ) {
|
||||
$sql .= " " . $this->_platform->getReadLockSQL();
|
||||
$sql .= ' ' . $this->_platform->getReadLockSQL();
|
||||
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
|
||||
$sql .= " " . $this->_platform->getWriteLockSQL();
|
||||
$sql .= ' ' . $this->_platform->getWriteLockSQL();
|
||||
} else if ($lockMode == LockMode::OPTIMISTIC) {
|
||||
foreach ($this->_selectedClasses AS $class) {
|
||||
if ( ! $class->isVersioned) {
|
||||
@ -400,10 +399,8 @@ class SqlWalker implements TreeWalker
|
||||
public function walkUpdateStatement(AST\UpdateStatement $AST)
|
||||
{
|
||||
$this->_useSqlTableAliases = false;
|
||||
$sql = $this->walkUpdateClause($AST->updateClause);
|
||||
$sql .= $this->walkWhereClause($AST->whereClause);
|
||||
|
||||
return $sql;
|
||||
return $this->walkUpdateClause($AST->updateClause) . $this->walkWhereClause($AST->whereClause);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -415,10 +412,8 @@ class SqlWalker implements TreeWalker
|
||||
public function walkDeleteStatement(AST\DeleteStatement $AST)
|
||||
{
|
||||
$this->_useSqlTableAliases = false;
|
||||
$sql = $this->walkDeleteClause($AST->deleteClause);
|
||||
$sql .= $this->walkWhereClause($AST->whereClause);
|
||||
|
||||
return $sql;
|
||||
return $this->walkDeleteClause($AST->deleteClause) . $this->walkWhereClause($AST->whereClause);
|
||||
}
|
||||
|
||||
|
||||
@ -440,7 +435,7 @@ class SqlWalker implements TreeWalker
|
||||
$class = $this->_em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
|
||||
}
|
||||
|
||||
return $this->getSQLTableAlias($class->table['name'], $identificationVariable);
|
||||
return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -489,7 +484,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
if ($this->_useSqlTableAliases) {
|
||||
$sql .= $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.';
|
||||
$sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
|
||||
}
|
||||
|
||||
$sql .= reset($assoc['targetToSourceKeyColumns']);
|
||||
@ -534,7 +529,7 @@ class SqlWalker implements TreeWalker
|
||||
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
|
||||
// Add discriminator columns to SQL
|
||||
$rootClass = $this->_em->getClassMetadata($class->rootEntityName);
|
||||
$tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias);
|
||||
$tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
|
||||
$discrColumn = $rootClass->discriminatorColumn;
|
||||
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
|
||||
|
||||
@ -551,9 +546,9 @@ class SqlWalker implements TreeWalker
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if (isset($assoc['inherited'])) {
|
||||
$owningClass = $this->_em->getClassMetadata($assoc['inherited']);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($owningClass->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
|
||||
} else {
|
||||
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
}
|
||||
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
@ -570,7 +565,8 @@ class SqlWalker implements TreeWalker
|
||||
} else {
|
||||
// Add foreign key columns to SQL, if necessary
|
||||
if ($addMetaColumns) {
|
||||
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
@ -612,7 +608,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
@ -724,12 +720,14 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$joinAssocPathExpr = $join->joinAssociationPathExpression;
|
||||
$joinedDqlAlias = $join->aliasIdentificationVariable;
|
||||
|
||||
$relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
|
||||
$targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
|
||||
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
|
||||
$targetTableName = $targetClass->getQuotedTableName($this->_platform);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name'], $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable);
|
||||
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable);
|
||||
|
||||
// Ensure we got the owning side, since it has all mapping info
|
||||
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
|
||||
@ -811,8 +809,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
// Join target table
|
||||
$sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
|
||||
? ' LEFT JOIN ' : ' INNER JOIN ';
|
||||
$sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
|
||||
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
|
||||
|
||||
$first = true;
|
||||
@ -980,6 +977,7 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
$sql = '';
|
||||
$expr = $selectExpression->expression;
|
||||
$hidden = $selectExpression->hiddenAliasResultVariable;
|
||||
|
||||
if ($expr instanceof AST\PathExpression) {
|
||||
if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
|
||||
@ -1009,7 +1007,10 @@ class SqlWalker implements TreeWalker
|
||||
$columnAlias = $this->getSQLColumnAlias($columnName);
|
||||
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
} else if ($expr instanceof AST\AggregateExpression) {
|
||||
if ( ! $selectExpression->fieldIdentificationVariable) {
|
||||
$resultAlias = $this->_scalarResultCounter++;
|
||||
@ -1022,7 +1023,10 @@ class SqlWalker implements TreeWalker
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
} else if ($expr instanceof AST\Subselect) {
|
||||
if ( ! $selectExpression->fieldIdentificationVariable) {
|
||||
$resultAlias = $this->_scalarResultCounter++;
|
||||
@ -1035,7 +1039,10 @@ class SqlWalker implements TreeWalker
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
} else if ($expr instanceof AST\Functions\FunctionNode) {
|
||||
if ( ! $selectExpression->fieldIdentificationVariable) {
|
||||
$resultAlias = $this->_scalarResultCounter++;
|
||||
@ -1048,7 +1055,10 @@ class SqlWalker implements TreeWalker
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
} else if (
|
||||
$expr instanceof AST\SimpleArithmeticExpression ||
|
||||
$expr instanceof AST\ArithmeticTerm ||
|
||||
@ -1073,7 +1083,10 @@ class SqlWalker implements TreeWalker
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
} else if (
|
||||
$expr instanceof AST\NullIfExpression ||
|
||||
$expr instanceof AST\CoalesceExpression ||
|
||||
@ -1093,7 +1106,10 @@ class SqlWalker implements TreeWalker
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
}
|
||||
} else {
|
||||
// IdentificationVariable or PartialObjectExpression
|
||||
if ($expr instanceof AST\PartialObjectExpression) {
|
||||
@ -1112,17 +1128,16 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
$beginning = true;
|
||||
|
||||
// Select all fields from the queried class
|
||||
foreach ($class->fieldMappings as $fieldName => $mapping) {
|
||||
if ($partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($mapping['inherited'])) {
|
||||
$tableName = $this->_em->getClassMetadata($mapping['inherited'])->table['name'];
|
||||
} else {
|
||||
$tableName = $class->table['name'];
|
||||
}
|
||||
$tableName = (isset($mapping['inherited']))
|
||||
? $this->_em->getClassMetadata($mapping['inherited'])->getTableName()
|
||||
: $class->getTableName();
|
||||
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
|
||||
@ -1132,6 +1147,7 @@ class SqlWalker implements TreeWalker
|
||||
. ' AS ' . $columnAlias;
|
||||
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
|
||||
}
|
||||
|
||||
@ -1142,7 +1158,8 @@ class SqlWalker implements TreeWalker
|
||||
if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
|
||||
foreach ($class->subClasses as $subClassName) {
|
||||
$subClass = $this->_em->getClassMetadata($subClassName);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias);
|
||||
$sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
|
||||
|
||||
foreach ($subClass->fieldMappings as $fieldName => $mapping) {
|
||||
if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
|
||||
continue;
|
||||
@ -1164,8 +1181,10 @@ class SqlWalker implements TreeWalker
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($beginning) $beginning = false; else $sql .= ', ';
|
||||
|
||||
$columnAlias = $this->getSQLColumnAlias($srcColumn);
|
||||
$sql .= $sqlTableAlias . '.' . $srcColumn . ' AS ' . $columnAlias;
|
||||
|
||||
$this->_rsm->addMetaResult($dqlAlias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
|
||||
}
|
||||
}
|
||||
@ -1235,7 +1254,7 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
$this->_rootAliases[] = $dqlAlias;
|
||||
|
||||
@ -1320,6 +1339,22 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$columnAlias = 'sclr' . $this->_aliasCounter++;
|
||||
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
} else if (
|
||||
$expr instanceof AST\NullIfExpression ||
|
||||
$expr instanceof AST\CoalesceExpression ||
|
||||
$expr instanceof AST\GeneralCaseExpression ||
|
||||
$expr instanceof AST\SimpleCaseExpression
|
||||
) {
|
||||
if ( ! $simpleSelectExpression->fieldIdentificationVariable) {
|
||||
$alias = $this->_scalarResultCounter++;
|
||||
} else {
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable;
|
||||
}
|
||||
|
||||
$columnAlias = 'sclr' . $this->_aliasCounter++;
|
||||
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
|
||||
|
||||
$this->_scalarResultAliasMap[$alias] = $columnAlias;
|
||||
} else {
|
||||
// IdentificationVariable
|
||||
@ -1590,14 +1625,12 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
|
||||
$sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
$sql .= $targetClass->getQuotedTableName($this->_platform)
|
||||
. ' ' . $targetTableAlias . ' WHERE ';
|
||||
$sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE ';
|
||||
|
||||
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
|
||||
|
||||
$first = true;
|
||||
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
|
||||
@ -1626,14 +1659,12 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
// SQL table aliases
|
||||
$joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
|
||||
$sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
// join to target table
|
||||
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform)
|
||||
. ' ' . $joinTableAlias . ' INNER JOIN '
|
||||
. $targetClass->getQuotedTableName($this->_platform)
|
||||
. ' ' . $targetTableAlias . ' ON ';
|
||||
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) . ' ' . $joinTableAlias
|
||||
. ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON ';
|
||||
|
||||
// join conditions
|
||||
$joinColumns = $assoc['isOwningSide']
|
||||
@ -1645,25 +1676,19 @@ class SqlWalker implements TreeWalker
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
|
||||
. $targetTableAlias . '.' . $targetClass->getQuotedColumnName(
|
||||
$targetClass->fieldNames[$joinColumn['referencedColumnName']],
|
||||
$this->_platform);
|
||||
. $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= ' WHERE ';
|
||||
|
||||
$joinColumns = $assoc['isOwningSide']
|
||||
? $joinTable['joinColumns']
|
||||
: $joinTable['inverseJoinColumns'];
|
||||
|
||||
$joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
|
||||
$first = true;
|
||||
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
|
||||
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
|
||||
. $sourceTableAlias . '.' . $class->getQuotedColumnName(
|
||||
$class->fieldNames[$joinColumn['referencedColumnName']],
|
||||
$this->_platform);
|
||||
. $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= ' AND ';
|
||||
@ -1760,7 +1785,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
if ($this->_useSqlTableAliases) {
|
||||
$sql .= $this->getSQLTableAlias($discrClass->table['name'], $dqlAlias) . '.';
|
||||
$sql .= $this->getSQLTableAlias($discrClass->getTableName(), $dqlAlias) . '.';
|
||||
}
|
||||
|
||||
$sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
|
||||
@ -1825,12 +1850,16 @@ class SqlWalker implements TreeWalker
|
||||
switch ($literal->type) {
|
||||
case AST\Literal::STRING:
|
||||
return $this->_conn->quote($literal->value);
|
||||
|
||||
case AST\Literal::BOOLEAN:
|
||||
$bool = strtolower($literal->value) == 'true' ? true : false;
|
||||
$boolVal = $this->_conn->getDatabasePlatform()->convertBooleans($bool);
|
||||
return is_string($boolVal) ? $this->_conn->quote($boolVal) : $boolVal;
|
||||
|
||||
return $boolVal;
|
||||
|
||||
case AST\Literal::NUMERIC:
|
||||
return $literal->value;
|
||||
|
||||
default:
|
||||
throw QueryException::invalidLiteral($literal);
|
||||
}
|
||||
@ -1971,6 +2000,12 @@ class SqlWalker implements TreeWalker
|
||||
public function walkArithmeticTerm($term)
|
||||
{
|
||||
if (is_string($term)) {
|
||||
if (isset($this->_queryComponents[$term])) {
|
||||
$columnName = $this->_queryComponents[$term]['token']['value'];
|
||||
|
||||
return $this->_scalarResultAliasMap[$columnName];
|
||||
}
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
|
@ -595,11 +595,12 @@ class QueryBuilder
|
||||
*
|
||||
* @param string $from The class name.
|
||||
* @param string $alias The alias of the class.
|
||||
* @param string $indexBy The index for the from.
|
||||
* @return QueryBuilder This QueryBuilder instance.
|
||||
*/
|
||||
public function from($from, $alias)
|
||||
public function from($from, $alias, $indexBy = null)
|
||||
{
|
||||
return $this->add('from', new Expr\From($from, $alias), true);
|
||||
return $this->add('from', new Expr\From($from, $alias, $indexBy), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
@ -23,7 +21,8 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument,
|
||||
Symfony\Component\Console\Input\InputOption,
|
||||
Symfony\Component\Console;
|
||||
Symfony\Component\Console,
|
||||
Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Command to clear the metadata cache of the various cache drivers.
|
||||
@ -31,7 +30,6 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
@ -47,9 +45,30 @@ class MetadataCommand extends Console\Command\Command
|
||||
$this
|
||||
->setName('orm:clear-cache:metadata')
|
||||
->setDescription('Clear all metadata cache of the various cache drivers.')
|
||||
->setDefinition(array())
|
||||
->setHelp(<<<EOT
|
||||
Clear all metadata cache of the various cache drivers.
|
||||
->setDefinition(array(
|
||||
new InputOption(
|
||||
'flush', null, InputOption::VALUE_NONE,
|
||||
'If defined, cache entries will be flushed instead of deleted/invalidated.'
|
||||
)
|
||||
));
|
||||
|
||||
$fullName = $this->getName();
|
||||
$this->setHelp(<<<EOT
|
||||
The <info>$fullName</info> command is meant to clear the metadata cache of associated Entity Manager.
|
||||
It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
|
||||
instance completely.
|
||||
|
||||
The execution type differ on how you execute the command.
|
||||
If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
|
||||
|
||||
<info>$fullName</info>
|
||||
|
||||
Alternatively, if you want to flush the cache provider using this command:
|
||||
|
||||
<info>$fullName --flush</info>
|
||||
|
||||
Finally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,
|
||||
because of a limitation of its execution nature.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
@ -66,20 +85,20 @@ EOT
|
||||
throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.');
|
||||
}
|
||||
|
||||
if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) {
|
||||
if ($cacheDriver instanceof Cache\ApcCache) {
|
||||
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
|
||||
}
|
||||
|
||||
$output->write('Clearing ALL Metadata cache entries' . PHP_EOL);
|
||||
|
||||
$cacheIds = $cacheDriver->deleteAll();
|
||||
$result = $cacheDriver->deleteAll();
|
||||
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
|
||||
|
||||
if ($cacheIds) {
|
||||
foreach ($cacheIds as $cacheId) {
|
||||
$output->write(' - ' . $cacheId . PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
$output->write('No entries to be deleted.' . PHP_EOL);
|
||||
if (true === $input->getOption('flush')) {
|
||||
$result = $cacheDriver->flushAll();
|
||||
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
|
||||
}
|
||||
|
||||
$output->write($message . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
@ -23,7 +21,8 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument,
|
||||
Symfony\Component\Console\Input\InputOption,
|
||||
Symfony\Component\Console;
|
||||
Symfony\Component\Console,
|
||||
Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Command to clear the query cache of the various cache drivers.
|
||||
@ -31,7 +30,6 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
@ -47,9 +45,30 @@ class QueryCommand extends Console\Command\Command
|
||||
$this
|
||||
->setName('orm:clear-cache:query')
|
||||
->setDescription('Clear all query cache of the various cache drivers.')
|
||||
->setDefinition(array())
|
||||
->setHelp(<<<EOT
|
||||
Clear all query cache of the various cache drivers.
|
||||
->setDefinition(array(
|
||||
new InputOption(
|
||||
'flush', null, InputOption::VALUE_NONE,
|
||||
'If defined, cache entries will be flushed instead of deleted/invalidated.'
|
||||
)
|
||||
));
|
||||
|
||||
$fullName = $this->getName();
|
||||
$this->setHelp(<<<EOT
|
||||
The <info>$fullName</info> command is meant to clear the query cache of associated Entity Manager.
|
||||
It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
|
||||
instance completely.
|
||||
|
||||
The execution type differ on how you execute the command.
|
||||
If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
|
||||
|
||||
<info>$fullName</info>
|
||||
|
||||
Alternatively, if you want to flush the cache provider using this command:
|
||||
|
||||
<info>$fullName --flush</info>
|
||||
|
||||
Finally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,
|
||||
because of a limitation of its execution nature.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
@ -66,20 +85,20 @@ EOT
|
||||
throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.');
|
||||
}
|
||||
|
||||
if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) {
|
||||
if ($cacheDriver instanceof Cache\ApcCache) {
|
||||
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
|
||||
}
|
||||
|
||||
$output->write('Clearing ALL Query cache entries' . PHP_EOL);
|
||||
|
||||
$cacheIds = $cacheDriver->deleteAll();
|
||||
$result = $cacheDriver->deleteAll();
|
||||
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
|
||||
|
||||
if ($cacheIds) {
|
||||
foreach ($cacheIds as $cacheId) {
|
||||
$output->write(' - ' . $cacheId . PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
$output->write('No entries to be deleted.' . PHP_EOL);
|
||||
if (true === $input->getOption('flush')) {
|
||||
$result = $cacheDriver->flushAll();
|
||||
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
|
||||
}
|
||||
|
||||
$output->write($message . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
@ -23,7 +21,8 @@ namespace Doctrine\ORM\Tools\Console\Command\ClearCache;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument,
|
||||
Symfony\Component\Console\Input\InputOption,
|
||||
Symfony\Component\Console;
|
||||
Symfony\Component\Console,
|
||||
Doctrine\Common\Cache;
|
||||
|
||||
/**
|
||||
* Command to clear the result cache of the various cache drivers.
|
||||
@ -31,7 +30,6 @@ use Symfony\Component\Console\Input\InputArgument,
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
@ -46,28 +44,31 @@ class ResultCommand extends Console\Command\Command
|
||||
{
|
||||
$this
|
||||
->setName('orm:clear-cache:result')
|
||||
->setDescription('Clear result cache of the various cache drivers.')
|
||||
->setDescription('Clear all result cache of the various cache drivers.')
|
||||
->setDefinition(array(
|
||||
new InputOption(
|
||||
'id', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'ID(s) of the cache entry to delete (accepts * wildcards).', array()
|
||||
),
|
||||
new InputOption(
|
||||
'regex', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'Delete cache entries that match the given regular expression(s).', array()
|
||||
),
|
||||
new InputOption(
|
||||
'prefix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'Delete cache entries that have the given prefix(es).', array()
|
||||
),
|
||||
new InputOption(
|
||||
'suffix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
|
||||
'Delete cache entries that have the given suffix(es).', array()
|
||||
),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
Clear result cache of the various cache drivers.
|
||||
If none of the options are defined, all cache entries will be removed.
|
||||
'flush', null, InputOption::VALUE_NONE,
|
||||
'If defined, cache entries will be flushed instead of deleted/invalidated.'
|
||||
)
|
||||
));
|
||||
|
||||
$fullName = $this->getName();
|
||||
$this->setHelp(<<<EOT
|
||||
The <info>$fullName</info> command is meant to clear the result cache of associated Entity Manager.
|
||||
It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
|
||||
instance completely.
|
||||
|
||||
The execution type differ on how you execute the command.
|
||||
If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
|
||||
|
||||
<info>$fullName</info>
|
||||
|
||||
Alternatively, if you want to flush the cache provider using this command:
|
||||
|
||||
<info>$fullName --flush</info>
|
||||
|
||||
Finally, be aware that if <info>--flush</info> option is passed, not all cache providers are able to flush entries,
|
||||
because of a limitation of its execution nature.
|
||||
EOT
|
||||
);
|
||||
}
|
||||
@ -84,85 +85,20 @@ EOT
|
||||
throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.');
|
||||
}
|
||||
|
||||
if ($cacheDriver instanceof \Doctrine\Common\Cache\ApcCache) {
|
||||
if ($cacheDriver instanceof Cache\ApcCache) {
|
||||
throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
|
||||
}
|
||||
|
||||
$outputed = false;
|
||||
|
||||
// Removing based on --id
|
||||
if (($ids = $input->getOption('id')) !== null && $ids) {
|
||||
foreach ($ids as $id) {
|
||||
$output->write($outputed ? PHP_EOL : '');
|
||||
$output->write(sprintf('Clearing Result cache entries that match the id "<info>%s</info>"', $id) . PHP_EOL);
|
||||
|
||||
$deleted = $cacheDriver->delete($id);
|
||||
|
||||
if (is_array($deleted)) {
|
||||
$this->_printDeleted($output, $deleted);
|
||||
} else if (is_bool($deleted) && $deleted) {
|
||||
$this->_printDeleted($output, array($id));
|
||||
}
|
||||
|
||||
$outputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Removing based on --regex
|
||||
if (($regexps = $input->getOption('regex')) !== null && $regexps) {
|
||||
foreach($regexps as $regex) {
|
||||
$output->write($outputed ? PHP_EOL : '');
|
||||
$output->write(sprintf('Clearing Result cache entries that match the regular expression "<info>%s</info>"', $regex) . PHP_EOL);
|
||||
|
||||
$this->_printDeleted($output, $cacheDriver->deleteByRegex('/' . $regex. '/'));
|
||||
|
||||
$outputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Removing based on --prefix
|
||||
if (($prefixes = $input->getOption('prefix')) !== null & $prefixes) {
|
||||
foreach ($prefixes as $prefix) {
|
||||
$output->write($outputed ? PHP_EOL : '');
|
||||
$output->write(sprintf('Clearing Result cache entries that have the prefix "<info>%s</info>"', $prefix) . PHP_EOL);
|
||||
|
||||
$this->_printDeleted($output, $cacheDriver->deleteByPrefix($prefix));
|
||||
|
||||
$outputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Removing based on --suffix
|
||||
if (($suffixes = $input->getOption('suffix')) !== null && $suffixes) {
|
||||
foreach ($suffixes as $suffix) {
|
||||
$output->write($outputed ? PHP_EOL : '');
|
||||
$output->write(sprintf('Clearing Result cache entries that have the suffix "<info>%s</info>"', $suffix) . PHP_EOL);
|
||||
|
||||
$this->_printDeleted($output, $cacheDriver->deleteBySuffix($suffix));
|
||||
|
||||
$outputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Removing ALL entries
|
||||
if ( ! $ids && ! $regexps && ! $prefixes && ! $suffixes) {
|
||||
$output->write($outputed ? PHP_EOL : '');
|
||||
$output->write('Clearing ALL Result cache entries' . PHP_EOL);
|
||||
|
||||
$this->_printDeleted($output, $cacheDriver->deleteAll());
|
||||
$result = $cacheDriver->deleteAll();
|
||||
$message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
|
||||
|
||||
$outputed = true;
|
||||
}
|
||||
if (true === $input->getOption('flush')) {
|
||||
$result = $cacheDriver->flushAll();
|
||||
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
|
||||
}
|
||||
|
||||
private function _printDeleted(Console\Output\OutputInterface $output, array $items)
|
||||
{
|
||||
if ($items) {
|
||||
foreach ($items as $item) {
|
||||
$output->write(' - ' . $item . PHP_EOL);
|
||||
}
|
||||
} else {
|
||||
$output->write('No entries to be deleted.' . PHP_EOL);
|
||||
}
|
||||
$output->write($message . PHP_EOL);
|
||||
}
|
||||
}
|
@ -137,7 +137,7 @@ EOT
|
||||
|
||||
if ( ! file_exists($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf("Mapping destination directory '<info>%s</info>' does not exist.", $destPath)
|
||||
sprintf("Mapping destination directory '<info>%s</info>' does not exist.", $input->getArgument('dest-path'))
|
||||
);
|
||||
} else if ( ! is_writable($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
|
@ -123,7 +123,7 @@ EOT
|
||||
|
||||
if ( ! file_exists($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf("Entities destination directory '<info>%s</info>' does not exist.", $destPath)
|
||||
sprintf("Entities destination directory '<info>%s</info>' does not exist.", $input->getArgument('dest-path'))
|
||||
);
|
||||
} else if ( ! is_writable($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
|
@ -87,7 +87,7 @@ EOT
|
||||
|
||||
if ( ! file_exists($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf("Proxies destination directory '<info>%s</info>' does not exist.", $destPath)
|
||||
sprintf("Proxies destination directory '<info>%s</info>' does not exist.", $em->getConfiguration()->getProxyDir())
|
||||
);
|
||||
} else if ( ! is_writable($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
|
@ -79,7 +79,7 @@ EOT
|
||||
|
||||
if ( ! file_exists($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf("Entities destination directory '<info>%s</info>' does not exist.", $destPath)
|
||||
sprintf("Entities destination directory '<info>%s</info>' does not exist.", $input->getArgument('dest-path'))
|
||||
);
|
||||
} else if ( ! is_writable($destPath)) {
|
||||
throw new \InvalidArgumentException(
|
||||
|
@ -46,7 +46,7 @@ class ValidateSchemaCommand extends Console\Command\Command
|
||||
{
|
||||
$this
|
||||
->setName('orm:validate-schema')
|
||||
->setDescription('Validate that the mapping files.')
|
||||
->setDescription('Validate the mapping files.')
|
||||
->setHelp(<<<EOT
|
||||
'Validate that the mapping files are correct and in sync with the database.'
|
||||
EOT
|
||||
|
@ -101,6 +101,7 @@ class ConvertDoctrine1Schema
|
||||
{
|
||||
if (isset($model['tableName']) && $model['tableName']) {
|
||||
$e = explode('.', $model['tableName']);
|
||||
|
||||
if (count($e) > 1) {
|
||||
$metadata->table['schema'] = $e[0];
|
||||
$metadata->table['name'] = $e[1];
|
||||
|
@ -115,10 +115,12 @@ public function <methodName>()
|
||||
* <description>
|
||||
*
|
||||
* @param <variableType>$<variableName>
|
||||
* @return <entity>
|
||||
*/
|
||||
public function <methodName>(<methodTypeHint>$<variableName>)
|
||||
{
|
||||
<spaces>$this-><fieldName> = $<variableName>;
|
||||
<spaces>return $this;
|
||||
}';
|
||||
|
||||
private static $_addMethodTemplate =
|
||||
@ -150,7 +152,7 @@ public function <methodName>()
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '3.0.0-DEV', '>=')) {
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
|
||||
$this->_annotationsPrefix = 'ORM\\';
|
||||
}
|
||||
}
|
||||
@ -302,9 +304,6 @@ public function <methodName>()
|
||||
*/
|
||||
public function setAnnotationPrefix($prefix)
|
||||
{
|
||||
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
|
||||
return;
|
||||
}
|
||||
$this->_annotationsPrefix = $prefix;
|
||||
}
|
||||
|
||||
@ -399,14 +398,17 @@ public function <methodName>()
|
||||
}
|
||||
|
||||
$collections = array();
|
||||
|
||||
foreach ($metadata->associationMappings AS $mapping) {
|
||||
if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
|
||||
$collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
|
||||
}
|
||||
}
|
||||
|
||||
if ($collections) {
|
||||
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -570,7 +572,12 @@ public function <methodName>()
|
||||
private function _generateTableAnnotation($metadata)
|
||||
{
|
||||
$table = array();
|
||||
if ($metadata->table['name']) {
|
||||
|
||||
if (isset($metadata->table['schema'])) {
|
||||
$table[] = 'schema="' . $metadata->table['schema'] . '"';
|
||||
}
|
||||
|
||||
if (isset($metadata->table['name'])) {
|
||||
$table[] = 'name="' . $metadata->table['name'] . '"';
|
||||
}
|
||||
|
||||
@ -729,7 +736,8 @@ public function <methodName>()
|
||||
'<variableType>' => $variableType,
|
||||
'<variableName>' => Inflector::camelize($fieldName),
|
||||
'<methodName>' => $methodName,
|
||||
'<fieldName>' => $fieldName
|
||||
'<fieldName>' => $fieldName,
|
||||
'<entity>' => $this->_getClassName($metadata)
|
||||
);
|
||||
|
||||
$method = str_replace(
|
||||
@ -783,7 +791,7 @@ public function <methodName>()
|
||||
}
|
||||
|
||||
if (isset($joinColumn['onDelete'])) {
|
||||
$joinColumnAnnot[] = 'onDelete=' . ($joinColumn['onDelete'] ? 'true' : 'false');
|
||||
$joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"');
|
||||
}
|
||||
|
||||
if (isset($joinColumn['columnDefinition'])) {
|
||||
|
@ -49,11 +49,13 @@ class YamlExporter extends AbstractExporter
|
||||
public function exportClassMetadata(ClassMetadataInfo $metadata)
|
||||
{
|
||||
$array = array();
|
||||
|
||||
if ($metadata->isMappedSuperclass) {
|
||||
$array['type'] = 'mappedSuperclass';
|
||||
} else {
|
||||
$array['type'] = 'entity';
|
||||
}
|
||||
|
||||
$array['table'] = $metadata->table['name'];
|
||||
|
||||
if (isset($metadata->table['schema'])) {
|
||||
@ -81,6 +83,10 @@ class YamlExporter extends AbstractExporter
|
||||
$array['indexes'] = $metadata->table['indexes'];
|
||||
}
|
||||
|
||||
if ($metadata->customRepositoryClassName) {
|
||||
$array['repositoryClass'] = $metadata->customRepositoryClassName;
|
||||
}
|
||||
|
||||
if (isset($metadata->table['uniqueConstraints'])) {
|
||||
$array['uniqueConstraints'] = $metadata->table['uniqueConstraints'];
|
||||
}
|
||||
|
@ -152,6 +152,17 @@ class SchemaValidator
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($targetClass->identifier) != count($assoc['joinTable']['inverseJoinColumns'])) {
|
||||
$ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
|
||||
"have to match to ALL identifier columns of the target entity '". $targetClass->name . "'";
|
||||
}
|
||||
|
||||
if (count($class->identifier) != count($assoc['joinTable']['joinColumns'])) {
|
||||
$ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " .
|
||||
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
|
||||
}
|
||||
|
||||
} else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
foreach ($assoc['joinColumns'] AS $joinColumn) {
|
||||
$targetClass = $cmf->getMetadataFor($assoc['targetEntity']);
|
||||
@ -167,6 +178,11 @@ class SchemaValidator
|
||||
"has to be a primary key column.";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($class->identifier) != count($assoc['joinColumns'])) {
|
||||
$ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " .
|
||||
"have to match to ALL identifier columns of the source entity '". $class->name . "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ class Setup
|
||||
require_once $directory . "/Doctrine/Common/ClassLoader.php";
|
||||
}
|
||||
|
||||
$loader = new ClassLoader("Doctrine");
|
||||
$loader = new ClassLoader("Doctrine", $directory);
|
||||
$loader->register();
|
||||
|
||||
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine");
|
||||
@ -115,13 +115,13 @@ class Setup
|
||||
*/
|
||||
static public function createAnnotationMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$config = self::createConfiguration($isDevMode, $cache, $proxyDir);
|
||||
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
|
||||
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration with an annotation metadata driver.
|
||||
* Create a configuration with a xml metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
* @param boolean $isDevMode
|
||||
@ -131,13 +131,13 @@ class Setup
|
||||
*/
|
||||
static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$config = self::createConfiguration($isDevMode, $cache, $proxyDir);
|
||||
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
|
||||
$config->setMetadataDriverImpl(new XmlDriver($paths));
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration with an annotation metadata driver.
|
||||
* Create a configuration with a yaml metadata driver.
|
||||
*
|
||||
* @param array $paths
|
||||
* @param boolean $isDevMode
|
||||
@ -147,7 +147,7 @@ class Setup
|
||||
*/
|
||||
static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$config = self::createConfiguration($isDevMode, $cache, $proxyDir);
|
||||
$config = self::createConfiguration($isDevMode, $proxyDir, $cache);
|
||||
$config->setMetadataDriverImpl(new YamlDriver($paths));
|
||||
return $config;
|
||||
}
|
||||
@ -162,6 +162,7 @@ class Setup
|
||||
*/
|
||||
static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null)
|
||||
{
|
||||
$proxyDir = $proxyDir ?: sys_get_temp_dir();
|
||||
if ($isDevMode === false && $cache === null) {
|
||||
if (extension_loaded('apc')) {
|
||||
$cache = new \Doctrine\Common\Cache\ApcCache;
|
||||
@ -175,16 +176,16 @@ class Setup
|
||||
} else {
|
||||
$cache = new ArrayCache;
|
||||
}
|
||||
$cache->setNamespace("dc2_"); // to avoid collisions
|
||||
} else if ($cache === null) {
|
||||
$cache = new ArrayCache;
|
||||
}
|
||||
$cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions
|
||||
|
||||
$config = new Configuration();
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$config->setQueryCacheImpl($cache);
|
||||
$config->setResultCacheImpl($cache);
|
||||
$config->setProxyDir( $proxyDir ?: sys_get_temp_dir() );
|
||||
$config->setProxyDir( $proxyDir );
|
||||
$config->setProxyNamespace('DoctrineProxies');
|
||||
$config->setAutoGenerateProxyClasses($isDevMode);
|
||||
|
||||
|
@ -216,7 +216,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
private $orphanRemovals = array();
|
||||
|
||||
//private $_readOnlyObjects = array();
|
||||
/**
|
||||
* Read-Only objects are never evaluated
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $readOnlyObjects = array();
|
||||
|
||||
/**
|
||||
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
|
||||
@ -393,6 +398,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* If a PersistentCollection has been de-referenced in a fully MANAGED entity,
|
||||
* then this collection is marked for deletion.
|
||||
*
|
||||
* @ignore
|
||||
* @internal Don't call from the outside.
|
||||
* @param ClassMetadata $class The class descriptor of the entity.
|
||||
* @param object $entity The entity for which to compute the changes.
|
||||
*/
|
||||
@ -403,6 +410,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
$oid = spl_object_hash($entity);
|
||||
|
||||
if (isset($this->readOnlyObjects[$oid])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$actualData = array();
|
||||
foreach ($class->reflFields as $name => $refProp) {
|
||||
$value = $refProp->getValue($entity);
|
||||
@ -458,7 +470,15 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$changeSet = ($isChangeTrackingNotify && isset($this->entityChangeSets[$oid])) ? $this->entityChangeSets[$oid] : array();
|
||||
|
||||
foreach ($actualData as $propName => $actualValue) {
|
||||
$orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
|
||||
if (isset($originalData[$propName])) {
|
||||
$orgValue = $originalData[$propName];
|
||||
} else if (array_key_exists($propName, $originalData)) {
|
||||
$orgValue = null;
|
||||
} else {
|
||||
// skip field, its a partially omitted one!
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($class->associationMappings[$propName])) {
|
||||
$assoc = $class->associationMappings[$propName];
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) {
|
||||
@ -470,8 +490,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
} else if ($orgValue instanceof PersistentCollection && $orgValue !== $actualValue) {
|
||||
// A PersistentCollection was de-referenced, so delete it.
|
||||
if ( ! in_array($orgValue, $this->collectionDeletions, true)) {
|
||||
$this->collectionDeletions[] = $orgValue;
|
||||
$coid = spl_object_hash($orgValue);
|
||||
if ( ! isset($this->collectionDeletions[$coid]) ) {
|
||||
$this->collectionDeletions[$coid] = $orgValue;
|
||||
$changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored.
|
||||
}
|
||||
}
|
||||
@ -528,7 +549,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
foreach ($entitiesToProcess as $entity) {
|
||||
// Ignore uninitialized proxy objects
|
||||
if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) {
|
||||
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
|
||||
continue;
|
||||
}
|
||||
// Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
|
||||
@ -549,10 +570,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
private function computeAssociationChanges($assoc, $value)
|
||||
{
|
||||
if ($value instanceof PersistentCollection && $value->isDirty()) {
|
||||
$coid = spl_object_hash($value);
|
||||
if ($assoc['isOwningSide']) {
|
||||
$this->collectionUpdates[] = $value;
|
||||
$this->collectionUpdates[$coid] = $value;
|
||||
}
|
||||
$this->visitedCollections[] = $value;
|
||||
$this->visitedCollections[$coid] = $value;
|
||||
}
|
||||
|
||||
// Look through the entities, and in any of their associations, for transient (new)
|
||||
@ -635,7 +657,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* @param object $entity The entity for which to (re)calculate the change set.
|
||||
* @throws InvalidArgumentException If the passed entity is not MANAGED.
|
||||
*/
|
||||
public function recomputeSingleEntityChangeSet($class, $entity)
|
||||
public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
|
||||
@ -834,6 +856,8 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
// See if there are any new classes in the changeset, that are not in the
|
||||
// commit order graph yet (dont have a node).
|
||||
|
||||
// TODO: Can we know the know the possible $newNodes based on something more efficient? IdentityMap?
|
||||
$newNodes = array();
|
||||
foreach ($entityChangeSet as $oid => $entity) {
|
||||
$className = get_class($entity);
|
||||
@ -845,12 +869,13 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
|
||||
// Calculate dependencies for new nodes
|
||||
foreach ($newNodes as $class) {
|
||||
while ($class = array_pop($newNodes)) {
|
||||
foreach ($class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
if ( ! $calc->hasClass($targetClass->name)) {
|
||||
$calc->addClass($targetClass);
|
||||
$newNodes[] = $targetClass;
|
||||
}
|
||||
$calc->addDependency($targetClass, $class);
|
||||
// If the target class has mapped subclasses,
|
||||
@ -860,6 +885,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$targetSubClass = $this->em->getClassMetadata($subClassName);
|
||||
if ( ! $calc->hasClass($subClassName)) {
|
||||
$calc->addClass($targetSubClass);
|
||||
$newNodes[] = $targetSubClass;
|
||||
}
|
||||
$calc->addDependency($targetSubClass, $class);
|
||||
}
|
||||
@ -1102,6 +1128,22 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!$class->idGenerator->isPostInsertGenerator()) {
|
||||
// if we have a pre insert generator we can't be sure that having an id
|
||||
// really means that the entity exists. We have to verify this through
|
||||
// the last resort: a db lookup
|
||||
|
||||
// Last try before db lookup: check the identity map.
|
||||
if ($this->tryGetById($id, $class->rootEntityName)) {
|
||||
return self::STATE_DETACHED;
|
||||
} else {
|
||||
// db lookup
|
||||
if ($this->getEntityPersister(get_class($entity))->exists($entity)) {
|
||||
return self::STATE_DETACHED;
|
||||
} else {
|
||||
return self::STATE_NEW;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return self::STATE_DETACHED;
|
||||
}
|
||||
@ -1368,6 +1410,10 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
|
||||
$managedCopy = $entity;
|
||||
} else {
|
||||
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
|
||||
$entity->__load();
|
||||
}
|
||||
|
||||
// Try to look the entity up in the identity map.
|
||||
$id = $class->getIdentifierValues($entity);
|
||||
|
||||
@ -1429,11 +1475,17 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) {
|
||||
$prop->setValue($managedCopy, $other);
|
||||
} else {
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
|
||||
$id = $targetClass->getIdentifierValues($other);
|
||||
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $id);
|
||||
$relatedId = $targetClass->getIdentifierValues($other);
|
||||
|
||||
if ($targetClass->subClasses) {
|
||||
$entity = $this->em->find($targetClass->name, $relatedId);
|
||||
} else {
|
||||
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
|
||||
$prop->setValue($managedCopy, $proxy);
|
||||
$this->registerManaged($proxy, $id, array());
|
||||
$this->registerManaged($proxy, $relatedId, array());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1515,8 +1567,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*
|
||||
* @param object $entity
|
||||
* @param array $visited
|
||||
* @param boolean $noCascade if true, don't cascade detach operation
|
||||
*/
|
||||
private function doDetach($entity, array &$visited)
|
||||
private function doDetach($entity, array &$visited, $noCascade = false)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
if (isset($visited[$oid])) {
|
||||
@ -1539,8 +1592,10 @@ class UnitOfWork implements PropertyChangedListener
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$noCascade) {
|
||||
$this->cascadeDetach($entity, $visited);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the state of the given entity from the database, overwriting
|
||||
@ -1742,7 +1797,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*/
|
||||
public function lock($entity, $lockMode, $lockVersion = null)
|
||||
{
|
||||
if ($this->getEntityState($entity) != self::STATE_MANAGED) {
|
||||
if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
|
||||
throw new InvalidArgumentException("Entity is not MANAGED.");
|
||||
}
|
||||
|
||||
@ -1790,9 +1845,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
/**
|
||||
* Clears the UnitOfWork.
|
||||
*
|
||||
* @param string $entityName if given, only entities of this type will get detached
|
||||
*/
|
||||
public function clear()
|
||||
public function clear($entityName = null)
|
||||
{
|
||||
if ($entityName === null) {
|
||||
$this->identityMap =
|
||||
$this->entityIdentifiers =
|
||||
$this->originalEntityData =
|
||||
@ -1809,9 +1867,19 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->commitOrderCalculator !== null) {
|
||||
$this->commitOrderCalculator->clear();
|
||||
}
|
||||
} else {
|
||||
$visited = array();
|
||||
foreach ($this->identityMap as $className => $entities) {
|
||||
if ($className === $entityName) {
|
||||
foreach ($entities as $entity) {
|
||||
$this->doDetach($entity, $visited, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->evm->hasListeners(Events::onClear)) {
|
||||
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em));
|
||||
$this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1839,12 +1907,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
//TODO: if $coll is already scheduled for recreation ... what to do?
|
||||
// Just remove $coll from the scheduled recreations?
|
||||
$this->collectionDeletions[] = $coll;
|
||||
$this->collectionDeletions[spl_object_hash($coll)] = $coll;
|
||||
}
|
||||
|
||||
public function isCollectionScheduledForDeletion(PersistentCollection $coll)
|
||||
{
|
||||
return in_array($coll, $this->collectionsDeletions, true);
|
||||
return isset( $this->collectionsDeletions[spl_object_hash($coll)] );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2164,9 +2232,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
public function tryGetById($id, $rootClassName)
|
||||
{
|
||||
$idHash = implode(' ', (array) $id);
|
||||
|
||||
if (isset($this->identityMap[$rootClassName][$idHash])) {
|
||||
return $this->identityMap[$rootClassName][$idHash];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2382,4 +2452,37 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit().
|
||||
*
|
||||
* This operation cannot be undone as some parts of the UnitOfWork now keep gathering information
|
||||
* on this object that might be necessary to perform a correct udpate.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @param $object
|
||||
* @return void
|
||||
*/
|
||||
public function markReadOnly($object)
|
||||
{
|
||||
if ( ! is_object($object) || ! $this->isInIdentityMap($object)) {
|
||||
throw new InvalidArgumentException("Managed entity required");
|
||||
}
|
||||
$this->readOnlyObjects[spl_object_hash($object)] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this entity read only?
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @param $object
|
||||
* @return void
|
||||
*/
|
||||
public function isReadOnly($object)
|
||||
{
|
||||
if ( ! is_object($object) ) {
|
||||
throw new InvalidArgumentException("Managed entity required");
|
||||
}
|
||||
return isset($this->readOnlyObjects[spl_object_hash($object)]);
|
||||
}
|
||||
}
|
||||
|
2
lib/vendor/doctrine-common
vendored
2
lib/vendor/doctrine-common
vendored
@ -1 +1 @@
|
||||
Subproject commit 74a2c924cd08b30785877808b1fb519b4b2e60b1
|
||||
Subproject commit ef431a14852d7e8f2d0ea789487509ab266e5ce2
|
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
@ -1 +1 @@
|
||||
Subproject commit be3790059cc43b674a55548eb42d5d25846ea6a9
|
||||
Subproject commit f91395b6f469b5076f52fefd64574c443b076485
|
48
tests/Doctrine/Tests/Models/CMS/CmsEmail.php
Normal file
48
tests/Doctrine/Tests/Models/CMS/CmsEmail.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CMS;
|
||||
|
||||
/**
|
||||
* CmsEmail
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="cms_emails")
|
||||
*/
|
||||
class CmsEmail
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @Id @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(length=250)
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="CmsUser", mappedBy="email")
|
||||
*/
|
||||
public $user;
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getEmail() {
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail($email) {
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getUser() {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(CmsUser $user) {
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@ -31,11 +31,11 @@ class CmsUser
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
|
||||
* @OneToMany(targetEntity="CmsPhonenumber", mappedBy="user", cascade={"persist", "merge"}, orphanRemoval=true)
|
||||
*/
|
||||
public $phonenumbers;
|
||||
/**
|
||||
* @OneToMany(targetEntity="CmsArticle", mappedBy="user")
|
||||
* @OneToMany(targetEntity="CmsArticle", mappedBy="user", cascade={"detach"})
|
||||
*/
|
||||
public $articles;
|
||||
/**
|
||||
@ -43,7 +43,12 @@ class CmsUser
|
||||
*/
|
||||
public $address;
|
||||
/**
|
||||
* @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"})
|
||||
* @OneToOne(targetEntity="CmsEmail", inversedBy="user", cascade={"persist"}, orphanRemoval=true)
|
||||
* @JoinColumn(referencedColumnName="id", nullable=true)
|
||||
*/
|
||||
public $email;
|
||||
/**
|
||||
* @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge", "detach"})
|
||||
* @JoinTable(name="cms_users_groups",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
|
||||
@ -119,4 +124,16 @@ class CmsUser
|
||||
$address->setUser($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getEmail() { return $this->email; }
|
||||
|
||||
public function setEmail(CmsEmail $email = null) {
|
||||
if ($this->email !== $email) {
|
||||
$this->email = $email;
|
||||
|
||||
if ($email) {
|
||||
$email->setUser($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class CompanyCar
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length="50")
|
||||
* @Column(type="string", length=50)
|
||||
*/
|
||||
private $brand;
|
||||
|
||||
|
@ -7,7 +7,11 @@ namespace Doctrine\Tests\Models\Company;
|
||||
* @Table(name="company_contracts")
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"fix" = "CompanyFixContract", "flexible" = "CompanyFlexContract", "flexultra" = "CompanyFlexUltraContract"})
|
||||
* @DiscriminatorMap({
|
||||
* "fix" = "CompanyFixContract",
|
||||
* "flexible" = "CompanyFlexContract",
|
||||
* "flexultra" = "CompanyFlexUltraContract"
|
||||
* })
|
||||
*/
|
||||
abstract class CompanyContract
|
||||
{
|
||||
|
@ -18,6 +18,11 @@ class CompanyEmployee extends CompanyPerson
|
||||
*/
|
||||
private $department;
|
||||
|
||||
/**
|
||||
* @Column(type="datetime", nullable=true)
|
||||
*/
|
||||
private $startDate;
|
||||
|
||||
public function getSalary() {
|
||||
return $this->salary;
|
||||
}
|
||||
@ -33,4 +38,12 @@ class CompanyEmployee extends CompanyPerson
|
||||
public function setDepartment($dep) {
|
||||
$this->department = $dep;
|
||||
}
|
||||
|
||||
public function getStartDate() {
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function setStartDate($date) {
|
||||
$this->startDate = $date;
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace Doctrine\Tests\Models\Company;
|
||||
class CompanyManager extends CompanyEmployee
|
||||
{
|
||||
/**
|
||||
* @Column(type="string", length="250")
|
||||
* @Column(type="string", length=250)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC753;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class DDC753CustomRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCustomRepository()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC753;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class DDC753DefaultRepository extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDefaultRepository()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC753;
|
||||
|
||||
/**
|
||||
* @Entity(repositoryClass = "Doctrine\Tests\Models\DDC753\DDC753CustomRepository")
|
||||
*/
|
||||
class DDC753EntityWithCustomRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @column(type="string") */
|
||||
protected $name;
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC753;
|
||||
|
||||
/**
|
||||
* @Entity()
|
||||
*/
|
||||
class DDC753EntityWithDefaultCustomRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @column(type="string") */
|
||||
protected $name;
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC753;
|
||||
|
||||
/**
|
||||
* @Entity(repositoryClass = "\stdClass")
|
||||
*/
|
||||
class DDC753EntityWithInvalidRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @column(type="string") */
|
||||
protected $name;
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC753;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class DDC753InvalidRepository
|
||||
{
|
||||
|
||||
}
|
40
tests/Doctrine/Tests/Models/DDC869/DDC869ChequePayment.php
Normal file
40
tests/Doctrine/Tests/Models/DDC869/DDC869ChequePayment.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC869;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC869ChequePayment extends DDC869Payment
|
||||
{
|
||||
|
||||
/** @column(type="string") */
|
||||
protected $serialNumber;
|
||||
|
||||
public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
|
||||
{
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'serialNumber',
|
||||
'type' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC869;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC869CreditCardPayment extends DDC869Payment
|
||||
{
|
||||
|
||||
/** @column(type="string") */
|
||||
protected $creditCardNumber;
|
||||
|
||||
public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
|
||||
{
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'creditCardNumber',
|
||||
'type' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
}
|
57
tests/Doctrine/Tests/Models/DDC869/DDC869Payment.php
Normal file
57
tests/Doctrine/Tests/Models/DDC869/DDC869Payment.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC869;
|
||||
|
||||
/**
|
||||
* @MappedSuperclass(repositoryClass = "Doctrine\Tests\Models\DDC869\DDC869PaymentRepository")
|
||||
*/
|
||||
class DDC869Payment
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/** @column(type="float") */
|
||||
protected $value;
|
||||
|
||||
|
||||
public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
|
||||
{
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'columnName' => 'id',
|
||||
));
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'value',
|
||||
'type' => 'float',
|
||||
));
|
||||
$metadata->isMappedSuperclass = true;
|
||||
$metadata->setCustomRepositoryClass("Doctrine\Tests\Models\DDC869\DDC869PaymentRepository");
|
||||
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadataInfo::GENERATOR_TYPE_AUTO);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC869;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class DDC869PaymentRepository extends EntityRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* Very complex method
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ class ECommerceProduct
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", length=50, nullable="true")
|
||||
* @Column(type="string", length=50, nullable=true)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
|
91
tests/Doctrine/Tests/ORM/Event/EntityEventDelegateeTest.php
Normal file
91
tests/Doctrine/Tests/ORM/Event/EntityEventDelegateeTest.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Event;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* @group DDC-1415
|
||||
*/
|
||||
class EntityEventDelegatorTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\Event\EntityEventDelegator
|
||||
*/
|
||||
private $delegator;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->delegator = new \Doctrine\ORM\Event\EntityEventDelegator();
|
||||
}
|
||||
|
||||
public function testGetSubscribedEventsWhenEmpty()
|
||||
{
|
||||
$this->assertEquals(array(), $this->delegator->getSubscribedEvents());
|
||||
}
|
||||
|
||||
public function testAddListener()
|
||||
{
|
||||
$this->delegator->addEventListener('postLoad', 'stdClass', new DelegateeEventListener());
|
||||
$this->assertEquals(array('postLoad'), $this->delegator->getSubscribedEvents());
|
||||
}
|
||||
|
||||
public function testAddSubscriber()
|
||||
{
|
||||
$this->delegator->addEventSubscriber(new DelegateeEventListener(), 'stdClass');
|
||||
$this->assertEquals(array('postLoad'), $this->delegator->getSubscribedEvents());
|
||||
}
|
||||
|
||||
public function testAddListenerAfterFrozenThrowsException()
|
||||
{
|
||||
$this->delegator->getSubscribedEvents(); // freezes
|
||||
|
||||
$this->setExpectedException("LogicException", "Cannot add event listeners aft");
|
||||
$this->delegator->addEventListener('postLoad', 'stdClass', new DelegateeEventListener());
|
||||
}
|
||||
|
||||
public function testDelegateEvent()
|
||||
{
|
||||
$delegatee = new DelegateeEventListener();
|
||||
$this->delegator->addEventListener('postLoad', 'stdClass', $delegatee);
|
||||
|
||||
$event = new \Doctrine\ORM\Event\LifecycleEventArgs(new \stdClass(), $this->_getTestEntityManager());
|
||||
$this->delegator->postLoad($event);
|
||||
$this->delegator->postLoad($event);
|
||||
|
||||
$this->assertEquals(2, $delegatee->postLoad);
|
||||
}
|
||||
|
||||
public function testDelegatePickEntity()
|
||||
{
|
||||
$delegatee = new DelegateeEventListener();
|
||||
$this->delegator->addEventListener('postLoad', 'stdClass', $delegatee);
|
||||
|
||||
$event1 = new \Doctrine\ORM\Event\LifecycleEventArgs(new \stdClass(), $this->_getTestEntityManager());
|
||||
$event2 = new \Doctrine\ORM\Event\LifecycleEventArgs(new \Doctrine\Tests\Models\CMS\CmsUser(), $this->_getTestEntityManager());
|
||||
$this->delegator->postLoad($event1);
|
||||
$this->delegator->postLoad($event2);
|
||||
|
||||
$this->assertEquals(1, $delegatee->postLoad);
|
||||
}
|
||||
}
|
||||
|
||||
class DelegateeEventListener implements \Doctrine\Common\EventSubscriber
|
||||
{
|
||||
public $postLoad = 0;
|
||||
|
||||
public function postLoad($args)
|
||||
{
|
||||
$this->postLoad++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of events this subscriber wants to listen to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getSubscribedEvents()
|
||||
{
|
||||
return array('postLoad');
|
||||
}
|
||||
}
|
@ -150,15 +150,15 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$user->username = 'gblanco';
|
||||
$user->status = 'developer';
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_NEW");
|
||||
|
||||
$this->_em->persist($user);
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_MANAGED");
|
||||
|
||||
$this->_em->remove($user);
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_NEW");
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
@ -166,10 +166,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->_em->remove($user);
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($user));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_REMOVED");
|
||||
$this->_em->flush();
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_NEW, $this->_em->getUnitOfWork()->getEntityState($user), "State should be UnitOfWork::STATE_NEW");
|
||||
|
||||
$this->assertNull($this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $id));
|
||||
}
|
||||
@ -863,7 +863,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt()
|
||||
{
|
||||
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
|
||||
$user = new CmsUser();
|
||||
$user->username = "beberlei";
|
||||
$user->name = "Benjamin E.";
|
||||
@ -882,7 +881,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->assertEquals('Stephan', $this->_em->find(get_class($user), $userId)->name);
|
||||
$this->assertEquals('Benjamin E.', $this->_em->find(get_class($user), $userId)->name);
|
||||
}
|
||||
|
||||
public function testMergePersistsNewEntities()
|
||||
@ -981,4 +980,54 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertTrue($article->user->__isInitialized__, "...but its initialized!");
|
||||
$this->assertEquals($qc+2, $this->getCurrentQueryCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1278
|
||||
*/
|
||||
public function testClearWithEntityName()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->name = 'Dominik';
|
||||
$user->username = 'domnikl';
|
||||
$user->status = 'developer';
|
||||
|
||||
$address = new CmsAddress();
|
||||
$address->city = "Springfield";
|
||||
$address->zip = "12354";
|
||||
$address->country = "Germany";
|
||||
$address->street = "Foo Street";
|
||||
$address->user = $user;
|
||||
$user->address = $address;
|
||||
|
||||
$article1 = new CmsArticle();
|
||||
$article1->topic = 'Foo';
|
||||
$article1->text = 'Foo Text';
|
||||
|
||||
$article2 = new CmsArticle();
|
||||
$article2->topic = 'Bar';
|
||||
$article2->text = 'Bar Text';
|
||||
|
||||
$user->addArticle($article1);
|
||||
$user->addArticle($article2);
|
||||
|
||||
$this->_em->persist($article1);
|
||||
$this->_em->persist($article2);
|
||||
$this->_em->persist($address);
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$unitOfWork = $this->_em->getUnitOfWork();
|
||||
|
||||
$this->_em->clear('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($user));
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($address));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($article1));
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($article2));
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($address));
|
||||
}
|
||||
}
|
||||
|
@ -292,6 +292,19 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertTrue(count($this->_em->createQuery(
|
||||
'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1')
|
||||
->getResult()) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1341
|
||||
*/
|
||||
public function testBulkUpdateNonScalarParameterDDC1341()
|
||||
{
|
||||
$dql = 'UPDATE Doctrine\Tests\Models\Company\CompanyEmployee AS p SET p.startDate = ?0 WHERE p.department = ?1';
|
||||
$query = $this->_em->createQuery($dql)
|
||||
->setParameter(0, new \DateTime())
|
||||
->setParameter(1, 'IT');
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'select u from Doctrine\Tests\Models\CMS\CmsUser u',
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = 1"
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ WHERE c0_.id = 1"
|
||||
);
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName',
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1"
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1"
|
||||
);
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name',
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1"
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.email_id AS email_id4 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,29 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertEquals('Poweruser', $a2->getUser()->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1386
|
||||
*/
|
||||
public function testGetPartialReferenceWithDefaultValueNotEvalutedInFlush()
|
||||
{
|
||||
$user = new DefaultValueUser;
|
||||
$user->name = 'romanb';
|
||||
$user->type = 'Normaluser';
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$user = $this->_em->getPartialReference('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id);
|
||||
$this->assertTrue($this->_em->getUnitOfWork()->isReadOnly($user));
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$user = $this->_em->find('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id);
|
||||
|
||||
$this->assertEquals('Normaluser', $user->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -433,5 +433,43 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertSame($usersAsc[0], $usersDesc[1]);
|
||||
$this->assertSame($usersAsc[1], $usersDesc[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @group DDC-753
|
||||
*/
|
||||
public function testDefaultRepositoryClassName()
|
||||
{
|
||||
$this->assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), "Doctrine\ORM\EntityRepository");
|
||||
$this->_em->getConfiguration()->setDefaultRepositoryClassName("Doctrine\Tests\Models\DDC753\DDC753DefaultRepository");
|
||||
$this->assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), "Doctrine\Tests\Models\DDC753\DDC753DefaultRepository");
|
||||
|
||||
$repos = $this->_em->getRepository('Doctrine\Tests\Models\DDC753\DDC753EntityWithDefaultCustomRepository');
|
||||
$this->assertInstanceOf("Doctrine\Tests\Models\DDC753\DDC753DefaultRepository", $repos);
|
||||
$this->assertTrue($repos->isDefaultRepository());
|
||||
|
||||
|
||||
$repos = $this->_em->getRepository('Doctrine\Tests\Models\DDC753\DDC753EntityWithCustomRepository');
|
||||
$this->assertInstanceOf("Doctrine\Tests\Models\DDC753\DDC753CustomRepository", $repos);
|
||||
$this->assertTrue($repos->isCustomRepository());
|
||||
|
||||
$this->assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), "Doctrine\Tests\Models\DDC753\DDC753DefaultRepository");
|
||||
$this->_em->getConfiguration()->setDefaultRepositoryClassName("Doctrine\ORM\EntityRepository");
|
||||
$this->assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), "Doctrine\ORM\EntityRepository");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @group DDC-753
|
||||
* @expectedException Doctrine\ORM\ORMException
|
||||
* @expectedExceptionMessage Invalid repository class 'Doctrine\Tests\Models\DDC753\DDC753InvalidRepository'. it must be a Doctrine\ORM\EntityRepository.
|
||||
*/
|
||||
public function testSetDefaultRepositoryInvalidClassError()
|
||||
{
|
||||
$this->assertEquals($this->_em->getConfiguration()->getDefaultRepositoryClassName(), "Doctrine\ORM\EntityRepository");
|
||||
$this->_em->getConfiguration()->setDefaultRepositoryClassName("Doctrine\Tests\Models\DDC753\DDC753InvalidRepository");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$reference = $this->_em->getReference('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity', $id);
|
||||
$this->assertFalse($reference->postLoadCallbackInvoked);
|
||||
|
||||
$reference->getId(); // trigger proxy load
|
||||
$reference->getValue(); // trigger proxy load
|
||||
$this->assertTrue($reference->postLoadCallbackInvoked);
|
||||
}
|
||||
|
||||
@ -210,6 +210,10 @@ class LifecycleCallbackTestEntity
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/** @PrePersist */
|
||||
public function doStuffOnPrePersist() {
|
||||
$this->prePersistCallbackInvoked = true;
|
||||
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\CMS\CmsUser,
|
||||
Doctrine\Tests\Models\CMS\CmsAddress,
|
||||
Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* Tests a bidirectional one-to-many association mapping with orphan removal.
|
||||
*/
|
||||
class OneToManyOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testOrphanRemoval()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->status = 'dev';
|
||||
$user->username = 'romanb';
|
||||
$user->name = 'Roman B.';
|
||||
|
||||
$phone = new CmsPhonenumber;
|
||||
$phone->phonenumber = '123456';
|
||||
|
||||
$user->addPhonenumber($phone);
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$userId = $user->getId();
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $userId);
|
||||
|
||||
$this->_em->remove($userProxy);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u');
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsUser should be removed by EntityManager');
|
||||
|
||||
$query = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p');
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsPhonenumber should be removed by orphanRemoval');
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\CMS\CmsUser,
|
||||
Doctrine\Tests\Models\CMS\CmsEmail,
|
||||
Doctrine\Tests\Models\CMS\CmsAddress,
|
||||
Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* Tests a bidirectional one-to-one association mapping with orphan removal.
|
||||
*/
|
||||
class OneToOneOrphanRemovalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testOrphanRemoval()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->status = 'dev';
|
||||
$user->username = 'romanb';
|
||||
$user->name = 'Roman B.';
|
||||
|
||||
$address = new CmsAddress;
|
||||
$address->country = 'de';
|
||||
$address->zip = 1234;
|
||||
$address->city = 'Berlin';
|
||||
|
||||
$user->setAddress($address);
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$userId = $user->getId();
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$userProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $userId);
|
||||
|
||||
$this->_em->remove($userProxy);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u');
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsUser should be removed by EntityManager');
|
||||
|
||||
$query = $this->_em->createQuery('SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a');
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsAddress should be removed by orphanRemoval');
|
||||
}
|
||||
|
||||
public function testOrphanRemovalWhenUnlink()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
$user->status = 'dev';
|
||||
$user->username = 'beberlei';
|
||||
$user->name = 'Bejamin Eberlei';
|
||||
|
||||
$email = new CmsEmail;
|
||||
$email->email = 'beberlei@domain.com';
|
||||
|
||||
$user->setEmail($email);
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$userId = $user->getId();
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId);
|
||||
|
||||
$user->setEmail(null);
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery('SELECT e FROM Doctrine\Tests\Models\CMS\CmsEmail e');
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertEquals(0, count($result), 'CmsEmail should be removed by orphanRemoval');
|
||||
}
|
||||
}
|
@ -14,11 +14,28 @@ require_once __DIR__ . '/../../TestInit.php';
|
||||
*/
|
||||
class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* @var \ReflectionProperty
|
||||
*/
|
||||
private $cacheDataReflection;
|
||||
|
||||
protected function setUp() {
|
||||
$this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
|
||||
$this->cacheDataReflection->setAccessible(true);
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayCache $cache
|
||||
* @return integer
|
||||
*/
|
||||
private function getCacheSize(ArrayCache $cache)
|
||||
{
|
||||
return sizeof($this->cacheDataReflection->getValue($cache));
|
||||
}
|
||||
|
||||
|
||||
public function testQueryCache_DependsOnHints()
|
||||
{
|
||||
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
|
||||
@ -27,12 +44,12 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$query->setQueryCacheDriver($cache);
|
||||
|
||||
$query->getResult();
|
||||
$this->assertEquals(1, count($cache->getIds()));
|
||||
$this->assertEquals(1, $this->getCacheSize($cache));
|
||||
|
||||
$query->setHint('foo', 'bar');
|
||||
|
||||
$query->getResult();
|
||||
$this->assertEquals(2, count($cache->getIds()));
|
||||
$this->assertEquals(2, $this->getCacheSize($cache));
|
||||
|
||||
return $query;
|
||||
}
|
||||
@ -44,13 +61,13 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testQueryCache_DependsOnFirstResult($query)
|
||||
{
|
||||
$cache = $query->getQueryCacheDriver();
|
||||
$cacheCount = count($cache->getIds());
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
|
||||
$query->setFirstResult(10);
|
||||
$query->setMaxResults(9999);
|
||||
|
||||
$query->getResult();
|
||||
$this->assertEquals($cacheCount + 1, count($cache->getIds()));
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,12 +77,12 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testQueryCache_DependsOnMaxResults($query)
|
||||
{
|
||||
$cache = $query->getQueryCacheDriver();
|
||||
$cacheCount = count($cache->getIds());
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
|
||||
$query->setMaxResults(10);
|
||||
|
||||
$query->getResult();
|
||||
$this->assertEquals($cacheCount + 1, count($cache->getIds()));
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,10 +92,10 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testQueryCache_DependsOnHydrationMode($query)
|
||||
{
|
||||
$cache = $query->getQueryCacheDriver();
|
||||
$cacheCount = count($cache->getIds());
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
|
||||
$query->getArrayResult();
|
||||
$this->assertEquals($cacheCount + 1, count($cache->getIds()));
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
public function testQueryCache_NoHitSaveParserResult()
|
||||
@ -87,13 +104,13 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
|
||||
|
||||
$cache = $this->getMock('Doctrine\Common\Cache\AbstractCache', array('_doFetch', '_doContains', '_doSave', '_doDelete', 'getIds'));
|
||||
$cache = $this->getMock('Doctrine\Common\Cache\ArrayCache', array('doFetch', 'doSave'));
|
||||
$cache->expects($this->at(0))
|
||||
->method('_doFetch')
|
||||
->method('doFetch')
|
||||
->with($this->isType('string'))
|
||||
->will($this->returnValue(false));
|
||||
$cache->expects($this->at(1))
|
||||
->method('_doSave')
|
||||
->method('doSave')
|
||||
->with($this->isType('string'), $this->isInstanceOf('Doctrine\ORM\Query\ParserResult'), $this->equalTo(null));
|
||||
|
||||
$query->setQueryCacheDriver($cache);
|
||||
@ -117,13 +134,14 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
->method('getSqlExecutor')
|
||||
->will($this->returnValue($sqlExecMock));
|
||||
|
||||
$cache = $this->getMock('Doctrine\Common\Cache\AbstractCache', array('_doFetch', '_doContains', '_doSave', '_doDelete', 'getIds'));
|
||||
$cache = $this->getMock('Doctrine\Common\Cache\CacheProvider',
|
||||
array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush'));
|
||||
$cache->expects($this->once())
|
||||
->method('_doFetch')
|
||||
->method('doFetch')
|
||||
->with($this->isType('string'))
|
||||
->will($this->returnValue($parserResultMock));
|
||||
$cache->expects($this->never())
|
||||
->method('_doSave');
|
||||
->method('doSave');
|
||||
|
||||
$query->setQueryCacheDriver($cache);
|
||||
|
||||
|
@ -501,4 +501,66 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->assertEquals(0, count($users));
|
||||
}
|
||||
|
||||
public function testQueryWithArrayOfEntitiesAsParameter()
|
||||
{
|
||||
$userA = new CmsUser;
|
||||
$userA->name = 'Benjamin';
|
||||
$userA->username = 'beberlei';
|
||||
$userA->status = 'developer';
|
||||
$this->_em->persist($userA);
|
||||
|
||||
$userB = new CmsUser;
|
||||
$userB->name = 'Roman';
|
||||
$userB->username = 'romanb';
|
||||
$userB->status = 'developer';
|
||||
$this->_em->persist($userB);
|
||||
|
||||
$userC = new CmsUser;
|
||||
$userC->name = 'Jonathan';
|
||||
$userC->username = 'jwage';
|
||||
$userC->status = 'developer';
|
||||
$this->_em->persist($userC);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u IN (?0) OR u.username = ?1");
|
||||
$query->setParameter(0, array($userA, $userC));
|
||||
$query->setParameter(1, 'beberlei');
|
||||
|
||||
$users = $query->execute();
|
||||
|
||||
$this->assertEquals(2, count($users));
|
||||
}
|
||||
|
||||
public function testQueryWithHiddenAsSelectExpression()
|
||||
{
|
||||
$userA = new CmsUser;
|
||||
$userA->name = 'Benjamin';
|
||||
$userA->username = 'beberlei';
|
||||
$userA->status = 'developer';
|
||||
$this->_em->persist($userA);
|
||||
|
||||
$userB = new CmsUser;
|
||||
$userB->name = 'Roman';
|
||||
$userB->username = 'romanb';
|
||||
$userB->status = 'developer';
|
||||
$this->_em->persist($userB);
|
||||
|
||||
$userC = new CmsUser;
|
||||
$userC->name = 'Jonathan';
|
||||
$userC->username = 'jwage';
|
||||
$userC->status = 'developer';
|
||||
$this->_em->persist($userC);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery("SELECT u, (SELECT COUNT(u2.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2) AS HIDDEN total FROM Doctrine\Tests\Models\CMS\CmsUser u");
|
||||
$users = $query->execute();
|
||||
|
||||
$this->assertEquals(3, count($users));
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
|
||||
}
|
||||
}
|
@ -147,4 +147,28 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup().");
|
||||
}
|
||||
|
||||
public function testDoNotInitializeProxyOnGettingTheIdentifier()
|
||||
{
|
||||
$id = $this->createProduct();
|
||||
|
||||
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
|
||||
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
|
||||
|
||||
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
|
||||
$this->assertEquals($id, $entity->getId());
|
||||
$this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy.");
|
||||
}
|
||||
|
||||
public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier()
|
||||
{
|
||||
$id = $this->createProduct();
|
||||
|
||||
/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
|
||||
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
|
||||
|
||||
$this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy.");
|
||||
$this->assertEquals('Doctrine Cookbook', $entity->getName());
|
||||
$this->assertTrue($entity->__isInitialized__, "Getting something other than the identifier initializes the proxy.");
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,27 @@ require_once __DIR__ . '/../../TestInit.php';
|
||||
*/
|
||||
class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* @var \ReflectionProperty
|
||||
*/
|
||||
private $cacheDataReflection;
|
||||
|
||||
protected function setUp() {
|
||||
$this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
|
||||
$this->cacheDataReflection->setAccessible(true);
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayCache $cache
|
||||
* @return integer
|
||||
*/
|
||||
private function getCacheSize(ArrayCache $cache)
|
||||
{
|
||||
return sizeof($this->cacheDataReflection->getValue($cache));
|
||||
}
|
||||
|
||||
public function testResultCache()
|
||||
{
|
||||
$user = new CmsUser;
|
||||
@ -125,9 +141,9 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$cache = new ArrayCache();
|
||||
$query->setResultCacheDriver($cache)->useResultCache(true);
|
||||
|
||||
$this->assertEquals(0, count($cache->getIds()));
|
||||
$this->assertEquals(0, $this->getCacheSize($cache));
|
||||
$query->getResult();
|
||||
$this->assertEquals(1, count($cache->getIds()));
|
||||
$this->assertEquals(1, $this->getCacheSize($cache));
|
||||
|
||||
return $query;
|
||||
}
|
||||
@ -139,12 +155,12 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testResultCacheDependsOnQueryHints($query)
|
||||
{
|
||||
$cache = $query->getResultCacheDriver();
|
||||
$cacheCount = count($cache->getIds());
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
|
||||
$query->setHint('foo', 'bar');
|
||||
$query->getResult();
|
||||
|
||||
$this->assertEquals($cacheCount + 1, count($cache->getIds()));
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,12 +170,12 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testResultCacheDependsOnParameters($query)
|
||||
{
|
||||
$cache = $query->getResultCacheDriver();
|
||||
$cacheCount = count($cache->getIds());
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
|
||||
$query->setParameter(1, 50);
|
||||
$query->getResult();
|
||||
|
||||
$this->assertEquals($cacheCount + 1, count($cache->getIds()));
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,12 +185,12 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
public function testResultCacheDependsOnHydrationMode($query)
|
||||
{
|
||||
$cache = $query->getResultCacheDriver();
|
||||
$cacheCount = count($cache->getIds());
|
||||
$cacheCount = $this->getCacheSize($cache);
|
||||
|
||||
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
|
||||
$query->getArrayResult();
|
||||
|
||||
$this->assertEquals($cacheCount + 1, count($cache->getIds()));
|
||||
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
|
||||
}
|
||||
|
||||
/**
|
||||
|
99
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1113Test.php
Normal file
99
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1113Test.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-1113
|
||||
* @group DDC-1306
|
||||
*/
|
||||
class DDC1113Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Engine'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Vehicle'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Car'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1113Bus'),
|
||||
));
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
$car = new DDC1113Car();
|
||||
$car->engine = new DDC1113Engine();
|
||||
|
||||
$bus = new DDC1113Bus();
|
||||
$bus->engine = new DDC1113Engine();
|
||||
|
||||
$this->_em->persist($car);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->_em->persist($bus);
|
||||
$this->_em->flush();
|
||||
|
||||
$this->_em->remove($bus);
|
||||
$this->_em->remove($car);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorMap({"car" = "DDC1113Car", "bus" = "DDC1113Bus"})
|
||||
*/
|
||||
class DDC1113Vehicle
|
||||
{
|
||||
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC1113Vehicle")
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/** @OneToOne(targetEntity="DDC1113Engine", cascade={"persist", "remove"}) */
|
||||
public $engine;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1113Car extends DDC1113Vehicle
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1113Bus extends DDC1113Vehicle
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1113Engine
|
||||
{
|
||||
|
||||
/** @Id @GeneratedValue @Column(type="integer") */
|
||||
public $id;
|
||||
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
// force proxy load, getId() doesn't work anymore
|
||||
$user->getName();
|
||||
$userId = $user->getId();
|
||||
$this->_em->clear();
|
||||
|
||||
@ -60,6 +62,8 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$user2 = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId);
|
||||
|
||||
// force proxy load, getId() doesn't work anymore
|
||||
$user->getName();
|
||||
$this->assertNull($user->getId(), "Now this is null, we already have a user instance of that type");
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user