1
0
mirror of synced 2025-03-17 13:33:52 +03:00

Merge branch 'master' into DDC-551

Conflicts:
	lib/Doctrine/ORM/Configuration.php
	lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
	lib/Doctrine/ORM/Persisters/ManyToManyPersister.php
This commit is contained in:
Alexander 2011-10-14 12:33:39 +02:00
commit 097d573d26
119 changed files with 4973 additions and 726 deletions

View File

@ -1,3 +1,19 @@
# Removed support for onUpdate in @JoinColumn # 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. 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);

View File

@ -8,6 +8,7 @@ report.dir=reports
log.archive.dir=logs log.archive.dir=logs
project.pirum_dir= project.pirum_dir=
project.download_dir= project.download_dir=
project.xsd_dir=
test.phpunit_configuration_file= test.phpunit_configuration_file=
test.phpunit_generate_coverage=0 test.phpunit_generate_coverage=0
test.pmd_reports=0 test.pmd_reports=0

View File

@ -223,7 +223,12 @@
<target name="distribute-download"> <target name="distribute-download">
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" /> <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>
<target name="update-dev-version"> <target name="update-dev-version">
@ -235,7 +240,7 @@
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" /> <exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
</target> </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 Builds distributable PEAR packages for the Symfony Dependencies

20
composer.json Normal file
View 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"
}
}

View File

@ -92,7 +92,7 @@
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/> <xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" /> <xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
<xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" /> <xs:element name="named-queries" type="orm:named-queries" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" /> <xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />

View File

@ -162,6 +162,16 @@ abstract class AbstractQuery
{ {
return $this->_params; 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. * Gets a query parameter.
@ -174,6 +184,17 @@ abstract class AbstractQuery
return isset($this->_params[$key]) ? $this->_params[$key] : null; 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. * Gets the SQL query that corresponds to this query object.
* The returned SQL syntax depends on the connection driver that is used * The returned SQL syntax depends on the connection driver that is used

View File

@ -528,4 +528,32 @@ class Configuration extends \Doctrine\DBAL\Configuration
return isset($this->_attributes['filters'][$name]) ? return isset($this->_attributes['filters'][$name]) ?
$this->_attributes['filters'][$name] : null; $this->_attributes['filters'][$name] : null;
} }
/**
* 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';
}
} }

View File

@ -584,7 +584,8 @@ class EntityManager implements ObjectManager
if ($customRepositoryClassName !== null) { if ($customRepositoryClassName !== null) {
$repository = new $customRepositoryClassName($this, $metadata); $repository = new $customRepositoryClassName($this, $metadata);
} else { } else {
$repository = new EntityRepository($this, $metadata); $repositoryClass = $this->config->getDefaultRepositoryClassName();
$repository = new $repositoryClass($this, $metadata);
} }
$this->repositories[$entityName] = $repository; $this->repositories[$entityName] = $repository;

View 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);
}
}
}
}

View File

@ -90,7 +90,7 @@ class PreUpdateEventArgs extends LifecycleEventArgs
if (!isset($this->_entityChangeSet[$field])) { if (!isset($this->_entityChangeSet[$field])) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
"Field '".$field."' is not a valid field of the entity ". "Field '".$field."' is not a valid field of the entity ".
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs." "'".get_class($this->getEntity())."' in PreUpdateEventArgs."
); );
} }
} }

View 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;
}
}

View 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();
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -343,4 +343,17 @@ class ClassMetadata extends ClassMetadataInfo
} }
return clone $this->_prototype; 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);
}
} }

View File

@ -50,7 +50,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
private $targetPlatform; private $targetPlatform;
/** /**
* @var Driver\Driver * @var \Doctrine\ORM\Mapping\Driver\Driver
*/ */
private $driver; private $driver;
@ -274,6 +274,9 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
$class->setDiscriminatorMap($parent->discriminatorMap); $class->setDiscriminatorMap($parent->discriminatorMap);
$class->setLifecycleCallbacks($parent->lifecycleCallbacks); $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy); $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
if ($parent->isMappedSuperclass) {
$class->setCustomRepositoryClass($parent->customRepositoryClassName);
}
} }
// Invoke driver // Invoke driver
@ -448,7 +451,7 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
// <table>_<column>_seq in PostgreSQL for SERIAL columns. // <table>_<column>_seq in PostgreSQL for SERIAL columns.
// Not pretty but necessary and the simplest solution that currently works. // Not pretty but necessary and the simplest solution that currently works.
$seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ? $seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' : $class->getTableName() . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
null; null;
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName)); $class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
break; break;
@ -478,4 +481,15 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
throw new ORMException("Unknown generator type: " . $class->generatorType); 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);
}
} }

View File

@ -774,9 +774,13 @@ class ClassMetadataInfo implements ClassMetadata
// If targetEntity is unqualified, assume it is in the same namespace as // If targetEntity is unqualified, assume it is in the same namespace as
// the sourceEntity. // the sourceEntity.
$mapping['sourceEntity'] = $this->name; $mapping['sourceEntity'] = $this->name;
if (isset($mapping['targetEntity']) && strpos($mapping['targetEntity'], '\\') === false
&& strlen($this->namespace) > 0) { if (isset($mapping['targetEntity'])) {
$mapping['targetEntity'] = $this->namespace . '\\' . $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 // Complete id mapping
@ -904,9 +908,8 @@ class ClassMetadataInfo implements ClassMetadata
$mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); $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']) ? $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
(bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) { if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']); throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
@ -931,9 +934,8 @@ class ClassMetadataInfo implements ClassMetadata
throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']); 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']) ? $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
(bool) $mapping['orphanRemoval'] : false;
if (isset($mapping['orderBy'])) { if (isset($mapping['orderBy'])) {
if ( ! is_array($mapping['orderBy'])) { if ( ! is_array($mapping['orderBy'])) {
@ -1268,7 +1270,7 @@ class ClassMetadataInfo implements ClassMetadata
public function getTemporaryIdTableName() public function getTemporaryIdTableName()
{ {
// replace dots with underscores because PostgreSQL creates temporary tables in a special schema // 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']; $this->table['name'] = $table['name'];
} }
} }
if (isset($table['indexes'])) { if (isset($table['indexes'])) {
$this->table['indexes'] = $table['indexes']; $this->table['indexes'] = $table['indexes'];
} }
if (isset($table['uniqueConstraints'])) { if (isset($table['uniqueConstraints'])) {
$this->table['uniqueConstraints'] = $table['uniqueConstraints']; $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
} }
@ -1518,6 +1522,10 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function setCustomRepositoryClass($repositoryClassName) public function setCustomRepositoryClass($repositoryClassName)
{ {
if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
&& strlen($this->namespace) > 0) {
$repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
}
$this->customRepositoryClassName = $repositoryClassName; $this->customRepositoryClassName = $repositoryClassName;
} }
@ -1560,9 +1568,6 @@ class ClassMetadataInfo implements ClassMetadata
/** /**
* Adds a lifecycle callback for entities of this class. * 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 $callback
* @param string $event * @param string $event
*/ */
@ -1621,20 +1626,33 @@ class ClassMetadataInfo implements ClassMetadata
public function setDiscriminatorMap(array $map) public function setDiscriminatorMap(array $map)
{ {
foreach ($map as $value => $className) { foreach ($map as $value => $className) {
if (strpos($className, '\\') === false && strlen($this->namespace)) { $this->addDiscriminatorMapClass($value, $className);
$className = $this->namespace . '\\' . $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[$name] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $name;
} else {
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
} }
$className = ltrim($className, '\\'); if (is_subclass_of($className, $this->name)) {
$this->discriminatorMap[$value] = $className; $this->subClasses[] = $className;
if ($this->name == $className) {
$this->discriminatorValue = $value;
} else {
if ( ! class_exists($className)) {
throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
}
if (is_subclass_of($className, $this->name)) {
$this->subClasses[] = $className;
}
} }
} }
} }
@ -1869,9 +1887,10 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function getAssociationTargetClass($assocName) public function getAssociationTargetClass($assocName)
{ {
if (!isset($this->associationMappings[$assocName])) { if ( ! isset($this->associationMappings[$assocName])) {
throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association."); throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
} }
return $this->associationMappings[$assocName]['targetEntity']; return $this->associationMappings[$assocName]['targetEntity'];
} }
@ -1895,9 +1914,7 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function getQuotedColumnName($field, $platform) public function getQuotedColumnName($field, $platform)
{ {
return isset($this->fieldMappings[$field]['quoted']) ? return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName'];
$platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) :
$this->fieldMappings[$field]['columnName'];
} }
/** /**
@ -1909,9 +1926,7 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function getQuotedTableName($platform) public function getQuotedTableName($platform)
{ {
return isset($this->table['quoted']) ? return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
$platform->quoteIdentifier($this->table['name']) :
$this->table['name'];
} }
/** /**
@ -1922,8 +1937,6 @@ class ClassMetadataInfo implements ClassMetadata
*/ */
public function getQuotedJoinTableName(array $assoc, $platform) public function getQuotedJoinTableName(array $assoc, $platform)
{ {
return isset($assoc['joinTable']['quoted']) return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
? $platform->quoteIdentifier($assoc['joinTable']['name'])
: $assoc['joinTable']['name'];
} }
} }

View File

@ -147,12 +147,15 @@ class AnnotationDriver implements Driver
// Evaluate Entity annotation // Evaluate Entity annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) { if (isset($classAnnotations['Doctrine\ORM\Mapping\Entity'])) {
$entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity']; $entityAnnot = $classAnnotations['Doctrine\ORM\Mapping\Entity'];
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass); if ($entityAnnot->repositoryClass !== null) {
$metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
}
if ($entityAnnot->readOnly) { if ($entityAnnot->readOnly) {
$metadata->markReadOnly(); $metadata->markReadOnly();
} }
} else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) { } else if (isset($classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'])) {
$mappedSuperclassAnnot = $classAnnotations['Doctrine\ORM\Mapping\MappedSuperclass'];
$metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
$metadata->isMappedSuperclass = true; $metadata->isMappedSuperclass = true;
} else { } else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);

View File

@ -19,7 +19,8 @@
namespace Doctrine\ORM\Mapping; namespace Doctrine\ORM\Mapping;
use Doctrine\Common\Annotations\Annotation; interface Annotation {}
/* Annotations */ /* Annotations */
@ -27,8 +28,10 @@ use Doctrine\Common\Annotations\Annotation;
* @Annotation * @Annotation
* @Target("CLASS") * @Target("CLASS")
*/ */
final class Entity extends Annotation { final class Entity implements Annotation {
/** @var string */
public $repositoryClass; public $repositoryClass;
/** @var boolean */
public $readOnly = false; public $readOnly = false;
} }
@ -36,42 +39,56 @@ final class Entity extends Annotation {
* @Annotation * @Annotation
* @Target("CLASS") * @Target("CLASS")
*/ */
final class MappedSuperclass extends Annotation {} final class MappedSuperclass implements Annotation {
/** @var string */
/** public $repositoryClass;
* @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;
} }
/** /**
* @Annotation * @Annotation
* @Target("CLASS") * @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 * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class Id extends Annotation {} final class Id implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class GeneratedValue extends Annotation { final class GeneratedValue implements Annotation {
/** @var string */
public $strategy = 'AUTO'; public $strategy = 'AUTO';
} }
@ -79,43 +96,60 @@ final class GeneratedValue extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class Version extends Annotation {} final class Version implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target({"PROPERTY","ANNOTATION"}) * @Target({"PROPERTY","ANNOTATION"})
*/ */
final class JoinColumn extends Annotation { final class JoinColumn implements Annotation {
/** @var string */
public $name; public $name;
public $fieldName; // field name used in non-object hydration (array/scalar) /** @var string */
public $referencedColumnName = 'id'; public $referencedColumnName = 'id';
/** @var boolean */
public $unique = false; public $unique = false;
/** @var boolean */
public $nullable = true; public $nullable = true;
/** @var mixed */
public $onDelete; public $onDelete;
/** @var string */
public $columnDefinition; public $columnDefinition;
/** @var string */
public $fieldName; // field name used in non-object hydration (array/scalar)
} }
/** /**
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class JoinColumns extends Annotation {} final class JoinColumns implements Annotation {
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
public $value;
}
/** /**
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class Column extends Annotation { final class Column implements Annotation {
public $type = 'string'; /** @var 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;
public $name; 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(); public $options = array();
/** @var string */
public $columnDefinition; public $columnDefinition;
} }
@ -123,12 +157,18 @@ final class Column extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class OneToOne extends Annotation { final class OneToOne implements Annotation {
/** @var string */
public $targetEntity; public $targetEntity;
/** @var string */
public $mappedBy; public $mappedBy;
/** @var string */
public $inversedBy; public $inversedBy;
/** @var array<string> */
public $cascade; public $cascade;
/** @var string */
public $fetch = 'LAZY'; public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false; public $orphanRemoval = false;
} }
@ -136,12 +176,18 @@ final class OneToOne extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class OneToMany extends Annotation { final class OneToMany implements Annotation {
/** @var string */
public $mappedBy; public $mappedBy;
/** @var string */
public $targetEntity; public $targetEntity;
/** @var array<string> */
public $cascade; public $cascade;
/** @var string */
public $fetch = 'LAZY'; public $fetch = 'LAZY';
/** @var boolean */
public $orphanRemoval = false; public $orphanRemoval = false;
/** @var string */
public $indexBy; public $indexBy;
} }
@ -149,10 +195,14 @@ final class OneToMany extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class ManyToOne extends Annotation { final class ManyToOne implements Annotation {
/** @var string */
public $targetEntity; public $targetEntity;
/** @var array<string> */
public $cascade; public $cascade;
/** @var string */
public $fetch = 'LAZY'; public $fetch = 'LAZY';
/** @var string */
public $inversedBy; public $inversedBy;
} }
@ -160,12 +210,18 @@ final class ManyToOne extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class ManyToMany extends Annotation { final class ManyToMany implements Annotation {
/** @var string */
public $targetEntity; public $targetEntity;
/** @var string */
public $mappedBy; public $mappedBy;
/** @var string */
public $inversedBy; public $inversedBy;
/** @var array<string> */
public $cascade; public $cascade;
/** @var string */
public $fetch = 'LAZY'; public $fetch = 'LAZY';
/** @var string */
public $indexBy; public $indexBy;
} }
@ -174,7 +230,8 @@ final class ManyToMany extends Annotation {
* @Target("ALL") * @Target("ALL")
* @todo check available targets * @todo check available targets
*/ */
final class ElementCollection extends Annotation { final class ElementCollection implements Annotation {
/** @var string */
public $tableName; public $tableName;
} }
@ -182,10 +239,14 @@ final class ElementCollection extends Annotation {
* @Annotation * @Annotation
* @Target("CLASS") * @Target("CLASS")
*/ */
final class Table extends Annotation { final class Table implements Annotation {
/** @var string */
public $name; public $name;
/** @var string */
public $schema; public $schema;
/** @var array<Doctrine\ORM\Mapping\Index> */
public $indexes; public $indexes;
/** @var array<Doctrine\ORM\Mapping\UniqueConstraint> */
public $uniqueConstraints; public $uniqueConstraints;
} }
@ -193,8 +254,10 @@ final class Table extends Annotation {
* @Annotation * @Annotation
* @Target("ANNOTATION") * @Target("ANNOTATION")
*/ */
final class UniqueConstraint extends Annotation { final class UniqueConstraint implements Annotation {
/** @var string */
public $name; public $name;
/** @var array<string> */
public $columns; public $columns;
} }
@ -202,8 +265,10 @@ final class UniqueConstraint extends Annotation {
* @Annotation * @Annotation
* @Target("ANNOTATION") * @Target("ANNOTATION")
*/ */
final class Index extends Annotation { final class Index implements Annotation {
/** @var string */
public $name; public $name;
/** @var array<string> */
public $columns; public $columns;
} }
@ -211,10 +276,14 @@ final class Index extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class JoinTable extends Annotation { final class JoinTable implements Annotation {
/** @var string */
public $name; public $name;
/** @var string */
public $schema; public $schema;
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
public $joinColumns = array(); public $joinColumns = array();
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
public $inverseJoinColumns = array(); public $inverseJoinColumns = array();
} }
@ -222,9 +291,12 @@ final class JoinTable extends Annotation {
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class SequenceGenerator extends Annotation { final class SequenceGenerator implements Annotation {
/** @var string */
public $sequenceName; public $sequenceName;
/** @var integer */
public $allocationSize = 1; public $allocationSize = 1;
/** @var integer */
public $initialValue = 1; public $initialValue = 1;
} }
@ -232,26 +304,37 @@ final class SequenceGenerator extends Annotation {
* @Annotation * @Annotation
* @Target("CLASS") * @Target("CLASS")
*/ */
final class ChangeTrackingPolicy extends Annotation {} final class ChangeTrackingPolicy implements Annotation {
/** @var string */
public $value;
}
/** /**
* @Annotation * @Annotation
* @Target("PROPERTY") * @Target("PROPERTY")
*/ */
final class OrderBy extends Annotation {} final class OrderBy implements Annotation {
/** @var array<string> */
public $value;
}
/** /**
* @Annotation * @Annotation
* @Target("CLASS") * @Target("CLASS")
*/ */
final class NamedQueries extends Annotation {} final class NamedQueries implements Annotation {
/** @var array<Doctrine\ORM\Mapping\NamedQuery> */
public $value;
}
/** /**
* @Annotation * @Annotation
* @Target("ANNOTATION") * @Target("ANNOTATION")
*/ */
final class NamedQuery extends Annotation { final class NamedQuery implements Annotation {
/** @var string */
public $name; public $name;
/** @var string */
public $query; public $query;
} }
@ -261,46 +344,46 @@ final class NamedQuery extends Annotation {
* @Annotation * @Annotation
* @Target("CLASS") * @Target("CLASS")
*/ */
final class HasLifecycleCallbacks extends Annotation {} final class HasLifecycleCallbacks implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PrePersist extends Annotation {} final class PrePersist implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PostPersist extends Annotation {} final class PostPersist implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PreUpdate extends Annotation {} final class PreUpdate implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PostUpdate extends Annotation {} final class PostUpdate implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PreRemove extends Annotation {} final class PreRemove implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PostRemove extends Annotation {} final class PostRemove implements Annotation {}
/** /**
* @Annotation * @Annotation
* @Target("METHOD") * @Target("METHOD")
*/ */
final class PostLoad extends Annotation {} final class PostLoad implements Annotation {}

View 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);
}
}

View 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);
}
}

View File

@ -52,13 +52,16 @@ class XmlDriver extends AbstractFileDriver
$xmlRoot = $this->getElement($className); $xmlRoot = $this->getElement($className);
if ($xmlRoot->getName() == 'entity') { if ($xmlRoot->getName() == 'entity') {
$metadata->setCustomRepositoryClass( if (isset($xmlRoot['repository-class'])) {
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null $metadata->setCustomRepositoryClass((string)$xmlRoot['repository-class']);
); }
if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") { if (isset($xmlRoot['read-only']) && $xmlRoot['read-only'] == "true") {
$metadata->markReadOnly(); $metadata->markReadOnly();
} }
} else if ($xmlRoot->getName() == 'mapped-superclass') { } else if ($xmlRoot->getName() == 'mapped-superclass') {
$metadata->setCustomRepositoryClass(
isset($xmlRoot['repository-class']) ? (string)$xmlRoot['repository-class'] : null
);
$metadata->isMappedSuperclass = true; $metadata->isMappedSuperclass = true;
} else { } else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);

