1
0
mirror of synced 2025-01-18 06:21:40 +03:00

More refactorings and optimizations.

This commit is contained in:
Guilherme Blanco 2011-12-01 10:00:26 -05:00
parent 5b73f1bd82
commit 5e3e8b3957
9 changed files with 388 additions and 273 deletions

View File

@ -1,6 +1,6 @@
# ResultCache implementation rewritten
The result cache is completly rewritten and now works on the database result level, not inside the ORM AbstractQuery
The result cache is completely rewritten and now works on the database result level, not inside the ORM AbstractQuery
anymore. This means that for result cached queries the hydration will now always be performed again, regardless of
the hydration mode. Affected areas are:
@ -12,20 +12,24 @@ The API is backwards compatible however most of the getter methods on the `Abstr
deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile`
instance with access to result cache driver, lifetime and cache key.
# EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they
haven't been in the identity map before. This means objects of this kind never lead to changes
in the UnitOfWork.
# Fields omitted in a partial DQL query or a native query are never updated
Fields of an entity that are not returned from a partial DQL Query or native SQL query
will never be updated through an UPDATE statement.
# Removed support for onUpdate in @JoinColumn
The onUpdate foreign key handling makes absolutly no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.
The onUpdate foreign key handling makes absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.
# Changes in Annotation Handling
@ -41,4 +45,32 @@ from 2.0 have to configure the annotation driver if they don't use `Configuratio
$driver = new AnnotationDriver($reader, (array)$paths);
$config->setMetadataDriverImpl($driver);
$config->setMetadataDriverImpl($driver);
# Scalar mappings can now be ommitted from DQL result
You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore.
Example:
SELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10
Your result will be a collection of Users, and not an array with key 0 as User object instance and "numArticles" as the number of articles per user
# Map entities as scalars in DQL result
When hydrating to array or even a mixed result in object hydrator, previously you had the 0 index holding you entity instance.
You are now allowed to alias this, providing more flexibility for you code.
Example:
SELECT u AS user FROM User u
Will now return a collection of arrays with index "user" pointing to the User object instance.
# Performance optimizations
Thousands of lines were completely reviewed and optimized for best performance.
Removed redundancy and improved code readability made now internal Doctrine code easier to understand.
Also, Doctrine 2.2 now is around 10-15% faster than 2.1.

View File

