Merge remote-tracking branch 'upstream/master' into DDC-551
Conflicts: lib/Doctrine/ORM/Persisters/ManyToManyPersister.php lib/Doctrine/ORM/Persisters/OneToManyPersister.php
This commit is contained in:
commit
04635ad4ff
@ -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.
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -138,10 +138,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()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,6 +185,7 @@ class EntityManager implements ObjectManager
|
||||
if ($this->expressionBuilder === null) {
|
||||
$this->expressionBuilder = new Query\Expr;
|
||||
}
|
||||
|
||||
return $this->expressionBuilder;
|
||||
}
|
||||
|
||||
@ -275,9 +278,11 @@ class EntityManager implements ObjectManager
|
||||
public function createQuery($dql = "")
|
||||
{
|
||||
$query = new Query($this);
|
||||
|
||||
if ( ! empty($dql)) {
|
||||
$query->setDql($dql);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@ -304,6 +309,7 @@ class EntityManager implements ObjectManager
|
||||
$query = new NativeQuery($this);
|
||||
$query->setSql($sql);
|
||||
$query->setResultSetMapping($rsm);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@ -316,6 +322,7 @@ class EntityManager implements ObjectManager
|
||||
public function createNamedNativeQuery($name)
|
||||
{
|
||||
list($sql, $rsm) = $this->config->getNamedNativeQuery($name);
|
||||
|
||||
return $this->createNativeQuery($sql, $rsm);
|
||||
}
|
||||
|
||||
@ -344,6 +351,7 @@ class EntityManager implements ObjectManager
|
||||
public function flush($entity = null)
|
||||
{
|
||||
$this->errorIfClosed();
|
||||
|
||||
$this->unitOfWork->commit($entity);
|
||||
}
|
||||
|
||||
@ -368,27 +376,39 @@ class EntityManager implements ObjectManager
|
||||
* without actually loading it, if the entity is not yet loaded.
|
||||
*
|
||||
* @param string $entityName The name of the entity type.
|
||||
* @param mixed $identifier The entity identifier.
|
||||
* @param mixed $id The entity identifier.
|
||||
* @return object The entity reference.
|
||||
*/
|
||||
public function getReference($entityName, $identifier)
|
||||
public function getReference($entityName, $id)
|
||||
{
|
||||
$class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
|
||||
if ( ! is_array($id)) {
|
||||
$id = array($class->identifier[0] => $id);
|
||||
}
|
||||
$sortedId = array();
|
||||
foreach ($class->identifier as $identifier) {
|
||||
if (!isset($id[$identifier])) {
|
||||
throw ORMException::missingIdentifierField($class->name, $identifier);
|
||||
}
|
||||
$sortedId[$identifier] = $id[$identifier];
|
||||
}
|
||||
|
||||
// Check identity map first, if its already in there just return it.
|
||||
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
|
||||
if ($entity = $this->unitOfWork->tryGetById($sortedId, $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, $sortedId);
|
||||
}
|
||||
|
||||
if ( ! is_array($sortedId)) {
|
||||
$sortedId = array($class->identifier[0] => $sortedId);
|
||||
}
|
||||
|
||||
$entity = $this->proxyFactory->getProxy($class->name, $sortedId);
|
||||
$this->unitOfWork->registerManaged($entity, $sortedId, array());
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
@ -419,6 +439,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);
|
||||
}
|
||||
@ -469,7 +490,9 @@ class EntityManager implements ObjectManager
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
|
||||
$this->errorIfClosed();
|
||||
|
||||
$this->unitOfWork->persist($entity);
|
||||
}
|
||||
|
||||
@ -486,7 +509,9 @@ class EntityManager implements ObjectManager
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
|
||||
$this->errorIfClosed();
|
||||
|
||||
$this->unitOfWork->remove($entity);
|
||||
}
|
||||
|
||||
@ -501,7 +526,9 @@ class EntityManager implements ObjectManager
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
|
||||
$this->errorIfClosed();
|
||||
|
||||
$this->unitOfWork->refresh($entity);
|
||||
}
|
||||
|
||||
@ -519,6 +546,7 @@ class EntityManager implements ObjectManager
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
|
||||
$this->unitOfWork->detach($entity);
|
||||
}
|
||||
|
||||
@ -535,7 +563,9 @@ class EntityManager implements ObjectManager
|
||||
if ( ! is_object($entity)) {
|
||||
throw new \InvalidArgumentException(gettype($entity));
|
||||
}
|
||||
|
||||
$this->errorIfClosed();
|
||||
|
||||
return $this->unitOfWork->merge($entity);
|
||||
}
|
||||
|
||||
@ -575,20 +605,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;
|
||||
@ -602,9 +632,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -687,29 +717,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -745,18 +773,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());
|
||||
|
@ -107,42 +107,51 @@ class EntityRepository implements ObjectRepository
|
||||
*/
|
||||
public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
|
||||
{
|
||||
if ( ! is_array($id)) {
|
||||
$id = array($this->_class->identifier[0] => $id);
|
||||
}
|
||||
$sortedId = array();
|
||||
foreach ($this->_class->identifier as $identifier) {
|
||||
if (!isset($id[$identifier])) {
|
||||
throw ORMException::missingIdentifierField($this->_class->name, $identifier);
|
||||
}
|
||||
$sortedId[$identifier] = $id[$identifier];
|
||||
}
|
||||
|
||||
// Check identity map first
|
||||
if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
|
||||
if (!($entity instanceof $this->_class->name)) {
|
||||
if ($entity = $this->_em->getUnitOfWork()->tryGetById($sortedId, $this->_class->rootEntityName)) {
|
||||
if ( ! ($entity instanceof $this->_class->name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($lockMode != LockMode::NONE) {
|
||||
|
||||
if ($lockMode !== LockMode::NONE) {
|
||||
$this->_em->lock($entity, $lockMode, $lockVersion);
|
||||
}
|
||||
|
||||
return $entity; // Hit!
|
||||
}
|
||||
|
||||
if ( ! is_array($id) || count($id) <= 1) {
|
||||
// @todo FIXME: Not correct. Relies on specific order.
|
||||
$value = is_array($id) ? array_values($id) : array($id);
|
||||
$id = array_combine($this->_class->identifier, $value);
|
||||
}
|
||||
switch ($lockMode) {
|
||||
case LockMode::NONE:
|
||||
return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId);
|
||||
|
||||
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);
|
||||
case LockMode::OPTIMISTIC:
|
||||
if ( ! $this->_class->isVersioned) {
|
||||
throw OptimisticLockException::notVersioned($this->_entityName);
|
||||
}
|
||||
|
||||
$this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
|
||||
$entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($sortedId);
|
||||
|
||||
return $entity;
|
||||
} 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($sortedId, null, null, array(), $lockMode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,30 +200,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,8 +23,9 @@ namespace Doctrine\ORM\Internal;
|
||||
* The CommitOrderCalculator is used by the UnitOfWork to sort out the
|
||||
* correct order in which changes to entities need to be persisted.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
*/
|
||||
class CommitOrderCalculator
|
||||
{
|
||||
@ -60,10 +61,9 @@ class CommitOrderCalculator
|
||||
{
|
||||
// Check whether we need to do anything. 0 or 1 node is easy.
|
||||
$nodeCount = count($this->_classes);
|
||||
if ($nodeCount == 0) {
|
||||
return array();
|
||||
} else if ($nodeCount == 1) {
|
||||
return array_values($this->_classes);
|
||||
|
||||
if ($nodeCount <= 1) {
|
||||
return ($nodeCount == 1) ? array_values($this->_classes) : array();
|
||||
}
|
||||
|
||||
// Init
|
||||
|
@ -28,11 +28,6 @@ use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
|
||||
*
|
||||
* @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable.
|
||||
* Example: SELECT u AS user FROM User u
|
||||
* The result should contains an array where each array index is an array: array('user' => [User object])
|
||||
* Problem must be solved somehow by removing the isMixed in ResultSetMapping
|
||||
*/
|
||||
class ArrayHydrator extends AbstractHydrator
|
||||
{
|
||||
|
@ -23,6 +23,8 @@ use PDO,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\PersistentCollection,
|
||||
Doctrine\ORM\Query,
|
||||
Doctrine\ORM\Event\LifecycleEventArgs,
|
||||
Doctrine\ORM\Events,
|
||||
Doctrine\Common\Collections\ArrayCollection,
|
||||
Doctrine\Common\Collections\Collection;
|
||||
|
||||
@ -34,11 +36,6 @@ use PDO,
|
||||
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
|
||||
*
|
||||
* @internal Highly performance-sensitive code.
|
||||
*
|
||||
* @todo General behavior is "wrong" if you define an alias to selected IdentificationVariable.
|
||||
* Example: SELECT u AS user FROM User u
|
||||
* The result should contains an array where each array index is an array: array('user' => [User object])
|
||||
* Problem must be solved somehow by removing the isMixed in ResultSetMapping
|
||||
*/
|
||||
class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
@ -235,7 +232,21 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
$this->_hints['fetchAlias'] = $dqlAlias;
|
||||
return $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
|
||||
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
|
||||
|
||||
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
|
||||
if (isset($this->_ce[$className]->lifecycleCallbacks[Events::postLoad])) {
|
||||
$this->_ce[$className]->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||
}
|
||||
|
||||
$evm = $this->_em->getEventManager();
|
||||
|
||||
if ($evm->hasListeners(Events::postLoad)) {
|
||||
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
private function _getEntityFromIdentityMap($className, array $data)
|
||||
|
@ -19,10 +19,12 @@
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use \PDO;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\ORM\Query;
|
||||
use \PDO,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Event\LifecycleEventArgs,
|
||||
Doctrine\ORM\Events,
|
||||
Doctrine\ORM\Query;
|
||||
|
||||
class SimpleObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
@ -125,7 +127,21 @@ class SimpleObjectHydrator extends AbstractHydrator
|
||||
$this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
}
|
||||
|
||||
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
$entity = $uow->createEntity($entityName, $data, $this->_hints);
|
||||
|
||||
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
|
||||
if (isset($this->class->lifecycleCallbacks[Events::postLoad])) {
|
||||
$this->class->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||
}
|
||||
|
||||
$evm = $this->_em->getEventManager();
|
||||
|
||||
if ($evm->hasListeners(Events::postLoad)) {
|
||||
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
|
||||
}
|
||||
|
||||
$result[] = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
|
24
lib/Doctrine/ORM/Mapping/Annotation.php
Normal file
24
lib/Doctrine/ORM/Mapping/Annotation.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
interface Annotation
|
||||
{
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php
Normal file
30
lib/Doctrine/ORM/Mapping/ChangeTrackingPolicy.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class ChangeTrackingPolicy implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -521,6 +521,12 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// Check for namespace alias
|
||||
if (strpos($class, ':') !== false) {
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $class);
|
||||
$class = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
}
|
||||
|
||||
return $this->driver->isTransient($class);
|
||||
}
|
||||
}
|
||||
|
46
lib/Doctrine/ORM/Mapping/Column.php
Normal file
46
lib/Doctrine/ORM/Mapping/Column.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Column implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var mixed */
|
||||
public $type = 'string';
|
||||
/** @var integer */
|
||||
public $length;
|
||||
/** @var integer */
|
||||
public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
/** @var integer */
|
||||
public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
/** @var boolean */
|
||||
public $unique = false;
|
||||
/** @var boolean */
|
||||
public $nullable = false;
|
||||
/** @var array */
|
||||
public $options = array();
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
}
|
36
lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php
Normal file
36
lib/Doctrine/ORM/Mapping/DiscriminatorColumn.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorColumn implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $type;
|
||||
/** @var integer */
|
||||
public $length;
|
||||
/** @var mixed */
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/DiscriminatorMap.php
Normal file
30
lib/Doctrine/ORM/Mapping/DiscriminatorMap.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorMap implements Annotation
|
||||
{
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
}
|
@ -17,379 +17,38 @@
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
interface Annotation {}
|
||||
|
||||
|
||||
/* Annotations */
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class Entity implements Annotation {
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
/** @var boolean */
|
||||
public $readOnly = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class MappedSuperclass implements Annotation {
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class InheritanceType implements Annotation {
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorColumn implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $type;
|
||||
/** @var integer */
|
||||
public $length;
|
||||
/** @var mixed */
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class DiscriminatorMap implements Annotation {
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Id implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class GeneratedValue implements Annotation {
|
||||
/** @var string */
|
||||
public $strategy = 'AUTO';
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Version implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY","ANNOTATION"})
|
||||
*/
|
||||
final class JoinColumn implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $referencedColumnName = 'id';
|
||||
/** @var boolean */
|
||||
public $unique = false;
|
||||
/** @var boolean */
|
||||
public $nullable = true;
|
||||
/** @var mixed */
|
||||
public $onDelete;
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
/** @var string */
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class JoinColumns implements Annotation {
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Column implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var mixed */
|
||||
public $type = 'string';
|
||||
/** @var integer */
|
||||
public $length;
|
||||
/** @var integer */
|
||||
public $precision = 0; // The precision for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
/** @var integer */
|
||||
public $scale = 0; // The scale for a decimal (exact numeric) column (Applies only for decimal column)
|
||||
/** @var boolean */
|
||||
public $unique = false;
|
||||
/** @var boolean */
|
||||
public $nullable = false;
|
||||
/** @var array */
|
||||
public $options = array();
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OneToOne implements Annotation {
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var boolean */
|
||||
public $orphanRemoval = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OneToMany implements Annotation {
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var boolean */
|
||||
public $orphanRemoval = false;
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class ManyToOne implements Annotation {
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class ManyToMany implements Annotation {
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @todo check available targets
|
||||
*/
|
||||
final class ElementCollection implements Annotation {
|
||||
/** @var string */
|
||||
public $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class Table implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $schema;
|
||||
/** @var array<Doctrine\ORM\Mapping\Index> */
|
||||
public $indexes;
|
||||
/** @var array<Doctrine\ORM\Mapping\UniqueConstraint> */
|
||||
public $uniqueConstraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class UniqueConstraint implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class Index implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class JoinTable implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $schema;
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $joinColumns = array();
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $inverseJoinColumns = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class SequenceGenerator implements Annotation {
|
||||
/** @var string */
|
||||
public $sequenceName;
|
||||
/** @var integer */
|
||||
public $allocationSize = 1;
|
||||
/** @var integer */
|
||||
public $initialValue = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class ChangeTrackingPolicy implements Annotation {
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OrderBy implements Annotation {
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class NamedQueries implements Annotation {
|
||||
/** @var array<Doctrine\ORM\Mapping\NamedQuery> */
|
||||
public $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class NamedQuery implements Annotation {
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $query;
|
||||
}
|
||||
|
||||
/* Annotations for lifecycle callbacks */
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class HasLifecycleCallbacks implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PrePersist implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostPersist implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreUpdate implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostUpdate implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreRemove implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostRemove implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostLoad implements Annotation {}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreFlush implements Annotation {}
|
||||
require_once __DIR__.'/../Annotation.php';
|
||||
require_once __DIR__.'/../Entity.php';
|
||||
require_once __DIR__.'/../MappedSuperclass.php';
|
||||
require_once __DIR__.'/../InheritanceType.php';
|
||||
require_once __DIR__.'/../DiscriminatorColumn.php';
|
||||
require_once __DIR__.'/../DiscriminatorMap.php';
|
||||
require_once __DIR__.'/../Id.php';
|
||||
require_once __DIR__.'/../GeneratedValue.php';
|
||||
require_once __DIR__.'/../Version.php';
|
||||
require_once __DIR__.'/../JoinColumn.php';
|
||||
require_once __DIR__.'/../JoinColumns.php';
|
||||
require_once __DIR__.'/../Column.php';
|
||||
require_once __DIR__.'/../OneToOne.php';
|
||||
require_once __DIR__.'/../OneToMany.php';
|
||||
require_once __DIR__.'/../ManyToOne.php';
|
||||
require_once __DIR__.'/../ManyToMany.php';
|
||||
require_once __DIR__.'/../ElementCollection.php';
|
||||
require_once __DIR__.'/../Table.php';
|
||||
require_once __DIR__.'/../UniqueConstraint.php';
|
||||
require_once __DIR__.'/../Index.php';
|
||||
require_once __DIR__.'/../JoinTable.php';
|
||||
require_once __DIR__.'/../SequenceGenerator.php';
|
||||
require_once __DIR__.'/../ChangeTrackingPolicy.php';
|
||||
require_once __DIR__.'/../OrderBy.php';
|
||||
require_once __DIR__.'/../NamedQueries.php';
|
||||
require_once __DIR__.'/../NamedQuery.php';
|
||||
require_once __DIR__.'/../HasLifecycleCallbacks.php';
|
||||
require_once __DIR__.'/../PrePersist.php';
|
||||
require_once __DIR__.'/../PostPersist.php';
|
||||
require_once __DIR__.'/../PreUpdate.php';
|
||||
require_once __DIR__.'/../PostUpdate.php';
|
||||
require_once __DIR__.'/../PreRemove.php';
|
||||
require_once __DIR__.'/../PostRemove.php';
|
||||
require_once __DIR__.'/../PostLoad.php';
|
||||
require_once __DIR__.'/../PreFlush.php';
|
||||
|
@ -232,6 +232,10 @@ class XmlDriver extends AbstractFileDriver
|
||||
if (isset($idElement['column'])) {
|
||||
$mapping['columnName'] = (string)$idElement['column'];
|
||||
}
|
||||
|
||||
if (isset($idElement['column-definition'])) {
|
||||
$mapping['columnDefinition'] = (string)$idElement['column-definition'];
|
||||
}
|
||||
|
||||
$metadata->mapField($mapping);
|
||||
|
||||
|
@ -181,6 +181,10 @@ class YamlDriver extends AbstractFileDriver
|
||||
if (isset($idElement['length'])) {
|
||||
$mapping['length'] = $idElement['length'];
|
||||
}
|
||||
|
||||
if (isset($idElement['columnDefinition'])) {
|
||||
$mapping['columnDefinition'] = $idElement['columnDefinition'];
|
||||
}
|
||||
|
||||
$metadata->mapField($mapping);
|
||||
|
||||
|
31
lib/Doctrine/ORM/Mapping/ElementCollection.php
Normal file
31
lib/Doctrine/ORM/Mapping/ElementCollection.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @todo check available targets
|
||||
*/
|
||||
final class ElementCollection implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $tableName;
|
||||
}
|
32
lib/Doctrine/ORM/Mapping/Entity.php
Normal file
32
lib/Doctrine/ORM/Mapping/Entity.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class Entity implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
/** @var boolean */
|
||||
public $readOnly = false;
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/GeneratedValue.php
Normal file
30
lib/Doctrine/ORM/Mapping/GeneratedValue.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class GeneratedValue implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $strategy = 'AUTO';
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php
Normal file
28
lib/Doctrine/ORM/Mapping/HasLifecycleCallbacks.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class HasLifecycleCallbacks implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/Id.php
Normal file
28
lib/Doctrine/ORM/Mapping/Id.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Id implements Annotation
|
||||
{
|
||||
}
|
32
lib/Doctrine/ORM/Mapping/Index.php
Normal file
32
lib/Doctrine/ORM/Mapping/Index.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class Index implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/InheritanceType.php
Normal file
30
lib/Doctrine/ORM/Mapping/InheritanceType.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class InheritanceType implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $value;
|
||||
}
|
42
lib/Doctrine/ORM/Mapping/JoinColumn.php
Normal file
42
lib/Doctrine/ORM/Mapping/JoinColumn.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY","ANNOTATION"})
|
||||
*/
|
||||
final class JoinColumn implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $referencedColumnName = 'id';
|
||||
/** @var boolean */
|
||||
public $unique = false;
|
||||
/** @var boolean */
|
||||
public $nullable = true;
|
||||
/** @var mixed */
|
||||
public $onDelete;
|
||||
/** @var string */
|
||||
public $columnDefinition;
|
||||
/** @var string */
|
||||
public $fieldName; // field name used in non-object hydration (array/scalar)
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/JoinColumns.php
Normal file
30
lib/Doctrine/ORM/Mapping/JoinColumns.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class JoinColumns implements Annotation
|
||||
{
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $value;
|
||||
}
|
36
lib/Doctrine/ORM/Mapping/JoinTable.php
Normal file
36
lib/Doctrine/ORM/Mapping/JoinTable.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class JoinTable implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $schema;
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $joinColumns = array();
|
||||
/** @var array<Doctrine\ORM\Mapping\JoinColumn> */
|
||||
public $inverseJoinColumns = array();
|
||||
}
|
40
lib/Doctrine/ORM/Mapping/ManyToMany.php
Normal file
40
lib/Doctrine/ORM/Mapping/ManyToMany.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class ManyToMany implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
}
|
36
lib/Doctrine/ORM/Mapping/ManyToOne.php
Normal file
36
lib/Doctrine/ORM/Mapping/ManyToOne.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class ManyToOne implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/MappedSuperclass.php
Normal file
30
lib/Doctrine/ORM/Mapping/MappedSuperclass.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class MappedSuperclass implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $repositoryClass;
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/NamedQueries.php
Normal file
30
lib/Doctrine/ORM/Mapping/NamedQueries.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class NamedQueries implements Annotation
|
||||
{
|
||||
/** @var array<Doctrine\ORM\Mapping\NamedQuery> */
|
||||
public $value;
|
||||
}
|
32
lib/Doctrine/ORM/Mapping/NamedQuery.php
Normal file
32
lib/Doctrine/ORM/Mapping/NamedQuery.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class NamedQuery implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $query;
|
||||
}
|
40
lib/Doctrine/ORM/Mapping/OneToMany.php
Normal file
40
lib/Doctrine/ORM/Mapping/OneToMany.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OneToMany implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var boolean */
|
||||
public $orphanRemoval = false;
|
||||
/** @var string */
|
||||
public $indexBy;
|
||||
}
|
40
lib/Doctrine/ORM/Mapping/OneToOne.php
Normal file
40
lib/Doctrine/ORM/Mapping/OneToOne.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OneToOne implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $targetEntity;
|
||||
/** @var string */
|
||||
public $mappedBy;
|
||||
/** @var string */
|
||||
public $inversedBy;
|
||||
/** @var array<string> */
|
||||
public $cascade;
|
||||
/** @var string */
|
||||
public $fetch = 'LAZY';
|
||||
/** @var boolean */
|
||||
public $orphanRemoval = false;
|
||||
}
|
30
lib/Doctrine/ORM/Mapping/OrderBy.php
Normal file
30
lib/Doctrine/ORM/Mapping/OrderBy.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class OrderBy implements Annotation
|
||||
{
|
||||
/** @var array<string> */
|
||||
public $value;
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PostLoad.php
Normal file
28
lib/Doctrine/ORM/Mapping/PostLoad.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostLoad implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PostPersist.php
Normal file
28
lib/Doctrine/ORM/Mapping/PostPersist.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostPersist implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PostRemove.php
Normal file
28
lib/Doctrine/ORM/Mapping/PostRemove.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostRemove implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PostUpdate.php
Normal file
28
lib/Doctrine/ORM/Mapping/PostUpdate.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PostUpdate implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PreFlush.php
Normal file
28
lib/Doctrine/ORM/Mapping/PreFlush.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreFlush implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PrePersist.php
Normal file
28
lib/Doctrine/ORM/Mapping/PrePersist.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PrePersist implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PreRemove.php
Normal file
28
lib/Doctrine/ORM/Mapping/PreRemove.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreRemove implements Annotation
|
||||
{
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/PreUpdate.php
Normal file
28
lib/Doctrine/ORM/Mapping/PreUpdate.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
*/
|
||||
final class PreUpdate implements Annotation
|
||||
{
|
||||
}
|
34
lib/Doctrine/ORM/Mapping/SequenceGenerator.php
Normal file
34
lib/Doctrine/ORM/Mapping/SequenceGenerator.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class SequenceGenerator implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $sequenceName;
|
||||
/** @var integer */
|
||||
public $allocationSize = 1;
|
||||
/** @var integer */
|
||||
public $initialValue = 1;
|
||||
}
|
36
lib/Doctrine/ORM/Mapping/Table.php
Normal file
36
lib/Doctrine/ORM/Mapping/Table.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class Table implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $schema;
|
||||
/** @var array<Doctrine\ORM\Mapping\Index> */
|
||||
public $indexes;
|
||||
/** @var array<Doctrine\ORM\Mapping\UniqueConstraint> */
|
||||
public $uniqueConstraints;
|
||||
}
|
32
lib/Doctrine/ORM/Mapping/UniqueConstraint.php
Normal file
32
lib/Doctrine/ORM/Mapping/UniqueConstraint.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ANNOTATION")
|
||||
*/
|
||||
final class UniqueConstraint implements Annotation
|
||||
{
|
||||
/** @var string */
|
||||
public $name;
|
||||
/** @var array<string> */
|
||||
public $columns;
|
||||
}
|
28
lib/Doctrine/ORM/Mapping/Version.php
Normal file
28
lib/Doctrine/ORM/Mapping/Version.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Mapping;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("PROPERTY")
|
||||
*/
|
||||
final class Version implements Annotation
|
||||
{
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
@ -144,4 +144,9 @@ class ORMException extends Exception
|
||||
return new self("Invalid repository class '".$className."'. ".
|
||||
"it must be a Doctrine\ORM\EntityRepository.");
|
||||
}
|
||||
|
||||
public static function missingIdentifierField($className, $fieldName)
|
||||
{
|
||||
return new self("The identifier $fieldName is missing for a query of " . $className);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\Common\Collections\Collection,
|
||||
Doctrine\Common\Collections\ArrayCollection,
|
||||
Closure;
|
||||
|
||||
/**
|
||||
@ -114,8 +115,8 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function __construct(EntityManager $em, $class, $coll)
|
||||
{
|
||||
$this->coll = $coll;
|
||||
$this->em = $em;
|
||||
$this->coll = $coll;
|
||||
$this->em = $em;
|
||||
$this->typeClass = $class;
|
||||
}
|
||||
|
||||
@ -129,8 +130,8 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function setOwner($entity, array $assoc)
|
||||
{
|
||||
$this->owner = $entity;
|
||||
$this->association = $assoc;
|
||||
$this->owner = $entity;
|
||||
$this->association = $assoc;
|
||||
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy'];
|
||||
}
|
||||
|
||||
@ -160,16 +161,18 @@ final class PersistentCollection implements Collection
|
||||
public function hydrateAdd($element)
|
||||
{
|
||||
$this->coll->add($element);
|
||||
|
||||
// If _backRefFieldName is set and its a one-to-many association,
|
||||
// we need to set the back reference.
|
||||
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||
if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) {
|
||||
// Set back reference to owner
|
||||
$this->typeClass->reflFields[$this->backRefFieldName]
|
||||
->setValue($element, $this->owner);
|
||||
$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
|
||||
$element, $this->owner
|
||||
);
|
||||
|
||||
$this->em->getUnitOfWork()->setOriginalEntityProperty(
|
||||
spl_object_hash($element),
|
||||
$this->backRefFieldName,
|
||||
$this->owner);
|
||||
spl_object_hash($element), $this->backRefFieldName, $this->owner
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,12 +186,14 @@ final class PersistentCollection implements Collection
|
||||
public function hydrateSet($key, $element)
|
||||
{
|
||||
$this->coll->set($key, $element);
|
||||
|
||||
// If _backRefFieldName is set, then the association is bidirectional
|
||||
// and we need to set the back reference.
|
||||
if ($this->backRefFieldName && $this->association['type'] == ClassMetadata::ONE_TO_MANY) {
|
||||
if ($this->backRefFieldName && $this->association['type'] === ClassMetadata::ONE_TO_MANY) {
|
||||
// Set back reference to owner
|
||||
$this->typeClass->reflFields[$this->backRefFieldName]
|
||||
->setValue($element, $this->owner);
|
||||
$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
|
||||
$element, $this->owner
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,23 +203,31 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
if ( ! $this->initialized && $this->association) {
|
||||
if ($this->isDirty) {
|
||||
// Has NEW objects added through add(). Remember them.
|
||||
$newObjects = $this->coll->toArray();
|
||||
}
|
||||
$this->coll->clear();
|
||||
$this->em->getUnitOfWork()->loadCollection($this);
|
||||
$this->takeSnapshot();
|
||||
// Reattach NEW objects added through add(), if any.
|
||||
if (isset($newObjects)) {
|
||||
foreach ($newObjects as $obj) {
|
||||
$this->coll->add($obj);
|
||||
}
|
||||
$this->isDirty = true;
|
||||
}
|
||||
$this->initialized = true;
|
||||
if ($this->initialized || ! $this->association) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Has NEW objects added through add(). Remember them.
|
||||
$newObjects = array();
|
||||
|
||||
if ($this->isDirty) {
|
||||
$newObjects = $this->coll->toArray();
|
||||
}
|
||||
|
||||
$this->coll->clear();
|
||||
$this->em->getUnitOfWork()->loadCollection($this);
|
||||
$this->takeSnapshot();
|
||||
|
||||
// Reattach NEW objects added through add(), if any.
|
||||
if ($newObjects) {
|
||||
foreach ($newObjects as $obj) {
|
||||
$this->coll->add($obj);
|
||||
}
|
||||
|
||||
$this->isDirty = true;
|
||||
}
|
||||
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,7 +237,7 @@ final class PersistentCollection implements Collection
|
||||
public function takeSnapshot()
|
||||
{
|
||||
$this->snapshot = $this->coll->toArray();
|
||||
$this->isDirty = false;
|
||||
$this->isDirty = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,8 +259,11 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function getDeleteDiff()
|
||||
{
|
||||
return array_udiff_assoc($this->snapshot, $this->coll->toArray(),
|
||||
function($a, $b) {return $a === $b ? 0 : 1;});
|
||||
return array_udiff_assoc(
|
||||
$this->snapshot,
|
||||
$this->coll->toArray(),
|
||||
function($a, $b) { return $a === $b ? 0 : 1; }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,8 +274,11 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function getInsertDiff()
|
||||
{
|
||||
return array_udiff_assoc($this->coll->toArray(), $this->snapshot,
|
||||
function($a, $b) {return $a === $b ? 0 : 1;});
|
||||
return array_udiff_assoc(
|
||||
$this->coll->toArray(),
|
||||
$this->snapshot,
|
||||
function($a, $b) { return $a === $b ? 0 : 1; }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,12 +296,17 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
private function changed()
|
||||
{
|
||||
if ( ! $this->isDirty) {
|
||||
$this->isDirty = true;
|
||||
if ($this->association !== null && $this->association['isOwningSide'] && $this->association['type'] == ClassMetadata::MANY_TO_MANY &&
|
||||
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
||||
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
||||
}
|
||||
if ($this->isDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->isDirty = true;
|
||||
|
||||
if ($this->association !== null &&
|
||||
$this->association['isOwningSide'] &&
|
||||
$this->association['type'] === ClassMetadata::MANY_TO_MANY &&
|
||||
$this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
|
||||
$this->em->getUnitOfWork()->scheduleForDirtyCheck($this->owner);
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,6 +355,7 @@ final class PersistentCollection implements Collection
|
||||
public function first()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->first();
|
||||
}
|
||||
|
||||
@ -338,6 +363,7 @@ final class PersistentCollection implements Collection
|
||||
public function last()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->last();
|
||||
}
|
||||
|
||||
@ -351,13 +377,19 @@ final class PersistentCollection implements Collection
|
||||
// not used we can issue a straight SQL delete/update on the
|
||||
// association (table). Without initializing the collection.
|
||||
$this->initialize();
|
||||
|
||||
$removed = $this->coll->remove($key);
|
||||
if ($removed) {
|
||||
$this->changed();
|
||||
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||
$this->association['orphanRemoval']) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
||||
}
|
||||
|
||||
if ( ! $removed) {
|
||||
return $removed;
|
||||
}
|
||||
|
||||
$this->changed();
|
||||
|
||||
if ($this->association !== null &&
|
||||
$this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||
$this->association['orphanRemoval']) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
|
||||
}
|
||||
|
||||
return $removed;
|
||||
@ -368,25 +400,36 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function removeElement($element)
|
||||
{
|
||||
// TODO: Assuming the identity of entities in a collection is always based
|
||||
// on their primary key (there is no equals/hashCode in PHP),
|
||||
// if the collection is not initialized, we could issue a straight
|
||||
// SQL DELETE/UPDATE on the association (table) without initializing
|
||||
// the collection.
|
||||
/*if ( ! $this->initialized) {
|
||||
$this->em->getUnitOfWork()->getCollectionPersister($this->association)
|
||||
->deleteRows($this, $element);
|
||||
}*/
|
||||
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
if ($this->coll->contains($element)) {
|
||||
return $this->coll->removeElement($element);
|
||||
}
|
||||
|
||||
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||
|
||||
if ($persister->removeElement($this, $element)) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
|
||||
$removed = $this->coll->removeElement($element);
|
||||
if ($removed) {
|
||||
$this->changed();
|
||||
if ($this->association !== null && $this->association['type'] == ClassMetadata::ONE_TO_MANY &&
|
||||
$this->association['orphanRemoval']) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||
}
|
||||
|
||||
if ( ! $removed) {
|
||||
return $removed;
|
||||
}
|
||||
|
||||
$this->changed();
|
||||
|
||||
if ($this->association !== null &&
|
||||
$this->association['type'] === ClassMetadata::ONE_TO_MANY &&
|
||||
$this->association['orphanRemoval']) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||
}
|
||||
|
||||
return $removed;
|
||||
}
|
||||
|
||||
@ -396,6 +439,7 @@ final class PersistentCollection implements Collection
|
||||
public function containsKey($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->containsKey($key);
|
||||
}
|
||||
|
||||
@ -404,14 +448,14 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function contains($element)
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->coll->contains($element) ||
|
||||
$this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->contains($this, $element);
|
||||
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||
|
||||
return $this->coll->contains($element) || $persister->contains($this, $element);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->contains($element);
|
||||
}
|
||||
|
||||
@ -421,6 +465,7 @@ final class PersistentCollection implements Collection
|
||||
public function exists(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->exists($p);
|
||||
}
|
||||
|
||||
@ -430,6 +475,7 @@ final class PersistentCollection implements Collection
|
||||
public function indexOf($element)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->indexOf($element);
|
||||
}
|
||||
|
||||
@ -439,6 +485,7 @@ final class PersistentCollection implements Collection
|
||||
public function get($key)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->get($key);
|
||||
}
|
||||
|
||||
@ -448,6 +495,7 @@ final class PersistentCollection implements Collection
|
||||
public function getKeys()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->getKeys();
|
||||
}
|
||||
|
||||
@ -457,6 +505,7 @@ final class PersistentCollection implements Collection
|
||||
public function getValues()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->getValues();
|
||||
}
|
||||
|
||||
@ -465,13 +514,14 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->count($this) + ($this->isDirty ? $this->coll->count() : 0);
|
||||
if ( ! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||
|
||||
return $persister->count($this) + ($this->isDirty ? $this->coll->count() : 0);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->count();
|
||||
}
|
||||
|
||||
@ -481,7 +531,9 @@ final class PersistentCollection implements Collection
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$this->coll->set($key, $value);
|
||||
|
||||
$this->changed();
|
||||
}
|
||||
|
||||
@ -491,7 +543,9 @@ final class PersistentCollection implements Collection
|
||||
public function add($value)
|
||||
{
|
||||
$this->coll->add($value);
|
||||
|
||||
$this->changed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -501,6 +555,7 @@ final class PersistentCollection implements Collection
|
||||
public function isEmpty()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->isEmpty();
|
||||
}
|
||||
|
||||
@ -510,6 +565,7 @@ final class PersistentCollection implements Collection
|
||||
public function getIterator()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->getIterator();
|
||||
}
|
||||
|
||||
@ -519,6 +575,7 @@ final class PersistentCollection implements Collection
|
||||
public function map(Closure $func)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->map($func);
|
||||
}
|
||||
|
||||
@ -528,6 +585,7 @@ final class PersistentCollection implements Collection
|
||||
public function filter(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->filter($p);
|
||||
}
|
||||
|
||||
@ -537,6 +595,7 @@ final class PersistentCollection implements Collection
|
||||
public function forAll(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->forAll($p);
|
||||
}
|
||||
|
||||
@ -546,6 +605,7 @@ final class PersistentCollection implements Collection
|
||||
public function partition(Closure $p)
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->partition($p);
|
||||
}
|
||||
|
||||
@ -555,6 +615,7 @@ final class PersistentCollection implements Collection
|
||||
public function toArray()
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->toArray();
|
||||
}
|
||||
|
||||
@ -566,19 +627,28 @@ final class PersistentCollection implements Collection
|
||||
if ($this->initialized && $this->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if ($this->association['type'] == ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
|
||||
|
||||
$uow = $this->em->getUnitOfWork();
|
||||
|
||||
if ($this->association['type'] === ClassMetadata::ONE_TO_MANY && $this->association['orphanRemoval']) {
|
||||
// we need to initialize here, as orphan removal acts like implicit cascadeRemove,
|
||||
// hence for event listeners we need the objects in memory.
|
||||
$this->initialize();
|
||||
|
||||
foreach ($this->coll as $element) {
|
||||
$this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
|
||||
$uow->scheduleOrphanRemoval($element);
|
||||
}
|
||||
}
|
||||
|
||||
$this->coll->clear();
|
||||
|
||||
$this->initialized = true; // direct call, {@link initialize()} is too expensive
|
||||
|
||||
if ($this->association['isOwningSide']) {
|
||||
$this->changed();
|
||||
$this->em->getUnitOfWork()->scheduleCollectionDeletion($this);
|
||||
|
||||
$uow->scheduleCollectionDeletion($this);
|
||||
|
||||
$this->takeSnapshot();
|
||||
}
|
||||
}
|
||||
@ -622,6 +692,7 @@ final class PersistentCollection implements Collection
|
||||
if ( ! isset($offset)) {
|
||||
return $this->add($value);
|
||||
}
|
||||
|
||||
return $this->set($offset, $value);
|
||||
}
|
||||
|
||||
@ -656,6 +727,8 @@ final class PersistentCollection implements Collection
|
||||
|
||||
/**
|
||||
* Retrieves the wrapped Collection instance.
|
||||
*
|
||||
* @return Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
public function unwrap()
|
||||
{
|
||||
@ -671,20 +744,19 @@ final class PersistentCollection implements Collection
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
if ( ! $this->initialized &&
|
||||
! $this->isDirty &&
|
||||
$this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
if ( ! $this->initialized && ! $this->isDirty && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
|
||||
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->slice($this, $offset, $length);
|
||||
return $persister->slice($this, $offset, $length);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
|
||||
return $this->coll->slice($offset, $length);
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,16 @@ abstract class AbstractCollectionPersister
|
||||
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function removeElement(PersistentCollection $coll, $element)
|
||||
{
|
||||
throw new \BadMethodCallException("Removing an element is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function removeKey(PersistentCollection $coll, $key)
|
||||
{
|
||||
throw new \BadMethodCallException("Removing a key is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function get(PersistentCollection $coll, $index)
|
||||
{
|
||||
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
|
||||
|
@ -28,8 +28,9 @@ use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
/**
|
||||
* Persister for many-to-many collections.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ManyToManyPersister extends AbstractCollectionPersister
|
||||
{
|
||||
@ -80,8 +81,10 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$columns = $mapping['joinTableColumns'];
|
||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||
|
||||
return 'INSERT INTO ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
|
||||
$joinTable = $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform());
|
||||
|
||||
return 'INSERT INTO ' . $joinTable . ' (' . implode(', ', $columns) . ')'
|
||||
. ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,27 +122,21 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
}
|
||||
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
||||
if ($isComposite) {
|
||||
if ($class1->containsForeignIdentifier) {
|
||||
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
} else {
|
||||
$params[] = array_pop($identifier1);
|
||||
}
|
||||
} else {
|
||||
if ($isComposite) {
|
||||
if ($class2->containsForeignIdentifier) {
|
||||
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
} else {
|
||||
$params[] = array_pop($identifier2);
|
||||
}
|
||||
$isRelationToSource = isset($mapping['relationToSourceKeyColumns'][$joinTableColumn]);
|
||||
|
||||
if ( ! $isComposite) {
|
||||
$params[] = $isRelationToSource ? array_pop($identifier1) : array_pop($identifier2);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isRelationToSource) {
|
||||
$params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||
}
|
||||
|
||||
return $params;
|
||||
@ -152,19 +149,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
*/
|
||||
protected function _getDeleteSQL(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$whereClause = '';
|
||||
|
||||
foreach ($mapping['relationToSourceKeyColumns'] as $relationColumn => $srcColumn) {
|
||||
if ($whereClause !== '') $whereClause .= ' AND ';
|
||||
|
||||
$whereClause .= $relationColumn . ' = ?';
|
||||
}
|
||||
$class = $this->_em->getClassMetadata(get_class($coll->getOwner()));
|
||||
$mapping = $coll->getMapping();
|
||||
|
||||
return 'DELETE FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . $whereClause;
|
||||
. ' WHERE ' . implode(' = ? AND ', array_keys($mapping['relationToSourceKeyColumns'])) . ' = ?';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,20 +165,24 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
*/
|
||||
protected function _getDeleteSQLParameters(PersistentCollection $coll)
|
||||
{
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
$identifier = $this->_uow->getEntityIdentifier($coll->getOwner());
|
||||
$mapping = $coll->getMapping();
|
||||
$params = array();
|
||||
|
||||
if (count($mapping['relationToSourceKeyColumns']) > 1) {
|
||||
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
|
||||
// Optimization for single column identifier
|
||||
if (count($mapping['relationToSourceKeyColumns']) === 1) {
|
||||
$params[] = array_pop($identifier);
|
||||
|
||||
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
|
||||
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
|
||||
}
|
||||
} else {
|
||||
$params[] = array_pop($identifier);
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
// Composite identifier
|
||||
$sourceClass = $this->_em->getClassMetadata(get_class($mapping->getOwner()));
|
||||
|
||||
foreach ($mapping['relationToSourceKeyColumns'] as $relColumn => $srcColumn) {
|
||||
$params[] = $identifier[$sourceClass->fieldNames[$srcColumn]];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
@ -198,7 +191,6 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
*/
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
@ -210,28 +202,30 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$joinColumns = $mapping['relationToTargetKeyColumns'];
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
$whereClauses = array();
|
||||
$params = array();
|
||||
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($joinColumns[$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "t.$joinTableColumn = ?";
|
||||
|
||||
$params[] = ($class->containsForeignIdentifier)
|
||||
? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]
|
||||
: $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||
if ( ! isset($joinColumns[$joinTableColumn])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$whereClauses[] = $joinTableColumn . ' = ?';
|
||||
|
||||
$params[] = ($class->containsForeignIdentifier)
|
||||
? $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]
|
||||
: $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||
}
|
||||
|
||||
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
|
||||
|
||||
if('' !== $filterSql) {
|
||||
$whereClauses[] = $filterSql;
|
||||
}
|
||||
|
||||
$sql = 'SELECT COUNT(*)'
|
||||
. ' FROM ' . $class->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) . ' t'
|
||||
. $joinTargetEntitySQL
|
||||
. ' WHERE ' . $whereClause
|
||||
. $filterSql;
|
||||
. ' WHERE ' . implode(' AND ', $whereClauses);
|
||||
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
@ -252,6 +246,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param object $element
|
||||
* @return boolean
|
||||
*/
|
||||
public function contains(PersistentCollection $coll, $element)
|
||||
{
|
||||
@ -262,9 +257,45 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, true);
|
||||
|
||||
$sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
|
||||
|
||||
return (bool) $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param object $element
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeElement(PersistentCollection $coll, $element)
|
||||
{
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
|
||||
// shortcut for new entities
|
||||
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, $element, false);
|
||||
|
||||
$sql = 'DELETE FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
|
||||
|
||||
return (bool) $this->_conn->executeUpdate($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Doctrine\ORM\PersistentCollection $coll
|
||||
* @param object $element
|
||||
* @param boolean $addFilters Whether the filter SQL should be included or not.
|
||||
* @return array
|
||||
*/
|
||||
private function getJoinTableRestrictions(PersistentCollection $coll, $element, $addFilters)
|
||||
{
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
$mapping = $coll->getMapping();
|
||||
|
||||
if ( ! $mapping['isOwningSide']) {
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
@ -279,41 +310,35 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
$targetId = $uow->getEntityIdentifier($element);
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform());
|
||||
$whereClauses = array();
|
||||
$params = array();
|
||||
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
|
||||
$whereClause .= $joinTableColumn . ' = ?';
|
||||
$whereClauses[] = $joinTableColumn . ' = ?';
|
||||
|
||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||
$params[] = ($targetClass->containsForeignIdentifier)
|
||||
? $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
|
||||
: $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
|
||||
$whereClause .= $joinTableColumn . ' = ?';
|
||||
|
||||
$params[] = ($sourceClass->containsForeignIdentifier)
|
||||
? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
|
||||
: $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
continue;
|
||||
}
|
||||
|
||||
// relationToSourceKeyColumns
|
||||
$params[] = ($sourceClass->containsForeignIdentifier)
|
||||
? $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]
|
||||
: $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
|
||||
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
|
||||
if($addFilters) {
|
||||
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping);
|
||||
if('' !== $filterSql) {
|
||||
$quotedJoinTable .= ' t ' . $joinTargetEntitySQL;
|
||||
$whereClauses[] = $filterSql;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT 1'
|
||||
. ' FROM ' . $sourceClass->getQuotedJoinTableName($mapping, $this->_conn->getDatabasePlatform()) . ' t'
|
||||
. $joinTargetEntitySQL
|
||||
. ' WHERE ' . $whereClause
|
||||
. $filterSql;
|
||||
|
||||
return (bool) $this->_conn->fetchColumn($sql, $params);
|
||||
return array($quotedJoinTable, $whereClauses, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,9 +381,11 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
{
|
||||
$filterSql = '';
|
||||
|
||||
$first = true;
|
||||
foreach($this->_em->getFilters()->getEnabledFilters() as $filter) {
|
||||
if('' !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) {
|
||||
$filterSql .= 'AND (' . $filterExpr . ')';
|
||||
if($first) $first = false; else $filterSql .= ' AND ';
|
||||
$filterSql .= '(' . $filterExpr . ')';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,13 +27,9 @@ use Doctrine\ORM\PersistentCollection,
|
||||
/**
|
||||
* Persister for one-to-many collections.
|
||||
*
|
||||
* IMPORTANT:
|
||||
* This persister is only used for uni-directional one-to-many mappings on a foreign key
|
||||
* (which are not yet supported). So currently this persister is not used.
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @todo Remove
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class OneToManyPersister extends AbstractCollectionPersister
|
||||
{
|
||||
@ -48,24 +44,19 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
protected function _getDeleteRowSQL(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$targetClass = $this->_em->getClassMetadata($mapping->getTargetEntityName());
|
||||
$table = $targetClass->getTableName();
|
||||
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
|
||||
return 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
|
||||
}
|
||||
|
||||
$ownerMapping = $targetClass->getAssociationMapping($mapping['mappedBy']);
|
||||
|
||||
$setClause = '';
|
||||
foreach ($ownerMapping->sourceToTargetKeyColumns as $sourceCol => $targetCol) {
|
||||
if ($setClause != '') $setClause .= ', ';
|
||||
$setClause .= "$sourceCol = NULL";
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
foreach ($targetClass->getIdentifierColumnNames() as $idColumn) {
|
||||
if ($whereClause != '') $whereClause .= ' AND ';
|
||||
$whereClause .= "$idColumn = ?";
|
||||
}
|
||||
|
||||
return array("UPDATE $table SET $setClause WHERE $whereClause", $this->_uow->getEntityIdentifier($element));
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
*/
|
||||
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
|
||||
{
|
||||
return array_values($this->_uow->getEntityIdentifier($element));
|
||||
}
|
||||
|
||||
protected function _getInsertRowSQL(PersistentCollection $coll)
|
||||
@ -73,6 +64,16 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL parameters for the corresponding SQL statement to insert the given
|
||||
* element of the given collection into the database.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
* @param mixed $element
|
||||
*/
|
||||
protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
|
||||
/* Not used for OneToManyPersister */
|
||||
protected function _getUpdateRowSQL(PersistentCollection $coll)
|
||||
{
|
||||
@ -98,60 +99,37 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
protected function _getDeleteSQLParameters(PersistentCollection $coll)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Gets the SQL parameters for the corresponding SQL statement to insert the given
|
||||
* element of the given collection into the database.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
* @param mixed $element
|
||||
*/
|
||||
protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Gets the SQL parameters for the corresponding SQL statement to delete the given
|
||||
* element from the given collection.
|
||||
*
|
||||
* @param PersistentCollection $coll
|
||||
* @param mixed $element
|
||||
*/
|
||||
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$mapping = $coll->getMapping();
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
$params = array();
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
$where = '';
|
||||
$whereClauses = array();
|
||||
$params = array();
|
||||
|
||||
foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
|
||||
if ($where != '') {
|
||||
$where .= ' AND ';
|
||||
}
|
||||
$where .= "t." . $joinColumn['name'] . " = ?";
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$params[] = $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])];
|
||||
} else {
|
||||
$params[] = $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
|
||||
}
|
||||
$whereClauses[] = $joinColumn['name'] . ' = ?';
|
||||
|
||||
$params[] = ($targetClass->containsForeignIdentifier)
|
||||
? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]
|
||||
: $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
|
||||
}
|
||||
|
||||
$sql = "SELECT count(*) FROM " . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . " t WHERE " . $where;
|
||||
|
||||
// Apply the filters
|
||||
foreach($this->_em->getFilters()->getEnabledFilters() as $filter) {
|
||||
if("" !== $filterExpr = $filter->addFilterConstraint($targetClass, 't')) {
|
||||
$sql .= ' AND (' . $filterExpr . ')';
|
||||
$whereClauses[] = '(' . $filterExpr . ')';
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT count(*)'
|
||||
. ' FROM ' . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' t'
|
||||
. ' WHERE ' . implode(' AND ', $whereClauses);
|
||||
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
@ -163,31 +141,57 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
*/
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
return $this->_em->getUnitOfWork()
|
||||
->getEntityPersister($mapping['targetEntity'])
|
||||
->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||
$mapping = $coll->getMapping();
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
$persister = $uow->getEntityPersister($mapping['targetEntity']);
|
||||
|
||||
return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param object $element
|
||||
* @return boolean
|
||||
*/
|
||||
public function contains(PersistentCollection $coll, $element)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
|
||||
// shortcut for new entities
|
||||
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only works with single id identifier entities. Will throw an exception in Entity Persisters
|
||||
// if that is not the case for the 'mappedBy' field.
|
||||
$persister = $uow->getEntityPersister($mapping['targetEntity']);
|
||||
|
||||
// only works with single id identifier entities. Will throw an
|
||||
// exception in Entity Persisters if that is not the case for the
|
||||
// 'mappedBy' field.
|
||||
$id = current( $uow->getEntityIdentifier($coll->getOwner()) );
|
||||
|
||||
return $uow->getEntityPersister($mapping['targetEntity'])
|
||||
->exists($element, array($mapping['mappedBy'] => $id));
|
||||
return $persister->exists($element, array($mapping['mappedBy'] => $id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param object $element
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeElement(PersistentCollection $coll, $element)
|
||||
{
|
||||
$uow = $this->_em->getUnitOfWork();
|
||||
|
||||
// shortcut for new entities
|
||||
if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform())
|
||||
. ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
|
||||
|
||||
return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
|
||||
}
|
||||
}
|
||||
|
@ -205,26 +205,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;
|
||||
}
|
||||
@ -235,6 +239,7 @@ final class Query extends AbstractQuery
|
||||
protected function _doExecute()
|
||||
{
|
||||
$executor = $this->_parse()->getSqlExecutor();
|
||||
|
||||
if ($this->_queryCacheProfile) {
|
||||
$executor->setQueryCacheProfile($this->_queryCacheProfile);
|
||||
}
|
||||
@ -342,6 +347,7 @@ final class Query extends AbstractQuery
|
||||
public function setQueryCacheDriver($queryCache)
|
||||
{
|
||||
$this->_queryCache = $queryCache;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -354,6 +360,7 @@ final class Query extends AbstractQuery
|
||||
public function useQueryCache($bool)
|
||||
{
|
||||
$this->_useQueryCache = $bool;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -367,9 +374,9 @@ final class Query extends AbstractQuery
|
||||
{
|
||||
if ($this->_queryCache) {
|
||||
return $this->_queryCache;
|
||||
} else {
|
||||
return $this->_em->getConfiguration()->getQueryCacheImpl();
|
||||
}
|
||||
|
||||
return $this->_em->getConfiguration()->getQueryCacheImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,6 +390,7 @@ final class Query extends AbstractQuery
|
||||
if ($timeToLive !== null) {
|
||||
$timeToLive = (int) $timeToLive;
|
||||
}
|
||||
|
||||
$this->_queryCacheTTL = $timeToLive;
|
||||
|
||||
return $this;
|
||||
@ -427,6 +435,7 @@ final class Query extends AbstractQuery
|
||||
public function free()
|
||||
{
|
||||
parent::free();
|
||||
|
||||
$this->_dql = null;
|
||||
$this->_state = self::STATE_CLEAN;
|
||||
}
|
||||
@ -443,6 +452,7 @@ final class Query extends AbstractQuery
|
||||
$this->_dql = $dqlQuery;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -492,6 +502,7 @@ final class Query extends AbstractQuery
|
||||
{
|
||||
$this->_firstResult = $firstResult;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -516,6 +527,7 @@ final class Query extends AbstractQuery
|
||||
{
|
||||
$this->_maxResults = $maxResults;
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -541,6 +553,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);
|
||||
}
|
||||
|
||||
@ -550,6 +563,7 @@ final class Query extends AbstractQuery
|
||||
public function setHint($name, $value)
|
||||
{
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
|
||||
return parent::setHint($name, $value);
|
||||
}
|
||||
|
||||
@ -559,6 +573,7 @@ final class Query extends AbstractQuery
|
||||
public function setHydrationMode($hydrationMode)
|
||||
{
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
|
||||
return parent::setHydrationMode($hydrationMode);
|
||||
}
|
||||
|
||||
@ -571,13 +586,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;
|
||||
}
|
||||
|
||||
@ -589,9 +605,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;
|
||||
}
|
||||
|
||||
@ -622,6 +640,7 @@ final class Query extends AbstractQuery
|
||||
public function __clone()
|
||||
{
|
||||
parent::__clone();
|
||||
|
||||
$this->_state = self::STATE_DIRTY;
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ namespace Doctrine\ORM\Query\AST;
|
||||
class InExpression extends Node
|
||||
{
|
||||
public $not;
|
||||
public $pathExpression;
|
||||
public $expression;
|
||||
public $literals = array();
|
||||
public $subselect;
|
||||
|
||||
public function __construct($pathExpression)
|
||||
public function __construct($expression)
|
||||
{
|
||||
$this->pathExpression = $pathExpression;
|
||||
$this->expression = $expression;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
|
@ -49,7 +49,7 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
const T_PLUS = 17;
|
||||
const T_OPEN_CURLY_BRACE = 18;
|
||||
const T_CLOSE_CURLY_BRACE = 19;
|
||||
|
||||
|
||||
// All tokens that are also identifiers should be >= 100
|
||||
const T_IDENTIFIER = 100;
|
||||
const T_ALL = 101;
|
||||
@ -133,7 +133,7 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
'\?[0-9]*|:[a-z]{1}[a-z0-9_]{0,}'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@ -149,50 +149,58 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
{
|
||||
$type = self::T_NONE;
|
||||
|
||||
// Recognizing numeric values
|
||||
if (is_numeric($value)) {
|
||||
return (strpos($value, '.') !== false || stripos($value, 'e') !== false)
|
||||
? self::T_FLOAT : self::T_INTEGER;
|
||||
}
|
||||
|
||||
// Differentiate between quoted names, identifiers, input parameters and symbols
|
||||
if ($value[0] === "'") {
|
||||
$value = str_replace("''", "'", substr($value, 1, strlen($value) - 2));
|
||||
return self::T_STRING;
|
||||
} else if (ctype_alpha($value[0]) || $value[0] === '_') {
|
||||
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
|
||||
|
||||
if (defined($name)) {
|
||||
$type = constant($name);
|
||||
|
||||
if ($type > 100) {
|
||||
return $type;
|
||||
switch (true) {
|
||||
// Recognize numeric values
|
||||
case (is_numeric($value)):
|
||||
if (strpos($value, '.') !== false || stripos($value, 'e') !== false) {
|
||||
return self::T_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
return self::T_IDENTIFIER;
|
||||
} else if ($value[0] === '?' || $value[0] === ':') {
|
||||
return self::T_INPUT_PARAMETER;
|
||||
} else {
|
||||
switch ($value) {
|
||||
case '.': return self::T_DOT;
|
||||
case ',': return self::T_COMMA;
|
||||
case '(': return self::T_OPEN_PARENTHESIS;
|
||||
case ')': return self::T_CLOSE_PARENTHESIS;
|
||||
case '=': return self::T_EQUALS;
|
||||
case '>': return self::T_GREATER_THAN;
|
||||
case '<': return self::T_LOWER_THAN;
|
||||
case '+': return self::T_PLUS;
|
||||
case '-': return self::T_MINUS;
|
||||
case '*': return self::T_MULTIPLY;
|
||||
case '/': return self::T_DIVIDE;
|
||||
case '!': return self::T_NEGATE;
|
||||
case '{': return self::T_OPEN_CURLY_BRACE;
|
||||
case '}': return self::T_CLOSE_CURLY_BRACE;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
return self::T_INTEGER;
|
||||
|
||||
// Recognize quoted strings
|
||||
case ($value[0] === "'"):
|
||||
$value = str_replace("''", "'", substr($value, 1, strlen($value) - 2));
|
||||
|
||||
return self::T_STRING;
|
||||
|
||||
// Recognize identifiers
|
||||
case (ctype_alpha($value[0]) || $value[0] === '_'):
|
||||
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
|
||||
|
||||
if (defined($name)) {
|
||||
$type = constant($name);
|
||||
|
||||
if ($type > 100) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
return self::T_IDENTIFIER;
|
||||
|
||||
// Recognize input parameters
|
||||
case ($value[0] === '?' || $value[0] === ':'):
|
||||
return self::T_INPUT_PARAMETER;
|
||||
|
||||
// Recognize symbols
|
||||
case ($value === '.'): return self::T_DOT;
|
||||
case ($value === ','): return self::T_COMMA;
|
||||
case ($value === '('): return self::T_OPEN_PARENTHESIS;
|
||||
case ($value === ')'): return self::T_CLOSE_PARENTHESIS;
|
||||
case ($value === '='): return self::T_EQUALS;
|
||||
case ($value === '>'): return self::T_GREATER_THAN;
|
||||
case ($value === '<'): return self::T_LOWER_THAN;
|
||||
case ($value === '+'): return self::T_PLUS;
|
||||
case ($value === '-'): return self::T_MINUS;
|
||||
case ($value === '*'): return self::T_MULTIPLY;
|
||||
case ($value === '/'): return self::T_DIVIDE;
|
||||
case ($value === '!'): return self::T_NEGATE;
|
||||
case ($value === '{'): return self::T_OPEN_CURLY_BRACE;
|
||||
case ($value === '}'): return self::T_CLOSE_CURLY_BRACE;
|
||||
|
||||
// Default
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return $type;
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHARNTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
@ -677,13 +677,10 @@ class Parser
|
||||
}
|
||||
|
||||
// Build the error message
|
||||
$semanticalError = 'Invalid PathExpression. ';
|
||||
|
||||
if (count($expectedStringTypes) == 1) {
|
||||
$semanticalError .= 'Must be a ' . $expectedStringTypes[0] . '.';
|
||||
} else {
|
||||
$semanticalError .= implode(' or ', $expectedStringTypes) . ' expected.';
|
||||
}
|
||||
$semanticalError = 'Invalid PathExpression. ';
|
||||
$semanticalError .= (count($expectedStringTypes) == 1)
|
||||
? 'Must be a ' . $expectedStringTypes[0] . '.'
|
||||
: implode(' or ', $expectedStringTypes) . ' expected.';
|
||||
|
||||
$this->semanticalError($semanticalError, $deferredItem['token']);
|
||||
}
|
||||
@ -1308,7 +1305,7 @@ class Parser
|
||||
}
|
||||
|
||||
/**
|
||||
* GroupByItem ::= IdentificationVariable | SingleValuedPathExpression
|
||||
* GroupByItem ::= IdentificationVariable | ResultVariable | SingleValuedPathExpression
|
||||
*
|
||||
* @return string | \Doctrine\ORM\Query\AST\PathExpression
|
||||
*/
|
||||
@ -1317,18 +1314,20 @@ class Parser
|
||||
// We need to check if we are in a IdentificationVariable or SingleValuedPathExpression
|
||||
$glimpse = $this->_lexer->glimpse();
|
||||
|
||||
if ($glimpse['type'] == Lexer::T_DOT) {
|
||||
if ($glimpse['type'] === Lexer::T_DOT) {
|
||||
return $this->SingleValuedPathExpression();
|
||||
}
|
||||
|
||||
$token = $this->_lexer->lookahead;
|
||||
$identVariable = $this->IdentificationVariable();
|
||||
// Still need to decide between IdentificationVariable or ResultVariable
|
||||
$lookaheadValue = $this->_lexer->lookahead['value'];
|
||||
|
||||
if ( ! isset($this->_queryComponents[$identVariable])) {
|
||||
$this->semanticalError('Cannot group by undefined identification variable.');
|
||||
if ( ! isset($this->_queryComponents[$lookaheadValue])) {
|
||||
$this->semanticalError('Cannot group by undefined identification or result variable.');
|
||||
}
|
||||
|
||||
return $identVariable;
|
||||
return (isset($this->_queryComponents[$lookaheadValue]['metadata']))
|
||||
? $this->IdentificationVariable()
|
||||
: $this->ResultVariable();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1866,53 +1865,78 @@ class Parser
|
||||
$expression = null;
|
||||
$identVariable = null;
|
||||
$peek = $this->_lexer->glimpse();
|
||||
$lookaheadType = $this->_lexer->lookahead['type'];
|
||||
|
||||
if ($this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT) {
|
||||
switch (true) {
|
||||
// ScalarExpression (u.name)
|
||||
$expression = $this->ScalarExpression();
|
||||
} else if ($this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS) {
|
||||
// IdentificationVariable (u)
|
||||
$expression = $identVariable = $this->IdentificationVariable();
|
||||
} else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) {
|
||||
// CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
|
||||
$expression = $this->CaseExpression();
|
||||
} else if ($this->_isFunction()) {
|
||||
// DQL Function (SUM(u.value) or SUM(u.value) + 1)
|
||||
$this->_lexer->peek(); // "("
|
||||
|
||||
$lookaheadType = $this->_lexer->lookahead['type'];
|
||||
$beyond = $this->_peekBeyondClosingParenthesis();
|
||||
|
||||
if ($this->_isMathOperator($beyond)) {
|
||||
// SUM(u.id) + COUNT(u.id)
|
||||
case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] === Lexer::T_DOT):
|
||||
$expression = $this->ScalarExpression();
|
||||
} else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
|
||||
// COUNT(u.id)
|
||||
$expression = $this->AggregateExpression();
|
||||
} else {
|
||||
// SUM(u.id)
|
||||
$expression = $this->FunctionDeclaration();
|
||||
}
|
||||
} else if ($this->_lexer->lookahead['type'] === Lexer::T_PARTIAL) {
|
||||
break;
|
||||
|
||||
// IdentificationVariable (u)
|
||||
case ($lookaheadType === Lexer::T_IDENTIFIER && $peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
|
||||
$expression = $identVariable = $this->IdentificationVariable();
|
||||
break;
|
||||
|
||||
// CaseExpression (CASE ... or NULLIF(...) or COALESCE(...))
|
||||
case ($lookaheadType === Lexer::T_CASE):
|
||||
case ($lookaheadType === Lexer::T_COALESCE):
|
||||
case ($lookaheadType === Lexer::T_NULLIF):
|
||||
$expression = $this->CaseExpression();
|
||||
break;
|
||||
|
||||
// DQL Function (SUM(u.value) or SUM(u.value) + 1)
|
||||
case ($this->_isFunction()):
|
||||
$this->_lexer->peek(); // "("
|
||||
|
||||
switch (true) {
|
||||
case ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())):
|
||||
// SUM(u.id) + COUNT(u.id)
|
||||
$expression = $this->ScalarExpression();
|
||||
break;
|
||||
|
||||
case ($this->_isAggregateFunction($lookaheadType)):
|
||||
// COUNT(u.id)
|
||||
$expression = $this->AggregateExpression();
|
||||
break;
|
||||
|
||||
default:
|
||||
// IDENTITY(u)
|
||||
$expression = $this->FunctionDeclaration();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// PartialObjectExpression (PARTIAL u.{id, name})
|
||||
$expression = $this->PartialObjectExpression();
|
||||
$identVariable = $expression->identificationVariable;
|
||||
} else if ($this->_lexer->lookahead['type'] === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT) {
|
||||
case ($lookaheadType === Lexer::T_PARTIAL):
|
||||
$expression = $this->PartialObjectExpression();
|
||||
$identVariable = $expression->identificationVariable;
|
||||
break;
|
||||
|
||||
// Subselect
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$expression = $this->Subselect();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
} else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_OPEN_PARENTHESIS, Lexer::T_INTEGER, Lexer::T_FLOAT, Lexer::T_STRING))) {
|
||||
case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS && $peek['type'] === Lexer::T_SELECT):
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$expression = $this->Subselect();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
break;
|
||||
|
||||
// Shortcut: ScalarExpression => SimpleArithmeticExpression
|
||||
$expression = $this->SimpleArithmeticExpression();
|
||||
} else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_PLUS, Lexer::T_MINUS))) {
|
||||
// SimpleArithmeticExpression : (- u.value ) or ( + u.value )
|
||||
$expression = $this->SimpleArithmeticExpression();
|
||||
} else {
|
||||
$this->syntaxError(
|
||||
'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
|
||||
$this->_lexer->lookahead
|
||||
);
|
||||
case ($lookaheadType === Lexer::T_OPEN_PARENTHESIS):
|
||||
case ($lookaheadType === Lexer::T_INTEGER):
|
||||
case ($lookaheadType === Lexer::T_STRING):
|
||||
case ($lookaheadType === Lexer::T_FLOAT):
|
||||
// SimpleArithmeticExpression : (- u.value ) or ( + u.value )
|
||||
case ($lookaheadType === Lexer::T_MINUS):
|
||||
case ($lookaheadType === Lexer::T_PLUS):
|
||||
$expression = $this->SimpleArithmeticExpression();
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->syntaxError(
|
||||
'IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression',
|
||||
$this->_lexer->lookahead
|
||||
);
|
||||
}
|
||||
|
||||
// [["AS"] ["HIDDEN"] AliasResultVariable]
|
||||
@ -1965,25 +1989,41 @@ class Parser
|
||||
{
|
||||
$peek = $this->_lexer->glimpse();
|
||||
|
||||
if ($peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) {
|
||||
// SingleValuedPathExpression | IdentificationVariable
|
||||
$expression = ($peek['value'] == '.')
|
||||
? $this->StateFieldPathExpression()
|
||||
: $this->IdentificationVariable();
|
||||
switch ($this->_lexer->lookahead['type']) {
|
||||
case Lexer::T_IDENTIFIER:
|
||||
switch (true) {
|
||||
case ($peek['type'] === Lexer::T_DOT):
|
||||
$expression = $this->StateFieldPathExpression();
|
||||
|
||||
return new AST\SimpleSelectExpression($expression);
|
||||
|
||||
case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS):
|
||||
$expression = $this->IdentificationVariable();
|
||||
|
||||
return new AST\SimpleSelectExpression($expression);
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
break;
|
||||
|
||||
case Lexer::T_OPEN_PARENTHESIS:
|
||||
if ($peek['type'] !== Lexer::T_SELECT) {
|
||||
// Shortcut: ScalarExpression => SimpleArithmeticExpression
|
||||
$expression = $this->SimpleArithmeticExpression();
|
||||
|
||||
return new AST\SimpleSelectExpression($expression);
|
||||
}
|
||||
|
||||
return new AST\SimpleSelectExpression($expression);
|
||||
} else if ($this->_lexer->lookahead['value'] == '(') {
|
||||
if ($peek['type'] == Lexer::T_SELECT) {
|
||||
// Subselect
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$expression = $this->Subselect();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
} else {
|
||||
// Shortcut: ScalarExpression => SimpleArithmeticExpression
|
||||
$expression = $this->SimpleArithmeticExpression();
|
||||
}
|
||||
|
||||
return new AST\SimpleSelectExpression($expression);
|
||||
return new AST\SimpleSelectExpression($expression);
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
$this->_lexer->peek();
|
||||
@ -2099,27 +2139,26 @@ class Parser
|
||||
{
|
||||
$condPrimary = new AST\ConditionalPrimary;
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
|
||||
// Peek beyond the matching closing paranthesis ')'
|
||||
$peek = $this->_peekBeyondClosingParenthesis();
|
||||
|
||||
if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) ||
|
||||
$peek['type'] === Lexer::T_NOT ||
|
||||
$peek['type'] === Lexer::T_BETWEEN ||
|
||||
$peek['type'] === Lexer::T_LIKE ||
|
||||
$peek['type'] === Lexer::T_IN ||
|
||||
$peek['type'] === Lexer::T_IS ||
|
||||
$peek['type'] === Lexer::T_EXISTS) {
|
||||
$condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
|
||||
} else {
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$condPrimary->conditionalExpression = $this->ConditionalExpression();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
}
|
||||
} else {
|
||||
if ( ! $this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) {
|
||||
$condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
|
||||
|
||||
return $condPrimary;
|
||||
}
|
||||
|
||||
// Peek beyond the matching closing paranthesis ')'
|
||||
$peek = $this->_peekBeyondClosingParenthesis();
|
||||
|
||||
if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) ||
|
||||
in_array($peek['type'], array(Lexer::T_NOT, Lexer::T_BETWEEN, Lexer::T_LIKE, Lexer::T_IN, Lexer::T_IS, Lexer::T_EXISTS))) {
|
||||
$condPrimary->simpleConditionalExpression = $this->SimpleConditionalExpression();
|
||||
|
||||
return $condPrimary;
|
||||
}
|
||||
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$condPrimary->conditionalExpression = $this->ConditionalExpression();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
|
||||
return $condPrimary;
|
||||
}
|
||||
|
||||
@ -2132,10 +2171,10 @@ class Parser
|
||||
*/
|
||||
public function SimpleConditionalExpression()
|
||||
{
|
||||
$token = $this->_lexer->lookahead;
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
|
||||
$token = $this->_lexer->glimpse();
|
||||
} else {
|
||||
$token = $this->_lexer->lookahead;
|
||||
}
|
||||
|
||||
if ($token['type'] === Lexer::T_EXISTS) {
|
||||
@ -2464,9 +2503,9 @@ class Parser
|
||||
}
|
||||
|
||||
return $this->FunctionDeclaration();
|
||||
} else {
|
||||
return $this->Literal();
|
||||
}
|
||||
|
||||
return $this->Literal();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2498,30 +2537,46 @@ class Parser
|
||||
*/
|
||||
public function StringPrimary()
|
||||
{
|
||||
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
$peek = $this->_lexer->glimpse();
|
||||
$lookaheadType = $this->_lexer->lookahead['type'];
|
||||
|
||||
switch ($lookaheadType) {
|
||||
case Lexer::T_IDENTIFIER:
|
||||
$peek = $this->_lexer->glimpse();
|
||||
|
||||
if ($peek['value'] == '.') {
|
||||
return $this->StateFieldPathExpression();
|
||||
}
|
||||
|
||||
if ($peek['value'] == '(') {
|
||||
// do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
|
||||
return $this->FunctionDeclaration();
|
||||
}
|
||||
|
||||
if ($peek['value'] == '.') {
|
||||
return $this->StateFieldPathExpression();
|
||||
} else if ($peek['value'] == '(') {
|
||||
// do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
|
||||
return $this->FunctionDeclaration();
|
||||
} else {
|
||||
$this->syntaxError("'.' or '('");
|
||||
}
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_STRING)) {
|
||||
$this->match(Lexer::T_STRING);
|
||||
break;
|
||||
|
||||
return $this->_lexer->token['value'];
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_INPUT_PARAMETER)) {
|
||||
return $this->InputParameter();
|
||||
} else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
|
||||
return $this->AggregateExpression();
|
||||
} else if (in_array($this->_lexer->lookahead['type'], array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) {
|
||||
return $this->CaseExpression();
|
||||
case Lexer::T_STRING:
|
||||
$this->match(Lexer::T_STRING);
|
||||
|
||||
return $this->_lexer->token['value'];
|
||||
|
||||
case Lexer::T_INPUT_PARAMETER:
|
||||
return $this->InputParameter();
|
||||
|
||||
case Lexer::T_CASE:
|
||||
case Lexer::T_COALESCE:
|
||||
case Lexer::T_NULLIF:
|
||||
return $this->CaseExpression();
|
||||
|
||||
default:
|
||||
if ($this->_isAggregateFunction($lookaheadType)) {
|
||||
return $this->AggregateExpression();
|
||||
}
|
||||
}
|
||||
|
||||
$this->syntaxError('StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression');
|
||||
$this->syntaxError(
|
||||
'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2552,7 +2607,7 @@ class Parser
|
||||
return $this->InputParameter();
|
||||
}
|
||||
|
||||
return $this->IdentificationVariable();
|
||||
return $this->StateFieldPathExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2564,40 +2619,28 @@ class Parser
|
||||
*/
|
||||
public function AggregateExpression()
|
||||
{
|
||||
$lookaheadType = $this->_lexer->lookahead['type'];
|
||||
$isDistinct = false;
|
||||
$functionName = '';
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_COUNT)) {
|
||||
$this->match(Lexer::T_COUNT);
|
||||
$functionName = $this->_lexer->token['value'];
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
|
||||
$this->match(Lexer::T_DISTINCT);
|
||||
$isDistinct = true;
|
||||
}
|
||||
|
||||
$pathExp = $this->SingleValuedPathExpression();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
} else {
|
||||
if ($this->_lexer->isNextToken(Lexer::T_AVG)) {
|
||||
$this->match(Lexer::T_AVG);
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_MAX)) {
|
||||
$this->match(Lexer::T_MAX);
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_MIN)) {
|
||||
$this->match(Lexer::T_MIN);
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_SUM)) {
|
||||
$this->match(Lexer::T_SUM);
|
||||
} else {
|
||||
$this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
|
||||
}
|
||||
|
||||
$functionName = $this->_lexer->token['value'];
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
$pathExp = $this->SimpleArithmeticExpression();
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
if ( ! in_array($lookaheadType, array(Lexer::T_COUNT, Lexer::T_AVG, Lexer::T_MAX, Lexer::T_MIN, Lexer::T_SUM))) {
|
||||
$this->syntaxError('One of: MAX, MIN, AVG, SUM, COUNT');
|
||||
}
|
||||
|
||||
$this->match($lookaheadType);
|
||||
$functionName = $this->_lexer->token['value'];
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_DISTINCT)) {
|
||||
$this->match(Lexer::T_DISTINCT);
|
||||
$isDistinct = true;
|
||||
}
|
||||
|
||||
$pathExp = ($lookaheadType === Lexer::T_COUNT)
|
||||
? $this->SingleValuedPathExpression()
|
||||
: $this->SimpleArithmeticExpression();
|
||||
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
|
||||
return new AST\AggregateExpression($functionName, $pathExp, $isDistinct);
|
||||
}
|
||||
|
||||
@ -2608,24 +2651,19 @@ class Parser
|
||||
*/
|
||||
public function QuantifiedExpression()
|
||||
{
|
||||
$type = '';
|
||||
$lookaheadType = $this->_lexer->lookahead['type'];
|
||||
$value = $this->_lexer->lookahead['value'];
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_ALL)) {
|
||||
$this->match(Lexer::T_ALL);
|
||||
$type = 'ALL';
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_ANY)) {
|
||||
$this->match(Lexer::T_ANY);
|
||||
$type = 'ANY';
|
||||
} else if ($this->_lexer->isNextToken(Lexer::T_SOME)) {
|
||||
$this->match(Lexer::T_SOME);
|
||||
$type = 'SOME';
|
||||
} else {
|
||||
if ( ! in_array($lookaheadType, array(Lexer::T_ALL, Lexer::T_ANY, Lexer::T_SOME))) {
|
||||
$this->syntaxError('ALL, ANY or SOME');
|
||||
}
|
||||
|
||||
$this->match($lookaheadType);
|
||||
$this->match(Lexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
$qExpr = new AST\QuantifiedExpression($this->Subselect());
|
||||
$qExpr->type = $type;
|
||||
$qExpr->type = $value;
|
||||
|
||||
$this->match(Lexer::T_CLOSE_PARENTHESIS);
|
||||
|
||||
return $qExpr;
|
||||
@ -2666,14 +2704,11 @@ class Parser
|
||||
{
|
||||
$peek = $this->_lexer->glimpse();
|
||||
|
||||
$leftExpr = $this->ArithmeticExpression();
|
||||
$operator = $this->ComparisonOperator();
|
||||
|
||||
if ($this->_isNextAllAnySome()) {
|
||||
$rightExpr = $this->QuantifiedExpression();
|
||||
} else {
|
||||
$rightExpr = $this->ArithmeticExpression();
|
||||
}
|
||||
$leftExpr = $this->ArithmeticExpression();
|
||||
$operator = $this->ComparisonOperator();
|
||||
$rightExpr = ($this->_isNextAllAnySome())
|
||||
? $this->QuantifiedExpression()
|
||||
: $this->ArithmeticExpression();
|
||||
|
||||
return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr);
|
||||
}
|
||||
@ -2685,7 +2720,7 @@ class Parser
|
||||
*/
|
||||
public function InExpression()
|
||||
{
|
||||
$inExpression = new AST\InExpression($this->SingleValuedPathExpression());
|
||||
$inExpression = new AST\InExpression($this->ArithmeticExpression());
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_NOT)) {
|
||||
$this->match(Lexer::T_NOT);
|
||||
|
@ -435,7 +435,8 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
$this->_useSqlTableAliases = false;
|
||||
|
||||
return $this->walkUpdateClause($AST->updateClause) . $this->walkWhereClause($AST->whereClause);
|
||||
return $this->walkUpdateClause($AST->updateClause)
|
||||
. $this->walkWhereClause($AST->whereClause);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,9 +449,29 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
$this->_useSqlTableAliases = false;
|
||||
|
||||
return $this->walkDeleteClause($AST->deleteClause) . $this->walkWhereClause($AST->whereClause);
|
||||
return $this->walkDeleteClause($AST->deleteClause)
|
||||
. $this->walkWhereClause($AST->whereClause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an IdentificationVariable AST node, thereby generating the appropriate SQL.
|
||||
* This one differs of ->walkIdentificationVariable() because it generates the entity identifiers.
|
||||
*
|
||||
* @param string $identVariable
|
||||
* @return string
|
||||
*/
|
||||
public function walkEntityIdentificationVariable($identVariable)
|
||||
{
|
||||
$class = $this->_queryComponents[$identVariable]['metadata'];
|
||||
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $identVariable);
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
|
||||
$sqlParts[] = $tableAlias . '.' . $columnName;
|
||||
}
|
||||
|
||||
return implode(', ', $sqlParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an IdentificationVariable (no AST node associated), thereby generating the SQL.
|
||||
@ -713,7 +734,7 @@ class SqlWalker implements TreeWalker
|
||||
$expr = $orderByItem->expression;
|
||||
$sql = ($expr instanceof AST\PathExpression)
|
||||
? $this->walkPathExpression($expr)
|
||||
: $this->_scalarResultAliasMap[$this->_queryComponents[$expr]['token']['value']];
|
||||
: $this->walkResultVariable($this->_queryComponents[$expr]['token']['value']);
|
||||
|
||||
return $sql . ' ' . strtoupper($orderByItem->type);
|
||||
}
|
||||
@ -1050,6 +1071,8 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sql .= $col . ' AS ' . $columnAlias;
|
||||
|
||||
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
|
||||
if ( ! $hidden) {
|
||||
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
|
||||
$this->_scalarFields[$dqlAlias][$fieldName] = $columnAlias;
|
||||
@ -1139,6 +1162,8 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sqlParts[] = $col . ' AS '. $columnAlias;
|
||||
|
||||
$this->_scalarResultAliasMap[$resultAlias][] = $columnAlias;
|
||||
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $class->name);
|
||||
}
|
||||
|
||||
@ -1168,6 +1193,8 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
$sqlParts[] = $col . ' AS ' . $columnAlias;
|
||||
|
||||
$this->_scalarResultAliasMap[$resultAlias][] = $columnAlias;
|
||||
|
||||
$this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName, $subClassName);
|
||||
}
|
||||
}
|
||||
@ -1317,15 +1344,7 @@ class SqlWalker implements TreeWalker
|
||||
break;
|
||||
|
||||
default: // IdentificationVariable
|
||||
$class = $this->_queryComponents[$expr]['metadata'];
|
||||
$tableAlias = $this->getSQLTableAlias($class->getTableName(), $expr);
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($class->getQuotedIdentifierColumnNames($this->_platform) as $columnName) {
|
||||
$sqlParts[] = $tableAlias . '.' . $columnName;
|
||||
}
|
||||
|
||||
$sql .= implode(', ', $sqlParts);
|
||||
$sql .= $this->walkEntityIdentificationVariable($expr);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1355,25 +1374,7 @@ class SqlWalker implements TreeWalker
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($groupByClause->groupByItems AS $groupByItem) {
|
||||
if ( ! is_string($groupByItem)) {
|
||||
$sqlParts[] = $this->walkGroupByItem($groupByItem);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->fieldNames AS $field) {
|
||||
$item = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field);
|
||||
$item->type = AST\PathExpression::TYPE_STATE_FIELD;
|
||||
$sqlParts[] = $this->walkGroupByItem($item);
|
||||
}
|
||||
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->associationMappings AS $mapping) {
|
||||
if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
$item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']);
|
||||
$item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
|
||||
$sqlParts[] = $this->walkGroupByItem($item);
|
||||
}
|
||||
}
|
||||
$sqlParts[] = $this->walkGroupByItem($groupByItem);
|
||||
}
|
||||
|
||||
return ' GROUP BY ' . implode(', ', $sqlParts);
|
||||
@ -1385,9 +1386,38 @@ class SqlWalker implements TreeWalker
|
||||
* @param GroupByItem
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkGroupByItem(AST\PathExpression $pathExpr)
|
||||
public function walkGroupByItem($groupByItem)
|
||||
{
|
||||
return $this->walkPathExpression($pathExpr);
|
||||
// StateFieldPathExpression
|
||||
if ( ! is_string($groupByItem)) {
|
||||
return $this->walkPathExpression($groupByItem);
|
||||
}
|
||||
|
||||
// ResultVariable
|
||||
if (isset($this->_queryComponents[$groupByItem]['resultVariable'])) {
|
||||
return $this->walkResultVariable($groupByItem);
|
||||
}
|
||||
|
||||
// IdentificationVariable
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->fieldNames AS $field) {
|
||||
$item = new AST\PathExpression(AST\PathExpression::TYPE_STATE_FIELD, $groupByItem, $field);
|
||||
$item->type = AST\PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
$sqlParts[] = $this->walkPathExpression($item);
|
||||
}
|
||||
|
||||
foreach ($this->_queryComponents[$groupByItem]['metadata']->associationMappings AS $mapping) {
|
||||
if ($mapping['isOwningSide'] && $mapping['type'] & ClassMetadataInfo::TO_ONE) {
|
||||
$item = new AST\PathExpression(AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $groupByItem, $mapping['fieldName']);
|
||||
$item->type = AST\PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
|
||||
|
||||
$sqlParts[] = $this->walkPathExpression($item);
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $sqlParts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1597,20 +1627,30 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
$sql = $collMemberExpr->not ? 'NOT ' : '';
|
||||
$sql .= 'EXISTS (SELECT 1 FROM ';
|
||||
$entityExpr = $collMemberExpr->entityExpression;
|
||||
|
||||
$entityExpr = $collMemberExpr->entityExpression;
|
||||
$collPathExpr = $collMemberExpr->collectionValuedPathExpression;
|
||||
|
||||
$fieldName = $collPathExpr->field;
|
||||
$dqlAlias = $collPathExpr->identificationVariable;
|
||||
$dqlAlias = $collPathExpr->identificationVariable;
|
||||
|
||||
$class = $this->_queryComponents[$dqlAlias]['metadata'];
|
||||
|
||||
if ($entityExpr instanceof AST\InputParameter) {
|
||||
$dqlParamKey = $entityExpr->name;
|
||||
$entity = $this->_query->getParameter($dqlParamKey);
|
||||
} else {
|
||||
//TODO
|
||||
throw new \BadMethodCallException("Not implemented");
|
||||
switch (true) {
|
||||
// InputParameter
|
||||
case ($entityExpr instanceof AST\InputParameter):
|
||||
$dqlParamKey = $entityExpr->name;
|
||||
$entity = $this->_query->getParameter($dqlParamKey);
|
||||
$entitySql = '?';
|
||||
break;
|
||||
|
||||
// SingleValuedAssociationPathExpression | IdentificationVariable
|
||||
case ($entityExpr instanceof AST\PathExpression):
|
||||
$entitySql = $this->walkPathExpression($entityExpr);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \BadMethodCallException("Not implemented");
|
||||
}
|
||||
|
||||
$assoc = $class->associationMappings[$fieldName];
|
||||
@ -1623,25 +1663,23 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE ';
|
||||
|
||||
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
|
||||
$first = true;
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
$targetColumn = $class->getQuotedColumnName($class->fieldNames[$targetColumn], $this->_platform);
|
||||
|
||||
$sql .= $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $this->_platform)
|
||||
. ' = '
|
||||
. $targetTableAlias . '.' . $sourceColumn;
|
||||
$sqlParts[] = $sourceTableAlias . '.' . $targetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
|
||||
}
|
||||
|
||||
$sql .= ' AND ';
|
||||
$first = true;
|
||||
|
||||
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
if (isset($dqlParamKey)) {
|
||||
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
|
||||
}
|
||||
|
||||
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
|
||||
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
|
||||
$sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql;
|
||||
}
|
||||
|
||||
$sql .= implode(' AND ', $sqlParts);
|
||||
} else { // many-to-many
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
@ -1658,39 +1696,42 @@ class SqlWalker implements TreeWalker
|
||||
. ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON ';
|
||||
|
||||
// join conditions
|
||||
$joinColumns = $assoc['isOwningSide']
|
||||
? $joinTable['inverseJoinColumns']
|
||||
: $joinTable['joinColumns'];
|
||||
$joinColumns = $assoc['isOwningSide'] ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns'];
|
||||
$joinSqlParts = array();
|
||||
|
||||
$first = true;
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
$targetColumn = $targetClass->getQuotedColumnName(
|
||||
$targetClass->fieldNames[$joinColumn['referencedColumnName']],
|
||||
$this->_platform
|
||||
);
|
||||
|
||||
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
|
||||
. $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetClass->fieldNames[$joinColumn['referencedColumnName']], $this->_platform);
|
||||
$joinSqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $targetTableAlias . '.' . $targetColumn;
|
||||
}
|
||||
|
||||
$sql .= implode(' AND ', $joinSqlParts);
|
||||
$sql .= ' WHERE ';
|
||||
|
||||
$joinColumns = $assoc['isOwningSide'] ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns'];
|
||||
$first = true;
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($joinColumns as $joinColumn) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
$targetColumn = $class->getQuotedColumnName(
|
||||
$class->fieldNames[$joinColumn['referencedColumnName']],
|
||||
$this->_platform
|
||||
);
|
||||
|
||||
$sql .= $joinTableAlias . '.' . $joinColumn['name'] . ' = '
|
||||
. $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$joinColumn['referencedColumnName']], $this->_platform);
|
||||
$sqlParts[] = $joinTableAlias . '.' . $joinColumn['name'] . ' = ' . $sourceTableAlias . '.' . $targetColumn;
|
||||
}
|
||||
|
||||
$sql .= ' AND ';
|
||||
$first = true;
|
||||
|
||||
foreach ($targetClass->getQuotedIdentifierColumnNames($this->_platform) as $targetColumnName) {
|
||||
if ($first) $first = false; else $sql .= ' AND ';
|
||||
if (isset($dqlParamKey)) {
|
||||
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
|
||||
}
|
||||
|
||||
$this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++);
|
||||
$sql .= $targetTableAlias . '.' . $targetColumnName . ' = ?';
|
||||
$sqlParts[] = $targetTableAlias . '.' . $targetColumnName . ' = ' . $entitySql;
|
||||
}
|
||||
|
||||
$sql .= implode(' AND ', $sqlParts);
|
||||
}
|
||||
|
||||
return $sql . ')';
|
||||
@ -1742,8 +1783,7 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
public function walkInExpression($inExpr)
|
||||
{
|
||||
$sql = $this->walkPathExpression($inExpr->pathExpression)
|
||||
. ($inExpr->not ? ' NOT' : '') . ' IN (';
|
||||
$sql = $this->walkArithmeticExpression($inExpr->expression) . ($inExpr->not ? ' NOT' : '') . ' IN (';
|
||||
|
||||
$sql .= ($inExpr->subselect)
|
||||
? $this->walkSubselect($inExpr->subselect)
|
||||
@ -1985,7 +2025,7 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
if (is_string($term)) {
|
||||
return (isset($this->_queryComponents[$term]))
|
||||
? $this->_scalarResultAliasMap[$this->_queryComponents[$term]['token']['value']]
|
||||
? $this->walkResultVariable($this->_queryComponents[$term]['token']['value'])
|
||||
: $term;
|
||||
}
|
||||
|
||||
@ -2037,8 +2077,7 @@ class SqlWalker implements TreeWalker
|
||||
return $primary->dispatch($this);
|
||||
}
|
||||
|
||||
// TODO: We need to deal with IdentificationVariable here
|
||||
return '';
|
||||
return $this->walkEntityIdentificationVariable($primary);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2053,4 +2092,21 @@ class SqlWalker implements TreeWalker
|
||||
? $this->_conn->quote($stringPrimary)
|
||||
: $stringPrimary->dispatch($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a ResultVriable that represents an AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param string $resultVariable
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkResultVariable($resultVariable)
|
||||
{
|
||||
$resultAlias = $this->_scalarResultAliasMap[$resultVariable];
|
||||
|
||||
if (is_array($resultAlias)) {
|
||||
return implode(', ', $resultAlias);
|
||||
}
|
||||
|
||||
return $resultAlias;
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ interface TreeWalker
|
||||
* @param GroupByItem
|
||||
* @return string The SQL.
|
||||
*/
|
||||
function walkGroupByItem(AST\PathExpression $pathExpr);
|
||||
function walkGroupByItem($groupByItem);
|
||||
|
||||
/**
|
||||
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
|
||||
@ -394,6 +394,14 @@ interface TreeWalker
|
||||
*/
|
||||
function walkPathExpression($pathExpr);
|
||||
|
||||
/**
|
||||
* Walks down an ResultVariable AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param string $resultVariable
|
||||
* @return string The SQL.
|
||||
*/
|
||||
function walkResultVariable($resultVariable);
|
||||
|
||||
/**
|
||||
* Gets an executor that can be used to execute the result of this walker.
|
||||
*
|
||||
|
@ -24,7 +24,7 @@ namespace Doctrine\ORM\Query;
|
||||
/**
|
||||
* An adapter implementation of the TreeWalker interface. The methods in this class
|
||||
* are empty. This class exists as convenience for creating tree walkers.
|
||||
*
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
@ -33,7 +33,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
private $_query;
|
||||
private $_parserResult;
|
||||
private $_queryComponents;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -71,7 +71,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
{
|
||||
return $this->_parserResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -202,7 +202,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
* @param GroupByItem
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkGroupByItem(AST\PathExpression $pathExpr) {}
|
||||
public function walkGroupByItem($groupByItem) {}
|
||||
|
||||
/**
|
||||
* Walks down an UpdateStatement AST node, thereby generating the appropriate SQL.
|
||||
@ -291,7 +291,7 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkExistsExpression($existsExpr) {}
|
||||
|
||||
|
||||
/**
|
||||
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -427,10 +427,18 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkPathExpression($pathExpr) {}
|
||||
|
||||
|
||||
/**
|
||||
* Walks down an ResultVariable AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param string $resultVariable
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkResultVariable($resultVariable) {}
|
||||
|
||||
/**
|
||||
* Gets an executor that can be used to execute the result of this walker.
|
||||
*
|
||||
*
|
||||
* @return AbstractExecutor
|
||||
*/
|
||||
public function getExecutor($AST) {}
|
||||
|
@ -25,7 +25,7 @@ namespace Doctrine\ORM\Query;
|
||||
* Represents a chain of tree walkers that modify an AST and finally emit output.
|
||||
* Only the last walker in the chain can emit output. Any previous walkers can modify
|
||||
* the AST to influence the final output produced by the last walker.
|
||||
*
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
@ -39,7 +39,7 @@ class TreeWalkerChain implements TreeWalker
|
||||
private $_parserResult;
|
||||
/** The query components of the original query (the "symbol table") that was produced by the Parser. */
|
||||
private $_queryComponents;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@ -49,17 +49,17 @@ class TreeWalkerChain implements TreeWalker
|
||||
$this->_parserResult = $parserResult;
|
||||
$this->_queryComponents = $queryComponents;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a tree walker to the chain.
|
||||
*
|
||||
*
|
||||
* @param string $walkerClass The class of the walker to instantiate.
|
||||
*/
|
||||
public function addTreeWalker($walkerClass)
|
||||
{
|
||||
$this->_walkers[] = new $walkerClass($this->_query, $this->_parserResult, $this->_queryComponents);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -270,10 +270,10 @@ class TreeWalkerChain implements TreeWalker
|
||||
* @param GroupByItem
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkGroupByItem(AST\PathExpression $pathExpr)
|
||||
public function walkGroupByItem($groupByItem)
|
||||
{
|
||||
foreach ($this->_walkers as $walker) {
|
||||
$walker->walkGroupByItem($pathExpr);
|
||||
$walker->walkGroupByItem($groupByItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,7 +419,7 @@ class TreeWalkerChain implements TreeWalker
|
||||
$walker->walkExistsExpression($existsExpr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walks down a CollectionMemberExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -640,10 +640,23 @@ class TreeWalkerChain implements TreeWalker
|
||||
$walker->walkPathExpression($pathExpr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walks down an ResultVariable AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param string $resultVariable
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkResultVariable($resultVariable)
|
||||
{
|
||||
foreach ($this->_walkers as $walker) {
|
||||
$walker->walkResultVariable($resultVariable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an executor that can be used to execute the result of this walker.
|
||||
*
|
||||
*
|
||||
* @return AbstractExecutor
|
||||
*/
|
||||
public function getExecutor($AST)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,7 +196,7 @@ public function <methodName>()
|
||||
if ($this->_backupExisting && file_exists($path)) {
|
||||
$backupPath = dirname($path) . DIRECTORY_SEPARATOR . basename($path) . "~";
|
||||
if (!copy($path, $backupPath)) {
|
||||
throw new \RuntimeException("Attempt to backup overwritten entitiy file but copy operation failed.");
|
||||
throw new \RuntimeException("Attempt to backup overwritten entity file but copy operation failed.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,11 +404,11 @@ public function <methodName>()
|
||||
$collections[] = '$this->'.$mapping['fieldName'].' = new \Doctrine\Common\Collections\ArrayCollection();';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($collections) {
|
||||
return $this->_prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->_spaces, $collections), self::$_constructorMethodTemplate));
|
||||
}
|
||||
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -576,7 +576,7 @@ public function <methodName>()
|
||||
if (isset($metadata->table['schema'])) {
|
||||
$table[] = 'schema="' . $metadata->table['schema'] . '"';
|
||||
}
|
||||
|
||||
|
||||
if (isset($metadata->table['name'])) {
|
||||
$table[] = 'name="' . $metadata->table['name'] . '"';
|
||||
}
|
||||
@ -754,7 +754,7 @@ public function <methodName>()
|
||||
'<variableName>' => Inflector::camelize($fieldName),
|
||||
'<methodName>' => $methodName,
|
||||
'<fieldName>' => $fieldName,
|
||||
'<variableDefault>' => ($defaultValue !== null ) ? ('='.$defaultValue) : '',
|
||||
'<variableDefault>' => ($defaultValue !== null ) ? (' = '.$defaultValue) : '',
|
||||
'<entity>' => $this->_getClassName($metadata)
|
||||
);
|
||||
|
||||
|
@ -1700,21 +1700,18 @@ class UnitOfWork implements PropertyChangedListener
|
||||
// do not merge fields marked lazy that have not been fetched.
|
||||
continue;
|
||||
} else if ( ! $assoc2['isCascadeMerge']) {
|
||||
if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) {
|
||||
$prop->setValue($managedCopy, $other);
|
||||
} else {
|
||||
|
||||
if ($this->getEntityState($other, self::STATE_DETACHED) !== self::STATE_MANAGED) {
|
||||
$targetClass = $this->em->getClassMetadata($assoc2['targetEntity']);
|
||||
$relatedId = $targetClass->getIdentifierValues($other);
|
||||
|
||||
if ($targetClass->subClasses) {
|
||||
$entity = $this->em->find($targetClass->name, $relatedId);
|
||||
$other = $this->em->find($targetClass->name, $relatedId);
|
||||
} else {
|
||||
$proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
|
||||
$prop->setValue($managedCopy, $proxy);
|
||||
$this->registerManaged($proxy, $relatedId, array());
|
||||
$other = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId);
|
||||
$this->registerManaged($other, $relatedId, array());
|
||||
}
|
||||
}
|
||||
$prop->setValue($managedCopy, $other);
|
||||
}
|
||||
} else {
|
||||
$mergeCol = $prop->getValue($entity);
|
||||
@ -1793,6 +1790,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
public function detach($entity)
|
||||
{
|
||||
$visited = array();
|
||||
|
||||
$this->doDetach($entity, $visited);
|
||||
}
|
||||
|
||||
@ -1806,6 +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
|
||||
}
|
||||
@ -1817,16 +1816,22 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->isInIdentityMap($entity)) {
|
||||
$this->removeFromIdentityMap($entity);
|
||||
}
|
||||
unset($this->entityInsertions[$oid], $this->entityUpdates[$oid],
|
||||
$this->entityDeletions[$oid], $this->entityIdentifiers[$oid],
|
||||
$this->entityStates[$oid], $this->originalEntityData[$oid]);
|
||||
|
||||
unset(
|
||||
$this->entityInsertions[$oid],
|
||||
$this->entityUpdates[$oid],
|
||||
$this->entityDeletions[$oid],
|
||||
$this->entityIdentifiers[$oid],
|
||||
$this->entityStates[$oid],
|
||||
$this->originalEntityData[$oid]
|
||||
);
|
||||
break;
|
||||
case self::STATE_NEW:
|
||||
case self::STATE_DETACHED:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$noCascade) {
|
||||
if ( ! $noCascade) {
|
||||
$this->cascadeDetach($entity, $visited);
|
||||
}
|
||||
}
|
||||
@ -1841,6 +1846,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
public function refresh($entity)
|
||||
{
|
||||
$visited = array();
|
||||
|
||||
$this->doRefresh($entity, $visited);
|
||||
}
|
||||
|
||||
@ -1854,6 +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
|
||||
}
|
||||
@ -1861,15 +1868,16 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$visited[$oid] = $entity; // mark visited
|
||||
|
||||
$class = $this->em->getClassMetadata(get_class($entity));
|
||||
if ($this->getEntityState($entity) == self::STATE_MANAGED) {
|
||||
$this->getEntityPersister($class->name)->refresh(
|
||||
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
||||
$entity
|
||||
);
|
||||
} else {
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
$this->cascadeRefresh($entity, $visited);
|
||||
}
|
||||
|
||||
@ -1882,21 +1890,34 @@ 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);
|
||||
if ($relatedEntities instanceof Collection) {
|
||||
if ($relatedEntities instanceof PersistentCollection) {
|
||||
|
||||
switch (true) {
|
||||
case ($relatedEntities instanceof PersistentCollection):
|
||||
// Unwrap so that foreach() does not initialize
|
||||
$relatedEntities = $relatedEntities->unwrap();
|
||||
}
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->doRefresh($relatedEntity, $visited);
|
||||
}
|
||||
} else if ($relatedEntities !== null) {
|
||||
$this->doRefresh($relatedEntities, $visited);
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1910,21 +1931,34 @@ 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);
|
||||
if ($relatedEntities instanceof Collection) {
|
||||
if ($relatedEntities instanceof PersistentCollection) {
|
||||
|
||||
switch (true) {
|
||||
case ($relatedEntities instanceof PersistentCollection):
|
||||
// Unwrap so that foreach() does not initialize
|
||||
$relatedEntities = $relatedEntities->unwrap();
|
||||
}
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->doDetach($relatedEntity, $visited);
|
||||
}
|
||||
} else if ($relatedEntities !== null) {
|
||||
$this->doDetach($relatedEntities, $visited);
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1939,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;
|
||||
@ -1953,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);
|
||||
}
|
||||
@ -1972,22 +2011,34 @@ 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);
|
||||
if (($relatedEntities instanceof Collection || is_array($relatedEntities))) {
|
||||
if ($relatedEntities instanceof PersistentCollection) {
|
||||
|
||||
switch (true) {
|
||||
case ($relatedEntities instanceof PersistentCollection):
|
||||
// Unwrap so that foreach() does not initialize
|
||||
$relatedEntities = $relatedEntities->unwrap();
|
||||
}
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->doPersist($relatedEntity, $visited);
|
||||
}
|
||||
} else if ($relatedEntities !== null) {
|
||||
$this->doPersist($relatedEntities, $visited);
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2002,24 +2053,33 @@ 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();
|
||||
}
|
||||
|
||||
$relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity);
|
||||
|
||||
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
|
||||
// If its a PersistentCollection initialization is intended! No unwrap!
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->doRemove($relatedEntity, $visited);
|
||||
}
|
||||
} else if ($relatedEntities !== null) {
|
||||
$this->doRemove($relatedEntities, $visited);
|
||||
switch (true) {
|
||||
case ($relatedEntities instanceof Collection):
|
||||
case (is_array($relatedEntities)):
|
||||
// If its a PersistentCollection initialization is intended! No unwrap!
|
||||
foreach ($relatedEntities as $relatedEntity) {
|
||||
$this->doRemove($relatedEntity, $visited);
|
||||
}
|
||||
break;
|
||||
|
||||
case ($relatedEntities !== null):
|
||||
$this->doRemove($relatedEntities, $visited);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2040,29 +2100,40 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$entityName = get_class($entity);
|
||||
$class = $this->em->getClassMetadata($entityName);
|
||||
|
||||
if ($lockMode == \Doctrine\DBAL\LockMode::OPTIMISTIC) {
|
||||
if (!$class->isVersioned) {
|
||||
throw OptimisticLockException::notVersioned($entityName);
|
||||
}
|
||||
switch ($lockMode) {
|
||||
case \Doctrine\DBAL\LockMode::OPTIMISTIC;
|
||||
if ( ! $class->isVersioned) {
|
||||
throw OptimisticLockException::notVersioned($entityName);
|
||||
}
|
||||
|
||||
if ($lockVersion === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($lockVersion != null) {
|
||||
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
|
||||
|
||||
if ($entityVersion != $lockVersion) {
|
||||
throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion);
|
||||
}
|
||||
}
|
||||
} else if (in_array($lockMode, array(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE))) {
|
||||
|
||||
if (!$this->em->getConnection()->isTransactionActive()) {
|
||||
throw TransactionRequiredException::transactionRequired();
|
||||
}
|
||||
break;
|
||||
|
||||
$oid = spl_object_hash($entity);
|
||||
case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ:
|
||||
case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE:
|
||||
if (!$this->em->getConnection()->isTransactionActive()) {
|
||||
throw TransactionRequiredException::transactionRequired();
|
||||
}
|
||||
|
||||
$this->getEntityPersister($class->name)->lock(
|
||||
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
||||
$lockMode
|
||||
);
|
||||
$oid = spl_object_hash($entity);
|
||||
|
||||
$this->getEntityPersister($class->name)->lock(
|
||||
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
|
||||
$lockMode
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@ -2076,6 +2147,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($this->commitOrderCalculator === null) {
|
||||
$this->commitOrderCalculator = new Internal\CommitOrderCalculator;
|
||||
}
|
||||
|
||||
return $this->commitOrderCalculator;
|
||||
}
|
||||
|
||||
@ -2164,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;
|
||||
}
|
||||
|
||||
@ -2190,35 +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 {
|
||||
/*echo $className;
|
||||
\Doctrine\Common\Util\Debug::dump($data);
|
||||
\Doctrine\Common\Util\Debug::dump($class->identifier);
|
||||
ob_end_flush();
|
||||
ob_start();*/
|
||||
|
||||
$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);
|
||||
}
|
||||
@ -2242,156 +2311,175 @@ 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) {
|
||||
foreach ($data as $field => $value) {
|
||||
if (isset($class->fieldMappings[$field])) {
|
||||
$class->reflFields[$field]->setValue($entity, $value);
|
||||
}
|
||||
if ( ! $overrideLocalValues) {
|
||||
return $entity;
|
||||
}
|
||||
|
||||
foreach ($data as $field => $value) {
|
||||
if (isset($class->fieldMappings[$field])) {
|
||||
$class->reflFields[$field]->setValue($entity, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
||||
|
||||
if (isset($this->eagerLoadingEntities[$class->rootEntityName]) && ! $this->eagerLoadingEntities[$class->rootEntityName]) {
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName]);
|
||||
}
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
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])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if (isset($this->eagerLoadingEntities[$class->rootEntityName]) &&
|
||||
! $this->eagerLoadingEntities[$class->rootEntityName]) {
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName]);
|
||||
}
|
||||
switch (true) {
|
||||
case ($assoc['type'] & ClassMetadata::TO_ONE):
|
||||
if ( ! $assoc['isOwningSide']) {
|
||||
// Inverse side of x-to-one can never be lazy
|
||||
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity));
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$associatedId = array();
|
||||
|
||||
// TODO: Is this even computed right in all cases of composite keys?
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
||||
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
|
||||
|
||||
if ($joinColumnValue !== null) {
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
|
||||
} else {
|
||||
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $associatedId) {
|
||||
// Foreign key is NULL
|
||||
$class->reflFields[$field]->setValue($entity, null);
|
||||
$this->originalEntityData[$oid][$field] = null;
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
|
||||
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])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if ($assoc['isOwningSide']) {
|
||||
$associatedId = array();
|
||||
// TODO: Is this even computed right in all cases of composite keys?
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
||||
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
|
||||
if ($joinColumnValue !== null) {
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
|
||||
} else {
|
||||
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! $associatedId) {
|
||||
// Foreign key is NULL
|
||||
$class->reflFields[$field]->setValue($entity, null);
|
||||
$this->originalEntityData[$oid][$field] = null;
|
||||
} else {
|
||||
if (!isset($hints['fetchMode'][$class->name][$field])) {
|
||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||
}
|
||||
|
||||
// Foreign key is set
|
||||
// Check identity map first
|
||||
// FIXME: Can break easily with composite keys if join column values are in
|
||||
// wrong order. The correct order is the one in ClassMetadata#identifier.
|
||||
$relatedIdHash = implode(' ', $associatedId);
|
||||
if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
|
||||
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
|
||||
|
||||
// if this is an uninitialized proxy, we are deferring eager loads,
|
||||
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
||||
// then we cann append this entity for eager loading!
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
||||
isset($hints['deferEagerLoad']) &&
|
||||
!$targetClass->isIdentifierComposite &&
|
||||
$newValue instanceof Proxy &&
|
||||
$newValue->__isInitialized__ === false) {
|
||||
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
}
|
||||
} else {
|
||||
if ($targetClass->subClasses) {
|
||||
// If it might be a subtype, it can not be lazy. There isn't even
|
||||
// a way to solve this with deferred eager loading, which means putting
|
||||
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
|
||||
$newValue = $this->getEntityPersister($assoc['targetEntity'])
|
||||
->loadOneToOneEntity($assoc, $entity, $associatedId);
|
||||
} else {
|
||||
// Deferred eager load only works for single identifier classes
|
||||
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER) {
|
||||
if (isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite) {
|
||||
// TODO: Is there a faster approach?
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
} else {
|
||||
// TODO: This is very imperformant, ignore it?
|
||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||
}
|
||||
} else {
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
}
|
||||
// PERF: Inlined & optimized code from UnitOfWork#registerManaged()
|
||||
$newValueOid = spl_object_hash($newValue);
|
||||
$this->entityIdentifiers[$newValueOid] = $associatedId;
|
||||
$this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
|
||||
$this->entityStates[$newValueOid] = self::STATE_MANAGED;
|
||||
// make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
|
||||
}
|
||||
}
|
||||
$this->originalEntityData[$oid][$field] = $newValue;
|
||||
$class->reflFields[$field]->setValue($entity, $newValue);
|
||||
|
||||
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
|
||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Inverse side of x-to-one can never be lazy
|
||||
$class->reflFields[$field]->setValue($entity, $this->getEntityPersister($assoc['targetEntity'])
|
||||
->loadOneToOneEntity($assoc, $entity));
|
||||
}
|
||||
} else {
|
||||
// Inject collection
|
||||
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
|
||||
$pColl->setOwner($entity, $assoc);
|
||||
|
||||
$reflField = $class->reflFields[$field];
|
||||
$reflField->setValue($entity, $pColl);
|
||||
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
} else {
|
||||
$pColl->setInitialized(false);
|
||||
}
|
||||
$this->originalEntityData[$oid][$field] = $pColl;
|
||||
if ( ! isset($hints['fetchMode'][$class->name][$field])) {
|
||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||
}
|
||||
}
|
||||
|
||||
// Foreign key is set
|
||||
// Check identity map first
|
||||
// FIXME: Can break easily with composite keys if join column values are in
|
||||
// wrong order. The correct order is the one in ClassMetadata#identifier.
|
||||
$relatedIdHash = implode(' ', $associatedId);
|
||||
|
||||
switch (true) {
|
||||
case (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])):
|
||||
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
|
||||
|
||||
// if this is an uninitialized proxy, we are deferring eager loads,
|
||||
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
||||
// then we cann append this entity for eager loading!
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
||||
isset($hints['deferEagerLoad']) &&
|
||||
!$targetClass->isIdentifierComposite &&
|
||||
$newValue instanceof Proxy &&
|
||||
$newValue->__isInitialized__ === false) {
|
||||
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ($targetClass->subClasses):
|
||||
// If it might be a subtype, it can not be lazy. There isn't even
|
||||
// a way to solve this with deferred eager loading, which means putting
|
||||
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
|
||||
$newValue = $this->getEntityPersister($assoc['targetEntity'])->loadOneToOneEntity($assoc, $entity, $associatedId);
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (true) {
|
||||
// We are negating the condition here. Other cases will assume it is valid!
|
||||
case ($hints['fetchMode'][$class->name][$field] !== ClassMetadata::FETCH_EAGER):
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
break;
|
||||
|
||||
// Deferred eager load only works for single identifier classes
|
||||
case (isset($hints['deferEagerLoad']) && ! $targetClass->isIdentifierComposite):
|
||||
// TODO: Is there a faster approach?
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO: This is very imperformant, ignore it?
|
||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||
break;
|
||||
}
|
||||
|
||||
// PERF: Inlined & optimized code from UnitOfWork#registerManaged()
|
||||
$newValueOid = spl_object_hash($newValue);
|
||||
$this->entityIdentifiers[$newValueOid] = $associatedId;
|
||||
$this->identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
|
||||
$this->entityStates[$newValueOid] = self::STATE_MANAGED;
|
||||
// make sure that when an proxy is then finally loaded, $this->originalEntityData is set also!
|
||||
break;
|
||||
}
|
||||
|
||||
$this->originalEntityData[$oid][$field] = $newValue;
|
||||
$class->reflFields[$field]->setValue($entity, $newValue);
|
||||
|
||||
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
|
||||
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
|
||||
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Inject collection
|
||||
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
|
||||
$pColl->setOwner($entity, $assoc);
|
||||
$pColl->setInitialized(false);
|
||||
|
||||
$reflField = $class->reflFields[$field];
|
||||
$reflField->setValue($entity, $pColl);
|
||||
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
}
|
||||
|
||||
$this->originalEntityData[$oid][$field] = $pColl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: These should be invoked later, after hydration, because associations may not yet be loaded here.
|
||||
if (isset($class->lifecycleCallbacks[Events::postLoad])) {
|
||||
$class->invokeLifecycleCallbacks(Events::postLoad, $entity);
|
||||
}
|
||||
|
||||
if ($this->evm->hasListeners(Events::postLoad)) {
|
||||
$this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em));
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
@ -2564,7 +2652,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
*
|
||||
* @param string $entityName The name of the Entity.
|
||||
*
|
||||
* @return Doctrine\ORM\Persisters\AbstractEntityPersister
|
||||
* @return Doctrine\ORM\Persisters\BasicEntityPersister
|
||||
*/
|
||||
public function getEntityPersister($entityName)
|
||||
{
|
||||
|
@ -92,4 +92,10 @@ class CompositePrimaryKeyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->assertEquals(1, count($tours));
|
||||
}
|
||||
|
||||
public function testSpecifiyUnknownIdentifierPrimaryKeyFails()
|
||||
{
|
||||
$this->setExpectedException('Doctrine\ORM\ORMException', 'The identifier long is missing for a query of Doctrine\Tests\Models\Navigation\NavPointOfInterest');
|
||||
$poi = $this->_em->find('Doctrine\Tests\Models\Navigation\NavPointOfInterest', array('key1' => 100));
|
||||
}
|
||||
}
|
@ -25,11 +25,10 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||
$class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||
|
||||
|
||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
|
||||
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||
|
||||
|
||||
$this->loadFixture();
|
||||
}
|
||||
|
||||
@ -256,17 +255,18 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertTrue($user->groups->contains($group));
|
||||
$this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
|
||||
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$group->name = "A New group!";
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertFalse($user->groups->contains($group));
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
@ -275,8 +275,9 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->_em->flush();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertFalse($user->groups->contains($group));
|
||||
$this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
}
|
||||
|
||||
@ -304,6 +305,107 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testRemoveElementOneToMany()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$user->articles->removeElement($article);
|
||||
|
||||
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$article = new \Doctrine\Tests\Models\CMS\CmsArticle();
|
||||
$article->topic = "Testnew";
|
||||
$article->text = "blub";
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$user->articles->removeElement($article);
|
||||
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a new entity should cause no query to be executed.");
|
||||
|
||||
$this->_em->persist($article);
|
||||
$this->_em->flush();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$user->articles->removeElement($article);
|
||||
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||
$this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testRemoveElementManyToMany()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$user->groups->removeElement($group);
|
||||
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
|
||||
$group = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$group->name = "A New group!";
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$user->groups->removeElement($group);
|
||||
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing new entity should cause no query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
|
||||
$this->_em->persist($group);
|
||||
$this->_em->flush();
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$user->groups->removeElement($group);
|
||||
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function testRemoveElementManyToManyInverse()
|
||||
{
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
$this->assertFalse($group->users->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$group->users->removeElement($user);
|
||||
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Removing a managed entity should cause one query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
|
||||
$newUser = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$newUser->name = "A New group!";
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$group->users->removeElement($newUser);
|
||||
|
||||
$this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Removing a new entity should cause no query to be executed.");
|
||||
$this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1399
|
||||
*/
|
||||
|
146
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php
Normal file
146
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
/**
|
||||
* @group DDC-1509
|
||||
*/
|
||||
class DDC1509Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
try {
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1509AbstractFile'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1509File'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1509Picture'),
|
||||
));
|
||||
} catch (\Exception $ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testFailingCase()
|
||||
{
|
||||
$file = new DDC1509File;
|
||||
$thumbnail = new DDC1509File;
|
||||
|
||||
$picture = new DDC1509Picture;
|
||||
$picture->setFile($file);
|
||||
$picture->setThumbnail($thumbnail);
|
||||
|
||||
|
||||
/* @var $em \Doctrine\ORM\EntityManager */
|
||||
$em = $this->_em;
|
||||
$em->persist($picture);
|
||||
$em->flush();
|
||||
$em->clear();
|
||||
|
||||
$id = $picture->getPictureId();
|
||||
|
||||
$pic = $em->merge($picture);
|
||||
/* @var $pic DDC1509Picture */
|
||||
|
||||
$this->assertNotNull($pic->getThumbnail());
|
||||
$this->assertNotNull($pic->getFile());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1509Picture
|
||||
{
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"})
|
||||
*/
|
||||
private $thumbnail;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"})
|
||||
*/
|
||||
private $file;
|
||||
|
||||
/**
|
||||
* Get pictureId
|
||||
*/
|
||||
public function getPictureId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file
|
||||
*/
|
||||
public function setFile($value = null)
|
||||
{
|
||||
$this->file = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function getThumbnail()
|
||||
{
|
||||
return $this->thumbnail;
|
||||
}
|
||||
|
||||
public function setThumbnail($thumbnail)
|
||||
{
|
||||
$this->thumbnail = $thumbnail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"file" = "DDC1509File"})
|
||||
*/
|
||||
class DDC1509AbstractFile
|
||||
{
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @Id
|
||||
* @GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Get fileId
|
||||
*/
|
||||
public function getFileId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1509File extends DDC1509AbstractFile
|
||||
{
|
||||
|
||||
}
|
@ -373,6 +373,25 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
$this->assertEquals(ClassMetadataInfo::GENERATOR_TYPE_NONE, $class->generatorType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1170
|
||||
*/
|
||||
public function testIdentifierColumnDefinition()
|
||||
{
|
||||
|
||||
$class = $this->createClassMetadata(__NAMESPACE__ . '\DDC1170Entity');
|
||||
|
||||
|
||||
$this->assertArrayHasKey('id', $class->fieldMappings);
|
||||
$this->assertArrayHasKey('value', $class->fieldMappings);
|
||||
|
||||
$this->assertArrayHasKey('columnDefinition', $class->fieldMappings['id']);
|
||||
$this->assertArrayHasKey('columnDefinition', $class->fieldMappings['value']);
|
||||
|
||||
$this->assertEquals("INT unsigned NOT NULL", $class->fieldMappings['id']['columnDefinition']);
|
||||
$this->assertEquals("VARCHAR(255) NOT NULL", $class->fieldMappings['value']['columnDefinition']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -597,4 +616,65 @@ class Dog extends Animal
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC1170Entity
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
function __construct($value = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue(strategy="NONE")
|
||||
* @Column(type="integer", columnDefinition = "INT unsigned NOT NULL")
|
||||
**/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(columnDefinition = "VARCHAR(255) NOT NULL")
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public static function loadMetadata(ClassMetadataInfo $metadata)
|
||||
{
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
'columnDefinition' => 'INT unsigned NOT NULL',
|
||||
));
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'value',
|
||||
'columnDefinition' => 'VARCHAR(255) NOT NULL'
|
||||
));
|
||||
|
||||
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE);
|
||||
}
|
||||
|
||||
}
|
@ -9,6 +9,7 @@ use Doctrine\Tests\Mocks\ConnectionMock;
|
||||
use Doctrine\Tests\Mocks\DriverMock;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
@ -81,6 +82,51 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertFalse($h2);
|
||||
$this->assertTrue($h1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1512
|
||||
*/
|
||||
public function testIsTransient()
|
||||
{
|
||||
$cmf = new ClassMetadataFactory();
|
||||
$driver = $this->getMock('Doctrine\ORM\Mapping\Driver\Driver');
|
||||
$driver->expects($this->at(0))
|
||||
->method('isTransient')
|
||||
->with($this->equalTo('Doctrine\Tests\Models\CMS\CmsUser'))
|
||||
->will($this->returnValue(true));
|
||||
$driver->expects($this->at(1))
|
||||
->method('isTransient')
|
||||
->with($this->equalTo('Doctrine\Tests\Models\CMS\CmsArticle'))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$em = $this->_createEntityManager($driver);
|
||||
|
||||
$this->assertTrue($em->getMetadataFactory()->isTransient('Doctrine\Tests\Models\CMS\CmsUser'));
|
||||
$this->assertFalse($em->getMetadataFactory()->isTransient('Doctrine\Tests\Models\CMS\CmsArticle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1512
|
||||
*/
|
||||
public function testIsTransientEntityNamespace()
|
||||
{
|
||||
$cmf = new ClassMetadataFactory();
|
||||
$driver = $this->getMock('Doctrine\ORM\Mapping\Driver\Driver');
|
||||
$driver->expects($this->at(0))
|
||||
->method('isTransient')
|
||||
->with($this->equalTo('Doctrine\Tests\Models\CMS\CmsUser'))
|
||||
->will($this->returnValue(true));
|
||||
$driver->expects($this->at(1))
|
||||
->method('isTransient')
|
||||
->with($this->equalTo('Doctrine\Tests\Models\CMS\CmsArticle'))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$em = $this->_createEntityManager($driver);
|
||||
$em->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS');
|
||||
|
||||
$this->assertTrue($em->getMetadataFactory()->isTransient('CMS:CmsUser'));
|
||||
$this->assertFalse($em->getMetadataFactory()->isTransient('CMS:CmsArticle'));
|
||||
}
|
||||
|
||||
protected function _createEntityManager($metadataDriver)
|
||||
{
|
||||
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
|
||||
$metadata->mapField(array(
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
'columnDefinition' => 'INT unsigned NOT NULL',
|
||||
));
|
||||
|
||||
$metadata->mapField(array(
|
||||
'fieldName' => 'value',
|
||||
'columnDefinition' => 'VARCHAR(255) NOT NULL'
|
||||
));
|
||||
|
||||
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE);
|
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\ORM\Mapping\DDC1170Entity">
|
||||
<id name="id" column-definition="INT unsigned NOT NULL">
|
||||
<generator strategy="NONE"/>
|
||||
</id>
|
||||
|
||||
<field name="value" column-definition="VARCHAR(255) NOT NULL"/>
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
@ -0,0 +1,10 @@
|
||||
Doctrine\Tests\ORM\Mapping\DDC1170Entity:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
columnDefinition: INT unsigned NOT NULL
|
||||
generator:
|
||||
strategy: NONE
|
||||
fields:
|
||||
value:
|
||||
columnDefinition: VARCHAR(255) NOT NULL
|
@ -40,7 +40,14 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
$query->setHint($name, $value);
|
||||
}
|
||||
|
||||
parent::assertEquals($sqlToBeConfirmed, $query->getSQL());
|
||||
$sqlGenerated = $query->getSQL();
|
||||
|
||||
parent::assertEquals(
|
||||
$sqlToBeConfirmed,
|
||||
$sqlGenerated,
|
||||
sprintf('"%s" is not equal of "%s"', $sqlGenerated, $sqlToBeConfirmed)
|
||||
);
|
||||
|
||||
$query->free();
|
||||
} catch (\Exception $e) {
|
||||
$this->fail($e->getMessage() ."\n".$e->getTraceAsString());
|
||||
@ -444,8 +451,8 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testSupportsSingleValuedInExpressionWithoutSpacesInWherePart()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN(46)",
|
||||
"SELECT c0_.name AS name0 FROM cms_users c0_ WHERE c0_.id IN (46)"
|
||||
"SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE IDENTITY(u.email) IN(46)",
|
||||
"SELECT c0_.name AS name0 FROM cms_users c0_ WHERE c0_.email_id IN (46)"
|
||||
);
|
||||
}
|
||||
|
||||
@ -460,8 +467,8 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testSupportsNotInExpressionInWherePart()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (1)',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id NOT IN (1)'
|
||||
'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :id NOT IN (1)',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE ? NOT IN (1)'
|
||||
);
|
||||
}
|
||||
|
||||
@ -529,43 +536,71 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsMemberOfExpression()
|
||||
public function testSupportsMemberOfExpressionOneToMany()
|
||||
{
|
||||
// "Get all users who have $phone as a phonenumber." (*cough* doesnt really make sense...)
|
||||
$q1 = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers');
|
||||
$q1->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
|
||||
$q = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers');
|
||||
$q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
|
||||
|
||||
$phone = new \Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
$phone->phonenumber = 101;
|
||||
$q1->setParameter('param', $phone);
|
||||
$q->setParameter('param', $phone);
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT c0_.id AS id0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id AND c1_.phonenumber = ?)',
|
||||
$q1->getSql()
|
||||
$q->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsMemberOfExpressionManyToMany()
|
||||
{
|
||||
// "Get all users who are members of $group."
|
||||
$q2 = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.groups');
|
||||
$q2->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
|
||||
$q = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.groups');
|
||||
$q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
|
||||
|
||||
$group = new \Doctrine\Tests\Models\CMS\CmsGroup;
|
||||
$group->id = 101;
|
||||
$q2->setParameter('param', $group);
|
||||
$q->setParameter('param', $group);
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT c0_.id AS id0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ INNER JOIN cms_groups c2_ ON c1_.group_id = c2_.id WHERE c1_.user_id = c0_.id AND c2_.id = ?)',
|
||||
$q2->getSql()
|
||||
$q->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsMemberOfExpressionSelfReferencing()
|
||||
{
|
||||
// "Get all persons who have $person as a friend."
|
||||
// Tough one: Many-many self-referencing ("friends") with class table inheritance
|
||||
$q3 = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p WHERE :param MEMBER OF p.friends');
|
||||
$q = $this->_em->createQuery('SELECT p FROM Doctrine\Tests\Models\Company\CompanyPerson p WHERE :param MEMBER OF p.friends');
|
||||
$person = new \Doctrine\Tests\Models\Company\CompanyPerson;
|
||||
$this->_em->getClassMetadata(get_class($person))->setIdentifierValues($person, array('id' => 101));
|
||||
$q3->setParameter('param', $person);
|
||||
$q->setParameter('param', $person);
|
||||
$this->assertEquals(
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c2_.salary AS salary3, c2_.department AS department4, c2_.startDate AS startDate5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7, c1_.car_id AS car_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.friend_id = c4_.id WHERE c3_.person_id = c0_.id AND c4_.id = ?)',
|
||||
$q3->getSql()
|
||||
$q->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsMemberOfWithSingleValuedAssociation()
|
||||
{
|
||||
// Impossible example, but it illustrates the purpose
|
||||
$q = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.email MEMBER OF u.groups');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT c0_.id AS id0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ INNER JOIN cms_groups c2_ ON c1_.group_id = c2_.id WHERE c1_.user_id = c0_.id AND c2_.id = c0_.email_id)',
|
||||
$q->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsMemberOfWithIdentificationVariable()
|
||||
{
|
||||
// Impossible example, but it illustrates the purpose
|
||||
$q = $this->_em->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u MEMBER OF u.groups');
|
||||
|
||||
$this->assertEquals(
|
||||
'SELECT c0_.id AS id0 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_users_groups c1_ INNER JOIN cms_groups c2_ ON c1_.group_id = c2_.id WHERE c1_.user_id = c0_.id AND c2_.id = c0_.id)',
|
||||
$q->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
@ -1302,7 +1337,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
"SELECT d0_.article_id AS article_id0, d0_.title AS title1 FROM DDC117Article d0_ WHERE EXISTS (SELECT d1_.source_id, d1_.target_id FROM DDC117Reference d1_ WHERE d1_.source_id = d0_.article_id)"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @group DDC-1474
|
||||
*/
|
||||
@ -1312,13 +1347,13 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
'SELECT - e.value AS value, e.id FROM ' . __NAMESPACE__ . '\DDC1474Entity e',
|
||||
'SELECT -d0_.value AS sclr0, d0_.id AS id1 FROM DDC1474Entity d0_'
|
||||
);
|
||||
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT e.id, + e.value AS value FROM ' . __NAMESPACE__ . '\DDC1474Entity e',
|
||||
'SELECT d0_.id AS id0, +d0_.value AS sclr1 FROM DDC1474Entity d0_'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @group DDC-1430
|
||||
*/
|
||||
@ -1328,13 +1363,35 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id'
|
||||
);
|
||||
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT e FROM Doctrine\Tests\Models\CMS\CmsEmployee e GROUP BY e',
|
||||
'SELECT c0_.id AS id0, c0_.name AS name1 FROM cms_employees c0_ GROUP BY c0_.id, c0_.name, c0_.spouse_id'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1236
|
||||
*/
|
||||
public function testGroupBySupportsResultVariable()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT u, u.status AS st FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY st',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c0_.status AS status4 FROM cms_users c0_ GROUP BY status4'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1236
|
||||
*/
|
||||
public function testGroupBySupportsIdentificationVariable()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT u AS user FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY user',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ GROUP BY id0, status1, username2, name3'
|
||||
);
|
||||
}
|
||||
|
||||
public function testCustomTypeValueSql()
|
||||
{
|
||||
if (DBALType::hasType('negative_to_positive')) {
|
||||
@ -1441,19 +1498,19 @@ class DDC1474Entity
|
||||
{
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue()
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @column(type="float")
|
||||
* @column(type="float")
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param string $float
|
||||
* @param string $float
|
||||
*/
|
||||
public function __construct($float)
|
||||
{
|
||||
@ -1469,7 +1526,7 @@ class DDC1474Entity
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
* @return float
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
@ -1477,7 +1534,7 @@ class DDC1474Entity
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $value
|
||||
* @param float $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
|
@ -52,6 +52,8 @@ abstract class OrmTestCase extends DoctrineTestCase
|
||||
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
|
||||
}
|
||||
}
|
||||
\Doctrine\Common\Annotations\AnnotationRegistry::registerFile(
|
||||
__DIR__ . "/../../../lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php");
|
||||
return new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, (array)$paths);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user