View File

@ -46,13 +46,16 @@ class YamlDriver extends AbstractFileDriver
$element = $this->getElement($className); $element = $this->getElement($className);
if ($element['type'] == 'entity') { if ($element['type'] == 'entity') {
$metadata->setCustomRepositoryClass( if (isset($element['repositoryClass'])) {
isset($element['repositoryClass']) ? $element['repositoryClass'] : null $metadata->setCustomRepositoryClass($element['repositoryClass']);
); }
if (isset($element['readOnly']) && $element['readOnly'] == true) { if (isset($element['readOnly']) && $element['readOnly'] == true) {
$metadata->markReadOnly(); $metadata->markReadOnly();
} }
} else if ($element['type'] == 'mappedSuperclass') { } else if ($element['type'] == 'mappedSuperclass') {
$metadata->setCustomRepositoryClass(
isset($element['repositoryClass']) ? $element['repositoryClass'] : null
);
$metadata->isMappedSuperclass = true; $metadata->isMappedSuperclass = true;
} else { } else {
throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className); throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);

View File

@ -293,4 +293,9 @@ class MappingException extends \Doctrine\ORM\ORMException
"to avoid this exception from occuring." "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.");
}
} }

View File

@ -130,4 +130,10 @@ class ORMException extends Exception
"Unknown Entity namespace alias '$entityNamespaceAlias'." "Unknown Entity namespace alias '$entityNamespaceAlias'."
); );
} }
public static function invalidEntityRepository($className)
{
return new self("Invalid repository class '".$className."'. ".
"it must be a Doctrine\ORM\EntityRepository.");
}
} }

View File

@ -65,9 +65,11 @@ abstract class AbstractCollectionPersister
public function delete(PersistentCollection $coll) public function delete(PersistentCollection $coll)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) { if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side return; // ignore inverse side
} }
$sql = $this->_getDeleteSQL($coll); $sql = $this->_getDeleteSQL($coll);
$this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll)); $this->_conn->executeUpdate($sql, $this->_getDeleteSQLParameters($coll));
} }
@ -96,9 +98,11 @@ abstract class AbstractCollectionPersister
public function update(PersistentCollection $coll) public function update(PersistentCollection $coll)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
if ( ! $mapping['isOwningSide']) { if ( ! $mapping['isOwningSide']) {
return; // ignore inverse side return; // ignore inverse side
} }
$this->deleteRows($coll); $this->deleteRows($coll);
//$this->updateRows($coll); //$this->updateRows($coll);
$this->insertRows($coll); $this->insertRows($coll);
@ -108,6 +112,7 @@ abstract class AbstractCollectionPersister
{ {
$deleteDiff = $coll->getDeleteDiff(); $deleteDiff = $coll->getDeleteDiff();
$sql = $this->_getDeleteRowSQL($coll); $sql = $this->_getDeleteRowSQL($coll);
foreach ($deleteDiff as $element) { foreach ($deleteDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element)); $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
} }
@ -120,6 +125,7 @@ abstract class AbstractCollectionPersister
{ {
$insertDiff = $coll->getInsertDiff(); $insertDiff = $coll->getInsertDiff();
$sql = $this->_getInsertRowSQL($coll); $sql = $this->_getInsertRowSQL($coll);
foreach ($insertDiff as $element) { foreach ($insertDiff as $element) {
$this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element)); $this->_conn->executeUpdate($sql, $this->_getInsertRowSQLParameters($coll, $element));
} }

View File

@ -39,10 +39,12 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
protected function _prepareInsertData($entity) protected function _prepareInsertData($entity)
{ {
$data = parent::_prepareInsertData($entity); $data = parent::_prepareInsertData($entity);
// Populate the discriminator column // Populate the discriminator column
$discColumn = $this->_class->discriminatorColumn; $discColumn = $this->_class->discriminatorColumn;
$this->_columnTypes[$discColumn['name']] = $discColumn['type']; $this->_columnTypes[$discColumn['name']] = $discColumn['type'];
$data[$this->_getDiscriminatorColumnTableName()][$discColumn['name']] = $this->_class->discriminatorValue; $data[$this->_getDiscriminatorColumnTableName()][$discColumn['name']] = $this->_class->discriminatorValue;
return $data; return $data;
} }
@ -63,7 +65,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); $columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name); $this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
return "$sql AS $columnAlias"; return $sql . ' AS ' . $columnAlias;
} }
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className) protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
@ -72,6 +74,6 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName); $this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
return $tableAlias . ".$joinColumnName AS $columnAlias"; return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
} }
} }

View File