@ -144,7 +144,7 @@ abstract class AbstractQuery
{
return $this->_params;
}
/**
* Get all defined parameter types.
*
@ -163,7 +163,11 @@ abstract class AbstractQuery
*/
public function getParameter($key)
{
return isset($this->_params[$key]) ? $this->_params[$key] : null;
if (isset($this->_params[$key])) {
return $this->_params[$key];
}
return null;
}
/**
@ -174,7 +178,11 @@ abstract class AbstractQuery
*/
public function getParameterType($key)
{
return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null;
if (isset($this->_paramTypes[$key])) {
return $this->_paramTypes[$key];
}
return null;
}
/**
@ -199,14 +207,14 @@ abstract class AbstractQuery
public function setParameter($key, $value, $type = null)
{
$key = trim($key, ':');
if ($type === null) {
$type = Query\ParameterTypeInferer::inferType($value);
}
$this->_paramTypes[$key] = $type;
$this->_params[$key] = $value;
return $this;
}
@ -220,12 +228,9 @@ abstract class AbstractQuery
public function setParameters(array $params, array $types = array())
{
foreach ($params as $key => $value) {
if (isset($types[$key])) {
$this->setParameter($key, $value, $types[$key]);
} else {
$this->setParameter($key, $value);
}
$this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
}
return $this;
}
@ -238,6 +243,7 @@ abstract class AbstractQuery
public function setResultSetMapping(Query\ResultSetMapping $rsm)
{
$this->_resultSetMapping = $rsm;
return $this;
}
@ -252,11 +258,11 @@ abstract class AbstractQuery
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver);
}
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
: new QueryCacheProfile(0, null, $resultCacheDriver);
return $this;
}
@ -270,9 +276,9 @@ abstract class AbstractQuery
{
if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $this->_queryCacheProfile->getResultCacheDriver();
} else {
return $this->_em->getConfiguration()->getResultCacheImpl();
}
return $this->_em->getConfiguration()->getResultCacheImpl();
}
/**
@ -289,9 +295,12 @@ abstract class AbstractQuery
if ($bool) {
$this->setResultCacheLifetime($lifetime);
$this->setResultCacheId($resultCacheId);
} else {
$this->_queryCacheProfile = null;
return $this;
}
$this->_queryCacheProfile = null;
return $this;
}
@ -303,16 +312,12 @@ abstract class AbstractQuery
*/
public function setResultCacheLifetime($lifetime)
{
if ($lifetime === null) {
$lifetime = 0;
} else {
$lifetime = (int)$lifetime;
}
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
} else {
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
}
$lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setLifetime($lifetime)
: new QueryCacheProfile($lifetime);
return $this;
}
@ -336,6 +341,7 @@ abstract class AbstractQuery
public function expireResultCache($expire = true)
{
$this->_expireResultCache = $expire;
return $this;
}
@ -374,6 +380,7 @@ abstract class AbstractQuery
}
$this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
return $this;
}
@ -387,6 +394,7 @@ abstract class AbstractQuery
public function setHydrationMode($hydrationMode)
{
$this->_hydrationMode = $hydrationMode;
return $this;
}
@ -451,14 +459,15 @@ abstract class AbstractQuery
return null;
}
if (is_array($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
if ( ! is_array($result)) {
return $result;
}
return $result;
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
/**
@ -482,14 +491,15 @@ abstract class AbstractQuery
throw new NoResultException;
}
if (is_array($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
if ( ! is_array($result)) {
return $result;
}
return $result;
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
}
/**
@ -515,6 +525,7 @@ abstract class AbstractQuery
public function setHint($name, $value)
{
$this->_hints[$name] = $value;
return $this;
}
@ -531,7 +542,7 @@ abstract class AbstractQuery
/**
* Return the key value map of query hints that are currently set.
*
*
* @return array
*/
public function getHints()
@ -588,8 +599,8 @@ abstract class AbstractQuery
}
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$stmt, $this->_resultSetMapping, $this->_hints
);
}
/**
@ -602,11 +613,10 @@ abstract class AbstractQuery
*/
public function setResultCacheId($id)
{
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
}
$this->_queryCacheProfile = $this->_queryCacheProfile
? $this->_queryCacheProfile->setCacheKey($id)
: new QueryCacheProfile(0, $id);
return $this;
}

View File

