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 # 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 anymore. This means that for result cached queries the hydration will now always be performed again, regardless of
the hydration mode. Affected areas are: 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` 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. instance with access to result cache driver, lifetime and cache key.
# EntityManager#getPartialReference() creates read-only entity # EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they 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 haven't been in the identity map before. This means objects of this kind never lead to changes
in the UnitOfWork. in the UnitOfWork.
# Fields omitted in a partial DQL query or a native query are never updated # 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 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. will never be updated through an UPDATE statement.
# 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 absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed.
# Changes in Annotation Handling # Changes in Annotation Handling
@ -42,3 +46,31 @@ from 2.0 have to configure the annotation driver if they don't use `Configuratio
$driver = new AnnotationDriver($reader, (array)$paths); $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

@ -163,7 +163,11 @@ abstract class AbstractQuery
*/ */
public function getParameter($key) 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) public function getParameterType($key)
{ {
return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null; if (isset($this->_paramTypes[$key])) {
return $this->_paramTypes[$key];
}
return null;
} }
/** /**
@ -220,12 +228,9 @@ abstract class AbstractQuery
public function setParameters(array $params, array $types = array()) public function setParameters(array $params, array $types = array())
{ {
foreach ($params as $key => $value) { foreach ($params as $key => $value) {
if (isset($types[$key])) { $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
$this->setParameter($key, $value, $types[$key]);
} else {
$this->setParameter($key, $value);
}
} }
return $this; return $this;
} }
@ -238,6 +243,7 @@ abstract class AbstractQuery
public function setResultSetMapping(Query\ResultSetMapping $rsm) public function setResultSetMapping(Query\ResultSetMapping $rsm)
{ {
$this->_resultSetMapping = $rsm; $this->_resultSetMapping = $rsm;
return $this; return $this;
} }
@ -252,11 +258,11 @@ abstract class AbstractQuery
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver(); throw ORMException::invalidResultCacheDriver();
} }
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver); $this->_queryCacheProfile = $this->_queryCacheProfile
} else { ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver); : new QueryCacheProfile(0, null, $resultCacheDriver);
}
return $this; return $this;
} }
@ -270,9 +276,9 @@ abstract class AbstractQuery
{ {
if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) { if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $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) { if ($bool) {
$this->setResultCacheLifetime($lifetime); $this->setResultCacheLifetime($lifetime);
$this->setResultCacheId($resultCacheId); $this->setResultCacheId($resultCacheId);
} else {
$this->_queryCacheProfile = null; return $this;
} }
$this->_queryCacheProfile = null;
return $this; return $this;
} }
@ -303,16 +312,12 @@ abstract class AbstractQuery
*/ */
public function setResultCacheLifetime($lifetime) public function setResultCacheLifetime($lifetime)
{ {
if ($lifetime === null) { $lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
$lifetime = 0;
} else { $this->_queryCacheProfile = $this->_queryCacheProfile
$lifetime = (int)$lifetime; ? $this->_queryCacheProfile->setLifetime($lifetime)
} : new QueryCacheProfile($lifetime);
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
} else {
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
}
return $this; return $this;
} }
@ -336,6 +341,7 @@ abstract class AbstractQuery
public function expireResultCache($expire = true) public function expireResultCache($expire = true)
{ {
$this->_expireResultCache = $expire; $this->_expireResultCache = $expire;
return $this; return $this;
} }
@ -374,6 +380,7 @@ abstract class AbstractQuery
} }
$this->_hints['fetchMode'][$class][$assocName] = $fetchMode; $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
return $this; return $this;
} }
@ -387,6 +394,7 @@ abstract class AbstractQuery
public function setHydrationMode($hydrationMode) public function setHydrationMode($hydrationMode)
{ {
$this->_hydrationMode = $hydrationMode; $this->_hydrationMode = $hydrationMode;
return $this; return $this;
} }
@ -451,14 +459,15 @@ abstract class AbstractQuery
return null; return null;
} }
if (is_array($result)) { if ( ! is_array($result)) {
if (count($result) > 1) { return $result;
throw new NonUniqueResultException;
}
return array_shift($result);
} }
return $result; if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
} }
/** /**
@ -482,14 +491,15 @@ abstract class AbstractQuery
throw new NoResultException; throw new NoResultException;
} }
if (is_array($result)) { if ( ! is_array($result)) {
if (count($result) > 1) { return $result;
throw new NonUniqueResultException;
}
return array_shift($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) public function setHint($name, $value)
{ {
$this->_hints[$name] = $value; $this->_hints[$name] = $value;
return $this; return $this;
} }
@ -588,8 +599,8 @@ abstract class AbstractQuery
} }
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( 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) public function setResultCacheId($id)
{ {
if ($this->_queryCacheProfile) { $this->_queryCacheProfile = $this->_queryCacheProfile
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id); ? $this->_queryCacheProfile->setCacheKey($id)
} else { : new QueryCacheProfile(0, $id);
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
}
return $this; return $this;
} }

View File

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

View File

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

@ -135,20 +135,25 @@ class ClassMetadata extends ClassMetadataInfo
{ {
if ($this->isIdentifierComposite) { if ($this->isIdentifierComposite) {
$id = array(); $id = array();
foreach ($this->identifier as $idField) { foreach ($this->identifier as $idField) {
$value = $this->reflFields[$idField]->getValue($entity); $value = $this->reflFields[$idField]->getValue($entity);
if ($value !== null) { if ($value !== null) {
$id[$idField] = $value; $id[$idField] = $value;
} }
} }
return $id; 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();
} }
/** /**
@ -310,21 +315,18 @@ class ClassMetadata extends ClassMetadataInfo
$this->reflClass = new ReflectionClass($this->name); $this->reflClass = new ReflectionClass($this->name);
foreach ($this->fieldMappings as $field => $mapping) { foreach ($this->fieldMappings as $field => $mapping) {
if (isset($mapping['declared'])) { $reflField = isset($mapping['declared'])
$reflField = new ReflectionProperty($mapping['declared'], $field); ? new ReflectionProperty($mapping['declared'], $field)
} else { : $this->reflClass->getProperty($field);
$reflField = $this->reflClass->getProperty($field);
}
$reflField->setAccessible(true); $reflField->setAccessible(true);
$this->reflFields[$field] = $reflField; $this->reflFields[$field] = $reflField;
} }
foreach ($this->associationMappings as $field => $mapping) { foreach ($this->associationMappings as $field => $mapping) {
if (isset($mapping['declared'])) { $reflField = isset($mapping['declared'])
$reflField = new ReflectionProperty($mapping['declared'], $field); ? new ReflectionProperty($mapping['declared'], $field)
} else { : $this->reflClass->getProperty($field);
$reflField = $this->reflClass->getProperty($field);
}
$reflField->setAccessible(true); $reflField->setAccessible(true);
$this->reflFields[$field] = $reflField; $this->reflFields[$field] = $reflField;
@ -341,6 +343,7 @@ class ClassMetadata extends ClassMetadataInfo
if ($this->_prototype === null) { if ($this->_prototype === null) {
$this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
} }
return clone $this->_prototype; return clone $this->_prototype;
} }
@ -354,6 +357,7 @@ class ClassMetadata extends ClassMetadataInfo
($this->reflClass->getMethod($callback)->getModifiers() & \ReflectionMethod::IS_PUBLIC) == 0) { ($this->reflClass->getMethod($callback)->getModifiers() & \ReflectionMethod::IS_PUBLIC) == 0) {
throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callback); throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callback);
} }
return parent::addLifecycleCallback($callback, $event); return parent::addLifecycleCallback($callback, $event);
} }
} }

View File

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

View File

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

View File

@ -1891,11 +1891,12 @@ class UnitOfWork implements PropertyChangedListener
{ {
$class = $this->em->getClassMetadata(get_class($entity)); $class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) { $associationMappings = array_filter(
if ( ! $assoc['isCascadeRefresh']) { $class->associationMappings,
continue; function ($assoc) { return $assoc['isCascadeRefresh']; }
} );
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
switch (true) { switch (true) {
@ -1931,11 +1932,12 @@ class UnitOfWork implements PropertyChangedListener
{ {
$class = $this->em->getClassMetadata(get_class($entity)); $class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) { $associationMappings = array_filter(
if ( ! $assoc['isCascadeDetach']) { $class->associationMappings,
continue; function ($assoc) { return $assoc['isCascadeDetach']; }
} );
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
switch (true) { switch (true) {
@ -1971,11 +1973,15 @@ class UnitOfWork implements PropertyChangedListener
private function cascadeMerge($entity, $managedCopy, array &$visited) private function cascadeMerge($entity, $managedCopy, array &$visited)
{ {
$class = $this->em->getClassMetadata(get_class($entity)); $class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) {
if ( ! $assoc['isCascadeMerge']) { $associationMappings = array_filter(
continue; $class->associationMappings,
} function ($assoc) { return $assoc['isCascadeMerge']; }
);
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
if ($relatedEntities instanceof Collection) { if ($relatedEntities instanceof Collection) {
if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) {
continue; continue;
@ -1985,6 +1991,7 @@ class UnitOfWork implements PropertyChangedListener
// Unwrap so that foreach() does not initialize // Unwrap so that foreach() does not initialize
$relatedEntities = $relatedEntities->unwrap(); $relatedEntities = $relatedEntities->unwrap();
} }
foreach ($relatedEntities as $relatedEntity) { foreach ($relatedEntities as $relatedEntity) {
$this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc);
} }
@ -2005,11 +2012,12 @@ class UnitOfWork implements PropertyChangedListener
{ {
$class = $this->em->getClassMetadata(get_class($entity)); $class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) { $associationMappings = array_filter(
if ( ! $assoc['isCascadePersist']) { $class->associationMappings,
continue; function ($assoc) { return $assoc['isCascadePersist']; }
} );
foreach ($associationMappings as $assoc) {
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
switch (true) { switch (true) {
@ -2045,11 +2053,12 @@ class UnitOfWork implements PropertyChangedListener
{ {
$class = $this->em->getClassMetadata(get_class($entity)); $class = $this->em->getClassMetadata(get_class($entity));
foreach ($class->associationMappings as $assoc) { $associationMappings = array_filter(
if ( ! $assoc['isCascadeRemove']) { $class->associationMappings,
continue; function ($assoc) { return $assoc['isCascadeRemove']; }
} );
foreach ($associationMappings as $assoc) {
if ($entity instanceof Proxy && !$entity->__isInitialized__) { if ($entity instanceof Proxy && !$entity->__isInitialized__) {
$entity->__load(); $entity->__load();
} }
@ -2138,6 +2147,7 @@ class UnitOfWork implements PropertyChangedListener
if ($this->commitOrderCalculator === null) { if ($this->commitOrderCalculator === null) {
$this->commitOrderCalculator = new Internal\CommitOrderCalculator; $this->commitOrderCalculator = new Internal\CommitOrderCalculator;
} }
return $this->commitOrderCalculator; return $this->commitOrderCalculator;
} }
@ -2226,9 +2236,11 @@ class UnitOfWork implements PropertyChangedListener
private function newInstance($class) private function newInstance($class)
{ {
$entity = $class->newInstance(); $entity = $class->newInstance();
if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) { if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) {
$entity->injectObjectManager($this->em, $class); $entity->injectObjectManager($this->em, $class);
} }
return $entity; return $entity;
} }
@ -2254,20 +2266,16 @@ class UnitOfWork implements PropertyChangedListener
$id = array(); $id = array();
foreach ($class->identifier as $fieldName) { foreach ($class->identifier as $fieldName) {
if (isset($class->associationMappings[$fieldName])) { $id[$fieldName] = isset($class->associationMappings[$fieldName])
$id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]; ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]
} else { : $data[$fieldName];
$id[$fieldName] = $data[$fieldName];
}
} }
$idHash = implode(' ', $id); $idHash = implode(' ', $id);
} else { } else {
if (isset($class->associationMappings[$class->identifier[0]])) { $idHash = isset($class->associationMappings[$class->identifier[0]])
$idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]; ? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]
} else { : $data[$class->identifier[0]];
$idHash = $data[$class->identifier[0]];
}
$id = array($class->identifier[0] => $idHash); $id = array($class->identifier[0] => $idHash);
} }