@ -222,7 +222,7 @@ class BasicEntityPersister
$isPostInsertId = $idGen->isPostInsertGenerator(); $isPostInsertId = $idGen->isPostInsertGenerator();
$stmt = $this->_conn->prepare($this->_getInsertSQL()); $stmt = $this->_conn->prepare($this->_getInsertSQL());
$tableName = $this->_class->table['name']; $tableName = $this->_class->getTableName();
foreach ($this->_queuedInserts as $entity) { foreach ($this->_queuedInserts as $entity) {
$insertData = $this->_prepareInsertData($entity); $insertData = $this->_prepareInsertData($entity);
@ -278,11 +278,14 @@ class BasicEntityPersister
protected function fetchVersionValue($versionedClass, $id) protected function fetchVersionValue($versionedClass, $id)
{ {
$versionField = $versionedClass->versionField; $versionField = $versionedClass->versionField;
$identifier = $versionedClass->getIdentifierColumnNames(); $identifier = $versionedClass->getIdentifierColumnNames();
$versionFieldColumnName = $versionedClass->getColumnName($versionField);
$versionFieldColumnName = $versionedClass->getQuotedColumnName($versionField, $this->_platform);
//FIXME: Order with composite keys might not be correct //FIXME: Order with composite keys might not be correct
$sql = "SELECT " . $versionFieldColumnName . " FROM " . $versionedClass->getQuotedTableName($this->_platform) $sql = 'SELECT ' . $versionFieldColumnName
. " WHERE " . implode(' = ? AND ', $identifier) . " = ?"; . ' FROM ' . $versionedClass->getQuotedTableName($this->_platform)
. ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';
$value = $this->_conn->fetchColumn($sql, array_values((array)$id)); $value = $this->_conn->fetchColumn($sql, array_values((array)$id));
return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform); return Type::getType($versionedClass->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform);
@ -305,7 +308,8 @@ class BasicEntityPersister
public function update($entity) public function update($entity)
{ {
$updateData = $this->_prepareUpdateData($entity); $updateData = $this->_prepareUpdateData($entity);
$tableName = $this->_class->table['name']; $tableName = $this->_class->getTableName();
if (isset($updateData[$tableName]) && $updateData[$tableName]) { if (isset($updateData[$tableName]) && $updateData[$tableName]) {
$this->_updateTable( $this->_updateTable(
$entity, $this->_class->getQuotedTableName($this->_platform), $entity, $this->_class->getQuotedTableName($this->_platform),
@ -333,17 +337,17 @@ class BasicEntityPersister
$set = $params = $types = array(); $set = $params = $types = array();
foreach ($updateData as $columnName => $value) { foreach ($updateData as $columnName => $value) {
if (isset($this->_class->fieldNames[$columnName])) { $set[] = (isset($this->_class->fieldNames[$columnName]))
$set[] = $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?'; ? $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?'
} else { : $columnName . ' = ?';
$set[] = $columnName . ' = ?';
}
$params[] = $value; $params[] = $value;
$types[] = $this->_columnTypes[$columnName]; $types[] = $this->_columnTypes[$columnName];
} }
$where = array(); $where = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($this->_class->identifier as $idField) { foreach ($this->_class->identifier as $idField) {
if (isset($this->_class->associationMappings[$idField])) { if (isset($this->_class->associationMappings[$idField])) {
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']); $targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
@ -361,18 +365,21 @@ class BasicEntityPersister
$versionField = $this->_class->versionField; $versionField = $this->_class->versionField;
$versionFieldType = $this->_class->fieldMappings[$versionField]['type']; $versionFieldType = $this->_class->fieldMappings[$versionField]['type'];
$versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform); $versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform);
if ($versionFieldType == Type::INTEGER) { if ($versionFieldType == Type::INTEGER) {
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
} else if ($versionFieldType == Type::DATETIME) { } else if ($versionFieldType == Type::DATETIME) {
$set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; $set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
} }
$where[] = $versionColumn; $where[] = $versionColumn;
$params[] = $this->_class->reflFields[$versionField]->getValue($entity); $params[] = $this->_class->reflFields[$versionField]->getValue($entity);
$types[] = $this->_class->fieldMappings[$versionField]['type']; $types[] = $this->_class->fieldMappings[$versionField]['type'];
} }
$sql = "UPDATE $quotedTableName SET " . implode(', ', $set) $sql = 'UPDATE ' . $quotedTableName
. ' WHERE ' . implode(' = ? AND ', $where) . ' = ?'; . ' SET ' . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
$result = $this->_conn->executeUpdate($sql, $params, $types); $result = $this->_conn->executeUpdate($sql, $params, $types);
@ -398,21 +405,29 @@ class BasicEntityPersister
$relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']); $relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$mapping = $relatedClass->associationMappings[$mapping['mappedBy']]; $mapping = $relatedClass->associationMappings[$mapping['mappedBy']];
$keys = array_keys($mapping['relationToTargetKeyColumns']); $keys = array_keys($mapping['relationToTargetKeyColumns']);
if ($selfReferential) { if ($selfReferential) {
$otherKeys = array_keys($mapping['relationToSourceKeyColumns']); $otherKeys = array_keys($mapping['relationToSourceKeyColumns']);
} }
} else { } else {
$keys = array_keys($mapping['relationToSourceKeyColumns']); $keys = array_keys($mapping['relationToSourceKeyColumns']);
if ($selfReferential) { if ($selfReferential) {
$otherKeys = array_keys($mapping['relationToTargetKeyColumns']); $otherKeys = array_keys($mapping['relationToTargetKeyColumns']);
} }
} }
if ( ! isset($mapping['isOnDeleteCascade'])) { 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) { 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 +478,7 @@ class BasicEntityPersister
$result = array(); $result = array();
$uow = $this->_em->getUnitOfWork(); $uow = $this->_em->getUnitOfWork();
if ($versioned = $this->_class->isVersioned) { if (($versioned = $this->_class->isVersioned) != false) {
$versionField = $this->_class->versionField; $versionField = $this->_class->versionField;
} }
@ -477,6 +492,7 @@ class BasicEntityPersister
if (isset($this->_class->associationMappings[$field])) { if (isset($this->_class->associationMappings[$field])) {
$assoc = $this->_class->associationMappings[$field]; $assoc = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column. // Only owning side of x-1 associations can have a FK column.
if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
continue; continue;
@ -484,6 +500,7 @@ class BasicEntityPersister
if ($newVal !== null) { if ($newVal !== null) {
$oid = spl_object_hash($newVal); $oid = spl_object_hash($newVal);
if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
// The associated entity $newVal is not yet persisted, so we must // The associated entity $newVal is not yet persisted, so we must
// set $newVal = null, in order to insert a null value and schedule an // set $newVal = null, in order to insert a null value and schedule an
@ -510,6 +527,7 @@ class BasicEntityPersister
} else { } else {
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]]; $result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
} }
$this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn); $this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn);
} }
} else { } else {
@ -518,6 +536,7 @@ class BasicEntityPersister
$result[$this->getOwningTable($field)][$columnName] = $newVal; $result[$this->getOwningTable($field)][$columnName] = $newVal;
} }
} }
return $result; return $result;
} }
@ -548,7 +567,7 @@ class BasicEntityPersister
*/ */
public function getOwningTable($fieldName) public function getOwningTable($fieldName)
{ {
return $this->_class->table['name']; return $this->_class->getTableName();
} }
/** /**
@ -574,12 +593,9 @@ class BasicEntityPersister
$hints[Query::HINT_REFRESH_ENTITY] = $entity; $hints[Query::HINT_REFRESH_ENTITY] = $entity;
} }
if ($this->_selectJoinSql) { $hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
} else {
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
}
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints); $entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
return $entities ? $entities[0] : null; return $entities ? $entities[0] : null;
} }
@ -596,7 +612,7 @@ class BasicEntityPersister
*/ */
public function loadOneToOneEntity(array $assoc, $sourceEntity, array $identifier = array()) 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; return $foundEntity;
} }
@ -608,14 +624,17 @@ class BasicEntityPersister
// Mark inverse side as fetched in the hints, otherwise the UoW would // 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). // try to load it in a separate query (remember: to-one inverse sides can not be lazy).
$hints = array(); $hints = array();
if ($isInverseSingleValued) { if ($isInverseSingleValued) {
$hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true; $hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true;
if ($targetClass->subClasses) { if ($targetClass->subClasses) {
foreach ($targetClass->subClasses as $targetSubclassName) { foreach ($targetClass->subClasses as $targetSubclassName) {
$hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true; $hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true;
} }
} }
} }
/* cascade read-only status /* cascade read-only status
if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) { if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) {
$hints[Query::HINT_READ_ONLY] = true; $hints[Query::HINT_READ_ONLY] = true;
@ -631,19 +650,21 @@ class BasicEntityPersister
} else { } else {
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
$owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']);
// TRICKY: since the association is specular source and target are flipped // TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { 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 {
throw MappingException::joinColumnMustPointToMappedField( throw MappingException::joinColumnMustPointToMappedField(
$sourceClass->name, $sourceKeyColumn $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); $targetEntity = $this->load($identifier, null, $assoc);
@ -675,7 +696,9 @@ class BasicEntityPersister
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) { if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity); $this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
} }
$evm = $this->_em->getEventManager(); $evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) { if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em)); $evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
} }
@ -697,11 +720,8 @@ class BasicEntityPersister
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
if ($this->_selectJoinSql) { $hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
} else {
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
}
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true)); return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
} }
@ -717,6 +737,7 @@ class BasicEntityPersister
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{ {
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt); return $this->loadArrayFromStatement($assoc, $stmt);
} }
@ -725,6 +746,7 @@ class BasicEntityPersister
* *
* @param array $assoc * @param array $assoc
* @param Doctrine\DBAL\Statement $stmt * @param Doctrine\DBAL\Statement $stmt
*
* @return array * @return array
*/ */
private function loadArrayFromStatement($assoc, $stmt) private function loadArrayFromStatement($assoc, $stmt)
@ -739,6 +761,7 @@ class BasicEntityPersister
} }
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $rsm, $hints); return $hydrator->hydrateAll($stmt, $rsm, $hints);
} }
@ -748,6 +771,8 @@ class BasicEntityPersister
* @param array $assoc * @param array $assoc
* @param Doctrine\DBAL\Statement $stmt * @param Doctrine\DBAL\Statement $stmt
* @param PersistentCollection $coll * @param PersistentCollection $coll
*
* @return array
*/ */
private function loadCollectionFromStatement($assoc, $stmt, $coll) private function loadCollectionFromStatement($assoc, $stmt, $coll)
{ {
@ -761,7 +786,8 @@ class BasicEntityPersister
} }
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $rsm, $hints);
return $hydrator->hydrateAll($stmt, $rsm, $hints);
} }
/** /**
@ -777,6 +803,7 @@ class BasicEntityPersister
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{ {
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity); $stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll); return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
} }
@ -784,12 +811,15 @@ class BasicEntityPersister
{ {
$criteria = array(); $criteria = array();
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
if ($assoc['isOwningSide']) { if ($assoc['isOwningSide']) {
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform); $quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform);
foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
if ($sourceClass->containsForeignIdentifier) { if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn); $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
@ -807,15 +837,18 @@ class BasicEntityPersister
} else { } else {
$owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']];
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform); $quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform);
// TRICKY: since the association is inverted source and target are flipped // TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
if ($sourceClass->containsForeignIdentifier) { if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn); $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
} }
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
@ -829,6 +862,7 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
return $this->_conn->executeQuery($sql, $params, $types); return $this->_conn->executeQuery($sql, $params, $types);
} }
@ -847,15 +881,14 @@ class BasicEntityPersister
*/ */
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null, array $orderBy = null) 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 ? $joinSql = ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : '';
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
$orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy; $orderBy = ($assoc !== null && isset($assoc['orderBy'])) ? $assoc['orderBy'] : $orderBy;
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : ''; $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
$lockSql = ''; $lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) { if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql(); $lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
@ -890,6 +923,7 @@ class BasicEntityPersister
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias) protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
{ {
$orderBySql = ''; $orderBySql = '';
foreach ($orderBy as $fieldName => $orientation) { foreach ($orderBy as $fieldName => $orientation) {
if ( ! isset($this->_class->fieldMappings[$fieldName])) { if ( ! isset($this->_class->fieldMappings[$fieldName])) {
throw ORMException::unrecognizedField($fieldName); throw ORMException::unrecognizedField($fieldName);
@ -900,6 +934,7 @@ class BasicEntityPersister
: $baseTableAlias; : $baseTableAlias;
$columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform); $columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform);
$orderBySql .= $orderBySql ? ', ' : ' ORDER BY '; $orderBySql .= $orderBySql ? ', ' : ' ORDER BY ';
$orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation; $orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation;
} }
@ -932,20 +967,25 @@ class BasicEntityPersister
// Add regular columns to select list // Add regular columns to select list
foreach ($this->_class->fieldNames as $field) { foreach ($this->_class->fieldNames as $field) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $this->_class); $columnList .= $this->_getSelectColumnSQL($field, $this->_class);
} }
$this->_selectJoinSql = ''; $this->_selectJoinSql = '';
$eagerAliasCounter = 0; $eagerAliasCounter = 0;
foreach ($this->_class->associationMappings as $assocField => $assoc) { foreach ($this->_class->associationMappings as $assocField => $assoc) {
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class); $assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
if ($assocColumnSQL) { if ($assocColumnSQL) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $assocColumnSQL; $columnList .= $assocColumnSQL;
} }
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) { if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
continue; // now this is why you shouldn't use inheritance continue; // now this is why you shouldn't use inheritance
} }
@ -955,41 +995,48 @@ class BasicEntityPersister
foreach ($eagerEntity->fieldNames AS $field) { foreach ($eagerEntity->fieldNames AS $field) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias); $columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
} }
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) { foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias); $assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
if ($assoc2ColumnSQL) { if ($assoc2ColumnSQL) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $assoc2ColumnSQL; $columnList .= $assoc2ColumnSQL;
} }
} }
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable. $this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
$first = true; $first = true;
if ($assoc['isOwningSide']) { 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) { foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
if (!$first) { if ( ! $first) {
$this->_selectJoinSql .= ' AND '; $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; $first = false;
} }
} else { } else {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']); $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) { foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
if (!$first) { if ( ! $first) {
$this->_selectJoinSql .= ' AND '; $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; $first = false;
} }
} }
@ -1001,19 +1048,32 @@ class BasicEntityPersister
return $this->_selectColumnListSql; 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') protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
{ {
$columnList = ''; $columnList = '';
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias"; $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $columnAlias;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true); $this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
} }
} }
return $columnList; return $columnList;
} }
@ -1035,23 +1095,22 @@ class BasicEntityPersister
} }
$joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform); $joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform);
$joinSql = ''; $joinSql = '';
foreach ($joinClauses as $joinTableColumn => $sourceColumn) { foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND '; if ($joinSql != '') $joinSql .= ' AND ';
if ($this->_class->containsForeignIdentifier && !isset($this->_class->fieldNames[$sourceColumn])) { if ($this->_class->containsForeignIdentifier && ! isset($this->_class->fieldNames[$sourceColumn])) {
$quotedColumn = $sourceColumn; // join columns cannot be quoted $quotedColumn = $sourceColumn; // join columns cannot be quoted
} else { } else {
$quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform); $quotedColumn = $this->_class->getQuotedColumnName($this->_class->fieldNames[$sourceColumn], $this->_platform);
} }
$joinSql .= $this->_getSQLTableAlias($this->_class->name) . $joinSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $quotedColumn . ' = '
'.' . $quotedColumn . ' = ' . $joinTableName . '.' . $joinTableColumn;
. $joinTableName . '.' . $joinTableColumn;
} }
return " INNER JOIN $joinTableName ON $joinSql"; return ' INNER JOIN ' . $joinTableName . ' ON ' . $joinSql;
} }
/** /**
@ -1064,21 +1123,23 @@ class BasicEntityPersister
if ($this->_insertSql === null) { if ($this->_insertSql === null) {
$insertSql = ''; $insertSql = '';
$columns = $this->_getInsertColumnList(); $columns = $this->_getInsertColumnList();
if (empty($columns)) { if (empty($columns)) {
$insertSql = $this->_platform->getEmptyIdentityInsertSQL( $insertSql = $this->_platform->getEmptyIdentityInsertSQL(
$this->_class->getQuotedTableName($this->_platform), $this->_class->getQuotedTableName($this->_platform),
$this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform) $this->_class->getQuotedColumnName($this->_class->identifier[0], $this->_platform)
); );
} else { } else {
$columns = array_unique($columns); $columns = array_unique($columns);
$values = array_fill(0, count($columns), '?'); $values = array_fill(0, count($columns), '?');
$insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform) $insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform)
. ' (' . implode(', ', $columns) . ') ' . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')';
. 'VALUES (' . implode(', ', $values) . ')';
} }
$this->_insertSql = $insertSql; $this->_insertSql = $insertSql;
} }
return $this->_insertSql; return $this->_insertSql;
} }
@ -1093,19 +1154,21 @@ class BasicEntityPersister
protected function _getInsertColumnList() protected function _getInsertColumnList()
{ {
$columns = array(); $columns = array();
foreach ($this->_class->reflFields as $name => $field) { foreach ($this->_class->reflFields as $name => $field) {
if ($this->_class->isVersioned && $this->_class->versionField == $name) { if ($this->_class->isVersioned && $this->_class->versionField == $name) {
continue; continue;
} }
if (isset($this->_class->associationMappings[$name])) { if (isset($this->_class->associationMappings[$name])) {
$assoc = $this->_class->associationMappings[$name]; $assoc = $this->_class->associationMappings[$name];
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) {
$columns[] = $sourceCol; $columns[] = $sourceCol;
} }
} }
} else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || } else if ($this->_class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $this->_class->identifier[0] != $name) {
$this->_class->identifier[0] != $name) {
$columns[] = $this->_class->getQuotedColumnName($name, $this->_platform); $columns[] = $this->_class->getQuotedColumnName($name, $this->_platform);
} }
} }
@ -1124,11 +1187,13 @@ class BasicEntityPersister
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{ {
$columnName = $class->columnNames[$field]; $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++); $columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field); $this->_rsm->addFieldResult($alias, $columnAlias, $field);
return "$sql AS $columnAlias"; return $sql . ' AS ' . $columnAlias;
} }
/** /**
@ -1141,15 +1206,17 @@ class BasicEntityPersister
protected function _getSQLTableAlias($className, $assocName = '') protected function _getSQLTableAlias($className, $assocName = '')
{ {
if ($assocName) { if ($assocName) {
$className .= '#'.$assocName; $className .= '#' . $assocName;
} }
if (isset($this->_sqlTableAliases[$className])) { if (isset($this->_sqlTableAliases[$className])) {
return $this->_sqlTableAliases[$className]; return $this->_sqlTableAliases[$className];
} }
$tableAlias = 't' . $this->_sqlAliasCounter++; $tableAlias = 't' . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$className] = $tableAlias; $this->_sqlTableAliases[$className] = $tableAlias;
return $tableAlias; return $tableAlias;
} }
@ -1173,7 +1240,9 @@ class BasicEntityPersister
$sql = 'SELECT 1 ' $sql = 'SELECT 1 '
. $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode) . $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode)
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql;
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
} }
@ -1185,7 +1254,7 @@ class BasicEntityPersister
protected function getLockTablesSql() protected function getLockTablesSql()
{ {
return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class->name); . $this->_getSQLTableAlias($this->_class->name);
} }
/** /**
@ -1202,28 +1271,26 @@ class BasicEntityPersister
protected function _getSelectConditionSQL(array $criteria, $assoc = null) protected function _getSelectConditionSQL(array $criteria, $assoc = null)
{ {
$conditionSql = ''; $conditionSql = '';
foreach ($criteria as $field => $value) { foreach ($criteria as $field => $value) {
$conditionSql .= $conditionSql ? ' AND ' : ''; $conditionSql .= $conditionSql ? ' AND ' : '';
if (isset($this->_class->columnNames[$field])) { if (isset($this->_class->columnNames[$field])) {
if (isset($this->_class->fieldMappings[$field]['inherited'])) { $className = (isset($this->_class->fieldMappings[$field]['inherited']))
$conditionSql .= $this->_getSQLTableAlias($this->_class->fieldMappings[$field]['inherited']) . '.'; ? $this->_class->fieldMappings[$field]['inherited']
} else { : $this->_class->name;
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
} $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform);
$conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
} else if (isset($this->_class->associationMappings[$field])) { } else if (isset($this->_class->associationMappings[$field])) {
if (!$this->_class->associationMappings[$field]['isOwningSide']) { if ( ! $this->_class->associationMappings[$field]['isOwningSide']) {
throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field); 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) . '.';
}
$conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name']; $className = (isset($this->_class->associationMappings[$field]['inherited']))
? $this->_class->associationMappings[$field]['inherited']
: $this->_class->name;
$conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
} else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { } else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
// very careless developers could potentially open up this normally hidden api for userland attacks, // 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. // therefore checking for spaces and function calls which are not allowed.
@ -1251,6 +1318,7 @@ class BasicEntityPersister
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{ {
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt); return $this->loadArrayFromStatement($assoc, $stmt);
} }
@ -1266,7 +1334,8 @@ class BasicEntityPersister
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{ {
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
$this->loadCollectionFromStatement($assoc, $stmt, $coll);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
} }
/** /**
@ -1284,18 +1353,18 @@ class BasicEntityPersister
$owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']]; $owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']];
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
$tableAlias = isset($owningAssoc['inherited']) ? $tableAlias = $this->_getSQLTableAlias(isset($owningAssoc['inherited']) ? $owningAssoc['inherited'] : $this->_class->name);
$this->_getSQLTableAlias($owningAssoc['inherited'])
: $this->_getSQLTableAlias($this->_class->name);
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if ($sourceClass->containsForeignIdentifier) { if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn); $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]];
} }
$criteria[$tableAlias . "." . $targetKeyColumn] = $value; $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
} else { } else {
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
@ -1424,23 +1493,25 @@ class BasicEntityPersister
public function exists($entity, array $extraConditions = array()) public function exists($entity, array $extraConditions = array())
{ {
$criteria = $this->_class->getIdentifierValues($entity); $criteria = $this->_class->getIdentifierValues($entity);
if ($extraConditions) { if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions); $criteria = array_merge($criteria, $extraConditions);
} }
$alias = $this->_getSQLTableAlias($this->_class->name); $alias = $this->_getSQLTableAlias($this->_class->name);
$sql = 'SELECT 1'
$sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $alias
. ' ' . $alias . ' WHERE ' . $this->_getSelectConditionSQL($criteria);
. ' WHERE ' . $this->_getSelectConditionSQL($criteria);
$filterSql = $this->generateFilterConditionSQL($this->_class, $alias); $filterSql = $this->generateFilterConditionSQL($this->_class, $alias);
if('' !== $filterSql) { if('' !== $filterSql) {
$sql .= ' AND ' . $filterSql; $sql .= ' AND ' . $filterSql;
} }
return (bool) $this->_conn->fetchColumn($sql, array_values($criteria)); list($params, $types) = $this->expandParameters($criteria);
return (bool) $this->_conn->fetchColumn($sql, $params);
} }
private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias)

View File

@ -22,6 +22,7 @@ namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException, use Doctrine\ORM\ORMException,
Doctrine\ORM\Mapping\ClassMetadata, Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\DBAL\LockMode, Doctrine\DBAL\LockMode,
Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\ResultSetMapping; Doctrine\ORM\Query\ResultSetMapping;
/** /**
@ -55,11 +56,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
*/ */
protected function _getDiscriminatorColumnTableName() protected function _getDiscriminatorColumnTableName()
{ {
if ($this->_class->name == $this->_class->rootEntityName) { $class = ($this->_class->name !== $this->_class->rootEntityName)
return $this->_class->table['name']; ? $this->_em->getClassMetadata($this->_class->rootEntityName)
} else { : $this->_class;
return $this->_em->getClassMetadata($this->_class->rootEntityName)->table['name'];
} return $class->getTableName();
} }
/** /**
@ -72,8 +73,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
{ {
if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) { if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) {
$definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited']; $definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited'];
return $this->_em->getClassMetadata($definingClassName); return $this->_em->getClassMetadata($definingClassName);
} }
return $this->_class; return $this->_class;
} }
@ -86,19 +89,24 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
*/ */
public function getOwningTable($fieldName) public function getOwningTable($fieldName)
{ {
if (!isset($this->_owningTableMap[$fieldName])) { if (isset($this->_owningTableMap[$fieldName])) {
if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { return $this->_owningTableMap[$fieldName];
$cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); }
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
$cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']); if (isset($this->_class->associationMappings[$fieldName]['inherited'])) {
} else { $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']);
$cm = $this->_class; } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
} $cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']);
$this->_owningTableMap[$fieldName] = $cm->table['name']; } else {
$this->_quotedTableMap[$cm->table['name']] = $cm->getQuotedTableName($this->_platform); $cm = $this->_class;
} }
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(); $isPostInsertId = $idGen->isPostInsertGenerator();
// Prepare statement for the root table // Prepare statement for the root table
$rootClass = $this->_class->name == $this->_class->rootEntityName ? $rootClass = ($this->_class->name !== $this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName) : $this->_class;
$this->_class : $this->_em->getClassMetadata($this->_class->rootEntityName);
$rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name); $rootPersister = $this->_em->getUnitOfWork()->getEntityPersister($rootClass->name);
$rootTableName = $rootClass->table['name']; $rootTableName = $rootClass->getTableName();
$rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL()); $rootTableStmt = $this->_conn->prepare($rootPersister->_getInsertSQL());
// Prepare statements for sub tables. // Prepare statements for sub tables.
$subTableStmts = array(); $subTableStmts = array();
if ($rootClass !== $this->_class) { 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) { foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName); $parentClass = $this->_em->getClassMetadata($parentClassName);
$parentTableName = $parentClass->table['name']; $parentTableName = $parentClass->getTableName();
if ($parentClass !== $rootClass) { if ($parentClass !== $rootClass) {
$parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName);
$subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL()); $subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL());
@ -143,9 +153,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Execute insert on root table // Execute insert on root table
$paramIndex = 1; $paramIndex = 1;
foreach ($insertData[$rootTableName] as $columnName => $value) { foreach ($insertData[$rootTableName] as $columnName => $value) {
$rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); $rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
} }
$rootTableStmt->execute(); $rootTableStmt->execute();
if ($isPostInsertId) { if ($isPostInsertId) {
@ -160,17 +172,23 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($subTableStmts as $tableName => $stmt) { foreach ($subTableStmts as $tableName => $stmt) {
$data = isset($insertData[$tableName]) ? $insertData[$tableName] : array(); $data = isset($insertData[$tableName]) ? $insertData[$tableName] : array();
$paramIndex = 1; $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) { foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); $stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
} }
$stmt->execute(); $stmt->execute();
} }
} }
$rootTableStmt->closeCursor(); $rootTableStmt->closeCursor();
foreach ($subTableStmts as $stmt) { foreach ($subTableStmts as $stmt) {
$stmt->closeCursor(); $stmt->closeCursor();
} }
@ -191,15 +209,18 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
{ {
$updateData = $this->_prepareUpdateData($entity); $updateData = $this->_prepareUpdateData($entity);
if ($isVersioned = $this->_class->isVersioned) { if (($isVersioned = $this->_class->isVersioned) != false) {
$versionedClass = $this->_getVersionedClassMetadata(); $versionedClass = $this->_getVersionedClassMetadata();
$versionedTable = $versionedClass->table['name']; $versionedTable = $versionedClass->getTableName();
} }
if ($updateData) { if ($updateData) {
foreach ($updateData as $tableName => $data) { 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 // Make sure the table with the version column is updated even if no columns on that
// table were affected. // table were affected.
if ($isVersioned && ! isset($updateData[$versionedTable])) { if ($isVersioned && ! isset($updateData[$versionedTable])) {
@ -224,13 +245,17 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// If the database platform supports FKs, just // If the database platform supports FKs, just
// delete the row from the root table. Cascades do the rest. // delete the row from the root table. Cascades do the rest.
if ($this->_platform->supportsForeignKeyConstraints()) { if ($this->_platform->supportsForeignKeyConstraints()) {
$this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName) $this->_conn->delete(
->getQuotedTableName($this->_platform), $id); $this->_em->getClassMetadata($this->_class->rootEntityName)->getQuotedTableName($this->_platform), $id
);
} else { } else {
// Delete from all tables individually, starting from this class' table up to the root table. // Delete from all tables individually, starting from this class' table up to the root table.
$this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id);
foreach ($this->_class->parentClasses as $parentClass) { 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 // Add regular columns
$columnList = ''; $columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) { foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($fieldName,
isset($mapping['inherited']) ? $columnList .= $this->_getSelectColumnSQL(
$this->_em->getClassMetadata($mapping['inherited']) : $fieldName,
$this->_class); isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class
);
} }
// Add foreign key columns // Add foreign key columns
foreach ($this->_class->associationMappings as $assoc2) { foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) { if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) {
$tableAlias = isset($assoc2['inherited']) ? $tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias;
$this->_getSQLTableAlias($assoc2['inherited'])
: $baseTableAlias;
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
$columnList .= $this->getSelectJoinColumnSQL(
$tableAlias,
$srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name 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). // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#_processSQLResult).
$discrColumn = $this->_class->discriminatorColumn['name']; $discrColumn = $this->_class->discriminatorColumn['name'];
if ($this->_class->rootEntityName == $this->_class->name) { $tableAlias = ($this->_class->rootEntityName == $this->_class->name) ? $baseTableAlias : $this->_getSQLTableAlias($this->_class->rootEntityName);
$columnList .= ", $baseTableAlias.$discrColumn"; $columnList .= ', ' . $tableAlias . '.' . $discrColumn;
} else {
$columnList .= ', ' . $this->_getSQLTableAlias($this->_class->rootEntityName)
. ".$discrColumn";
}
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName); $this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
} }
// INNER JOIN parent tables // INNER JOIN parent tables
$joinSql = ''; $joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) { foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName); $parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName); $tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($idColumns as $idColumn) { foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND '; if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
} }
@ -309,19 +338,20 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
if ($this->_selectColumnListSql === null) { if ($this->_selectColumnListSql === null) {
// Add subclass columns // Add subclass columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) { foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited'])) { if (isset($mapping['inherited'])) continue;
continue;
}
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
} }
// Add join columns (foreign keys) // Add join columns (foreign keys)
foreach ($subClass->associationMappings as $assoc2) { foreach ($subClass->associationMappings as $assoc2) {
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) {
&& ! isset($assoc2['inherited'])) {
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
$columnList .= $this->getSelectJoinColumnSQL(
$tableAlias,
$srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name
); );
} }
@ -332,14 +362,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add LEFT JOIN // Add LEFT JOIN
$joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($idColumns as $idColumn) { foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND '; if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
} }
$joinSql .= $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ? $joinSql .= ($assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY) ? $this->_getSelectManyToManyJoinSQL($assoc) : '';
$this->_getSelectManyToManyJoinSQL($assoc) : '';
$conditionSql = $this->_getSelectConditionSQL($criteria, $assoc); $conditionSql = $this->_getSelectConditionSQL($criteria, $assoc);
@ -351,6 +382,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
} }
$lockSql = ''; $lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) { if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql(); $lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
@ -376,13 +408,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// INNER JOIN parent tables // INNER JOIN parent tables
$joinSql = ''; $joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) { foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName); $parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName); $tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($idColumns as $idColumn) { foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND '; if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
} }

View File

@ -40,9 +40,10 @@ class ManyToManyPersister extends AbstractCollectionPersister
protected function _getDeleteRowSQL(PersistentCollection $coll) protected function _getDeleteRowSQL(PersistentCollection $coll)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$joinTable = $mapping['joinTable']; $class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$columns = $mapping['joinTableColumns'];
return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; 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) protected function _getInsertRowSQL(PersistentCollection $coll)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$joinTable = $mapping['joinTable'];
$columns = $mapping['joinTableColumns']; $columns = $mapping['joinTableColumns'];
return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')' $class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
return 'INSERT INTO ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
} }
/** /**
@ -103,8 +105,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/ */
private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element) private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element)
{ {
$params = array(); $params = array();
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$isComposite = count($mapping['joinTableColumns']) > 2; $isComposite = count($mapping['joinTableColumns']) > 2;
$identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner()); $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner());
@ -149,14 +151,19 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/ */
protected function _getDeleteSQL(PersistentCollection $coll) protected function _getDeleteSQL(PersistentCollection $coll)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
$joinTable = $mapping['joinTable']; $joinTable = $mapping['joinTable'];
$whereClause = ''; $whereClause = '';
foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) { foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) {
if ($whereClause !== '') $whereClause .= ' AND '; 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(); $params = array();
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner()); $identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
if (count($mapping['relationToSourceKeyColumns']) > 1) { if (count($mapping['relationToSourceKeyColumns']) > 1) {
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner())); $sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) { foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]]; $params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
} }
@ -188,21 +197,20 @@ class ManyToManyPersister extends AbstractCollectionPersister
*/ */
public function count(PersistentCollection $coll) public function count(PersistentCollection $coll)
{ {
$params = array(); $params = array();
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
$class = $this->_em->getClassMetadata($mapping['sourceEntity']); $class = $this->_em->getClassMetadata($mapping['sourceEntity']);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner()); $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
if ($mapping['isOwningSide']) { if ($mapping['isOwningSide']) {
$joinTable = $mapping['joinTable'];
$joinColumns = $mapping['relationToSourceKeyColumns']; $joinColumns = $mapping['relationToSourceKeyColumns'];
} else { } else {
$mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']]; $mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']];
$joinTable = $mapping['joinTable'];
$joinColumns = $mapping['relationToTargetKeyColumns']; $joinColumns = $mapping['relationToTargetKeyColumns'];
} }
$whereClause = ''; $whereClause = '';
foreach ($mapping['joinTableColumns'] as $joinTableColumn) { foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($joinColumns[$joinTableColumn])) { if (isset($joinColumns[$joinTableColumn])) {
if ($whereClause !== '') { if ($whereClause !== '') {
@ -210,18 +218,16 @@ class ManyToManyPersister extends AbstractCollectionPersister
} }
$whereClause .= "t.$joinTableColumn = ?"; $whereClause .= "t.$joinTableColumn = ?";
if ($class->containsForeignIdentifier) { $params[] = ($class->containsForeignIdentifier)
$params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]; ? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]
} else { : $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
$params[] = $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
}
} }
} }
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
$sql = 'SELECT count(*)' $sql = 'SELECT COUNT(*)'
. ' FROM ' . $joinTable['name'] . ' t' . ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) . ' t'
. $joinTargetEntitySQL . $joinTargetEntitySQL
. ' WHERE ' . $whereClause . ' WHERE ' . $whereClause
. $filterSql; . $filterSql;
@ -238,9 +244,8 @@ class ManyToManyPersister extends AbstractCollectionPersister
public function slice(PersistentCollection $coll, $offset, $length = null) public function slice(PersistentCollection $coll, $offset, $length = null)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
return $this->_em->getUnitOfWork()
->getEntityPersister($mapping['targetEntity']) return $this->_em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
} }
/** /**
@ -259,7 +264,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
$params = array(); $params = array();
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
if (!$mapping['isOwningSide']) { if ( ! $mapping['isOwningSide']) {
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']); $sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
$sourceId = $uow->getEntityIdentifier($element); $sourceId = $uow->getEntityIdentifier($element);
@ -272,44 +277,42 @@ class ManyToManyPersister extends AbstractCollectionPersister
$sourceId = $uow->getEntityIdentifier($coll->getOwner()); $sourceId = $uow->getEntityIdentifier($coll->getOwner());
$targetId = $uow->getEntityIdentifier($element); $targetId = $uow->getEntityIdentifier($element);
} }
$joinTable = $mapping['joinTable'];
$whereClause = ''; $whereClause = '';
foreach ($mapping['joinTableColumns'] as $joinTableColumn) { foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
if ($whereClause !== '') { if ($whereClause !== '') {
$whereClause .= ' AND '; $whereClause .= ' AND ';
} }
$whereClause .= "$joinTableColumn = ?";
$whereClause .= $joinTableColumn . ' = ?';
if ($targetClass->containsForeignIdentifier) { $params[] = ($targetClass->containsForeignIdentifier)
$params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; ? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
} else { : $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
$params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
}
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) { } else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
if ($whereClause !== '') { if ($whereClause !== '') {
$whereClause .= ' AND '; $whereClause .= ' AND ';
} }
$whereClause .= "$joinTableColumn = ?";
$whereClause .= $joinTableColumn . ' = ?';
if ($sourceClass->containsForeignIdentifier) { $params[] = ($sourceClass->containsForeignIdentifier)
$params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; ? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
} else { : $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
$params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
}
} }
} }
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
$sql = 'SELECT 1' $sql = 'SELECT 1'
. ' FROM ' . $joinTable['name'] . ' t' . ' FROM ' . $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) . ' t'
. $joinTargetEntitySQL . $joinTargetEntitySQL
. ' WHERE ' . $whereClause . ' WHERE ' . $whereClause
. $filterSql; . $filterSql;
return (bool)$this->_conn->fetchColumn($sql, $params); return (bool) $this->_conn->fetchColumn($sql, $params);
} }
public function getFilterSql($mapping) public function getFilterSql($mapping)

View File

@ -35,38 +35,49 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
/** {@inheritdoc} */ /** {@inheritdoc} */
protected function _getDiscriminatorColumnTableName() protected function _getDiscriminatorColumnTableName()
{ {
return $this->_class->table['name']; return $this->_class->getTableName();
} }
/** {@inheritdoc} */ /** {@inheritdoc} */
protected function _getSelectColumnListSQL() protected function _getSelectColumnListSQL()
{ {
if ($this->_selectColumnListSql !== null) {
return $this->_selectColumnListSql;
}
$columnList = parent::_getSelectColumnListSQL(); $columnList = parent::_getSelectColumnListSQL();
// Append discriminator column // Append discriminator column
$discrColumn = $this->_class->discriminatorColumn['name']; $discrColumn = $this->_class->discriminatorColumn['name'];
$columnList .= ", $discrColumn"; $columnList .= ', ' . $discrColumn;
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
$tableAlias = $this->_getSQLTableAlias($rootClass->name); $tableAlias = $this->_getSQLTableAlias($rootClass->name);
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName); $this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
// Append subclass columns // Append subclass columns
foreach ($this->_class->subClasses as $subClassName) { foreach ($this->_class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName); $subClass = $this->_em->getClassMetadata($subClassName);
// Regular columns // Regular columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) { foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited'])) { if ( ! isset($mapping['inherited'])) {
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
} }
} }
// Foreign key columns // Foreign key columns
foreach ($subClass->associationMappings as $assoc) { foreach ($subClass->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
$columnList .= $this->getSelectJoinColumnSQL(
$tableAlias,
$srcColumn,
isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name 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} */ /** {@inheritdoc} */
protected function _getInsertColumnList() protected function _getInsertColumnList()
{ {
$columns = parent::_getInsertColumnList(); $columns = parent::_getInsertColumnList();
// Add discriminator column to the INSERT SQL // Add discriminator column to the INSERT SQL
$columns[] = $this->_class->discriminatorColumn['name']; $columns[] = $this->_class->discriminatorColumn['name'];
@ -100,18 +113,21 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
// Append discriminator condition // Append discriminator condition
if ($conditionSql) $conditionSql .= ' AND '; if ($conditionSql) $conditionSql .= ' AND ';
$values = array(); $values = array();
if ($this->_class->discriminatorValue !== null) { // discriminators can be 0 if ($this->_class->discriminatorValue !== null) { // discriminators can be 0
$values[] = $this->_conn->quote($this->_class->discriminatorValue); $values[] = $this->_conn->quote($this->_class->discriminatorValue);
} }
$discrValues = array_flip($this->_class->discriminatorMap); $discrValues = array_flip($this->_class->discriminatorMap);
foreach ($this->_class->subClasses as $subclassName) { foreach ($this->_class->subClasses as $subclassName) {
$values[] = $this->_conn->quote($discrValues[$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) . ')'; . ' IN (' . implode(', ', $values) . ')';
return $conditionSql; return $conditionSql;
} }

View File

@ -207,8 +207,9 @@ final class Query extends AbstractQuery
// Check query cache. // Check query cache.
if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) { if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) {
$hash = $this->_getQueryCacheId(); $hash = $this->_getQueryCacheId();
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash); $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
if ($cached === false) { if ($cached === false) {
// Cache miss. // Cache miss.
$parser = new Parser($this); $parser = new Parser($this);
@ -222,6 +223,7 @@ final class Query extends AbstractQuery
$parser = new Parser($this); $parser = new Parser($this);
$this->_parserResult = $parser->parse(); $this->_parserResult = $parser->parse();
} }
$this->_state = self::STATE_CLEAN; $this->_state = self::STATE_CLEAN;
return $this->_parserResult; return $this->_parserResult;
@ -241,50 +243,85 @@ final class Query extends AbstractQuery
throw QueryException::invalidParameterNumber(); throw QueryException::invalidParameterNumber();
} }
$sqlParams = $types = array(); list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
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;
}
}
}
if ($sqlParams) {
ksort($sqlParams);
$sqlParams = array_values($sqlParams);
}
if ($this->_resultSetMapping === null) { if ($this->_resultSetMapping === null) {
$this->_resultSetMapping = $this->_parserResult->getResultSetMapping(); $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
} }
return $executor->execute($this->_em->getConnection(), $sqlParams, $types); 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];
}
}
$sqlPositions = $paramMappings[$key];
$value = array_values($this->processParameterValue($value));
$countValue = count($value);
for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
$sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
}
}
if ($sqlParams) {
ksort($sqlParams);
$sqlParams = array_values($sqlParams);
}
return array($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);
}
}
/** /**
* Defines a cache driver to be used for caching queries. * Defines a cache driver to be used for caching queries.

View 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);
}
}

View File

@ -53,8 +53,8 @@ class SizeFunction extends FunctionNode
if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) { if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) {
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']); $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->table['name']); $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias); $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
$sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE '; $sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE ';
@ -77,7 +77,7 @@ class SizeFunction extends FunctionNode
// SQL table aliases // SQL table aliases
$joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']); $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->table['name'], $dqlAlias); $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
// join to target table // join to target table
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE '; $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE ';

View File

@ -23,7 +23,7 @@ namespace Doctrine\ORM\Query\AST;
/** /**
* SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression |
* (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable] * (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable]
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
@ -37,11 +37,13 @@ class SelectExpression extends Node
{ {
public $expression; public $expression;
public $fieldIdentificationVariable; public $fieldIdentificationVariable;
public $hiddenAliasResultVariable;
public function __construct($expression, $fieldIdentificationVariable) public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false)
{ {
$this->expression = $expression; $this->expression = $expression;
$this->fieldIdentificationVariable = $fieldIdentificationVariable; $this->fieldIdentificationVariable = $fieldIdentificationVariable;
$this->hiddenAliasResultVariable = $hiddenAliasResultVariable;
} }
public function dispatch($sqlWalker) public function dispatch($sqlWalker)

View File

@ -63,7 +63,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
$idColumnList = implode(', ', $idColumnNames); $idColumnList = implode(', ', $idColumnNames);
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() // 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 . ')' $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
. ' SELECT t0.' . implode(', t0.', $idColumnNames); . ' SELECT t0.' . implode(', t0.', $idColumnNames);
@ -98,7 +98,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
} }
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
$this->_dropTempTableSql = 'DROP TABLE ' . $tempTable; $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
} }
/** /**

View File

@ -64,7 +64,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$idColumnList = implode(', ', $idColumnNames); $idColumnList = implode(', ', $idColumnNames);
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() // 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 . ')' $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
. ' SELECT t0.' . implode(', t0.', $idColumnNames); . ' 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. //FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage.
if ($newValue instanceof AST\InputParameter) { if ($newValue instanceof AST\InputParameter) {
$paramKey = $newValue->name; $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; ++$this->_numParametersInUpdateClause;
} }
@ -136,7 +137,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
$this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; . $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); $conn->executeUpdate($this->_createTempTableSql);
// Insert identifiers. Parameters from the update clause are cut off. // 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 // Execute UPDATE statements
for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) { 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 // Drop temporary table

View File

@ -34,27 +34,55 @@ namespace Doctrine\ORM\Query\Expr;
*/ */
class From class From
{ {
/**
* @var string
*/
private $_from; private $_from;
/**
* @var string
*/
private $_alias; 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->_from = $from;
$this->_alias = $alias; $this->_alias = $alias;
$this->_indexBy = $indexBy;
} }
/**
* @return string
*/
public function getFrom() public function getFrom()
{ {
return $this->_from; return $this->_from;
} }
/**
* @return string
*/
public function getAlias() public function getAlias()
{ {
return $this->_alias; return $this->_alias;
} }
/**
* @return string
*/
public function __toString() public function __toString()
{ {
return $this->_from . ' ' . $this->_alias; return $this->_from . ' ' . $this->_alias .
($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : '');
} }
} }

View File

