More refactorings and optimizations.
This commit is contained in:
parent
5b73f1bd82
commit
5e3e8b3957
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
$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.
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,21 +315,18 @@ 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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1891,11 +1891,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
$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) {
|
||||
@ -1931,11 +1932,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
$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) {
|
||||
@ -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);
|
||||
}
|
||||
@ -2005,11 +2012,12 @@ class UnitOfWork implements PropertyChangedListener
|
||||
{
|
||||
$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) {
|
||||
@ -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();
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -2254,20 +2266,16 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user