@ -130,10 +130,12 @@ class EntityManager implements ObjectManager
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory($this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses());
$this->proxyFactory = new ProxyFactory(
$this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses()
);
}
/**
@ -175,6 +177,7 @@ class EntityManager implements ObjectManager
if ($this->expressionBuilder === null) {
$this->expressionBuilder = new Query\Expr;
}
return $this->expressionBuilder;
}
@ -267,9 +270,11 @@ class EntityManager implements ObjectManager
public function createQuery($dql = "")
{
$query = new Query($this);
if ( ! empty($dql)) {
$query->setDql($dql);
}
return $query;
}
@ -296,6 +301,7 @@ class EntityManager implements ObjectManager
$query = new NativeQuery($this);
$query->setSql($sql);
$query->setResultSetMapping($rsm);
return $query;
}
@ -308,6 +314,7 @@ class EntityManager implements ObjectManager
public function createNamedNativeQuery($name)
{
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
return $this->createNativeQuery($sql, $rsm);
}
@ -336,6 +343,7 @@ class EntityManager implements ObjectManager
public function flush($entity = null)
{
$this->errorIfClosed();
$this->unitOfWork->commit($entity);
}
@ -371,16 +379,18 @@ class EntityManager implements ObjectManager
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return ($entity instanceof $class->name) ? $entity : null;
}
if ($class->subClasses) {
$entity = $this->find($entityName, $identifier);
} else {
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
return $this->find($entityName, $identifier);
}
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
return $entity;
}
@ -411,6 +421,7 @@ class EntityManager implements ObjectManager
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return ($entity instanceof $class->name) ? $entity : null;
}
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
@ -461,7 +472,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
$this->unitOfWork->persist($entity);
}
@ -478,7 +491,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
$this->unitOfWork->remove($entity);
}
@ -493,7 +508,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
$this->unitOfWork->refresh($entity);
}
@ -511,6 +528,7 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->unitOfWork->detach($entity);
}
@ -527,7 +545,9 @@ class EntityManager implements ObjectManager
if ( ! is_object($entity)) {
throw new \InvalidArgumentException(gettype($entity));
}
$this->errorIfClosed();
return $this->unitOfWork->merge($entity);
}
@ -567,20 +587,20 @@ class EntityManager implements ObjectManager
public function getRepository($entityName)
{
$entityName = ltrim($entityName, '\\');
if (isset($this->repositories[$entityName])) {
return $this->repositories[$entityName];
}
$metadata = $this->getClassMetadata($entityName);
$customRepositoryClassName = $metadata->customRepositoryClassName;
$repositoryClassName = $metadata->customRepositoryClassName;
if ($customRepositoryClassName !== null) {
$repository = new $customRepositoryClassName($this, $metadata);
} else {
$repositoryClass = $this->config->getDefaultRepositoryClassName();
$repository = new $repositoryClass($this, $metadata);
if ($repositoryClassName === null) {
$repositoryClassName = $this->config->getDefaultRepositoryClassName();
}
$repository = new $repositoryClassName($this, $metadata);
$this->repositories[$entityName] = $repository;
return $repository;
@ -594,9 +614,9 @@ class EntityManager implements ObjectManager
*/
public function contains($entity)
{
return $this->unitOfWork->isScheduledForInsert($entity) ||
$this->unitOfWork->isInIdentityMap($entity) &&
! $this->unitOfWork->isScheduledForDelete($entity);
return $this->unitOfWork->isScheduledForInsert($entity)
|| $this->unitOfWork->isInIdentityMap($entity)
&& ! $this->unitOfWork->isScheduledForDelete($entity);
}
/**
@ -679,29 +699,27 @@ class EntityManager implements ObjectManager
{
switch ($hydrationMode) {
case Query::HYDRATE_OBJECT:
$hydrator = new Internal\Hydration\ObjectHydrator($this);
break;
return new Internal\Hydration\ObjectHydrator($this);
case Query::HYDRATE_ARRAY:
$hydrator = new Internal\Hydration\ArrayHydrator($this);
break;
return new Internal\Hydration\ArrayHydrator($this);
case Query::HYDRATE_SCALAR:
$hydrator = new Internal\Hydration\ScalarHydrator($this);
break;
return new Internal\Hydration\ScalarHydrator($this);
case Query::HYDRATE_SINGLE_SCALAR:
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
break;
return new Internal\Hydration\SingleScalarHydrator($this);
case Query::HYDRATE_SIMPLEOBJECT:
$hydrator = new Internal\Hydration\SimpleObjectHydrator($this);
break;
return new Internal\Hydration\SimpleObjectHydrator($this);
default:
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
$hydrator = new $class($this);
break;
return new $class($this);
}
throw ORMException::invalidHydrationMode($hydrationMode);
}
return $hydrator;
throw ORMException::invalidHydrationMode($hydrationMode);
}
/**
@ -737,18 +755,25 @@ class EntityManager implements ObjectManager
*/
public static function create($conn, Configuration $config, EventManager $eventManager = null)
{
if (!$config->getMetadataDriverImpl()) {
if ( ! $config->getMetadataDriverImpl()) {
throw ORMException::missingMappingDriverImpl();
}
if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager()));
} else if ($conn instanceof Connection) {
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
} else {
throw new \InvalidArgumentException("Invalid argument: " . $conn);
switch (true) {
case (is_array($conn)):
$conn = \Doctrine\DBAL\DriverManager::getConnection(
$conn, $config, ($eventManager ?: new EventManager())
);
break;
case ($conn instanceof Connection):
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
break;
default:
throw new \InvalidArgumentException("Invalid argument: " . $conn);
}
return new EntityManager($conn, $config, $conn->getEventManager());

View File

@ -109,11 +109,11 @@ class EntityRepository implements ObjectRepository
{
// Check identity map first
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
if (!($entity instanceof $this->_class->name)) {
if ( ! ($entity instanceof $this->_class->name)) {
return null;
}
if ($lockMode != LockMode::NONE) {
if ($lockMode !== LockMode::NONE) {
$this->_em->lock($entity, $lockMode, $lockVersion);
}
@ -126,23 +126,27 @@ class EntityRepository implements ObjectRepository
$id = array_combine($this->_class->identifier, $value);
}
if ($lockMode == LockMode::NONE) {
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
} else if ($lockMode == LockMode::OPTIMISTIC) {
if (!$this->_class->isVersioned) {
throw OptimisticLockException::notVersioned($this->_entityName);
}
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
switch ($lockMode) {
case LockMode::NONE:
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
case LockMode::OPTIMISTIC:
if ( ! $this->_class->isVersioned) {
throw OptimisticLockException::notVersioned($this->_entityName);
}
return $entity;
} else {
if (!$this->_em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
return $entity;
default:
if ( ! $this->_em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
}
}
@ -191,30 +195,35 @@ class EntityRepository implements ObjectRepository
*/
public function __call($method, $arguments)
{
if (substr($method, 0, 6) == 'findBy') {
$by = substr($method, 6, strlen($method));
$method = 'findBy';
} else if (substr($method, 0, 9) == 'findOneBy') {
$by = substr($method, 9, strlen($method));
$method = 'findOneBy';
} else {
throw new \BadMethodCallException(
"Undefined method '$method'. The method name must start with ".
"either findBy or findOneBy!"
);
switch (true) {
case (substr($method, 0, 6) == 'findBy'):
$by = substr($method, 6, strlen($method));
$method = 'findBy';
break;
case (substr($method, 0, 9) == 'findOneBy'):
$by = substr($method, 9, strlen($method));
$method = 'findOneBy';
break;
default:
throw new \BadMethodCallException(
"Undefined method '$method'. The method name must start with ".
"either findBy or findOneBy!"
);
}
if (empty($arguments)) {
throw ORMException::findByRequiresParameter($method.$by);
throw ORMException::findByRequiresParameter($method . $by);
}
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
return $this->$method(array($fieldName => $arguments[0]));
} else {
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
}
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);
}
/**

View File

@ -24,7 +24,7 @@ use ReflectionClass, ReflectionProperty;
/**
* A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
* of an entity and it's associations.
*
*
* Once populated, ClassMetadata instances are usually cached in a serialized form.
*
* <b>IMPORTANT NOTE:</b>
@ -47,10 +47,10 @@ class ClassMetadata extends ClassMetadataInfo
* @var array
*/
public $reflFields = array();
/**
* The prototype from which new instances of the mapped class are created.
*
*
* @var object
*/
private $_prototype;
@ -103,13 +103,13 @@ class ClassMetadata extends ClassMetadataInfo
}
return $this->reflFields[$this->identifier[0]];
}
/**
* Validates & completes the given field mapping.
*
* @param array $mapping The field mapping to validated & complete.
* @return array The validated and completed field mapping.
*
*
* @throws MappingException
*/
protected function _validateAndCompleteFieldMapping(array &$mapping)
@ -124,7 +124,7 @@ class ClassMetadata extends ClassMetadataInfo
/**
* Extracts the identifier values of an entity of this class.
*
*
* For composite identifiers, the identifier values are returned as an array
* with the same order as the field order in {@link identifier}.
*
@ -135,20 +135,25 @@ class ClassMetadata extends ClassMetadataInfo
{
if ($this->isIdentifierComposite) {
$id = array();
foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) {
$id[$idField] = $value;
}
}
return $id;
} else {
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
}
return array();
}
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
}
return array();
}
/**
@ -215,18 +220,18 @@ class ClassMetadata extends ClassMetadataInfo
{
return __CLASS__ . '@' . spl_object_hash($this);
}
/**
* Determines which fields get serialized.
*
* It is only serialized what is necessary for best unserialization performance.
* That means any metadata properties that are not set or empty or simply have
* their default value are NOT serialized.
*
*
* Parts that are also NOT serialized because they can not be properly unserialized:
* - reflClass (ReflectionClass)
* - reflFields (ReflectionProperty array)
*
*
* @return array The names of all the fields that should be serialized.
*/
public function __sleep()
@ -301,7 +306,7 @@ class ClassMetadata extends ClassMetadataInfo
/**
* Restores some state that can not be serialized/unserialized.
*
*
* @return void
*/
public function __wakeup()
@ -310,30 +315,27 @@ class ClassMetadata extends ClassMetadataInfo
$this->reflClass = new ReflectionClass($this->name);
foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declared'])) {
$reflField = new ReflectionProperty($mapping['declared'], $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}
$reflField = isset($mapping['declared'])
? new ReflectionProperty($mapping['declared'], $field)
: $this->reflClass->getProperty($field);
$reflField->setAccessible(true);
$this->reflFields[$field] = $reflField;
}
foreach ($this->associationMappings as $field => $mapping) {
if (isset($mapping['declared'])) {
$reflField = new ReflectionProperty($mapping['declared'], $field);
} else {
$reflField = $this->reflClass->getProperty($field);
}
$reflField = isset($mapping['declared'])
? new ReflectionProperty($mapping['declared'], $field)
: $this->reflClass->getProperty($field);
$reflField->setAccessible(true);
$this->reflFields[$field] = $reflField;
}
}
/**
* Creates a new instance of the mapped class, without invoking the constructor.
*
*
* @return object
*/
public function newInstance()
@ -341,6 +343,7 @@ class ClassMetadata extends ClassMetadataInfo
if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
}
return clone $this->_prototype;
}
@ -354,6 +357,7 @@ class ClassMetadata extends ClassMetadataInfo
($this->reflClass->getMethod($callback)->getModifiers() & \ReflectionMethod::IS_PUBLIC) == 0) {
throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callback);
}
return parent::addLifecycleCallback($callback, $event);
}
}