@ -76,39 +76,40 @@ class Lexer extends \Doctrine\Common\Lexer
const T_FROM = 122; const T_FROM = 122;
const T_GROUP = 123; const T_GROUP = 123;
const T_HAVING = 124; const T_HAVING = 124;
const T_IN = 125; const T_HIDDEN = 125;
const T_INDEX = 126; const T_IN = 126;
const T_INNER = 127; const T_INDEX = 127;
const T_INSTANCE = 128; const T_INNER = 128;
const T_IS = 129; const T_INSTANCE = 129;
const T_JOIN = 130; const T_IS = 130;
const T_LEADING = 131; const T_JOIN = 131;
const T_LEFT = 132; const T_LEADING = 132;
const T_LIKE = 133; const T_LEFT = 133;
const T_MAX = 134; const T_LIKE = 134;
const T_MEMBER = 135; const T_MAX = 135;
const T_MIN = 136; const T_MEMBER = 136;
const T_NOT = 137; const T_MIN = 137;
const T_NULL = 138; const T_NOT = 138;
const T_NULLIF = 139; const T_NULL = 139;
const T_OF = 140; const T_NULLIF = 140;
const T_OR = 141; const T_OF = 141;
const T_ORDER = 142; const T_OR = 142;
const T_OUTER = 143; const T_ORDER = 143;
const T_SELECT = 144; const T_OUTER = 144;
const T_SET = 145; const T_SELECT = 145;
const T_SIZE = 146; const T_SET = 146;
const T_SOME = 147; const T_SIZE = 147;
const T_SUM = 148; const T_SOME = 148;
const T_THEN = 149; const T_SUM = 149;
const T_TRAILING = 150; const T_THEN = 150;
const T_TRUE = 151; const T_TRAILING = 151;
const T_UPDATE = 152; const T_TRUE = 152;
const T_WHEN = 153; const T_UPDATE = 153;
const T_WHERE = 154; const T_WHEN = 154;
const T_WITH = 155; const T_WHERE = 155;
const T_PARTIAL = 156; const T_WITH = 156;
const T_MOD = 157; const T_PARTIAL = 157;
const T_MOD = 158;
/** /**
* Creates a new query scanner object. * Creates a new query scanner object.

View File

@ -40,7 +40,8 @@ class Parser
'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction', 'substring' => 'Doctrine\ORM\Query\AST\Functions\SubstringFunction',
'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction', 'trim' => 'Doctrine\ORM\Query\AST\Functions\TrimFunction',
'lower' => 'Doctrine\ORM\Query\AST\Functions\LowerFunction', '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. */ /** READ-ONLY: Maps BUILT-IN numeric function names to AST class names. */
@ -1695,6 +1696,7 @@ class Parser
return $this->CoalesceExpression(); return $this->CoalesceExpression();
case Lexer::T_CASE: case Lexer::T_CASE:
$this->_lexer->resetPeek();
$peek = $this->_lexer->peek(); $peek = $this->_lexer->peek();
return ($peek['type'] === Lexer::T_WHEN) return ($peek['type'] === Lexer::T_WHEN)
@ -1829,7 +1831,7 @@ class Parser
/** /**
* SelectExpression ::= * SelectExpression ::=
* IdentificationVariable | StateFieldPathExpression | * IdentificationVariable | StateFieldPathExpression |
* (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable] * (AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
* *
* @return Doctrine\ORM\Query\AST\SelectExpression * @return Doctrine\ORM\Query\AST\SelectExpression
*/ */
@ -1837,6 +1839,7 @@ class Parser
{ {
$expression = null; $expression = null;
$identVariable = null; $identVariable = null;
$hiddenAliasResultVariable = false;
$fieldAliasIdentificationVariable = null; $fieldAliasIdentificationVariable = null;
$peek = $this->_lexer->glimpse(); $peek = $this->_lexer->glimpse();
@ -1898,6 +1901,12 @@ class Parser
if ($this->_lexer->isNextToken(Lexer::T_AS)) { if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS); $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)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead; $token = $this->_lexer->lookahead;
@ -1912,10 +1921,12 @@ class Parser
} }
} }
$expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable); $expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable, $hiddenAliasResultVariable);
if (!$supportsAlias) {
if ( ! $supportsAlias) {
$this->_identVariableExpressions[$identVariable] = $expr; $this->_identVariableExpressions[$identVariable] = $expr;
} }
return $expr; return $expr;
} }
@ -2381,7 +2392,7 @@ class Parser
/** /**
* ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")" * ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
* | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings * | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
* | FunctionsReturningDatetime | IdentificationVariable | CaseExpression * | FunctionsReturningDatetime | IdentificationVariable | ResultVariable | CaseExpression
*/ */
public function ArithmeticPrimary() public function ArithmeticPrimary()
{ {
@ -2410,7 +2421,11 @@ class Parser
if ($peek['value'] == '.') { if ($peek['value'] == '.') {
return $this->SingleValuedPathExpression(); return $this->SingleValuedPathExpression();
} }
if (isset($this->_queryComponents[$this->_lexer->lookahead['value']]['resultVariable'])) {
return $this->ResultVariable();
}
return $this->StateFieldPathExpression(); return $this->StateFieldPathExpression();
case Lexer::T_INPUT_PARAMETER: case Lexer::T_INPUT_PARAMETER:

View File

@ -244,24 +244,23 @@ class SqlWalker implements TreeWalker
{ {
$sql = ''; $sql = '';
$baseTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); $baseTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
// INNER JOIN parent class tables // INNER JOIN parent class tables
foreach ($class->parentClasses as $parentClassName) { foreach ($class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($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. // 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 .= isset($this->_queryComponents[$dqlAlias]['relation']) ? ' LEFT ' : ' INNER ';
$sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) $sql .= 'JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
. ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($class->identifier as $idField) { foreach ($class->identifier as $idField) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform); $columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName $sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
. ' = '
. $tableAlias . '.' . $columnName;
} }
} }
@ -269,17 +268,15 @@ class SqlWalker implements TreeWalker
if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { if ( ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
foreach ($class->subClasses as $subClassName) { foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($subClassName); $subClass = $this->_em->getClassMetadata($subClassName);
$tableAlias = $this->getSQLTableAlias($subClass->table['name'], $dqlAlias); $tableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias);
$sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) $sql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
. ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($class->identifier as $idField) { foreach ($class->identifier as $idField) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$columnName = $class->getQuotedColumnName($idField, $this->_platform); $columnName = $class->getQuotedColumnName($idField, $this->_platform);
$sql .= $baseTableAlias . '.' . $columnName $sql .= $baseTableAlias . '.' . $columnName . ' = ' . $tableAlias . '.' . $columnName;
. ' = '
. $tableAlias . '.' . $columnName;
} }
} }
} }
@ -290,24 +287,26 @@ class SqlWalker implements TreeWalker
private function _generateOrderedCollectionOrderByItems() private function _generateOrderedCollectionOrderByItems()
{ {
$sql = ''; $sql = '';
foreach ($this->_selectedClasses AS $dqlAlias => $class) { foreach ($this->_selectedClasses AS $dqlAlias => $class) {
$qComp = $this->_queryComponents[$dqlAlias]; $qComp = $this->_queryComponents[$dqlAlias];
if (isset($qComp['relation']['orderBy'])) { if (isset($qComp['relation']['orderBy'])) {
foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) { foreach ($qComp['relation']['orderBy'] AS $fieldName => $orientation) {
if ($qComp['metadata']->isInheritanceTypeJoined()) { $tableName = ($qComp['metadata']->isInheritanceTypeJoined())
$tableName = $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName); ? $this->_em->getUnitOfWork()->getEntityPersister($class->name)->getOwningTable($fieldName)
} else { : $qComp['metadata']->getTableName();
$tableName = $qComp['metadata']->table['name'];
}
if ($sql != '') { if ($sql != '') {
$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; return $sql;
} }
@ -328,6 +327,7 @@ class SqlWalker implements TreeWalker
if ($class->isInheritanceTypeSingleTable()) { if ($class->isInheritanceTypeSingleTable()) {
$conn = $this->_em->getConnection(); $conn = $this->_em->getConnection();
$values = array(); $values = array();
if ($class->discriminatorValue !== null) { // discrimnators can be 0 if ($class->discriminatorValue !== null) { // discrimnators can be 0
$values[] = $conn->quote($class->discriminatorValue); $values[] = $conn->quote($class->discriminatorValue);
} }
@ -342,7 +342,7 @@ class SqlWalker implements TreeWalker
} }
$sql .= ($sql != '' ? ' AND ' : '') $sql .= ($sql != '' ? ' AND ' : '')
. (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.' : '') . (($this->_useSqlTableAliases) ? $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.' : '')
. $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')';
} }
} }
@ -373,7 +373,7 @@ class SqlWalker implements TreeWalker
*/ */
public function walkSelectStatement(AST\SelectStatement $AST) public function walkSelectStatement(AST\SelectStatement $AST)
{ {
$sql = $this->walkSelectClause($AST->selectClause); $sql = $this->walkSelectClause($AST->selectClause);
$sql .= $this->walkFromClause($AST->fromClause); $sql .= $this->walkFromClause($AST->fromClause);
$sql .= $this->walkWhereClause($AST->whereClause); $sql .= $this->walkWhereClause($AST->whereClause);
$sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : ''; $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : '';
@ -382,19 +382,18 @@ class SqlWalker implements TreeWalker
if (($orderByClause = $AST->orderByClause) !== null) { if (($orderByClause = $AST->orderByClause) !== null) {
$sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : ''; $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
} else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') { } else if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') {
$sql .= ' ORDER BY '.$orderBySql; $sql .= ' ORDER BY ' . $orderBySql;
} }
$sql = $this->_platform->modifyLimitQuery( $sql = $this->_platform->modifyLimitQuery(
$sql, $this->_query->getMaxResults(), $this->_query->getFirstResult() $sql, $this->_query->getMaxResults(), $this->_query->getFirstResult()
); );
if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) { if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) {
if ($lockMode == LockMode::PESSIMISTIC_READ) { if ($lockMode == LockMode::PESSIMISTIC_READ) {
$sql .= " " . $this->_platform->getReadLockSQL(); $sql .= ' ' . $this->_platform->getReadLockSQL();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
$sql .= " " . $this->_platform->getWriteLockSQL(); $sql .= ' ' . $this->_platform->getWriteLockSQL();
} else if ($lockMode == LockMode::OPTIMISTIC) { } else if ($lockMode == LockMode::OPTIMISTIC) {
foreach ($this->_selectedClasses AS $class) { foreach ($this->_selectedClasses AS $class) {
if ( ! $class->isVersioned) { if ( ! $class->isVersioned) {
@ -416,10 +415,8 @@ class SqlWalker implements TreeWalker
public function walkUpdateStatement(AST\UpdateStatement $AST) public function walkUpdateStatement(AST\UpdateStatement $AST)
{ {
$this->_useSqlTableAliases = false; $this->_useSqlTableAliases = false;
$sql = $this->walkUpdateClause($AST->updateClause);
$sql .= $this->walkWhereClause($AST->whereClause); return $this->walkUpdateClause($AST->updateClause) . $this->walkWhereClause($AST->whereClause);
return $sql;
} }
/** /**
@ -431,10 +428,8 @@ class SqlWalker implements TreeWalker
public function walkDeleteStatement(AST\DeleteStatement $AST) public function walkDeleteStatement(AST\DeleteStatement $AST)
{ {
$this->_useSqlTableAliases = false; $this->_useSqlTableAliases = false;
$sql = $this->walkDeleteClause($AST->deleteClause);
$sql .= $this->walkWhereClause($AST->whereClause); return $this->walkDeleteClause($AST->deleteClause) . $this->walkWhereClause($AST->whereClause);
return $sql;
} }
@ -456,7 +451,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']); $class = $this->_em->getClassMetadata($class->fieldMappings[$fieldName]['inherited']);
} }
return $this->getSQLTableAlias($class->table['name'], $identificationVariable); return $this->getSQLTableAlias($class->getTableName(), $identificationVariable);
} }
/** /**
@ -505,7 +500,7 @@ class SqlWalker implements TreeWalker
} }
if ($this->_useSqlTableAliases) { if ($this->_useSqlTableAliases) {
$sql .= $this->getSQLTableAlias($class->table['name'], $dqlAlias) . '.'; $sql .= $this->getSQLTableAlias($class->getTableName(), $dqlAlias) . '.';
} }
$sql .= reset($assoc['targetToSourceKeyColumns']); $sql .= reset($assoc['targetToSourceKeyColumns']);
@ -550,7 +545,7 @@ class SqlWalker implements TreeWalker
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
// Add discriminator columns to SQL // Add discriminator columns to SQL
$rootClass = $this->_em->getClassMetadata($class->rootEntityName); $rootClass = $this->_em->getClassMetadata($class->rootEntityName);
$tblAlias = $this->getSQLTableAlias($rootClass->table['name'], $dqlAlias); $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias);
$discrColumn = $rootClass->discriminatorColumn; $discrColumn = $rootClass->discriminatorColumn;
$columnAlias = $this->getSQLColumnAlias($discrColumn['name']); $columnAlias = $this->getSQLColumnAlias($discrColumn['name']);
@ -567,9 +562,9 @@ class SqlWalker implements TreeWalker
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
if (isset($assoc['inherited'])) { if (isset($assoc['inherited'])) {
$owningClass = $this->_em->getClassMetadata($assoc['inherited']); $owningClass = $this->_em->getClassMetadata($assoc['inherited']);
$sqlTableAlias = $this->getSQLTableAlias($owningClass->table['name'], $dqlAlias); $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias);
} else { } else {
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); $sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
} }
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
@ -586,7 +581,8 @@ class SqlWalker implements TreeWalker
} else { } else {
// Add foreign key columns to SQL, if necessary // Add foreign key columns to SQL, if necessary
if ($addMetaColumns) { if ($addMetaColumns) {
$sqlTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); $sqlTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
foreach ($class->associationMappings as $assoc) { foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
@ -628,7 +624,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform) . ' ' $sql .= $class->getQuotedTableName($this->_platform) . ' '
. $this->getSQLTableAlias($class->table['name'], $dqlAlias); . $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
if ($class->isInheritanceTypeJoined()) { if ($class->isInheritanceTypeJoined()) {
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
@ -739,13 +735,15 @@ class SqlWalker implements TreeWalker
} }
$joinAssocPathExpr = $join->joinAssociationPathExpression; $joinAssocPathExpr = $join->joinAssociationPathExpression;
$joinedDqlAlias = $join->aliasIdentificationVariable; $joinedDqlAlias = $join->aliasIdentificationVariable;
$relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
$targetClass = $this->_em->getClassMetadata($relation['targetEntity']); $relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']); $targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
$targetTableName = $targetClass->getQuotedTableName($this->_platform); $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 // Ensure we got the owning side, since it has all mapping info
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation; $assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
@ -833,8 +831,7 @@ class SqlWalker implements TreeWalker
} }
// Join target table // Join target table
$sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
? ' LEFT JOIN ' : ' INNER JOIN ';
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
$first = true; $first = true;
@ -1006,8 +1003,9 @@ class SqlWalker implements TreeWalker
*/ */
public function walkSelectExpression($selectExpression) public function walkSelectExpression($selectExpression)
{ {
$sql = ''; $sql = '';
$expr = $selectExpression->expression; $expr = $selectExpression->expression;
$hidden = $selectExpression->hiddenAliasResultVariable;
if ($expr instanceof AST\PathExpression) { if ($expr instanceof AST\PathExpression) {
if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) { if ($expr->type !== AST\PathExpression::TYPE_STATE_FIELD) {
@ -1037,7 +1035,10 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->getSQLColumnAlias($columnName); $columnAlias = $this->getSQLColumnAlias($columnName);
$sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias; $sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\AggregateExpression) { } else if ($expr instanceof AST\AggregateExpression) {
if ( ! $selectExpression->fieldIdentificationVariable) { if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++; $resultAlias = $this->_scalarResultCounter++;
@ -1050,7 +1051,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\Subselect) { } else if ($expr instanceof AST\Subselect) {
if ( ! $selectExpression->fieldIdentificationVariable) { if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++; $resultAlias = $this->_scalarResultCounter++;
@ -1063,7 +1067,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ($expr instanceof AST\Functions\FunctionNode) { } else if ($expr instanceof AST\Functions\FunctionNode) {
if ( ! $selectExpression->fieldIdentificationVariable) { if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++; $resultAlias = $this->_scalarResultCounter++;
@ -1076,7 +1083,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ( } else if (
$expr instanceof AST\SimpleArithmeticExpression || $expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm || $expr instanceof AST\ArithmeticTerm ||
@ -1101,7 +1111,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else if ( } else if (
$expr instanceof AST\NullIfExpression || $expr instanceof AST\NullIfExpression ||
$expr instanceof AST\CoalesceExpression || $expr instanceof AST\CoalesceExpression ||
@ -1121,7 +1134,10 @@ class SqlWalker implements TreeWalker
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
if ( ! $hidden) {
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
}
} else { } else {
// IdentificationVariable or PartialObjectExpression // IdentificationVariable or PartialObjectExpression
if ($expr instanceof AST\PartialObjectExpression) { if ($expr instanceof AST\PartialObjectExpression) {
@ -1146,11 +1162,9 @@ class SqlWalker implements TreeWalker
continue; continue;
} }
if (isset($mapping['inherited'])) { $tableName = (isset($mapping['inherited']))
$tableName = $this->_em->getClassMetadata($mapping['inherited'])->table['name']; ? $this->_em->getClassMetadata($mapping['inherited'])->getTableName()
} else { : $class->getTableName();
$tableName = $class->table['name'];
}
if ($beginning) $beginning = false; else $sql .= ', '; if ($beginning) $beginning = false; else $sql .= ', ';
@ -1170,7 +1184,7 @@ class SqlWalker implements TreeWalker
if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) { if ($class->isInheritanceTypeSingleTable() || ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD)) {
foreach ($class->subClasses as $subClassName) { foreach ($class->subClasses as $subClassName) {
$subClass = $this->_em->getClassMetadata($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) { foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) { if (isset($mapping['inherited']) || $partialFieldSet && !in_array($fieldName, $partialFieldSet)) {
continue; continue;
@ -1263,7 +1277,7 @@ class SqlWalker implements TreeWalker
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
$sql .= $class->getQuotedTableName($this->_platform) . ' ' $sql .= $class->getQuotedTableName($this->_platform) . ' '
. $this->getSQLTableAlias($class->table['name'], $dqlAlias); . $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
$this->_rootAliases[] = $dqlAlias; $this->_rootAliases[] = $dqlAlias;
@ -1348,6 +1362,22 @@ class SqlWalker implements TreeWalker
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias; $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; $this->_scalarResultAliasMap[$alias] = $columnAlias;
} else { } else {
// IdentificationVariable // IdentificationVariable
@ -1637,15 +1667,13 @@ class SqlWalker implements TreeWalker
$assoc = $class->associationMappings[$fieldName]; $assoc = $class->associationMappings[$fieldName];
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) { if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']); $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
$sql .= $targetClass->getQuotedTableName($this->_platform) $sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE ';
. ' ' . $targetTableAlias . ' WHERE ';
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']]; $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
$first = true; $first = true;
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) { foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
@ -1673,15 +1701,13 @@ class SqlWalker implements TreeWalker
$joinTable = $owningAssoc['joinTable']; $joinTable = $owningAssoc['joinTable'];
// SQL table aliases // SQL table aliases
$joinTableAlias = $this->getSQLTableAlias($joinTable['name']); $joinTableAlias = $this->getSQLTableAlias($joinTable['name']);
$targetTableAlias = $this->getSQLTableAlias($targetClass->table['name']); $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias); $sourceTableAlias = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
// join to target table // join to target table
$sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $this->_platform) . ' ' . $joinTableAlias
. ' ' . $joinTableAlias . ' INNER JOIN ' . ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON ';
. $targetClass->getQuotedTableName($this->_platform)
. ' ' . $targetTableAlias . ' ON ';
// join conditions // join conditions
$joinColumns = $assoc['isOwningSide'] $joinColumns = $assoc['isOwningSide']
@ -1693,25 +1719,19 @@ class SqlWalker implements TreeWalker
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = ' $sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
. $targetTableAlias . '.' . $targetClass->getQuotedColumnName( . $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $this->_platform);
$targetClass->fieldNames[$joinColumn['referencedColumnName']],
$this->_platform);
} }
$sql .= ' WHERE '; $sql .= ' WHERE ';
$joinColumns = $assoc['isOwningSide'] $joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
? $joinTable['joinColumns']
: $joinTable['inverseJoinColumns'];
$first = true; $first = true;
foreach ($joinColumns as $joinColumn) { foreach ($joinColumns as $joinColumn) {
if ($first) $first = false; else $sql .= ' AND '; if ($first) $first = false; else $sql .= ' AND ';
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = ' $sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
. $sourceTableAlias . '.' . $class->getQuotedColumnName( . $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $this->_platform);
$class->fieldNames[$joinColumn['referencedColumnName']],
$this->_platform);
} }
$sql .= ' AND '; $sql .= ' AND ';
@ -1808,7 +1828,7 @@ class SqlWalker implements TreeWalker
} }
if ($this->_useSqlTableAliases) { 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 '); $sql .= $class->discriminatorColumn['name'] . ($instanceOfExpr->not ? ' NOT IN ' : ' IN ');
@ -2019,6 +2039,12 @@ class SqlWalker implements TreeWalker
public function walkArithmeticTerm($term) public function walkArithmeticTerm($term)
{ {
if (is_string($term)) { if (is_string($term)) {
if (isset($this->_queryComponents[$term])) {
$columnName = $this->_queryComponents[$term]['token']['value'];
return $this->_scalarResultAliasMap[$columnName];
}
return $term; return $term;
} }

View File

@ -595,11 +595,12 @@ class QueryBuilder
* *
* @param string $from The class name. * @param string $from The class name.
* @param string $alias The alias of the class. * @param string $alias The alias of the class.
* @param string $indexBy The index for the from.
* @return QueryBuilder This QueryBuilder instance. * @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);
} }
/** /**

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * 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, use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption, 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. * 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 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com> * @author Jonathan Wage <jonwage@gmail.com>
@ -47,9 +45,30 @@ class MetadataCommand extends Console\Command\Command
$this $this
->setName('orm:clear-cache:metadata') ->setName('orm:clear-cache:metadata')
->setDescription('Clear all metadata cache of the various cache drivers.') ->setDescription('Clear all metadata cache of the various cache drivers.')
->setDefinition(array()) ->setDefinition(array(
->setHelp(<<<EOT new InputOption(
Clear all metadata cache of the various cache drivers. '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 EOT
); );
} }
@ -65,21 +84,21 @@ EOT
if ( ! $cacheDriver) { if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Metadata cache driver is configured on given EntityManager.'); 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."); 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); $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) { if (true === $input->getOption('flush')) {
$output->write(' - ' . $cacheId . PHP_EOL); $result = $cacheDriver->flushAll();
} $message = ($result) ? 'Successfully flushed cache entries.' : $message;
} else {
$output->write('No entries to be deleted.' . PHP_EOL);
} }
$output->write($message . PHP_EOL);
} }
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * 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, use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption, 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. * 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 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com> * @author Jonathan Wage <jonwage@gmail.com>
@ -47,9 +45,30 @@ class QueryCommand extends Console\Command\Command
$this $this
->setName('orm:clear-cache:query') ->setName('orm:clear-cache:query')
->setDescription('Clear all query cache of the various cache drivers.') ->setDescription('Clear all query cache of the various cache drivers.')
->setDefinition(array()) ->setDefinition(array(
->setHelp(<<<EOT new InputOption(
Clear all query cache of the various cache drivers. '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 EOT
); );
} }
@ -65,21 +84,21 @@ EOT
if ( ! $cacheDriver) { if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Query cache driver is configured on given EntityManager.'); 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."); 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); $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) { if (true === $input->getOption('flush')) {
$output->write(' - ' . $cacheId . PHP_EOL); $result = $cacheDriver->flushAll();
} $message = ($result) ? 'Successfully flushed cache entries.' : $message;
} else {
$output->write('No entries to be deleted.' . PHP_EOL);
} }
$output->write($message . PHP_EOL);
} }
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * 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, use Symfony\Component\Console\Input\InputArgument,
Symfony\Component\Console\Input\InputOption, 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. * 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 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com> * @author Jonathan Wage <jonwage@gmail.com>
@ -46,28 +44,31 @@ class ResultCommand extends Console\Command\Command
{ {
$this $this
->setName('orm:clear-cache:result') ->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( ->setDefinition(array(
new InputOption( new InputOption(
'id', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'flush', null, InputOption::VALUE_NONE,
'ID(s) of the cache entry to delete (accepts * wildcards).', array() 'If defined, cache entries will be flushed instead of deleted/invalidated.'
), )
new InputOption( ));
'regex', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that match the given regular expression(s).', array() $fullName = $this->getName();
), $this->setHelp(<<<EOT
new InputOption( The <info>$fullName</info> command is meant to clear the result cache of associated Entity Manager.
'prefix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, It is possible to invalidate all cache entries at once - called delete -, or flushes the cache provider
'Delete cache entries that have the given prefix(es).', array() instance completely.
),
new InputOption( The execution type differ on how you execute the command.
'suffix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, If you want to invalidate the entries (and not delete from cache instance), this command would do the work:
'Delete cache entries that have the given suffix(es).', array()
), <info>$fullName</info>
))
->setHelp(<<<EOT Alternatively, if you want to flush the cache provider using this command:
Clear result cache of the various cache drivers.
If none of the options are defined, all cache entries will be removed. <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 EOT
); );
} }
@ -83,86 +84,21 @@ EOT
if ( ! $cacheDriver) { if ( ! $cacheDriver) {
throw new \InvalidArgumentException('No Result cache driver is configured on given EntityManager.'); 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."); throw new \LogicException("Cannot clear APC Cache from Console, its shared in the Webserver memory and not accessible from the CLI.");
} }
$outputed = false; $output->write('Clearing ALL Result cache entries' . PHP_EOL);
// Removing based on --id $result = $cacheDriver->deleteAll();
if (($ids = $input->getOption('id')) !== null && $ids) { $message = ($result) ? 'Successfully deleted cache entries.' : 'No cache entries were deleted.';
foreach ($ids as $id) {
$output->write($outputed ? PHP_EOL : ''); if (true === $input->getOption('flush')) {
$output->write(sprintf('Clearing Result cache entries that match the id "<info>%s</info>"', $id) . PHP_EOL); $result = $cacheDriver->flushAll();
$message = ($result) ? 'Successfully flushed cache entries.' : $message;
$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());
$outputed = true;
} }
$output->write($message . PHP_EOL);
} }
}
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);
}
}
}

View File

@ -137,7 +137,7 @@ EOT
if ( ! file_exists($destPath)) { if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException( 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)) { } else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(

View File

@ -123,7 +123,7 @@ EOT
if ( ! file_exists($destPath)) { if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException( 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)) { } else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(

View File

@ -87,7 +87,7 @@ EOT
if ( ! file_exists($destPath)) { if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException( 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)) { } else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(

View File

@ -79,7 +79,7 @@ EOT
if ( ! file_exists($destPath)) { if ( ! file_exists($destPath)) {
throw new \InvalidArgumentException( 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)) { } else if ( ! is_writable($destPath)) {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(

View File

@ -46,7 +46,7 @@ class ValidateSchemaCommand extends Console\Command\Command
{ {
$this $this
->setName('orm:validate-schema') ->setName('orm:validate-schema')
->setDescription('Validate that the mapping files.') ->setDescription('Validate the mapping files.')
->setHelp(<<<EOT ->setHelp(<<<EOT
'Validate that the mapping files are correct and in sync with the database.' 'Validate that the mapping files are correct and in sync with the database.'
EOT EOT

View File

@ -101,6 +101,7 @@ class ConvertDoctrine1Schema
{ {
if (isset($model['tableName']) && $model['tableName']) { if (isset($model['tableName']) && $model['tableName']) {
$e = explode('.', $model['tableName']); $e = explode('.', $model['tableName']);
if (count($e) > 1) { if (count($e) > 1) {
$metadata->table['schema'] = $e[0]; $metadata->table['schema'] = $e[0];
$metadata->table['name'] = $e[1]; $metadata->table['name'] = $e[1];

View File

@ -150,7 +150,7 @@ public function <methodName>()
public function __construct() 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\\'; $this->_annotationsPrefix = 'ORM\\';
} }
} }
@ -399,14 +399,17 @@ public function <methodName>()
} }
$collections = array(); $collections = array();
foreach ($metadata->associationMappings AS $mapping) { foreach ($metadata->associationMappings AS $mapping) {
if ($mapping['type'] & ClassMetadataInfo::TO_MANY) { if ($mapping['type'] & ClassMetadataInfo::TO_MANY) {
$collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();'; $collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
} }
} }
if ($collections) { if ($collections) {
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate)); return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n", $collections), self::$_constructorMethodTemplate));
} }
return ''; return '';
} }
@ -570,7 +573,12 @@ public function <methodName>()
private function _generateTableAnnotation($metadata) private function _generateTableAnnotation($metadata)
{ {
$table = array(); $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'] . '"'; $table[] = 'name="' . $metadata->table['name'] . '"';
} }

View File

@ -49,11 +49,13 @@ class YamlExporter extends AbstractExporter
public function exportClassMetadata(ClassMetadataInfo $metadata) public function exportClassMetadata(ClassMetadataInfo $metadata)
{ {
$array = array(); $array = array();
if ($metadata->isMappedSuperclass) { if ($metadata->isMappedSuperclass) {
$array['type'] = 'mappedSuperclass'; $array['type'] = 'mappedSuperclass';
} else { } else {
$array['type'] = 'entity'; $array['type'] = 'entity';
} }
$array['table'] = $metadata->table['name']; $array['table'] = $metadata->table['name'];
if (isset($metadata->table['schema'])) { if (isset($metadata->table['schema'])) {
@ -81,6 +83,10 @@ class YamlExporter extends AbstractExporter
$array['indexes'] = $metadata->table['indexes']; $array['indexes'] = $metadata->table['indexes'];
} }
if ($metadata->customRepositoryClassName) {
$array['repositoryClass'] = $metadata->customRepositoryClassName;
}
if (isset($metadata->table['uniqueConstraints'])) { if (isset($metadata->table['uniqueConstraints'])) {
$array['uniqueConstraints'] = $metadata->table['uniqueConstraints']; $array['uniqueConstraints'] = $metadata->table['uniqueConstraints'];
} }

View File

@ -97,7 +97,7 @@ class Setup
require_once $directory . "/Doctrine/Common/ClassLoader.php"; require_once $directory . "/Doctrine/Common/ClassLoader.php";
} }
$loader = new ClassLoader("Doctrine"); $loader = new ClassLoader("Doctrine", $directory);
$loader->register(); $loader->register();
$loader = new ClassLoader("Symfony\Component", $directory . "/Doctrine"); $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) 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)); $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
return $config; return $config;
} }
/** /**
* Create a configuration with an annotation metadata driver. * Create a configuration with a xml metadata driver.
* *
* @param array $paths * @param array $paths
* @param boolean $isDevMode * @param boolean $isDevMode
@ -131,13 +131,13 @@ class Setup
*/ */
static public function createXMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) 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)); $config->setMetadataDriverImpl(new XmlDriver($paths));
return $config; return $config;
} }
/** /**
* Create a configuration with an annotation metadata driver. * Create a configuration with a yaml metadata driver.
* *
* @param array $paths * @param array $paths
* @param boolean $isDevMode * @param boolean $isDevMode
@ -147,7 +147,7 @@ class Setup
*/ */
static public function createYAMLMetadataConfiguration(array $paths, $isDevMode = false, $proxyDir = null, Cache $cache = null) 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)); $config->setMetadataDriverImpl(new YamlDriver($paths));
return $config; return $config;
} }
@ -162,6 +162,7 @@ class Setup
*/ */
static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null) static public function createConfiguration($isDevMode = false, $proxyDir = null, Cache $cache = null)
{ {
$proxyDir = $proxyDir ?: sys_get_temp_dir();
if ($isDevMode === false && $cache === null) { if ($isDevMode === false && $cache === null) {
if (extension_loaded('apc')) { if (extension_loaded('apc')) {
$cache = new \Doctrine\Common\Cache\ApcCache; $cache = new \Doctrine\Common\Cache\ApcCache;
@ -175,16 +176,16 @@ class Setup
} else { } else {
$cache = new ArrayCache; $cache = new ArrayCache;
} }
$cache->setNamespace("dc2_"); // to avoid collisions
} else if ($cache === null) { } else if ($cache === null) {
$cache = new ArrayCache; $cache = new ArrayCache;
} }
$cache->setNamespace("dc2_" . md5($proxyDir) . "_"); // to avoid collisions
$config = new Configuration(); $config = new Configuration();
$config->setMetadataCacheImpl($cache); $config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache); $config->setQueryCacheImpl($cache);
$config->setResultCacheImpl($cache); $config->setResultCacheImpl($cache);
$config->setProxyDir( $proxyDir ?: sys_get_temp_dir() ); $config->setProxyDir( $proxyDir );
$config->setProxyNamespace('DoctrineProxies'); $config->setProxyNamespace('DoctrineProxies');
$config->setAutoGenerateProxyClasses($isDevMode); $config->setAutoGenerateProxyClasses($isDevMode);

View File

@ -834,6 +834,8 @@ class UnitOfWork implements PropertyChangedListener
// See if there are any new classes in the changeset, that are not in the // See if there are any new classes in the changeset, that are not in the
// commit order graph yet (dont have a node). // 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(); $newNodes = array();
foreach ($entityChangeSet as $oid => $entity) { foreach ($entityChangeSet as $oid => $entity) {
$className = get_class($entity); $className = get_class($entity);
@ -845,7 +847,7 @@ class UnitOfWork implements PropertyChangedListener
} }
// Calculate dependencies for new nodes // Calculate dependencies for new nodes
foreach ($newNodes as $class) { while ($class = array_pop($newNodes)) {
foreach ($class->associationMappings as $assoc) { foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']); $targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
@ -860,6 +862,7 @@ class UnitOfWork implements PropertyChangedListener
$targetSubClass = $this->em->getClassMetadata($subClassName); $targetSubClass = $this->em->getClassMetadata($subClassName);
if ( ! $calc->hasClass($subClassName)) { if ( ! $calc->hasClass($subClassName)) {
$calc->addClass($targetSubClass); $calc->addClass($targetSubClass);
$newNodes[] = $targetSubClass;
} }
$calc->addDependency($targetSubClass, $class); $calc->addDependency($targetSubClass, $class);
} }
@ -1102,6 +1105,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 { } else {
return self::STATE_DETACHED; return self::STATE_DETACHED;
} }
@ -1368,6 +1387,10 @@ class UnitOfWork implements PropertyChangedListener
if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) { if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
$managedCopy = $entity; $managedCopy = $entity;
} else { } else {
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__load();
}
// Try to look the entity up in the identity map. // Try to look the entity up in the identity map.
$id = $class->getIdentifierValues($entity); $id = $class->getIdentifierValues($entity);
@ -1406,7 +1429,7 @@ class UnitOfWork implements PropertyChangedListener
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity); $entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
// Throw exception if versions dont match. // Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) { if ($managedCopyVersion != $entityVersion) {
throw OptimisticLockException::lockFailedVersionMissmatch($entityVersion, $managedCopyVersion); throw OptimisticLockException::lockFailedVersionMissmatch($entity, $entityVersion, $managedCopyVersion);
} }
} }
@ -1742,7 +1765,7 @@ class UnitOfWork implements PropertyChangedListener
*/ */
public function lock($entity, $lockMode, $lockVersion = null) 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."); throw new InvalidArgumentException("Entity is not MANAGED.");
} }
@ -2164,9 +2187,11 @@ class UnitOfWork implements PropertyChangedListener
public function tryGetById($id, $rootClassName) public function tryGetById($id, $rootClassName)
{ {
$idHash = implode(' ', (array) $id); $idHash = implode(' ', (array) $id);
if (isset($this->identityMap[$rootClassName][$idHash])) { if (isset($this->identityMap[$rootClassName][$idHash])) {
return $this->identityMap[$rootClassName][$idHash]; return $this->identityMap[$rootClassName][$idHash];
} }
return false; return false;
} }

@ -1 +1 @@
Subproject commit 74a2c924cd08b30785877808b1fb519b4b2e60b1 Subproject commit ef431a14852d7e8f2d0ea789487509ab266e5ce2

@ -1 +1 @@
Subproject commit be3790059cc43b674a55548eb42d5d25846ea6a9 Subproject commit f91395b6f469b5076f52fefd64574c443b076485

View 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;
}
}

View File

@ -31,7 +31,7 @@ class CmsUser
*/ */
public $name; 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; public $phonenumbers;
/** /**
@ -42,6 +42,11 @@ class CmsUser
* @OneToOne(targetEntity="CmsAddress", mappedBy="user", cascade={"persist"}, orphanRemoval=true) * @OneToOne(targetEntity="CmsAddress", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
*/ */
public $address; public $address;
/**
* @OneToOne(targetEntity="CmsEmail", inversedBy="user", cascade={"persist"}, orphanRemoval=true)
* @JoinColumn(referencedColumnName="id", nullable=true)
*/
public $email;
/** /**
* @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"}) * @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"})
* @JoinTable(name="cms_users_groups", * @JoinTable(name="cms_users_groups",
@ -119,4 +124,16 @@ class CmsUser
$address->setUser($this); $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);
}
}
}
} }

View File

@ -15,7 +15,7 @@ class CompanyCar
private $id; private $id;
/** /**
* @Column(type="string", length="50") * @Column(type="string", length=50)
*/ */
private $brand; private $brand;

View File

@ -17,6 +17,11 @@ class CompanyEmployee extends CompanyPerson
* @Column(type="string", length=255) * @Column(type="string", length=255)
*/ */
private $department; private $department;
/**
* @Column(type="datetime", nullable=true)
*/
private $startDate;
public function getSalary() { public function getSalary() {
return $this->salary; return $this->salary;
@ -33,4 +38,12 @@ class CompanyEmployee extends CompanyPerson
public function setDepartment($dep) { public function setDepartment($dep) {
$this->department = $dep; $this->department = $dep;
} }
public function getStartDate() {
return $this->startDate;
}
public function setStartDate($date) {
$this->startDate = $date;
}
} }

View File

@ -9,7 +9,7 @@ namespace Doctrine\Tests\Models\Company;
class CompanyManager extends CompanyEmployee class CompanyManager extends CompanyEmployee
{ {
/** /**
* @Column(type="string", length="250") * @Column(type="string", length=250)
*/ */
private $title; private $title;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
{
}

View 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',
));
}
}

View 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 DDC869CreditCardPayment extends DDC869Payment
{
/** @column(type="string") */
protected $creditCardNumber;
public static function loadMetadata(\Doctrine\ORM\Mapping\ClassMetadataInfo $metadata)
{
$metadata->mapField(array(
'fieldName' => 'creditCardNumber',
'type' => 'string',
));
}
}

View 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);
}
}

View File

@ -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;
}
}

View File

@ -22,7 +22,7 @@ class ECommerceProduct
private $id; private $id;
/** /**
* @Column(type="string", length=50, nullable="true") * @Column(type="string", length=50, nullable=true)
*/ */
private $name; private $name;

View 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');
}
}

View File

@ -150,15 +150,15 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->username = 'gblanco'; $user->username = 'gblanco';
$user->status = 'developer'; $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->_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->_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->persist($user);
$this->_em->flush(); $this->_em->flush();
@ -166,10 +166,10 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->remove($user); $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->_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)); $this->assertNull($this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $id));
} }

View File

@ -291,8 +291,21 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue(count($this->_em->createQuery( $this->assertTrue(count($this->_em->createQuery(
'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1') 'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1')
->getResult()) > 0); ->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();
} }
/** /**

View File

@ -59,7 +59,7 @@ class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
{ {
$this->assertSqlGeneration( $this->assertSqlGeneration(
'select u from Doctrine\Tests\Models\CMS\CmsUser u', '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( $this->assertSqlGeneration(
'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName', '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( $this->assertSqlGeneration(
'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name', '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"
); );
} }
} }

View File

@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\ORM\UnitOfWork; use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../TestInit.php'; require_once __DIR__ . '/../../TestInit.php';
@ -192,5 +193,26 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($this->_em->contains($user)); $this->assertFalse($this->_em->contains($user));
$this->assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user)); $this->assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user));
} }
/**
* @group DDC-1340
*/
public function testMergeArticleWrongVersion()
{
$article = new CmsArticle();
$article->topic = "test";
$article->text = "test";
$this->_em->persist($article);
$this->_em->flush();
$this->_em->detach($article);
$sql = "UPDATE cms_articles SET version = version+1 WHERE id = " . $article->id;
$this->_em->getConnection()->executeUpdate($sql);
$this->setExpectedException('Doctrine\ORM\OptimisticLockException', 'The optimistic lock failed, version 1 was expected, but is actually 2');
$this->_em->merge($article);
}
} }

View File

@ -433,5 +433,43 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertSame($usersAsc[0], $usersDesc[1]); $this->assertSame($usersAsc[0], $usersDesc[1]);
$this->assertSame($usersAsc[1], $usersDesc[0]); $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");
}
} }

View File

@ -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');
}
}

View File

@ -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');
}
}

View File

@ -14,10 +14,27 @@ require_once __DIR__ . '/../../TestInit.php';
*/ */
class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
{ {
/**
* @var \ReflectionProperty
*/
private $cacheDataReflection;
protected function setUp() { protected function setUp() {
$this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
$this->cacheDataReflection->setAccessible(true);
$this->useModelSet('cms'); $this->useModelSet('cms');
parent::setUp(); parent::setUp();
} }
/**
* @param ArrayCache $cache
* @return integer
*/
private function getCacheSize(ArrayCache $cache)
{
return sizeof($this->cacheDataReflection->getValue($cache));
}
public function testQueryCache_DependsOnHints() public function testQueryCache_DependsOnHints()
{ {
@ -27,12 +44,12 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setQueryCacheDriver($cache); $query->setQueryCacheDriver($cache);
$query->getResult(); $query->getResult();
$this->assertEquals(1, count($cache->getIds())); $this->assertEquals(1, $this->getCacheSize($cache));
$query->setHint('foo', 'bar'); $query->setHint('foo', 'bar');
$query->getResult(); $query->getResult();
$this->assertEquals(2, count($cache->getIds())); $this->assertEquals(2, $this->getCacheSize($cache));
return $query; return $query;
} }
@ -44,13 +61,13 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testQueryCache_DependsOnFirstResult($query) public function testQueryCache_DependsOnFirstResult($query)
{ {
$cache = $query->getQueryCacheDriver(); $cache = $query->getQueryCacheDriver();
$cacheCount = count($cache->getIds()); $cacheCount = $this->getCacheSize($cache);
$query->setFirstResult(10); $query->setFirstResult(10);
$query->setMaxResults(9999); $query->setMaxResults(9999);
$query->getResult(); $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) public function testQueryCache_DependsOnMaxResults($query)
{ {
$cache = $query->getQueryCacheDriver(); $cache = $query->getQueryCacheDriver();
$cacheCount = count($cache->getIds()); $cacheCount = $this->getCacheSize($cache);
$query->setMaxResults(10); $query->setMaxResults(10);
$query->getResult(); $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) public function testQueryCache_DependsOnHydrationMode($query)
{ {
$cache = $query->getQueryCacheDriver(); $cache = $query->getQueryCacheDriver();
$cacheCount = count($cache->getIds()); $cacheCount = $this->getCacheSize($cache);
$query->getArrayResult(); $query->getArrayResult();
$this->assertEquals($cacheCount + 1, count($cache->getIds())); $this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
} }
public function testQueryCache_NoHitSaveParserResult() 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'); $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)) $cache->expects($this->at(0))
->method('_doFetch') ->method('doFetch')
->with($this->isType('string')) ->with($this->isType('string'))
->will($this->returnValue(false)); ->will($this->returnValue(false));
$cache->expects($this->at(1)) $cache->expects($this->at(1))
->method('_doSave') ->method('doSave')
->with($this->isType('string'), $this->isInstanceOf('Doctrine\ORM\Query\ParserResult'), $this->equalTo(null)); ->with($this->isType('string'), $this->isInstanceOf('Doctrine\ORM\Query\ParserResult'), $this->equalTo(null));
$query->setQueryCacheDriver($cache); $query->setQueryCacheDriver($cache);
@ -117,13 +134,14 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
->method('getSqlExecutor') ->method('getSqlExecutor')
->will($this->returnValue($sqlExecMock)); ->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()) $cache->expects($this->once())
->method('_doFetch') ->method('doFetch')
->with($this->isType('string')) ->with($this->isType('string'))
->will($this->returnValue($parserResultMock)); ->will($this->returnValue($parserResultMock));
$cache->expects($this->never()) $cache->expects($this->never())
->method('_doSave'); ->method('doSave');
$query->setQueryCacheDriver($cache); $query->setQueryCacheDriver($cache);

View File

@ -501,4 +501,66 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals(0, count($users)); $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]);
}
} }

View File

@ -15,10 +15,26 @@ require_once __DIR__ . '/../../TestInit.php';
*/ */
class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
{ {
/**
* @var \ReflectionProperty
*/
private $cacheDataReflection;
protected function setUp() { protected function setUp() {
$this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
$this->cacheDataReflection->setAccessible(true);
$this->useModelSet('cms'); $this->useModelSet('cms');
parent::setUp(); parent::setUp();
} }
/**
* @param ArrayCache $cache
* @return integer
*/
private function getCacheSize(ArrayCache $cache)
{
return sizeof($this->cacheDataReflection->getValue($cache));
}
public function testResultCache() public function testResultCache()
{ {
@ -125,9 +141,9 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$cache = new ArrayCache(); $cache = new ArrayCache();
$query->setResultCacheDriver($cache)->useResultCache(true); $query->setResultCacheDriver($cache)->useResultCache(true);
$this->assertEquals(0, count($cache->getIds())); $this->assertEquals(0, $this->getCacheSize($cache));
$query->getResult(); $query->getResult();
$this->assertEquals(1, count($cache->getIds())); $this->assertEquals(1, $this->getCacheSize($cache));
return $query; return $query;
} }
@ -139,12 +155,12 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testResultCacheDependsOnQueryHints($query) public function testResultCacheDependsOnQueryHints($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = count($cache->getIds()); $cacheCount = $this->getCacheSize($cache);
$query->setHint('foo', 'bar'); $query->setHint('foo', 'bar');
$query->getResult(); $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) public function testResultCacheDependsOnParameters($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = count($cache->getIds()); $cacheCount = $this->getCacheSize($cache);
$query->setParameter(1, 50); $query->setParameter(1, 50);
$query->getResult(); $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) public function testResultCacheDependsOnHydrationMode($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = count($cache->getIds()); $cacheCount = $this->getCacheSize($cache);
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode()); $this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
$query->getArrayResult(); $query->getArrayResult();
$this->assertEquals($cacheCount + 1, count($cache->getIds())); $this->assertEquals($cacheCount + 1, $this->getCacheSize($cache));
} }
/** /**

View File

@ -253,24 +253,27 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testQueryCache_DependsOnFilters() public function testQueryCache_DependsOnFilters()
{ {
$cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
$cacheDataReflection->setAccessible(true);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$cache = new ArrayCache(); $cache = new ArrayCache();
$query->setQueryCacheDriver($cache); $query->setQueryCacheDriver($cache);
$query->getResult(); $query->getResult();
$this->assertEquals(1, count($cache->getIds())); $this->assertEquals(1, sizeof($cacheDataReflection->getValue($cache)));
$conf = $this->_em->getConfiguration(); $conf = $this->_em->getConfiguration();
$conf->addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter"); $conf->addFilter("locale", "\Doctrine\Tests\ORM\Functional\MyLocaleFilter");
$this->_em->getFilters()->enable("locale"); $this->_em->getFilters()->enable("locale");
$query->getResult(); $query->getResult();
$this->assertEquals(2, count($cache->getIds())); $this->assertEquals(2, sizeof($cacheDataReflection->getValue($cache)));
// Another time doesn't add another cache entry // Another time doesn't add another cache entry
$query->getResult(); $query->getResult();
$this->assertEquals(2, count($cache->getIds())); $this->assertEquals(2, sizeof($cacheDataReflection->getValue($cache)));
} }
public function testQueryGeneration_DependsOnFilters() public function testQueryGeneration_DependsOnFilters()

View 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;
}

View File

@ -0,0 +1,54 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1306
*/
class DDC1306Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
public function setUp()
{
$this->useModelSet('cms');
parent::setUp();
}
public function testIssue()
{
$phone = new CmsPhonenumber();
$phone->phonenumber = "1234";
// puts user and phone into commit order calculator
$this->_em->persist($phone);
$this->_em->flush();
$address = new \Doctrine\Tests\Models\CMS\CmsAddress();
$address->city = "bonn";
$address->country = "Germany";
$address->street = "somestreet!";
$address->zip = 12345;
$this->_em->persist($address);
$user = new CmsUser();
$user->username = "beberlei";
$user->name = "benjamin";
$user->status = "active";
$user->setAddress($address);
// puts user and address into commit order calculator, but does not calculate user dependencies new
$this->_em->persist($user);
$this->_em->flush();
$this->_em->remove($user->getAddress());
$this->_em->remove($user);
$this->_em->flush();
}
}

View File

@ -0,0 +1,214 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use DateTime;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1135
*/
class DDC1135Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135User'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1135Phone'),
));
$this->loadFixture();
} catch(\Exception $e) {
}
}
public function testDql()
{
$dql = 'SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result);
$dql = 'SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id';
$query = $this->_em->createQuery($dql);
$result = $query->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals(sizeof($result['foo@foo.com']->phones), 3);
$this->assertEquals(sizeof($result['bar@bar.com']->phones), 3);
$this->assertEquals(sizeof($result['foobar@foobar.com']->phones), 3);
$this->assertArrayHasKey(1, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(2, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(3, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(4, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(5, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(6, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(7, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());
}
public function testTicket()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.id');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey(1, $result);
$this->assertArrayHasKey(2, $result);
$this->assertArrayHasKey(3, $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.id', $dql);
}
public function testIndexByUnique()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u')->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals('SELECT u FROM ' . __NAMESPACE__ . '\DDC1135User u INDEX BY u.email', $dql);
}
public function testIndexWithJoin()
{
$builder = $this->_em->createQueryBuilder();
$builder->select('u','p')
->from(__NAMESPACE__ . '\DDC1135User', 'u', 'u.email')
->join('u.phones', 'p', null, null, 'p.id');
$dql = $builder->getQuery()->getDQL();
$result = $builder->getQuery()->getResult();
$this->assertEquals(sizeof($result), 3);
$this->assertArrayHasKey('foo@foo.com', $result);
$this->assertArrayHasKey('bar@bar.com', $result);
$this->assertArrayHasKey('foobar@foobar.com', $result);
$this->assertEquals(sizeof($result['foo@foo.com']->phones), 3);
$this->assertEquals(sizeof($result['bar@bar.com']->phones), 3);
$this->assertEquals(sizeof($result['foobar@foobar.com']->phones), 3);
$this->assertArrayHasKey(1, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(2, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(3, $result['foo@foo.com']->phones->toArray());
$this->assertArrayHasKey(4, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(5, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(6, $result['bar@bar.com']->phones->toArray());
$this->assertArrayHasKey(7, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(8, $result['foobar@foobar.com']->phones->toArray());
$this->assertArrayHasKey(9, $result['foobar@foobar.com']->phones->toArray());
$this->assertEquals('SELECT u, p FROM '.__NAMESPACE__ . '\DDC1135User u INDEX BY u.email INNER JOIN u.phones p INDEX BY p.id', $dql);
}
private function loadFixture()
{
$p1 = array('11 xxxx-xxxx','11 yyyy-yyyy','11 zzzz-zzzz');
$p2 = array('22 xxxx-xxxx','22 yyyy-yyyy','22 zzzz-zzzz');
$p3 = array('33 xxxx-xxxx','33 yyyy-yyyy','33 zzzz-zzzz');
$u1 = new DDC1135User("foo@foo.com", "Foo",$p1);
$u2 = new DDC1135User("bar@bar.com", "Bar",$p2);
$u3 = new DDC1135User("foobar@foobar.com", "Foo Bar",$p3);
$this->_em->persist($u1);
$this->_em->persist($u2);
$this->_em->persist($u3);
$this->_em->flush();
$this->_em->clear();
}
}
/**
* @Entity
*/
class DDC1135User
{
/**
* @Id @Column(type="integer")
* @GeneratedValue
*/
public $id;
/**
* @Column(type="string", unique=true)
*/
public $email;
/**
* @Column(type="string")
*/
public $name;
/**
* @OneToMany(targetEntity="DDC1135Phone", mappedBy="user", cascade={"persist", "remove"})
*/
public $phones;
public function __construct($email, $name, array $numbers = array())
{
$this->name = $name;
$this->email = $email;
$this->phones = new \Doctrine\Common\Collections\ArrayCollection();
foreach ($numbers as $number) {
$this->phones->add(new DDC1135Phone($this,$number));
}
}
}
/**
* @Entity
*/
class DDC1135Phone
{
/**
* @Id
* @Column(name="id", type="integer")
* @GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @Column(name="number", type="string", nullable = false)
*/
public $number;
/**
* @ManyToOne(targetEntity="DDC1135User", inversedBy="phones")
* @JoinColumn(name="user_id", referencedColumnName="id", nullable = false)
*/
public $user;
public function __construct($user, $number)
{
$this->user = $user;
$this->number = $number;
}
}

View File

@ -0,0 +1,127 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1392
*/
class DDC1392Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1392File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1392Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$file = new DDC1392File;
$picture = new DDC1392Picture;
$picture->setFile($file);
$em = $this->_em;
$em->persist($picture);
$em->flush();
$em->clear();
$fileId = $file->getFileId();
$pictureId = $picture->getPictureId();
$this->assertTrue($fileId > 0);
$picture = $em->find(__NAMESPACE__ . '\DDC1392Picture', $pictureId);
$this->assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), "Lazy Proxy should be marked MANAGED.");
$file = $picture->getFile();
// With this activated there will be no problem
//$file->__load();
$picture->setFile(null);
$em->clear();
$em->merge($file);
$em->flush();
$q = $this->_em->createQuery("SELECT COUNT(e) FROM " . __NAMESPACE__ . '\DDC1392File e');
$result = $q->getSingleScalarResult();
self::assertEquals(1, $result);
}
}
/**
* @Entity
*/
class DDC1392Picture
{
/**
* @Column(name="picture_id", type="integer")
* @Id @GeneratedValue
*/
private $pictureId;
/**
* @ManyToOne(targetEntity="DDC1392File", cascade={"persist", "remove"})
* @JoinColumn(name="file_id", referencedColumnName="file_id")
*/
private $file;
/**
* Get pictureId
*/
public function getPictureId()
{
return $this->pictureId;
}
/**
* Set file
*/
public function setFile($value = null)
{
$this->file = $value;
}
/**
* Get file
*/
public function getFile()
{
return $this->file;
}
}
/**
* @Entity
*/
class DDC1392File
{
/**
* @Column(name="file_id", type="integer")
* @Id
* @GeneratedValue(strategy="AUTO")
*/
public $fileId;
/**
* Get fileId
*/
public function getFileId()
{
return $this->fileId;
}
}

View File

@ -92,12 +92,12 @@ abstract class DDC258Super
class DDC258Class1 extends DDC258Super class DDC258Class1 extends DDC258Super
{ {
/** /**
* @Column(name="title", type="string", length="150") * @Column(name="title", type="string", length=150)
*/ */
public $title; public $title;
/** /**
* @Column(name="content", type="string", length="500") * @Column(name="content", type="string", length=500)
*/ */
public $description; public $description;
} }
@ -108,12 +108,12 @@ class DDC258Class1 extends DDC258Super
class DDC258Class2 extends DDC258Super class DDC258Class2 extends DDC258Super
{ {
/** /**
* @Column(name="title", type="string", length="150") * @Column(name="title", type="string", length=150)
*/ */
public $title; public $title;
/** /**
* @Column(name="content", type="string", length="500") * @Column(name="content", type="string", length=500)
*/ */
public $description; public $description;
@ -131,12 +131,12 @@ class DDC258Class2 extends DDC258Super
class DDC258Class3 extends DDC258Super class DDC258Class3 extends DDC258Super
{ {
/** /**
* @Column(name="title", type="string", length="150") * @Column(name="title", type="string", length=150)
*/ */
public $apples; public $apples;
/** /**
* @Column(name="content", type="string", length="500") * @Column(name="content", type="string", length=500)
*/ */
public $bananas; public $bananas;
} }

View File

@ -111,7 +111,7 @@ class DDC522Cart {
class DDC522ForeignKeyTest { class DDC522ForeignKeyTest {
/** @Id @Column(type="integer") @GeneratedValue */ /** @Id @Column(type="integer") @GeneratedValue */
public $id; public $id;
/** @Column(type="integer", name="cart_id", nullable="true") */ /** @Column(type="integer", name="cart_id", nullable=true) */
public $cartId; public $cartId;
/** /**
* @OneToOne(targetEntity="DDC522Cart") * @OneToOne(targetEntity="DDC522Cart")

View File

@ -49,14 +49,14 @@ class DDC698Role
protected $roleID; protected $roleID;
/** /**
* @Column(name="name", type="string", length="45") * @Column(name="name", type="string", length=45)
* *
* *
*/ */
protected $name; protected $name;
/** /**
* @Column(name="shortName", type="string", length="45") * @Column(name="shortName", type="string", length=45)
* *
* *
*/ */
@ -91,7 +91,7 @@ class DDC698Privilege
protected $privilegeID; protected $privilegeID;
/** /**
* @Column(name="name", type="string", length="45") * @Column(name="name", type="string", length=45)
* *
* *
*/ */

View File

@ -111,12 +111,12 @@ abstract class DDC837Super
class DDC837Class1 extends DDC837Super class DDC837Class1 extends DDC837Super
{ {
/** /**
* @Column(name="title", type="string", length="150") * @Column(name="title", type="string", length=150)
*/ */
public $title; public $title;
/** /**
* @Column(name="content", type="string", length="500") * @Column(name="content", type="string", length=500)
*/ */
public $description; public $description;
@ -132,12 +132,12 @@ class DDC837Class1 extends DDC837Super
class DDC837Class2 extends DDC837Super class DDC837Class2 extends DDC837Super
{ {
/** /**
* @Column(name="title", type="string", length="150") * @Column(name="title", type="string", length=150)
*/ */
public $title; public $title;
/** /**
* @Column(name="content", type="string", length="500") * @Column(name="content", type="string", length=500)
*/ */
public $description; public $description;
@ -160,12 +160,12 @@ class DDC837Class2 extends DDC837Super
class DDC837Class3 extends DDC837Super class DDC837Class3 extends DDC837Super
{ {
/** /**
* @Column(name="title", type="string", length="150") * @Column(name="title", type="string", length=150)
*/ */
public $apples; public $apples;
/** /**
* @Column(name="content", type="string", length="500") * @Column(name="content", type="string", length=500)
*/ */
public $bananas; public $bananas;
} }

View File

@ -124,7 +124,7 @@ class DDC992Role
*/ */
public $roleID; public $roleID;
/** /**
* @Column (name="name", type="string", length="45") * @Column (name="name", type="string", length=45)
*/ */
public $name; public $name;
/** /**

View File

@ -182,7 +182,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(isset($class->associationMappings['phonenumbers'])); $this->assertTrue(isset($class->associationMappings['phonenumbers']));
$this->assertFalse($class->associationMappings['phonenumbers']['isOwningSide']); $this->assertFalse($class->associationMappings['phonenumbers']['isOwningSide']);
$this->assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']); $this->assertTrue($class->associationMappings['phonenumbers']['isCascadePersist']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRemove']); $this->assertTrue($class->associationMappings['phonenumbers']['isCascadeRemove']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']); $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeRefresh']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']); $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeDetach']);
$this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']); $this->assertFalse($class->associationMappings['phonenumbers']['isCascadeMerge']);
@ -291,6 +291,42 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
$class->discriminatorColumn $class->discriminatorColumn
); );
} }
/**
* @group DDC-869
*/
public function testMappedSuperclassWithRepository()
{
$driver = $this->_loadDriver();
$em = $this->_getTestEntityManager();
$factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory();
$em->getConfiguration()->setMetadataDriverImpl($driver);
$factory->setEntityManager($em);
$class = $factory->getMetadataFor('Doctrine\Tests\Models\DDC869\DDC869CreditCardPayment');
$this->assertTrue(isset($class->fieldMappings['id']));
$this->assertTrue(isset($class->fieldMappings['value']));
$this->assertTrue(isset($class->fieldMappings['creditCardNumber']));
$this->assertEquals($class->customRepositoryClassName, "Doctrine\Tests\Models\DDC869\DDC869PaymentRepository");
$this->assertInstanceOf("Doctrine\Tests\Models\DDC869\DDC869PaymentRepository",
$em->getRepository("Doctrine\Tests\Models\DDC869\DDC869CreditCardPayment"));
$this->assertTrue($em->getRepository("Doctrine\Tests\Models\DDC869\DDC869ChequePayment")->isTrue());
$class = $factory->getMetadataFor('Doctrine\Tests\Models\DDC869\DDC869ChequePayment');
$this->assertTrue(isset($class->fieldMappings['id']));
$this->assertTrue(isset($class->fieldMappings['value']));
$this->assertTrue(isset($class->fieldMappings['serialNumber']));
$this->assertEquals($class->customRepositoryClassName, "Doctrine\Tests\Models\DDC869\DDC869PaymentRepository");
$this->assertInstanceOf("Doctrine\Tests\Models\DDC869\DDC869PaymentRepository",
$em->getRepository("Doctrine\Tests\Models\DDC869\DDC869ChequePayment"));
$this->assertTrue($em->getRepository("Doctrine\Tests\Models\DDC869\DDC869ChequePayment")->isTrue());
}
} }
/** /**

View File

@ -52,6 +52,35 @@ class BasicInheritanceMappingTest extends \Doctrine\Tests\OrmTestCase
$this->assertTrue(isset($class->associationMappings['mappedRelated1'])); $this->assertTrue(isset($class->associationMappings['mappedRelated1']));
} }
/**
* @group DDC-869
*/
public function testGetMetadataForSubclassWithMappedSuperclassWhithRepository()
{
$class = $this->_factory->getMetadataFor('Doctrine\Tests\Models\DDC869\DDC869CreditCardPayment');
$this->assertTrue(isset($class->fieldMappings['id']));
$this->assertTrue(isset($class->fieldMappings['value']));
$this->assertTrue(isset($class->fieldMappings['creditCardNumber']));
$this->assertEquals($class->customRepositoryClassName, "Doctrine\Tests\Models\DDC869\DDC869PaymentRepository");
$class = $this->_factory->getMetadataFor('Doctrine\Tests\Models\DDC869\DDC869ChequePayment');
$this->assertTrue(isset($class->fieldMappings['id']));
$this->assertTrue(isset($class->fieldMappings['value']));
$this->assertTrue(isset($class->fieldMappings['serialNumber']));
$this->assertEquals($class->customRepositoryClassName, "Doctrine\Tests\Models\DDC869\DDC869PaymentRepository");
// override repositoryClass
$class = $this->_factory->getMetadataFor('Doctrine\Tests\ORM\Mapping\SubclassWithRepository');
$this->assertTrue(isset($class->fieldMappings['id']));
$this->assertTrue(isset($class->fieldMappings['value']));
$this->assertEquals($class->customRepositoryClassName, "Doctrine\ORM\EntityRepository");
}
/** /**
* @group DDC-388 * @group DDC-388
@ -191,7 +220,7 @@ abstract class HierachyBase
{ {
/** /**
* @Column(type="integer") @Id @GeneratedValue(strategy="SEQUENCE") * @Column(type="integer") @Id @GeneratedValue(strategy="SEQUENCE")
* @SequenceGenerator(sequenceName="foo", initialValue="10") * @SequenceGenerator(sequenceName="foo", initialValue=10)
* @var int * @var int
*/ */
public $id; public $id;
@ -257,7 +286,7 @@ abstract class SuperclassBase
{ {
/** /**
* @Column(type="integer") @Id @GeneratedValue(strategy="SEQUENCE") * @Column(type="integer") @Id @GeneratedValue(strategy="SEQUENCE")
* @SequenceGenerator(sequenceName="foo", initialValue="10") * @SequenceGenerator(sequenceName="foo", initialValue=10)
* @var int * @var int
*/ */
public $id; public $id;
@ -277,4 +306,12 @@ abstract class MediumSuperclassBase extends SuperclassBase
class MediumSuperclassEntity extends MediumSuperclassBase class MediumSuperclassEntity extends MediumSuperclassBase
{ {
}
/**
* @Entity(repositoryClass = "Doctrine\ORM\EntityRepository")
*/
class SubclassWithRepository extends \Doctrine\Tests\Models\DDC869\DDC869Payment
{
} }

View File

@ -0,0 +1,426 @@
<?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\ORM\Mapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
/**
* @group DDC-659
*/
class ClassMetadataBuilderTest extends \Doctrine\Tests\OrmTestCase
{
/**
* @var ClassMetadata
*/
private $cm;
/**
* @var ClassMetadataBuilder
*/
private $builder;
public function setUp()
{
$this->cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$this->builder = new ClassMetadataBuilder($this->cm);
}
public function testSetMappedSuperClass()
{
$this->assertIsFluent($this->builder->setMappedSuperClass());
$this->assertTrue($this->cm->isMappedSuperclass);
}
public function testSetCustomRepositoryClass()
{
$this->assertIsFluent($this->builder->setCustomRepositoryClass('Doctrine\Tests\Models\CMS\CmsGroup'));
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsGroup', $this->cm->customRepositoryClassName);
}
public function testSetReadOnly()
{
$this->assertIsFluent($this->builder->setReadOnly());
$this->assertTrue($this->cm->isReadOnly);
}
public function testSetTable()
{
$this->assertIsFluent($this->builder->setTable('users'));
$this->assertEquals('users', $this->cm->table['name']);
}
public function testAddIndex()
{
$this->assertIsFluent($this->builder->addIndex(array('username', 'name'), 'users_idx'));
$this->assertEquals(array('users_idx' => array('columns' => array('username', 'name'))), $this->cm->table['indexes']);
}
public function testAddUniqueConstraint()
{
$this->assertIsFluent($this->builder->addUniqueConstraint(array('username', 'name'), 'users_idx'));
$this->assertEquals(array('users_idx' => array('columns' => array('username', 'name'))), $this->cm->table['uniqueConstraints']);
}
public function testSetPrimaryTableRelated()
{
$this->builder->addUniqueConstraint(array('username', 'name'), 'users_idx');
$this->builder->addIndex(array('username', 'name'), 'users_idx');
$this->builder->setTable('users');
$this->assertEquals(
array(
'name' => 'users',
'indexes' => array('users_idx' => array('columns' => array('username', 'name'))),
'uniqueConstraints' => array('users_idx' => array('columns' => array('username', 'name'))),
),
$this->cm->table
);
}
public function testSetInheritanceJoined()
{
$this->assertIsFluent($this->builder->setJoinedTableInheritance());
$this->assertEquals(ClassMetadata::INHERITANCE_TYPE_JOINED, $this->cm->inheritanceType);
}
public function testSetInheritanceSingleTable()
{
$this->assertIsFluent($this->builder->setSingleTableInheritance());
$this->assertEquals(ClassMetadata::INHERITANCE_TYPE_SINGLE_TABLE, $this->cm->inheritanceType);
}
public function testSetDiscriminatorColumn()
{
$this->assertIsFluent($this->builder->setDiscriminatorColumn('discr', 'string', '124'));
$this->assertEquals(array('fieldName' => 'discr', 'name' => 'discr', 'type' => 'string', 'length' => '124'), $this->cm->discriminatorColumn);
}
public function testAddDiscriminatorMapClass()
{
$this->assertIsFluent($this->builder->addDiscriminatorMapClass('test', 'Doctrine\Tests\Models\CMS\CmsUser'));
$this->assertIsFluent($this->builder->addDiscriminatorMapClass('test2', 'Doctrine\Tests\Models\CMS\CmsGroup'));
$this->assertEquals(array('test' => 'Doctrine\Tests\Models\CMS\CmsUser', 'test2' => 'Doctrine\Tests\Models\CMS\CmsGroup'), $this->cm->discriminatorMap);
$this->assertEquals('test', $this->cm->discriminatorValue);
}
public function testChangeTrackingPolicyExplicit()
{
$this->assertIsFluent($this->builder->setChangeTrackingPolicyDeferredExplicit());
$this->assertEquals(ClassMetadata::CHANGETRACKING_DEFERRED_EXPLICIT, $this->cm->changeTrackingPolicy);
}
public function testChangeTrackingPolicyNotify()
{
$this->assertIsFluent($this->builder->setChangeTrackingPolicyNotify());
$this->assertEquals(ClassMetadata::CHANGETRACKING_NOTIFY, $this->cm->changeTrackingPolicy);
}
public function testAddField()
{
$this->assertIsFluent($this->builder->addField('name', 'string'));
$this->assertEquals(array('columnName' => 'name', 'fieldName' => 'name', 'type' => 'string'), $this->cm->fieldMappings['name']);
}
public function testCreateField()
{
$fieldBuilder = ($this->builder->createField('name', 'string'));
$this->assertInstanceOf('Doctrine\ORM\Mapping\Builder\FieldBuilder', $fieldBuilder);
$this->assertFalse(isset($this->cm->fieldMappings['name']));
$this->assertIsFluent($fieldBuilder->build());
$this->assertEquals(array('columnName' => 'name', 'fieldName' => 'name', 'type' => 'string'), $this->cm->fieldMappings['name']);
}
public function testCreateVersionedField()
{
$this->builder->createField('name', 'integer')->columnName('username')->length(124)->nullable()->columnDefinition('foobar')->unique()->isVersionField()->build();
$this->assertEquals(array(
'columnDefinition' => 'foobar',
'columnName' => 'username',
'default' => 1,
'fieldName' => 'name',
'length' => 124,
'type' => 'integer',
'nullable' => true,
'unique' => true,
), $this->cm->fieldMappings['name']);
}
public function testCreatePrimaryField()
{
$this->builder->createField('id', 'integer')->isPrimaryKey()->generatedValue()->build();
$this->assertEquals(array('id'), $this->cm->identifier);
$this->assertEquals(array('columnName' => 'id', 'fieldName' => 'id', 'id' => true, 'type' => 'integer'), $this->cm->fieldMappings['id']);
}
public function testAddLifecycleEvent()
{
$this->builder->addLifecycleEvent('getStatus', 'postLoad');
$this->assertEquals(array('postLoad' => array('getStatus')), $this->cm->lifecycleCallbacks);
}
public function testCreateManyToOne()
{
$this->assertIsFluent(
$this->builder->createManyToOne('groups', 'Doctrine\Tests\Models\CMS\CmsGroup')
->addJoinColumn('group_id', 'id', true, false, 'CASCADE')
->cascadeAll()
->fetchExtraLazy()
->build()
);
$this->assertEquals(array('groups' => array (
'fieldName' => 'groups',
'targetEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsGroup',
'cascade' => array (
0 => 'remove',
1 => 'persist',
2 => 'refresh',
3 => 'merge',
4 => 'detach',
),
'fetch' => 4,
'joinColumns' => array (
0 =>
array (
'name' => 'group_id',
'referencedColumnName' => 'id',
'nullable' => true,
'unique' => false,
'onDelete' => 'CASCADE',
'columnDefinition' => NULL,
),
),
'type' => 2,
'mappedBy' => NULL,
'inversedBy' => NULL,
'isOwningSide' => true,
'sourceEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser',
'isCascadeRemove' => true,
'isCascadePersist' => true,
'isCascadeRefresh' => true,
'isCascadeMerge' => true,
'isCascadeDetach' => true,
'sourceToTargetKeyColumns' =>
array (
'group_id' => 'id',
),
'joinColumnFieldNames' =>
array (
'group_id' => 'group_id',
),
'targetToSourceKeyColumns' =>
array (
'id' => 'group_id',
),
'orphanRemoval' => false,
),
), $this->cm->associationMappings);
}
public function testCreateOneToOne()
{
$this->assertIsFluent(
$this->builder->createOneToOne('groups', 'Doctrine\Tests\Models\CMS\CmsGroup')
->addJoinColumn('group_id', 'id', true, false, 'CASCADE')
->cascadeAll()
->fetchExtraLazy()
->build()
);
$this->assertEquals(array('groups' => array (
'fieldName' => 'groups',
'targetEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsGroup',
'cascade' => array (
0 => 'remove',
1 => 'persist',
2 => 'refresh',
3 => 'merge',
4 => 'detach',
),
'fetch' => 4,
'joinColumns' => array (
0 =>
array (
'name' => 'group_id',
'referencedColumnName' => 'id',
'nullable' => true,
'unique' => true,
'onDelete' => 'CASCADE',
'columnDefinition' => NULL,
),
),
'type' => 1,
'mappedBy' => NULL,
'inversedBy' => NULL,
'isOwningSide' => true,
'sourceEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser',
'isCascadeRemove' => true,
'isCascadePersist' => true,
'isCascadeRefresh' => true,
'isCascadeMerge' => true,
'isCascadeDetach' => true,
'sourceToTargetKeyColumns' =>
array (
'group_id' => 'id',
),
'joinColumnFieldNames' =>
array (
'group_id' => 'group_id',
),
'targetToSourceKeyColumns' =>
array (
'id' => 'group_id',
),
'orphanRemoval' => false,
),
), $this->cm->associationMappings);
}
public function testCreateManyToMany()
{
$this->assertIsFluent(
$this->builder->createManyToMany('groups', 'Doctrine\Tests\Models\CMS\CmsGroup')
->setJoinTable('groups_users')
->addJoinColumn('group_id', 'id', true, false, 'CASCADE')
->addInverseJoinColumn('user_id', 'id')
->cascadeAll()
->fetchExtraLazy()
->build()
);
$this->assertEquals(array(
'groups' =>
array(
'fieldName' => 'groups',
'targetEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsGroup',
'cascade' =>
array(
0 => 'remove',
1 => 'persist',
2 => 'refresh',
3 => 'merge',
4 => 'detach',
),
'fetch' => 4,
'joinTable' =>
array(
'joinColumns' =>
array(
0 =>
array(
'name' => 'group_id',
'referencedColumnName' => 'id',
'nullable' => true,
'unique' => false,
'onDelete' => 'CASCADE',
'columnDefinition' => NULL,
),
),
'inverseJoinColumns' =>
array(
0 =>
array(
'name' => 'user_id',
'referencedColumnName' => 'id',
'nullable' => true,
'unique' => false,
'onDelete' => NULL,
'columnDefinition' => NULL,
),
),
'name' => 'groups_users',
),
'type' => 8,
'mappedBy' => NULL,
'inversedBy' => NULL,
'isOwningSide' => true,
'sourceEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser',
'isCascadeRemove' => true,
'isCascadePersist' => true,
'isCascadeRefresh' => true,
'isCascadeMerge' => true,
'isCascadeDetach' => true,
'isOnDeleteCascade' => true,
'relationToSourceKeyColumns' =>
array(
'group_id' => 'id',
),
'joinTableColumns' =>
array(
0 => 'group_id',
1 => 'user_id',
),
'relationToTargetKeyColumns' =>
array(
'user_id' => 'id',
),
),
), $this->cm->associationMappings);
}
public function testCreateOneToMany()
{
$this->assertIsFluent(
$this->builder->createOneToMany('groups', 'Doctrine\Tests\Models\CMS\CmsGroup')
->mappedBy('test')
->setOrderBy(array('test'))
->setIndexBy('test')
->build()
);
$this->assertEquals(array(
'groups' =>
array(
'fieldName' => 'groups',
'targetEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsGroup',
'mappedBy' => 'test',
'orderBy' =>
array(
0 => 'test',
),
'indexBy' => 'test',
'type' => 4,
'inversedBy' => NULL,
'isOwningSide' => false,
'sourceEntity' => 'Doctrine\\Tests\\Models\\CMS\\CmsUser',
'fetch' => 2,
'cascade' =>
array(
),
'isCascadeRemove' => false,
'isCascadePersist' => false,
'isCascadeRefresh' => false,
'isCascadeMerge' => false,
'isCascadeDetach' => false,
'orphanRemoval' => false,
),
), $this->cm->associationMappings);
}
public function assertIsFluent($ret)
{
$this->assertSame($this->builder, $ret, "Return Value has to be same instance as used builder");
}
}

View File

@ -45,7 +45,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals('UserParent', $cm->rootEntityName); $this->assertEquals('UserParent', $cm->rootEntityName);
$this->assertEquals(array('Doctrine\Tests\Models\CMS\One', 'Doctrine\Tests\Models\CMS\Two', 'Doctrine\Tests\Models\CMS\Three'), $cm->subClasses); $this->assertEquals(array('Doctrine\Tests\Models\CMS\One', 'Doctrine\Tests\Models\CMS\Two', 'Doctrine\Tests\Models\CMS\Three'), $cm->subClasses);
$this->assertEquals(array('UserParent'), $cm->parentClasses); $this->assertEquals(array('UserParent'), $cm->parentClasses);
$this->assertEquals('UserRepository', $cm->customRepositoryClassName); $this->assertEquals('Doctrine\Tests\Models\CMS\UserRepository', $cm->customRepositoryClassName);
$this->assertEquals(array('name' => 'disc', 'type' => 'integer', 'fieldName' => 'disc'), $cm->discriminatorColumn); $this->assertEquals(array('name' => 'disc', 'type' => 'integer', 'fieldName' => 'disc'), $cm->discriminatorColumn);
$this->assertTrue($cm->associationMappings['phonenumbers']['type'] == ClassMetadata::ONE_TO_ONE); $this->assertTrue($cm->associationMappings['phonenumbers']['type'] == ClassMetadata::ONE_TO_ONE);
$this->assertEquals(1, count($cm->associationMappings)); $this->assertEquals(1, count($cm->associationMappings));
@ -471,4 +471,15 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase
$cm = new ClassMetadata('DOCTRINE\TESTS\MODELS\CMS\CMSUSER'); $cm = new ClassMetadata('DOCTRINE\TESTS\MODELS\CMS\CMSUSER');
$this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->name); $this->assertEquals('Doctrine\Tests\Models\CMS\CmsUser', $cm->name);
} }
/**
* @group DDC-659
*/
public function testLifecycleCallbackNotFound()
{
$cm = new ClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$this->setExpectedException("Doctrine\ORM\Mapping\MappingException", "Entity 'Doctrine\Tests\Models\CMS\CmsUser' has no method 'notfound' to be registered as lifecycle callback.");
$cm->addLifecycleCallback('notfound', 'postLoad');
}
} }

View File

@ -0,0 +1,113 @@
<?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\ORM\Mapping\Symfony;
/**
* @group DDC-1418
*/
abstract class AbstractDriverTest extends \PHPUnit_Framework_TestCase
{
public function testFindMappingFile()
{
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\EntityFoo' => 'foo',
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
touch($filename = $this->dir.'/Foo'.$this->getFileExtension());
$this->assertEquals($filename, $this->invoke($driver, '_findMappingFile', array('MyNamespace\MySubnamespace\Entity\Foo')));
}
public function testFindMappingFileInSubnamespace()
{
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
touch($filename = $this->dir.'/Foo.Bar'.$this->getFileExtension());
$this->assertEquals($filename, $this->invoke($driver, '_findMappingFile', array('MyNamespace\MySubnamespace\Entity\Foo\Bar')));
}
public function testFindMappingFileNamespacedFoundFileNotFound()
{
$this->setExpectedException(
'Doctrine\ORM\Mapping\MappingException',
"No mapping file found named '".$this->dir."/Foo".$this->getFileExtension()."' for class 'MyNamespace\MySubnamespace\Entity\Foo'."
);
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
$this->invoke($driver, '_findMappingFile', array('MyNamespace\MySubnamespace\Entity\Foo'));
}
public function testFindMappingNamespaceNotFound()
{
$this->setExpectedException(
'Doctrine\ORM\Mapping\MappingException',
"No mapping file found named 'Foo".$this->getFileExtension()."' for class 'MyOtherNamespace\MySubnamespace\Entity\Foo'."
);
$driver = $this->getDriver(array(
'MyNamespace\MySubnamespace\Entity' => $this->dir,
));
$this->invoke($driver, '_findMappingFile', array('MyOtherNamespace\MySubnamespace\Entity\Foo'));
}
protected function setUp()
{
$this->dir = sys_get_temp_dir().'/abstract_driver_test';
@mkdir($this->dir, 0777, true);
}
protected function tearDown()
{
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->dir), \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $path) {
if ($path->isDir()) {
@rmdir($path);
} else {
@unlink($path);
}
}
@rmdir($this->dir);
}
abstract protected function getFileExtension();
abstract protected function getDriver(array $paths = array());
private function setField($obj, $field, $value)
{
$ref = new \ReflectionProperty($obj, $field);
$ref->setAccessible(true);
$ref->setValue($obj, $value);
}
private function invoke($obj, $method, array $args = array()) {
$ref = new \ReflectionMethod($obj, $method);
$ref->setAccessible(true);
return $ref->invokeArgs($obj, $args);
}
}

Some files were not shown because too many files have changed in this diff Show More