View File

@ -1,4 +1,4 @@
<?php
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@ -38,6 +38,7 @@ final class NativeQuery extends AbstractQuery
public function setSQL($sql)
{
$this->_sql = $sql;
return $this;
}
@ -58,16 +59,18 @@ final class NativeQuery extends AbstractQuery
protected function _doExecute()
{
$params = $this->_params;
$types = $this->_paramTypes;
if ($params) {
if (is_int(key($params))) {
ksort($params);
ksort($types);
$params = array_values($params);
$types = array_values($types);
}
$types = $this->_paramTypes;
if ($params && is_int(key($params))) {
ksort($params);
ksort($types);
$params = array_values($params);
$types = array_values($types);
}
return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile);
return $this->_em->getConnection()->executeQuery(
$this->_sql, $params, $types, $this->_queryCacheProfile
);
}
}

View File

@ -202,26 +202,30 @@ final class Query extends AbstractQuery
return $this->_parserResult;
}
// Check query cache.
if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) {
$hash = $this->_getQueryCacheId();
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
$this->_state = self::STATE_CLEAN;
if ($cached === false) {
// Cache miss.
$parser = new Parser($this);
$this->_parserResult = $parser->parse();
$queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
} else {
// Cache hit.
$this->_parserResult = $cached;
}
} else {
// Check query cache.
if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
$parser = new Parser($this);
$this->_parserResult = $parser->parse();
return $this->_parserResult;
}
$this->_state = self::STATE_CLEAN;
$hash = $this->_getQueryCacheId();
$cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
if ($cached !== false) {
// Cache hit.
$this->_parserResult = $cached;
return $this->_parserResult;
}
// Cache miss.
$parser = new Parser($this);
$this->_parserResult = $parser->parse();
$queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
return $this->_parserResult;
}
@ -232,6 +236,7 @@ final class Query extends AbstractQuery
protected function _doExecute()
{
$executor = $this->_parse()->getSqlExecutor();
if ($this->_queryCacheProfile) {
$executor->setQueryCacheProfile($this->_queryCacheProfile);
}
@ -339,6 +344,7 @@ final class Query extends AbstractQuery
public function setQueryCacheDriver($queryCache)
{
$this->_queryCache = $queryCache;
return $this;
}
@ -351,6 +357,7 @@ final class Query extends AbstractQuery
public function useQueryCache($bool)
{
$this->_useQueryCache = $bool;
return $this;
}
@ -364,9 +371,9 @@ final class Query extends AbstractQuery
{
if ($this->_queryCache) {
return $this->_queryCache;
} else {
return $this->_em->getConfiguration()->getQueryCacheImpl();
}
return $this->_em->getConfiguration()->getQueryCacheImpl();
}
/**
@ -380,6 +387,7 @@ final class Query extends AbstractQuery
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
}
$this->_queryCacheTTL = $timeToLive;
return $this;
@ -424,6 +432,7 @@ final class Query extends AbstractQuery
public function free()
{
parent::free();
$this->_dql = null;
$this->_state = self::STATE_CLEAN;
}
@ -440,6 +449,7 @@ final class Query extends AbstractQuery
$this->_dql = $dqlQuery;
$this->_state = self::STATE_DIRTY;
}
return $this;
}
@ -489,6 +499,7 @@ final class Query extends AbstractQuery
{
$this->_firstResult = $firstResult;
$this->_state = self::STATE_DIRTY;
return $this;
}
@ -513,6 +524,7 @@ final class Query extends AbstractQuery
{
$this->_maxResults = $maxResults;
$this->_state = self::STATE_DIRTY;
return $this;
}
@ -538,6 +550,7 @@ final class Query extends AbstractQuery
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{
$this->setHint(self::HINT_INTERNAL_ITERATION, true);
return parent::iterate($params, $hydrationMode);
}
@ -547,6 +560,7 @@ final class Query extends AbstractQuery
public function setHint($name, $value)
{
$this->_state = self::STATE_DIRTY;
return parent::setHint($name, $value);
}
@ -556,6 +570,7 @@ final class Query extends AbstractQuery
public function setHydrationMode($hydrationMode)
{
$this->_state = self::STATE_DIRTY;
return parent::setHydrationMode($hydrationMode);
}
@ -568,13 +583,14 @@ final class Query extends AbstractQuery
*/
public function setLockMode($lockMode)
{
if ($lockMode == LockMode::PESSIMISTIC_READ || $lockMode == LockMode::PESSIMISTIC_WRITE) {
if (!$this->_em->getConnection()->isTransactionActive()) {
if ($lockMode === LockMode::PESSIMISTIC_READ || $lockMode === LockMode::PESSIMISTIC_WRITE) {
if ( ! $this->_em->getConnection()->isTransactionActive()) {
throw TransactionRequiredException::transactionRequired();
}
}
$this->setHint(self::HINT_LOCK_MODE, $lockMode);
return $this;
}
@ -586,9 +602,11 @@ final class Query extends AbstractQuery
public function getLockMode()
{
$lockMode = $this->getHint(self::HINT_LOCK_MODE);
if (!$lockMode) {
if ( ! $lockMode) {
return LockMode::NONE;
}
return $lockMode;
}
@ -618,6 +636,7 @@ final class Query extends AbstractQuery
public function __clone()
{
parent::__clone();
$this->_state = self::STATE_DIRTY;
}
}

View File

@ -355,12 +355,9 @@ class QueryBuilder
public function setParameters(array $params, array $types = array())
{
foreach ($params as $key => $value) {
if (isset($types[$key])) {
$this->setParameter($key, $value, $types[$key]);
} else {
$this->setParameter($key, $value);
}
$this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
}
return $this;
}
@ -394,6 +391,7 @@ class QueryBuilder
public function setFirstResult($firstResult)
{
$this->_firstResult = $firstResult;
return $this;
}
@ -417,6 +415,7 @@ class QueryBuilder
public function setMaxResults($maxResults)
{
$this->_maxResults = $maxResults;
return $this;
}
@ -652,13 +651,16 @@ class QueryBuilder
public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
{
$rootAlias = substr($join, 0, strpos($join, '.'));
if (!in_array($rootAlias, $this->getRootAliases())) {
if ( ! in_array($rootAlias, $this->getRootAliases())) {
$rootAlias = $this->getRootAlias();
}
return $this->add('join', array(
$rootAlias => new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
), true);
$join = new Expr\Join(
Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy
);
return $this->add('join', array($rootAlias => $join), true);
}
/**
@ -685,13 +687,16 @@ class QueryBuilder
public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
{
$rootAlias = substr($join, 0, strpos($join, '.'));
if (!in_array($rootAlias, $this->getRootAliases())) {
if ( ! in_array($rootAlias, $this->getRootAliases())) {
$rootAlias = $this->getRootAlias();
}
return $this->add('join', array(
$rootAlias => new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy)
), true);
$join = new Expr\Join(
Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy
);
return $this->add('join', array($rootAlias => $join), true);
}
/**

View File

@ -1790,7 +1790,7 @@ class UnitOfWork implements PropertyChangedListener
public function detach($entity)
{
$visited = array();
$this->doDetach($entity, $visited);
}
@ -1804,7 +1804,7 @@ class UnitOfWork implements PropertyChangedListener
private function doDetach($entity, array &$visited, $noCascade = false)
{
$oid = spl_object_hash($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion
}
@ -1816,13 +1816,13 @@ class UnitOfWork implements PropertyChangedListener
if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity);
}
unset(
$this->entityInsertions[$oid],
$this->entityInsertions[$oid],
$this->entityUpdates[$oid],
$this->entityDeletions[$oid],
$this->entityDeletions[$oid],
$this->entityIdentifiers[$oid],
$this->entityStates[$oid],
$this->entityStates[$oid],
$this->originalEntityData[$oid]
);
break;
@ -1846,7 +1846,7 @@ class UnitOfWork implements PropertyChangedListener
public function refresh($entity)
{
$visited = array();
$this->doRefresh($entity, $visited);
}
@ -1860,7 +1860,7 @@ class UnitOfWork implements PropertyChangedListener
private function doRefresh($entity, array &$visited)
{
$oid = spl_object_hash($entity);
if (isset($visited[$oid])) {
return; // Prevent infinite recursion
}
@ -1868,11 +1868,11 @@ class UnitOfWork implements PropertyChangedListener
$visited[$oid] = $entity; // mark visited
$class = $this->em->getClassMetadata(get_class($entity));
if ($this->getEntityState($entity) !== self::STATE_MANAGED) {
throw new InvalidArgumentException("Entity is not MANAGED.");
}
$this->getEntityPersister($class->name)->refresh(
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
$entity
@ -1890,31 +1890,32 @@ class UnitOfWork implements PropertyChangedListener
private function cascadeRefresh($entity, array &$visited)
{
$class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) {
if ( ! $assoc['isCascadeRefresh']) {
continue;
}
$associationMappings = array_filter(
$class->associationMappings,
function ($assoc) { return $assoc['isCascadeRefresh']; }
);
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
switch (true) {
case ($relatedEntities instanceof PersistentCollection):
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
// break; is commented intentionally!
case ($relatedEntities instanceof Collection):
case (is_array($relatedEntities)):
foreach ($relatedEntities as $relatedEntity) {
$this->doRefresh($relatedEntity, $visited);
}
break;
case ($relatedEntities !== null):
$this->doRefresh($relatedEntities, $visited);
break;
default:
// Do nothing
}
@ -1930,31 +1931,32 @@ class UnitOfWork implements PropertyChangedListener
private function cascadeDetach($entity, array &$visited)
{
$class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) {
if ( ! $assoc['isCascadeDetach']) {
continue;
}
$associationMappings = array_filter(
$class->associationMappings,
function ($assoc) { return $assoc['isCascadeDetach']; }
);
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
switch (true) {
case ($relatedEntities instanceof PersistentCollection):
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
// break; is commented intentionally!
case ($relatedEntities instanceof Collection):
case (is_array($relatedEntities)):
foreach ($relatedEntities as $relatedEntity) {
$this->doDetach($relatedEntity, $visited);
}
break;
case ($relatedEntities !== null):
$this->doDetach($relatedEntities, $visited);
break;
default:
// Do nothing
}
@ -1971,11 +1973,15 @@ class UnitOfWork implements PropertyChangedListener
private function cascadeMerge($entity, $managedCopy, array &$visited)
{
$class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) {
if ( ! $assoc['isCascadeMerge']) {
continue;
}
$associationMappings = array_filter(
$class->associationMappings,
function ($assoc) { return $assoc['isCascadeMerge']; }
);
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
continue;
@ -1985,6 +1991,7 @@ class UnitOfWork implements PropertyChangedListener
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
}
foreach ($relatedEntities as $relatedEntity) {
$this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
}
@ -2004,31 +2011,32 @@ class UnitOfWork implements PropertyChangedListener
private function cascadePersist($entity, array &$visited)
{
$class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) {
if ( ! $assoc['isCascadePersist']) {
continue;
}
$associationMappings = array_filter(
$class->associationMappings,
function ($assoc) { return $assoc['isCascadePersist']; }
);
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
switch (true) {
case ($relatedEntities instanceof PersistentCollection):
// Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap();
// break; is commented intentionally!
case ($relatedEntities instanceof Collection):
case (is_array($relatedEntities)):
foreach ($relatedEntities as $relatedEntity) {
$this->doPersist($relatedEntity, $visited);
}
break;
case ($relatedEntities !== null):
$this->doPersist($relatedEntities, $visited);
break;
default:
// Do nothing
}
@ -2045,11 +2053,12 @@ class UnitOfWork implements PropertyChangedListener
{
$class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) {
if ( ! $assoc['isCascadeRemove']) {
continue;
}
$associationMappings = array_filter(
$class->associationMappings,
function ($assoc) { return $assoc['isCascadeRemove']; }
);
foreach ($associationMappings as $assoc) {
if ($entity instanceof Proxy && !$entity->__isInitialized__) {
$entity->__load();
}
@ -2064,11 +2073,11 @@ class UnitOfWork implements PropertyChangedListener
$this->doRemove($relatedEntity, $visited);
}
break;
case ($relatedEntities !== null):
$this->doRemove($relatedEntities, $visited);
break;
default:
// Do nothing
}
@ -2100,15 +2109,15 @@ class UnitOfWork implements PropertyChangedListener
if ($lockVersion === null) {
return;
}
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
if ($entityVersion != $lockVersion) {
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion);
}
break;
case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ:
case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE:
if (!$this->em->getConnection()->isTransactionActive()) {
@ -2122,7 +2131,7 @@ class UnitOfWork implements PropertyChangedListener
$lockMode
);
break;
default:
// Do nothing
}
@ -2138,6 +2147,7 @@ class UnitOfWork implements PropertyChangedListener
if ($this->commitOrderCalculator === null) {
$this->commitOrderCalculator = new Internal\CommitOrderCalculator;
}
return $this->commitOrderCalculator;
}
@ -2226,9 +2236,11 @@ class UnitOfWork implements PropertyChangedListener
private function newInstance($class)
{
$entity = $class->newInstance();
if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
$entity->injectObjectManager($this->em, $class);
}
return $entity;
}
@ -2252,34 +2264,30 @@ class UnitOfWork implements PropertyChangedListener
if ($class->isIdentifierComposite) {
$id = array();
foreach ($class->identifier as $fieldName) {
if (isset($class->associationMappings[$fieldName])) {
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']];
} else {
$id[$fieldName] = $data[$fieldName];
}
$id[$fieldName] = isset($class->associationMappings[$fieldName])
? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]
: $data[$fieldName];
}
$idHash = implode(' ', $id);
} else {
if (isset($class->associationMappings[$class->identifier[0]])) {
$idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']];
} else {
$idHash = $data[$class->identifier[0]];
}
$idHash = isset($class->associationMappings[$class->identifier[0]])
? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]
: $data[$class->identifier[0]];
$id = array($class->identifier[0] => $idHash);
}
if (isset($this->identityMap[$class->rootEntityName][$idHash])) {
$entity = $this->identityMap[$class->rootEntityName][$idHash];
$oid = spl_object_hash($entity);
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__isInitialized__ = true;
$overrideLocalValues = true;
if ($entity instanceof NotifyPropertyChanged) {
$entity->addPropertyChangedListener($this);
}
@ -2303,23 +2311,23 @@ class UnitOfWork implements PropertyChangedListener
} else {
$entity = $this->newInstance($class);
$oid = spl_object_hash($entity);
$this->entityIdentifiers[$oid] = $id;
$this->entityStates[$oid] = self::STATE_MANAGED;
$this->originalEntityData[$oid] = $data;
$this->identityMap[$class->rootEntityName][$idHash] = $entity;
if ($entity instanceof NotifyPropertyChanged) {
$entity->addPropertyChangedListener($this);
}
$overrideLocalValues = true;
}
if ( ! $overrideLocalValues) {
return $entity;
}
foreach ($data as $field => $value) {
if (isset($class->fieldMappings[$field])) {
$class->reflFields[$field]->setValue($entity, $value);
@ -2337,7 +2345,7 @@ class UnitOfWork implements PropertyChangedListener
if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
return $entity;
}
foreach ($class->associationMappings as $field => $assoc) {
// Check if the association is not among the fetch-joined associations already.
if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {