1
0
mirror of synced 2025-03-05 04:13:20 +03:00

Merge branch 'master' of github.com:doctrine/doctrine2

This commit is contained in:
Guilherme Blanco 2011-10-28 12:49:21 -02:00
commit 7841ccb7c0
15 changed files with 365 additions and 343 deletions

View File

@ -1,3 +1,17 @@
# ResultCache implementation rewritten
The result cache is completly 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:
1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork
leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore.
2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result.
The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now
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 # EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they Entities returned from EntityManager#getPartialReference() are now marked as read-only if they

View File

@ -20,7 +20,8 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type, use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\QueryException; Doctrine\ORM\Query\QueryException,
Doctrine\DBAL\Cache\QueryCacheProfile;
/** /**
* Base contract for ORM queries. Base class for Query and NativeQuery. * Base contract for ORM queries. Base class for Query and NativeQuery.
@ -91,34 +92,15 @@ abstract class AbstractQuery
protected $_hydrationMode = self::HYDRATE_OBJECT; protected $_hydrationMode = self::HYDRATE_OBJECT;
/** /**
* The locally set cache driver used for caching result sets of this query. * @param \Doctrine\DBAL\Cache\QueryCacheProfile
*
* @var CacheDriver
*/ */
protected $_resultCacheDriver; protected $_queryCacheProfile;
/**
* Boolean flag for whether or not to cache the results of this query.
*
* @var boolean
*/
protected $_useResultCache;
/**
* @var string The id to store the result cache entry under.
*/
protected $_resultCacheId;
/** /**
* @var boolean Boolean value that indicates whether or not expire the result cache. * @var boolean Boolean value that indicates whether or not expire the result cache.
*/ */
protected $_expireResultCache = false; protected $_expireResultCache = false;
/**
* @var int Result Cache lifetime.
*/
protected $_resultCacheTTL;
/** /**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>. * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
* *
@ -260,7 +242,7 @@ abstract class AbstractQuery
} }
/** /**
* Defines a cache driver to be used for caching result sets. * Defines a cache driver to be used for caching result sets and implictly enables caching.
* *
* @param Doctrine\Common\Cache\Cache $driver Cache driver * @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery * @return Doctrine\ORM\AbstractQuery
@ -270,9 +252,10 @@ abstract class AbstractQuery
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver(); throw ORMException::invalidResultCacheDriver();
} }
$this->_resultCacheDriver = $resultCacheDriver; if ($this->_queryCacheProfile) {
if ($resultCacheDriver) { $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver);
$this->_useResultCache = true; } else {
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver);
} }
return $this; return $this;
} }
@ -280,12 +263,13 @@ abstract class AbstractQuery
/** /**
* Returns the cache driver used for caching result sets. * Returns the cache driver used for caching result sets.
* *
* @deprecated
* @return Doctrine\Common\Cache\Cache Cache driver * @return Doctrine\Common\Cache\Cache Cache driver
*/ */
public function getResultCacheDriver() public function getResultCacheDriver()
{ {
if ($this->_resultCacheDriver) { if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $this->_resultCacheDriver; return $this->_queryCacheProfile->getResultCacheDriver();
} else { } else {
return $this->_em->getConfiguration()->getResultCacheImpl(); return $this->_em->getConfiguration()->getResultCacheImpl();
} }
@ -296,18 +280,17 @@ abstract class AbstractQuery
* how long and which ID to use for the cache entry. * how long and which ID to use for the cache entry.
* *
* @param boolean $bool * @param boolean $bool
* @param integer $timeToLive * @param integer $lifetime
* @param string $resultCacheId * @param string $resultCacheId
* @return Doctrine\ORM\AbstractQuery This query instance. * @return Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null) public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
{ {
$this->_useResultCache = $bool; if ($bool) {
if ($timeToLive) { $this->setResultCacheLifetime($lifetime);
$this->setResultCacheLifetime($timeToLive); $this->setResultCacheId($resultCacheId);
} } else {
if ($resultCacheId) { $this->_queryCacheProfile = null;
$this->_resultCacheId = $resultCacheId;
} }
return $this; return $this;
} }
@ -315,27 +298,33 @@ abstract class AbstractQuery
/** /**
* Defines how long the result cache will be active before expire. * Defines how long the result cache will be active before expire.
* *
* @param integer $timeToLive How long the cache entry is valid. * @param integer $lifetime How long the cache entry is valid.
* @return Doctrine\ORM\AbstractQuery This query instance. * @return Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function setResultCacheLifetime($timeToLive) public function setResultCacheLifetime($lifetime)
{ {
if ($timeToLive !== null) { if ($lifetime === null) {
$timeToLive = (int) $timeToLive; $lifetime = 0;
} else {
$lifetime = (int)$lifetime;
}
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
} else {
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
} }
$this->_resultCacheTTL = $timeToLive;
return $this; return $this;
} }
/** /**
* Retrieves the lifetime of resultset cache. * Retrieves the lifetime of resultset cache.
* *
* @deprecated
* @return integer * @return integer
*/ */
public function getResultCacheLifetime() public function getResultCacheLifetime()
{ {
return $this->_resultCacheTTL; return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
} }
/** /**
@ -360,6 +349,14 @@ abstract class AbstractQuery
return $this->_expireResultCache; return $this->_expireResultCache;
} }
/**
* @return QueryCacheProfile
*/
public function getQueryCacheProfile()
{
return $this->_queryCacheProfile;
}
/** /**
* Change the default fetch mode of an association for this query. * Change the default fetch mode of an association for this query.
* *
@ -584,28 +581,6 @@ abstract class AbstractQuery
$this->setParameters($params); $this->setParameters($params);
} }
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
list($key, $hash) = $this->getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
if ($cached === false || !isset($cached[$key])) {
// Cache miss.
$stmt = $this->_doExecute();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached[$key];
}
}
$stmt = $this->_doExecute(); $stmt = $this->_doExecute();
if (is_numeric($stmt)) { if (is_numeric($stmt)) {
@ -627,43 +602,23 @@ abstract class AbstractQuery
*/ */
public function setResultCacheId($id) public function setResultCacheId($id)
{ {
$this->_resultCacheId = $id; if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
}
return $this; return $this;
} }
/** /**
* Get the result cache id to use to store the result set cache entry. * Get the result cache id to use to store the result set cache entry if set.
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
* *
* @return array ($key, $hash) * @deprecated
* @return string
*/ */
protected function getResultCacheId() public function getResultCacheId()
{ {
if ($this->_resultCacheId) { return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
return array($this->_resultCacheId, $this->_resultCacheId);
} else {
$params = $this->_params;
foreach ($params AS $key => $value) {
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$params[$key] = $idValues;
} else {
$params[$key] = $value;
}
}
$sql = $this->getSql();
ksort($this->_hints);
$key = implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
return array($key, md5($key));
}
} }
/** /**

View File

@ -209,27 +209,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['metadataDriverImpl'] : null; $this->_attributes['metadataDriverImpl'] : null;
} }
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getResultCacheImpl()
{
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/** /**
* Gets the cache driver implementation that is used for the query cache (SQL cache). * Gets the cache driver implementation that is used for the query cache (SQL cache).
* *

View File

@ -57,17 +57,17 @@ final class NativeQuery extends AbstractQuery
*/ */
protected function _doExecute() protected function _doExecute()
{ {
$stmt = $this->_em->getConnection()->prepare($this->_sql);
$params = $this->_params; $params = $this->_params;
foreach ($params as $key => $value) { $types = $this->_paramTypes;
if (isset($this->_paramTypes[$key])) { if ($params) {
$stmt->bindValue($key, $value, $this->_paramTypes[$key]); if (is_int(key($params))) {
} else { ksort($params);
$stmt->bindValue($key, $value); ksort($types);
$params = array_values($params);
$types = array_values($types);
} }
} }
$stmt->execute();
return $stmt; return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile);
} }
} }

View File

@ -92,7 +92,7 @@ class BasicEntityPersister
/** /**
* The database platform. * The database platform.
* *
* @var Doctrine\DBAL\Platforms\AbstractPlatform * @var Doctrine\DBAL\Platforms\AbstractPlatform
*/ */
protected $_platform; protected $_platform;
@ -110,12 +110,12 @@ class BasicEntityPersister
* @var array * @var array
*/ */
protected $_queuedInserts = array(); protected $_queuedInserts = array();
/** /**
* ResultSetMapping that is used for all queries. Is generated lazily once per request. * ResultSetMapping that is used for all queries. Is generated lazily once per request.
* *
* TODO: Evaluate Caching in combination with the other cached SQL snippets. * TODO: Evaluate Caching in combination with the other cached SQL snippets.
* *
* @var Query\ResultSetMapping * @var Query\ResultSetMapping
*/ */
protected $_rsm; protected $_rsm;
@ -123,7 +123,7 @@ class BasicEntityPersister
/** /**
* The map of column names to DBAL mapping types of all prepared columns used * The map of column names to DBAL mapping types of all prepared columns used
* when INSERTing or UPDATEing an entity. * when INSERTing or UPDATEing an entity.
* *
* @var array * @var array
* @see _prepareInsertData($entity) * @see _prepareInsertData($entity)
* @see _prepareUpdateData($entity) * @see _prepareUpdateData($entity)
@ -133,7 +133,7 @@ class BasicEntityPersister
/** /**
* The INSERT SQL statement used for entities handled by this persister. * The INSERT SQL statement used for entities handled by this persister.
* This SQL is only generated once per request, if at all. * This SQL is only generated once per request, if at all.
* *
* @var string * @var string
*/ */
private $_insertSql; private $_insertSql;
@ -141,29 +141,29 @@ class BasicEntityPersister
/** /**
* The SELECT column list SQL fragment used for querying entities by this persister. * The SELECT column list SQL fragment used for querying entities by this persister.
* This SQL fragment is only generated once per request, if at all. * This SQL fragment is only generated once per request, if at all.
* *
* @var string * @var string
*/ */
protected $_selectColumnListSql; protected $_selectColumnListSql;
/** /**
* The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one * The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one
* associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations. * associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations.
* *
* @var string * @var string
*/ */
protected $_selectJoinSql; protected $_selectJoinSql;
/** /**
* Counter for creating unique SQL table and column aliases. * Counter for creating unique SQL table and column aliases.
* *
* @var integer * @var integer
*/ */
protected $_sqlAliasCounter = 0; protected $_sqlAliasCounter = 0;
/** /**
* Map from class names (FQCN) to the corresponding generated SQL table aliases. * Map from class names (FQCN) to the corresponding generated SQL table aliases.
* *
* @var array * @var array
*/ */
protected $_sqlTableAliases = array(); protected $_sqlTableAliases = array();
@ -171,7 +171,7 @@ class BasicEntityPersister
/** /**
* Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager * Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
* and persists instances of the class described by the given ClassMetadata descriptor. * and persists instances of the class described by the given ClassMetadata descriptor.
* *
* @param Doctrine\ORM\EntityManager $em * @param Doctrine\ORM\EntityManager $em
* @param Doctrine\ORM\Mapping\ClassMetadata $class * @param Doctrine\ORM\Mapping\ClassMetadata $class
*/ */
@ -205,7 +205,7 @@ class BasicEntityPersister
/** /**
* Executes all queued entity insertions and returns any generated post-insert * Executes all queued entity insertions and returns any generated post-insert
* identifiers that were created as a result of the insertions. * identifiers that were created as a result of the insertions.
* *
* If no inserts are queued, invoking this method is a NOOP. * If no inserts are queued, invoking this method is a NOOP.
* *
* @return array An array of any generated post-insert IDs. This will be an empty array * @return array An array of any generated post-insert IDs. This will be an empty array
@ -229,7 +229,7 @@ class BasicEntityPersister
if (isset($insertData[$tableName])) { if (isset($insertData[$tableName])) {
$paramIndex = 1; $paramIndex = 1;
foreach ($insertData[$tableName] as $column => $value) { foreach ($insertData[$tableName] as $column => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]); $stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$column]);
} }
@ -257,7 +257,7 @@ class BasicEntityPersister
/** /**
* Retrieves the default version value which was created * Retrieves the default version value which was created
* by the preceding INSERT statement and assigns it back in to the * by the preceding INSERT statement and assigns it back in to the
* entities version field. * entities version field.
* *
* @param object $entity * @param object $entity
@ -271,7 +271,7 @@ class BasicEntityPersister
/** /**
* Fetch the current version value of a versioned entity. * Fetch the current version value of a versioned entity.
* *
* @param Doctrine\ORM\Mapping\ClassMetadata $versionedClass * @param Doctrine\ORM\Mapping\ClassMetadata $versionedClass
* @param mixed $id * @param mixed $id
* @return mixed * @return mixed
@ -280,9 +280,9 @@ class BasicEntityPersister
{ {
$versionField = $versionedClass->versionField; $versionField = $versionedClass->versionField;
$identifier = $versionedClass->getIdentifierColumnNames(); $identifier = $versionedClass->getIdentifierColumnNames();
$versionFieldColumnName = $versionedClass->getQuotedColumnName($versionField, $this->_platform); $versionFieldColumnName = $versionedClass->getQuotedColumnName($versionField, $this->_platform);
//FIXME: Order with composite keys might not be correct //FIXME: Order with composite keys might not be correct
$sql = 'SELECT ' . $versionFieldColumnName $sql = 'SELECT ' . $versionFieldColumnName
. ' FROM ' . $versionedClass->getQuotedTableName($this->_platform) . ' FROM ' . $versionedClass->getQuotedTableName($this->_platform)
@ -299,7 +299,7 @@ class BasicEntityPersister
* The data to update is retrieved through {@link _prepareUpdateData}. * The data to update is retrieved through {@link _prepareUpdateData}.
* Subclasses that override this method are supposed to obtain the update data * Subclasses that override this method are supposed to obtain the update data
* in the same way, through {@link _prepareUpdateData}. * in the same way, through {@link _prepareUpdateData}.
* *
* Subclasses are also supposed to take care of versioning when overriding this method, * Subclasses are also supposed to take care of versioning when overriding this method,
* if necessary. The {@link _updateTable} method can be used to apply the data retrieved * if necessary. The {@link _updateTable} method can be used to apply the data retrieved
* from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning. * from {@_prepareUpdateData} on the target tables, thereby optionally applying versioning.
@ -310,7 +310,7 @@ class BasicEntityPersister
{ {
$updateData = $this->_prepareUpdateData($entity); $updateData = $this->_prepareUpdateData($entity);
$tableName = $this->_class->getTableName(); $tableName = $this->_class->getTableName();
if (isset($updateData[$tableName]) && $updateData[$tableName]) { if (isset($updateData[$tableName]) && $updateData[$tableName]) {
$this->_updateTable( $this->_updateTable(
$entity, $this->_class->getQuotedTableName($this->_platform), $entity, $this->_class->getQuotedTableName($this->_platform),
@ -338,17 +338,17 @@ class BasicEntityPersister
$set = $params = $types = array(); $set = $params = $types = array();
foreach ($updateData as $columnName => $value) { foreach ($updateData as $columnName => $value) {
$set[] = (isset($this->_class->fieldNames[$columnName])) $set[] = (isset($this->_class->fieldNames[$columnName]))
? $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?' ? $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?'
: $columnName . ' = ?'; : $columnName . ' = ?';
$params[] = $value; $params[] = $value;
$types[] = $this->_columnTypes[$columnName]; $types[] = $this->_columnTypes[$columnName];
} }
$where = array(); $where = array();
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity); $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
foreach ($this->_class->identifier as $idField) { foreach ($this->_class->identifier as $idField) {
if (isset($this->_class->associationMappings[$idField])) { if (isset($this->_class->associationMappings[$idField])) {
$targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']); $targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
@ -366,13 +366,13 @@ class BasicEntityPersister
$versionField = $this->_class->versionField; $versionField = $this->_class->versionField;
$versionFieldType = $this->_class->fieldMappings[$versionField]['type']; $versionFieldType = $this->_class->fieldMappings[$versionField]['type'];
$versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform); $versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform);
if ($versionFieldType == Type::INTEGER) { if ($versionFieldType == Type::INTEGER) {
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1'; $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
} else if ($versionFieldType == Type::DATETIME) { } else if ($versionFieldType == Type::DATETIME) {
$set[] = $versionColumn . ' = CURRENT_TIMESTAMP'; $set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
} }
$where[] = $versionColumn; $where[] = $versionColumn;
$params[] = $this->_class->reflFields[$versionField]->getValue($entity); $params[] = $this->_class->reflFields[$versionField]->getValue($entity);
$types[] = $this->_class->fieldMappings[$versionField]['type']; $types[] = $this->_class->fieldMappings[$versionField]['type'];
@ -401,18 +401,18 @@ class BasicEntityPersister
// @Todo this only covers scenarios with no inheritance or of the same level. Is there something // @Todo this only covers scenarios with no inheritance or of the same level. Is there something
// like self-referential relationship between different levels of an inheritance hierachy? I hope not! // like self-referential relationship between different levels of an inheritance hierachy? I hope not!
$selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']); $selfReferential = ($mapping['targetEntity'] == $mapping['sourceEntity']);
if ( ! $mapping['isOwningSide']) { if ( ! $mapping['isOwningSide']) {
$relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']); $relatedClass = $this->_em->getClassMetadata($mapping['targetEntity']);
$mapping = $relatedClass->associationMappings[$mapping['mappedBy']]; $mapping = $relatedClass->associationMappings[$mapping['mappedBy']];
$keys = array_keys($mapping['relationToTargetKeyColumns']); $keys = array_keys($mapping['relationToTargetKeyColumns']);
if ($selfReferential) { if ($selfReferential) {
$otherKeys = array_keys($mapping['relationToSourceKeyColumns']); $otherKeys = array_keys($mapping['relationToSourceKeyColumns']);
} }
} else { } else {
$keys = array_keys($mapping['relationToSourceKeyColumns']); $keys = array_keys($mapping['relationToSourceKeyColumns']);
if ($selfReferential) { if ($selfReferential) {
$otherKeys = array_keys($mapping['relationToTargetKeyColumns']); $otherKeys = array_keys($mapping['relationToTargetKeyColumns']);
} }
@ -420,13 +420,13 @@ class BasicEntityPersister
if ( ! isset($mapping['isOnDeleteCascade'])) { if ( ! isset($mapping['isOnDeleteCascade'])) {
$this->_conn->delete( $this->_conn->delete(
$this->_class->getQuotedJoinTableName($mapping, $this->_platform), $this->_class->getQuotedJoinTableName($mapping, $this->_platform),
array_combine($keys, $identifier) array_combine($keys, $identifier)
); );
if ($selfReferential) { if ($selfReferential) {
$this->_conn->delete( $this->_conn->delete(
$this->_class->getQuotedJoinTableName($mapping, $this->_platform), $this->_class->getQuotedJoinTableName($mapping, $this->_platform),
array_combine($otherKeys, $identifier) array_combine($otherKeys, $identifier)
); );
} }
@ -458,7 +458,7 @@ class BasicEntityPersister
* Prepares the changeset of an entity for database insertion (UPDATE). * Prepares the changeset of an entity for database insertion (UPDATE).
* *
* The changeset is obtained from the currently running UnitOfWork. * The changeset is obtained from the currently running UnitOfWork.
* *
* During this preparation the array that is passed as the second parameter is filled with * During this preparation the array that is passed as the second parameter is filled with
* <columnName> => <value> pairs, grouped by table name. * <columnName> => <value> pairs, grouped by table name.
* *
@ -493,7 +493,7 @@ class BasicEntityPersister
if (isset($this->_class->associationMappings[$field])) { if (isset($this->_class->associationMappings[$field])) {
$assoc = $this->_class->associationMappings[$field]; $assoc = $this->_class->associationMappings[$field];
// Only owning side of x-1 associations can have a FK column. // Only owning side of x-1 associations can have a FK column.
if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) { if ( ! $assoc['isOwningSide'] || ! ($assoc['type'] & ClassMetadata::TO_ONE)) {
continue; continue;
@ -501,7 +501,7 @@ class BasicEntityPersister
if ($newVal !== null) { if ($newVal !== null) {
$oid = spl_object_hash($newVal); $oid = spl_object_hash($newVal);
if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) { if (isset($this->_queuedInserts[$oid]) || $uow->isScheduledForInsert($newVal)) {
// The associated entity $newVal is not yet persisted, so we must // The associated entity $newVal is not yet persisted, so we must
// set $newVal = null, in order to insert a null value and schedule an // set $newVal = null, in order to insert a null value and schedule an
@ -528,7 +528,7 @@ class BasicEntityPersister
} else { } else {
$result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]]; $result[$owningTable][$sourceColumn] = $newValId[$targetClass->fieldNames[$targetColumn]];
} }
$this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn); $this->_columnTypes[$sourceColumn] = $targetClass->getTypeOfColumn($targetColumn);
} }
} else { } else {
@ -537,7 +537,7 @@ class BasicEntityPersister
$result[$this->getOwningTable($field)][$columnName] = $newVal; $result[$this->getOwningTable($field)][$columnName] = $newVal;
} }
} }
return $result; return $result;
} }
@ -589,7 +589,7 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit); $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit);
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
if ($entity !== null) { if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true; $hints[Query::HINT_REFRESH] = true;
$hints[Query::HINT_REFRESH_ENTITY] = $entity; $hints[Query::HINT_REFRESH_ENTITY] = $entity;
@ -597,7 +597,7 @@ class BasicEntityPersister
$hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); $hydrator = $this->_em->newHydrator($this->_selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints); $entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
return $entities ? $entities[0] : null; return $entities ? $entities[0] : null;
} }
@ -626,17 +626,17 @@ class BasicEntityPersister
// Mark inverse side as fetched in the hints, otherwise the UoW would // Mark inverse side as fetched in the hints, otherwise the UoW would
// try to load it in a separate query (remember: to-one inverse sides can not be lazy). // try to load it in a separate query (remember: to-one inverse sides can not be lazy).
$hints = array(); $hints = array();
if ($isInverseSingleValued) { if ($isInverseSingleValued) {
$hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true; $hints['fetched'][$targetClass->name][$assoc['inversedBy']] = true;
if ($targetClass->subClasses) { if ($targetClass->subClasses) {
foreach ($targetClass->subClasses as $targetSubclassName) { foreach ($targetClass->subClasses as $targetSubclassName) {
$hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true; $hints['fetched'][$targetSubclassName][$assoc['inversedBy']] = true;
} }
} }
} }
/* cascade read-only status /* cascade read-only status
if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) { if ($this->_em->getUnitOfWork()->isReadOnly($sourceEntity)) {
$hints[Query::HINT_READ_ONLY] = true; $hints[Query::HINT_READ_ONLY] = true;
@ -652,7 +652,7 @@ class BasicEntityPersister
} else { } else {
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
$owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']); $owningAssoc = $targetClass->getAssociationMapping($assoc['mappedBy']);
// TRICKY: since the association is specular source and target are flipped // TRICKY: since the association is specular source and target are flipped
foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) { foreach ($owningAssoc['targetToSourceKeyColumns'] as $sourceKeyColumn => $targetKeyColumn) {
if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) { if ( ! isset($sourceClass->fieldNames[$sourceKeyColumn])) {
@ -660,12 +660,12 @@ class BasicEntityPersister
$sourceClass->name, $sourceKeyColumn $sourceClass->name, $sourceKeyColumn
); );
} }
// unset the old value and set the new sql aliased value here. By definition // unset the old value and set the new sql aliased value here. By definition
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method. // unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
$identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] = $identifier[$this->_getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
unset($identifier[$targetKeyColumn]); unset($identifier[$targetKeyColumn]);
} }
@ -681,7 +681,7 @@ class BasicEntityPersister
/** /**
* Refreshes a managed entity. * Refreshes a managed entity.
* *
* @param array $id The identifier of the entity as an associative array from * @param array $id The identifier of the entity as an associative array from
* column or field names to values. * column or field names to values.
* @param object $entity The entity to refresh. * @param object $entity The entity to refresh.
@ -691,16 +691,16 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($id); $sql = $this->_getSelectEntitiesSQL($id);
list($params, $types) = $this->expandParameters($id); list($params, $types) = $this->expandParameters($id);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true)); $hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) { if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity); $this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
} }
$evm = $this->_em->getEventManager(); $evm = $this->_em->getEventManager();
if ($evm->hasListeners(Events::postLoad)) { if ($evm->hasListeners(Events::postLoad)) {
$evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em)); $evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->_em));
} }
@ -708,7 +708,7 @@ class BasicEntityPersister
/** /**
* Loads a list of entities by a list of field criteria. * Loads a list of entities by a list of field criteria.
* *
* @param array $criteria * @param array $criteria
* @param array $orderBy * @param array $orderBy
* @param int $limit * @param int $limit
@ -723,13 +723,13 @@ class BasicEntityPersister
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
$hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT); $hydrator = $this->_em->newHydrator(($this->_selectJoinSql) ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true)); return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
} }
/** /**
* Get (sliced or full) elements of the given collection. * Get (sliced or full) elements of the given collection.
* *
* @param array $assoc * @param array $assoc
* @param object $sourceEntity * @param object $sourceEntity
* @param int|null $offset * @param int|null $offset
@ -739,16 +739,16 @@ class BasicEntityPersister
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{ {
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit); $stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt); return $this->loadArrayFromStatement($assoc, $stmt);
} }
/** /**
* Load an array of entities from a given dbal statement. * Load an array of entities from a given dbal statement.
* *
* @param array $assoc * @param array $assoc
* @param Doctrine\DBAL\Statement $stmt * @param Doctrine\DBAL\Statement $stmt
* *
* @return array * @return array
*/ */
private function loadArrayFromStatement($assoc, $stmt) private function loadArrayFromStatement($assoc, $stmt)
@ -763,21 +763,21 @@ class BasicEntityPersister
} }
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $rsm, $hints); return $hydrator->hydrateAll($stmt, $rsm, $hints);
} }
/** /**
* Hydrate a collection from a given dbal statement. * Hydrate a collection from a given dbal statement.
* *
* @param array $assoc * @param array $assoc
* @param Doctrine\DBAL\Statement $stmt * @param Doctrine\DBAL\Statement $stmt
* @param PersistentCollection $coll * @param PersistentCollection $coll
* *
* @return array * @return array
*/ */
private function loadCollectionFromStatement($assoc, $stmt, $coll) private function loadCollectionFromStatement($assoc, $stmt, $coll)
{ {
$hints = array('deferEagerLoads' => true, 'collection' => $coll); $hints = array('deferEagerLoads' => true, 'collection' => $coll);
if (isset($assoc['indexBy'])) { if (isset($assoc['indexBy'])) {
@ -788,7 +788,7 @@ class BasicEntityPersister
} }
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT); $hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $rsm, $hints); return $hydrator->hydrateAll($stmt, $rsm, $hints);
} }
@ -805,7 +805,7 @@ class BasicEntityPersister
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{ {
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity); $stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll); return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
} }
@ -813,15 +813,15 @@ class BasicEntityPersister
{ {
$criteria = array(); $criteria = array();
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']); $sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
if ($assoc['isOwningSide']) { if ($assoc['isOwningSide']) {
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform); $quotedJoinTable = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform);
foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
if ($sourceClass->containsForeignIdentifier) { if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn); $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
@ -839,18 +839,18 @@ class BasicEntityPersister
} else { } else {
$owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']]; $owningAssoc = $this->_em->getClassMetadata($assoc['targetEntity'])->associationMappings[$assoc['mappedBy']];
$quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform); $quotedJoinTable = $sourceClass->getQuotedJoinTableName($owningAssoc, $this->_platform);
// TRICKY: since the association is inverted source and target are flipped // TRICKY: since the association is inverted source and target are flipped
foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) { foreach ($owningAssoc['relationToTargetKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
if ($sourceClass->containsForeignIdentifier) { if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn); $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
} }
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value;
} else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) { } else if (isset($sourceClass->fieldNames[$sourceKeyColumn])) {
$criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
@ -864,7 +864,7 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset); $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
return $this->_conn->executeQuery($sql, $params, $types); return $this->_conn->executeQuery($sql, $params, $types);
} }
@ -890,7 +890,7 @@ class BasicEntityPersister
$orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : ''; $orderBySql = $orderBy ? $this->_getOrderBySQL($orderBy, $this->_getSQLTableAlias($this->_class->name)) : '';
$lockSql = ''; $lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) { if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql(); $lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
@ -908,7 +908,7 @@ class BasicEntityPersister
/** /**
* Gets the ORDER BY SQL snippet for ordered collections. * Gets the ORDER BY SQL snippet for ordered collections.
* *
* @param array $orderBy * @param array $orderBy
* @param string $baseTableAlias * @param string $baseTableAlias
* @return string * @return string
@ -917,7 +917,7 @@ class BasicEntityPersister
protected final function _getOrderBySQL(array $orderBy, $baseTableAlias) protected final function _getOrderBySQL(array $orderBy, $baseTableAlias)
{ {
$orderBySql = ''; $orderBySql = '';
foreach ($orderBy as $fieldName => $orientation) { foreach ($orderBy as $fieldName => $orientation) {
if ( ! isset($this->_class->fieldMappings[$fieldName])) { if ( ! isset($this->_class->fieldMappings[$fieldName])) {
throw ORMException::unrecognizedField($fieldName); throw ORMException::unrecognizedField($fieldName);
@ -928,7 +928,7 @@ class BasicEntityPersister
: $baseTableAlias; : $baseTableAlias;
$columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform); $columnName = $this->_class->getQuotedColumnName($fieldName, $this->_platform);
$orderBySql .= $orderBySql ? ', ' : ' ORDER BY '; $orderBySql .= $orderBySql ? ', ' : ' ORDER BY ';
$orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation; $orderBySql .= $tableAlias . '.' . $columnName . ' ' . $orientation;
} }
@ -944,7 +944,7 @@ class BasicEntityPersister
* list SQL fragment. Note that in the implementation of BasicEntityPersister * list SQL fragment. Note that in the implementation of BasicEntityPersister
* the resulting SQL fragment is generated only once and cached in {@link _selectColumnListSql}. * the resulting SQL fragment is generated only once and cached in {@link _selectColumnListSql}.
* Subclasses may or may not do the same. * Subclasses may or may not do the same.
* *
* @return string The SQL fragment. * @return string The SQL fragment.
* @todo Rename: _getSelectColumnsSQL() * @todo Rename: _getSelectColumnsSQL()
*/ */
@ -961,75 +961,75 @@ class BasicEntityPersister
// Add regular columns to select list // Add regular columns to select list
foreach ($this->_class->fieldNames as $field) { foreach ($this->_class->fieldNames as $field) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $this->_class); $columnList .= $this->_getSelectColumnSQL($field, $this->_class);
} }
$this->_selectJoinSql = ''; $this->_selectJoinSql = '';
$eagerAliasCounter = 0; $eagerAliasCounter = 0;
foreach ($this->_class->associationMappings as $assocField => $assoc) { foreach ($this->_class->associationMappings as $assocField => $assoc) {
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class); $assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
if ($assocColumnSQL) { if ($assocColumnSQL) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $assocColumnSQL; $columnList .= $assocColumnSQL;
} }
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) { if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) { if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
continue; // now this is why you shouldn't use inheritance continue; // now this is why you shouldn't use inheritance
} }
$assocAlias = 'e' . ($eagerAliasCounter++); $assocAlias = 'e' . ($eagerAliasCounter++);
$this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField); $this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
foreach ($eagerEntity->fieldNames AS $field) { foreach ($eagerEntity->fieldNames AS $field) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias); $columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
} }
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) { foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias); $assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
if ($assoc2ColumnSQL) { if ($assoc2ColumnSQL) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnList .= $assoc2ColumnSQL; $columnList .= $assoc2ColumnSQL;
} }
} }
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable. $this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
$first = true; $first = true;
if ($assoc['isOwningSide']) { if ($assoc['isOwningSide']) {
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
if ( ! $first) { if ( ! $first) {
$this->_selectJoinSql .= ' AND '; $this->_selectJoinSql .= ' AND ';
} }
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = '
. $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol . ' '; . $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol . ' ';
$first = false; $first = false;
} }
} else { } else {
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']); $owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
$this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON '; . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON ';
foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
if ( ! $first) { if ( ! $first) {
$this->_selectJoinSql .= ' AND '; $this->_selectJoinSql .= ' AND ';
} }
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' $this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = '
. $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' '; . $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
$first = false; $first = false;
} }
@ -1041,33 +1041,33 @@ class BasicEntityPersister
return $this->_selectColumnListSql; return $this->_selectColumnListSql;
} }
/** /**
* Gets the SQL join fragment used when selecting entities from an association. * Gets the SQL join fragment used when selecting entities from an association.
* *
* @param string $field * @param string $field
* @param array $assoc * @param array $assoc
* @param ClassMetadata $class * @param ClassMetadata $class
* @param string $alias * @param string $alias
* *
* @return string * @return string
*/ */
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r') protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
{ {
$columnList = ''; $columnList = '';
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList) $columnList .= ', '; if ($columnList) $columnList .= ', ';
$columnAlias = $srcColumn . $this->_sqlAliasCounter++; $columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias); $resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) $columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $srcColumn . ' AS ' . $resultColumnName; . '.' . $srcColumn . ' AS ' . $resultColumnName;
$this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true); $this->_rsm->addMetaResult($alias, $resultColumnName, $srcColumn, isset($assoc['id']) && $assoc['id'] === true);
} }
} }
return $columnList; return $columnList;
} }
@ -1087,10 +1087,10 @@ class BasicEntityPersister
$owningAssoc = $this->_em->getClassMetadata($manyToMany['targetEntity'])->associationMappings[$manyToMany['mappedBy']]; $owningAssoc = $this->_em->getClassMetadata($manyToMany['targetEntity'])->associationMappings[$manyToMany['mappedBy']];
$joinClauses = $owningAssoc['relationToSourceKeyColumns']; $joinClauses = $owningAssoc['relationToSourceKeyColumns'];
} }
$joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform); $joinTableName = $this->_class->getQuotedJoinTableName($owningAssoc, $this->_platform);
$joinSql = ''; $joinSql = '';
foreach ($joinClauses as $joinTableColumn => $sourceColumn) { foreach ($joinClauses as $joinTableColumn => $sourceColumn) {
if ($joinSql != '') $joinSql .= ' AND '; if ($joinSql != '') $joinSql .= ' AND ';
@ -1109,7 +1109,7 @@ class BasicEntityPersister
/** /**
* Gets the INSERT SQL used by the persister to persist a new entity. * Gets the INSERT SQL used by the persister to persist a new entity.
* *
* @return string * @return string
*/ */
protected function _getInsertSQL() protected function _getInsertSQL()
@ -1117,7 +1117,7 @@ class BasicEntityPersister
if ($this->_insertSql === null) { if ($this->_insertSql === null) {
$insertSql = ''; $insertSql = '';
$columns = $this->_getInsertColumnList(); $columns = $this->_getInsertColumnList();
if (empty($columns)) { if (empty($columns)) {
$insertSql = $this->_platform->getEmptyIdentityInsertSQL( $insertSql = $this->_platform->getEmptyIdentityInsertSQL(
$this->_class->getQuotedTableName($this->_platform), $this->_class->getQuotedTableName($this->_platform),
@ -1130,10 +1130,10 @@ class BasicEntityPersister
$insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform) $insertSql = 'INSERT INTO ' . $this->_class->getQuotedTableName($this->_platform)
. ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')'; . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ')';
} }
$this->_insertSql = $insertSql; $this->_insertSql = $insertSql;
} }
return $this->_insertSql; return $this->_insertSql;
} }
@ -1148,15 +1148,15 @@ class BasicEntityPersister
protected function _getInsertColumnList() protected function _getInsertColumnList()
{ {
$columns = array(); $columns = array();
foreach ($this->_class->reflFields as $name => $field) { foreach ($this->_class->reflFields as $name => $field) {
if ($this->_class->isVersioned && $this->_class->versionField == $name) { if ($this->_class->isVersioned && $this->_class->versionField == $name) {
continue; continue;
} }
if (isset($this->_class->associationMappings[$name])) { if (isset($this->_class->associationMappings[$name])) {
$assoc = $this->_class->associationMappings[$name]; $assoc = $this->_class->associationMappings[$name];
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) { foreach ($assoc['targetToSourceKeyColumns'] as $sourceCol) {
$columns[] = $sourceCol; $columns[] = $sourceCol;
@ -1181,10 +1181,10 @@ class BasicEntityPersister
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r') protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
{ {
$columnName = $class->columnNames[$field]; $columnName = $class->columnNames[$field];
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) $sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias)
. '.' . $class->getQuotedColumnName($field, $this->_platform); . '.' . $class->getQuotedColumnName($field, $this->_platform);
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++); $columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
$this->_rsm->addFieldResult($alias, $columnAlias, $field); $this->_rsm->addFieldResult($alias, $columnAlias, $field);
return $sql . ' AS ' . $columnAlias; return $sql . ' AS ' . $columnAlias;
@ -1192,7 +1192,7 @@ class BasicEntityPersister
/** /**
* Gets the SQL table alias for the given class name. * Gets the SQL table alias for the given class name.
* *
* @param string $className * @param string $className
* @return string The SQL table alias. * @return string The SQL table alias.
* @todo Reconsider. Binding table aliases to class names is not such a good idea. * @todo Reconsider. Binding table aliases to class names is not such a good idea.
@ -1202,15 +1202,15 @@ class BasicEntityPersister
if ($assocName) { if ($assocName) {
$className .= '#' . $assocName; $className .= '#' . $assocName;
} }
if (isset($this->_sqlTableAliases[$className])) { if (isset($this->_sqlTableAliases[$className])) {
return $this->_sqlTableAliases[$className]; return $this->_sqlTableAliases[$className];
} }
$tableAlias = 't' . $this->_sqlAliasCounter++; $tableAlias = 't' . $this->_sqlAliasCounter++;
$this->_sqlTableAliases[$className] = $tableAlias; $this->_sqlTableAliases[$className] = $tableAlias;
return $tableAlias; return $tableAlias;
} }
@ -1234,9 +1234,9 @@ class BasicEntityPersister
$sql = 'SELECT 1 ' $sql = 'SELECT 1 '
. $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode) . $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode)
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql; . ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql;
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types); $stmt = $this->_conn->executeQuery($sql, $params, $types);
} }
@ -1265,25 +1265,25 @@ class BasicEntityPersister
protected function _getSelectConditionSQL(array $criteria, $assoc = null) protected function _getSelectConditionSQL(array $criteria, $assoc = null)
{ {
$conditionSql = ''; $conditionSql = '';
foreach ($criteria as $field => $value) { foreach ($criteria as $field => $value) {
$conditionSql .= $conditionSql ? ' AND ' : ''; $conditionSql .= $conditionSql ? ' AND ' : '';
if (isset($this->_class->columnNames[$field])) { if (isset($this->_class->columnNames[$field])) {
$className = (isset($this->_class->fieldMappings[$field]['inherited'])) $className = (isset($this->_class->fieldMappings[$field]['inherited']))
? $this->_class->fieldMappings[$field]['inherited'] ? $this->_class->fieldMappings[$field]['inherited']
: $this->_class->name; : $this->_class->name;
$conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform); $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->getQuotedColumnName($field, $this->_platform);
} else if (isset($this->_class->associationMappings[$field])) { } else if (isset($this->_class->associationMappings[$field])) {
if ( ! $this->_class->associationMappings[$field]['isOwningSide']) { if ( ! $this->_class->associationMappings[$field]['isOwningSide']) {
throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field); throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field);
} }
$className = (isset($this->_class->associationMappings[$field]['inherited'])) $className = (isset($this->_class->associationMappings[$field]['inherited']))
? $this->_class->associationMappings[$field]['inherited'] ? $this->_class->associationMappings[$field]['inherited']
: $this->_class->name; : $this->_class->name;
$conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name']; $conditionSql .= $this->_getSQLTableAlias($className) . '.' . $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
} else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { } else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) {
// very careless developers could potentially open up this normally hidden api for userland attacks, // very careless developers could potentially open up this normally hidden api for userland attacks,
@ -1294,7 +1294,7 @@ class BasicEntityPersister
} else { } else {
throw ORMException::unrecognizedField($field); throw ORMException::unrecognizedField($field);
} }
$conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ?'); $conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ?');
} }
return $conditionSql; return $conditionSql;
@ -1312,7 +1312,7 @@ class BasicEntityPersister
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null) public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
{ {
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
return $this->loadArrayFromStatement($assoc, $stmt); return $this->loadArrayFromStatement($assoc, $stmt);
} }
@ -1328,7 +1328,7 @@ class BasicEntityPersister
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll) public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
{ {
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity); $stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
return $this->loadCollectionFromStatement($assoc, $stmt, $coll); return $this->loadCollectionFromStatement($assoc, $stmt, $coll);
} }
@ -1353,12 +1353,12 @@ class BasicEntityPersister
if ($sourceClass->containsForeignIdentifier) { if ($sourceClass->containsForeignIdentifier) {
$field = $sourceClass->getFieldForColumn($sourceKeyColumn); $field = $sourceClass->getFieldForColumn($sourceKeyColumn);
$value = $sourceClass->reflFields[$field]->getValue($sourceEntity); $value = $sourceClass->reflFields[$field]->getValue($sourceEntity);
if (isset($sourceClass->associationMappings[$field])) { if (isset($sourceClass->associationMappings[$field])) {
$value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
$value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]];
} }
$criteria[$tableAlias . "." . $targetKeyColumn] = $value; $criteria[$tableAlias . "." . $targetKeyColumn] = $value;
} else { } else {
$criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); $criteria[$tableAlias . "." . $targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
@ -1389,13 +1389,13 @@ class BasicEntityPersister
$types[] = $this->getType($field, $value); $types[] = $this->getType($field, $value);
$params[] = $this->getValue($value); $params[] = $this->getValue($value);
} }
return array($params, $types); return array($params, $types);
} }
/** /**
* Infer field type to be used by parameter type casting. * Infer field type to be used by parameter type casting.
* *
* @param string $field * @param string $field
* @param mixed $value * @param mixed $value
* @return integer * @return integer
@ -1409,11 +1409,11 @@ class BasicEntityPersister
case (isset($this->_class->associationMappings[$field])): case (isset($this->_class->associationMappings[$field])):
$assoc = $this->_class->associationMappings[$field]; $assoc = $this->_class->associationMappings[$field];
if (count($assoc['sourceToTargetKeyColumns']) > 1) { if (count($assoc['sourceToTargetKeyColumns']) > 1) {
throw Query\QueryException::associationPathCompositeKeyNotSupported(); throw Query\QueryException::associationPathCompositeKeyNotSupported();
} }
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
$targetColumn = $assoc['joinColumns'][0]['referencedColumnName']; $targetColumn = $assoc['joinColumns'][0]['referencedColumnName'];
$type = null; $type = null;
@ -1431,36 +1431,36 @@ class BasicEntityPersister
if (is_array($value)) { if (is_array($value)) {
$type += Connection::ARRAY_PARAM_OFFSET; $type += Connection::ARRAY_PARAM_OFFSET;
} }
return $type; return $type;
} }
/** /**
* Retrieve parameter value * Retrieve parameter value
* *
* @param mixed $value * @param mixed $value
* @return mixed * @return mixed
*/ */
private function getValue($value) private function getValue($value)
{ {
if (is_array($value)) { if (is_array($value)) {
$newValue = array(); $newValue = array();
foreach ($value as $itemValue) { foreach ($value as $itemValue) {
$newValue[] = $this->getIndividualValue($itemValue); $newValue[] = $this->getIndividualValue($itemValue);
} }
return $newValue; return $newValue;
} }
return $this->getIndividualValue($value); return $this->getIndividualValue($value);
} }
/** /**
* Retrieve an invidiual parameter value * Retrieve an invidiual parameter value
* *
* @param mixed $value * @param mixed $value
* @return mixed * @return mixed
*/ */
private function getIndividualValue($value) private function getIndividualValue($value)
{ {
@ -1471,11 +1471,11 @@ class BasicEntityPersister
$class = $this->_em->getClassMetadata(get_class($value)); $class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value); $idValues = $class->getIdentifierValues($value);
} }
$value = $idValues[key($idValues)]; $value = $idValues[key($idValues)];
} }
return $value; return $value;
} }
/** /**
@ -1487,17 +1487,17 @@ class BasicEntityPersister
public function exists($entity, array $extraConditions = array()) public function exists($entity, array $extraConditions = array())
{ {
$criteria = $this->_class->getIdentifierValues($entity); $criteria = $this->_class->getIdentifierValues($entity);
if ($extraConditions) { if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions); $criteria = array_merge($criteria, $extraConditions);
} }
$sql = 'SELECT 1' $sql = 'SELECT 1 '
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name) . $this->getLockTablesSql()
. ' WHERE ' . $this->_getSelectConditionSQL($criteria); . ' WHERE ' . $this->_getSelectConditionSQL($criteria);
list($params, $types) = $this->expandParameters($criteria); list($params, $types) = $this->expandParameters($criteria);
return (bool) $this->_conn->fetchColumn($sql, $params); return (bool) $this->_conn->fetchColumn($sql, $params);
} }
} }

View File

@ -46,7 +46,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/** /**
* Map of table to quoted table names. * Map of table to quoted table names.
* *
* @var array * @var array
*/ */
private $_quotedTableMap = array(); private $_quotedTableMap = array();
@ -59,7 +59,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$class = ($this->_class->name !== $this->_class->rootEntityName) $class = ($this->_class->name !== $this->_class->rootEntityName)
? $this->_em->getClassMetadata($this->_class->rootEntityName) ? $this->_em->getClassMetadata($this->_class->rootEntityName)
: $this->_class; : $this->_class;
return $class->getTableName(); return $class->getTableName();
} }
@ -73,10 +73,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
{ {
if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) { if (isset($this->_class->fieldMappings[$this->_class->versionField]['inherited'])) {
$definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited']; $definingClassName = $this->_class->fieldMappings[$this->_class->versionField]['inherited'];
return $this->_em->getClassMetadata($definingClassName); return $this->_em->getClassMetadata($definingClassName);
} }
return $this->_class; return $this->_class;
} }
@ -92,7 +92,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
if (isset($this->_owningTableMap[$fieldName])) { if (isset($this->_owningTableMap[$fieldName])) {
return $this->_owningTableMap[$fieldName]; return $this->_owningTableMap[$fieldName];
} }
if (isset($this->_class->associationMappings[$fieldName]['inherited'])) { if (isset($this->_class->associationMappings[$fieldName]['inherited'])) {
$cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']); $cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']);
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) { } else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
@ -130,15 +130,15 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Prepare statements for sub tables. // Prepare statements for sub tables.
$subTableStmts = array(); $subTableStmts = array();
if ($rootClass !== $this->_class) { if ($rootClass !== $this->_class) {
$subTableStmts[$this->_class->getTableName()] = $this->_conn->prepare($this->_getInsertSQL()); $subTableStmts[$this->_class->getTableName()] = $this->_conn->prepare($this->_getInsertSQL());
} }
foreach ($this->_class->parentClasses as $parentClassName) { foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName); $parentClass = $this->_em->getClassMetadata($parentClassName);
$parentTableName = $parentClass->getTableName(); $parentTableName = $parentClass->getTableName();
if ($parentClass !== $rootClass) { if ($parentClass !== $rootClass) {
$parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName); $parentPersister = $this->_em->getUnitOfWork()->getEntityPersister($parentClassName);
$subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL()); $subTableStmts[$parentTableName] = $this->_conn->prepare($parentPersister->_getInsertSQL());
@ -153,11 +153,11 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Execute insert on root table // Execute insert on root table
$paramIndex = 1; $paramIndex = 1;
foreach ($insertData[$rootTableName] as $columnName => $value) { foreach ($insertData[$rootTableName] as $columnName => $value) {
$rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); $rootTableStmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
} }
$rootTableStmt->execute(); $rootTableStmt->execute();
if ($isPostInsertId) { if ($isPostInsertId) {
@ -172,23 +172,23 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($subTableStmts as $tableName => $stmt) { foreach ($subTableStmts as $tableName => $stmt) {
$data = isset($insertData[$tableName]) ? $insertData[$tableName] : array(); $data = isset($insertData[$tableName]) ? $insertData[$tableName] : array();
$paramIndex = 1; $paramIndex = 1;
foreach ((array) $id as $idName => $idVal) { foreach ((array) $id as $idName => $idVal) {
$type = isset($this->_columnTypes[$idName]) ? $this->_columnTypes[$idName] : Type::STRING; $type = isset($this->_columnTypes[$idName]) ? $this->_columnTypes[$idName] : Type::STRING;
$stmt->bindValue($paramIndex++, $idVal, $type); $stmt->bindValue($paramIndex++, $idVal, $type);
} }
foreach ($data as $columnName => $value) { foreach ($data as $columnName => $value) {
$stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]); $stmt->bindValue($paramIndex++, $value, $this->_columnTypes[$columnName]);
} }
$stmt->execute(); $stmt->execute();
} }
} }
$rootTableStmt->closeCursor(); $rootTableStmt->closeCursor();
foreach ($subTableStmts as $stmt) { foreach ($subTableStmts as $stmt) {
$stmt->closeCursor(); $stmt->closeCursor();
} }
@ -220,7 +220,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName $entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName
); );
} }
// Make sure the table with the version column is updated even if no columns on that // Make sure the table with the version column is updated even if no columns on that
// table were affected. // table were affected.
if ($isVersioned && ! isset($updateData[$versionedTable])) { if ($isVersioned && ! isset($updateData[$versionedTable])) {
@ -251,7 +251,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
} else { } else {
// Delete from all tables individually, starting from this class' table up to the root table. // Delete from all tables individually, starting from this class' table up to the root table.
$this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id); $this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id);
foreach ($this->_class->parentClasses as $parentClass) { foreach ($this->_class->parentClasses as $parentClass) {
$this->_conn->delete( $this->_conn->delete(
$this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id $this->_em->getClassMetadata($parentClass)->getQuotedTableName($this->_platform), $id
@ -270,16 +270,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Create the column list fragment only once // Create the column list fragment only once
if ($this->_selectColumnListSql === null) { if ($this->_selectColumnListSql === null) {
$this->_rsm = new ResultSetMapping(); $this->_rsm = new ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r'); $this->_rsm->addEntityResult($this->_class->name, 'r');
// Add regular columns // Add regular columns
$columnList = ''; $columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) { foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->_getSelectColumnSQL( $columnList .= $this->_getSelectColumnSQL(
$fieldName, $fieldName,
isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class isset($mapping['inherited']) ? $this->_em->getClassMetadata($mapping['inherited']) : $this->_class
@ -290,12 +290,12 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
foreach ($this->_class->associationMappings as $assoc2) { foreach ($this->_class->associationMappings as $assoc2) {
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) { if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE) {
$tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias; $tableAlias = isset($assoc2['inherited']) ? $this->_getSQLTableAlias($assoc2['inherited']) : $baseTableAlias;
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL( $columnList .= $this->getSelectJoinColumnSQL(
$tableAlias, $tableAlias,
$srcColumn, $srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name
); );
@ -309,23 +309,23 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$columnList .= ', ' . $tableAlias . '.' . $discrColumn; $columnList .= ', ' . $tableAlias . '.' . $discrColumn;
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn); $resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_rsm->setDiscriminatorColumn('r', $resultColumnName); $this->_rsm->setDiscriminatorColumn('r', $resultColumnName);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn); $this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
} }
// INNER JOIN parent tables // INNER JOIN parent tables
$joinSql = ''; $joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) { foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName); $parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName); $tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($idColumns as $idColumn) { foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND '; if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
} }
@ -339,7 +339,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add subclass columns // Add subclass columns
foreach ($subClass->fieldMappings as $fieldName => $mapping) { foreach ($subClass->fieldMappings as $fieldName => $mapping) {
if (isset($mapping['inherited'])) continue; if (isset($mapping['inherited'])) continue;
$columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass); $columnList .= ', ' . $this->_getSelectColumnSQL($fieldName, $subClass);
} }
@ -348,9 +348,9 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) { if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE && ! isset($assoc2['inherited'])) {
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) { foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
if ($columnList != '') $columnList .= ', '; if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL( $columnList .= $this->getSelectJoinColumnSQL(
$tableAlias, $tableAlias,
$srcColumn, $srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name
); );
@ -362,10 +362,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Add LEFT JOIN // Add LEFT JOIN
$joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $joinSql .= ' LEFT JOIN ' . $subClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($idColumns as $idColumn) { foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND '; if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
} }
@ -382,7 +382,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
} }
$lockSql = ''; $lockSql = '';
if ($lockMode == LockMode::PESSIMISTIC_READ) { if ($lockMode == LockMode::PESSIMISTIC_READ) {
$lockSql = ' ' . $this->_platform->getReadLockSql(); $lockSql = ' ' . $this->_platform->getReadLockSql();
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) { } else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
@ -408,29 +408,29 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// INNER JOIN parent tables // INNER JOIN parent tables
$joinSql = ''; $joinSql = '';
foreach ($this->_class->parentClasses as $parentClassName) { foreach ($this->_class->parentClasses as $parentClassName) {
$parentClass = $this->_em->getClassMetadata($parentClassName); $parentClass = $this->_em->getClassMetadata($parentClassName);
$tableAlias = $this->_getSQLTableAlias($parentClassName); $tableAlias = $this->_getSQLTableAlias($parentClassName);
$joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $joinSql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON ';
$first = true; $first = true;
foreach ($idColumns as $idColumn) { foreach ($idColumns as $idColumn) {
if ($first) $first = false; else $joinSql .= ' AND '; if ($first) $first = false; else $joinSql .= ' AND ';
$joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn; $joinSql .= $baseTableAlias . '.' . $idColumn . ' = ' . $tableAlias . '.' . $idColumn;
} }
} }
return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias . $joinSql; return 'FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias . $joinSql;
} }
/* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */ /* Ensure this method is never called. This persister overrides _getSelectEntitiesSQL directly. */
protected function _getSelectColumnListSQL() protected function _getSelectColumnListSQL()
{ {
throw new \BadMethodCallException("Illegal invocation of ".__METHOD__."."); throw new \BadMethodCallException("Illegal invocation of ".__METHOD__.".");
} }
/** {@inheritdoc} */ /** {@inheritdoc} */
protected function _getInsertColumnList() protected function _getInsertColumnList()
{ {

View File

@ -232,6 +232,9 @@ final class Query extends AbstractQuery
protected function _doExecute() protected function _doExecute()
{ {
$executor = $this->_parse()->getSqlExecutor(); $executor = $this->_parse()->getSqlExecutor();
if ($this->_queryCacheProfile) {
$executor->setQueryCacheProfile($this->_queryCacheProfile);
}
// Prepare parameters // Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings(); $paramMappings = $this->_parserResult->getParameterMappings();

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -22,6 +20,7 @@
namespace Doctrine\ORM\Query\Exec; namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
/** /**
* Base class for SQL statement executors. * Base class for SQL statement executors.
@ -34,8 +33,16 @@ use Doctrine\DBAL\Connection;
*/ */
abstract class AbstractSqlExecutor abstract class AbstractSqlExecutor
{ {
/**
* @var array
*/
protected $_sqlStatements; protected $_sqlStatements;
/**
* @var QueryCacheProfile
*/
protected $queryCacheProfile;
/** /**
* Gets the SQL statements that are executed by the executor. * Gets the SQL statements that are executed by the executor.
* *
@ -46,12 +53,18 @@ abstract class AbstractSqlExecutor
return $this->_sqlStatements; return $this->_sqlStatements;
} }
public function setQueryCacheProfile(QueryCacheProfile $qcp)
{
$this->queryCacheProfile = $qcp;
}
/** /**
* Executes all sql statements. * Executes all sql statements.
* *
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries. * @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters. * @param array $params The parameters.
* @param array $types The parameter types.
* @return Doctrine\DBAL\Driver\Statement * @return Doctrine\DBAL\Driver\Statement
*/ */
abstract public function execute(Connection $conn, array $params, array $types); abstract public function execute(Connection $conn, array $params, array $types);
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -32,7 +30,6 @@ use Doctrine\DBAL\Connection,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org * @link http://www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
*/ */
class MultiTableDeleteExecutor extends AbstractSqlExecutor class MultiTableDeleteExecutor extends AbstractSqlExecutor
{ {
@ -102,11 +99,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
} }
/** /**
* Executes all SQL statements. * {@inheritDoc}
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/ */
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {

View File

@ -141,11 +141,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
} }
/** /**
* Executes all SQL statements. * {@inheritDoc}
*
* @param Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/ */
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
*/ */
@ -41,8 +38,11 @@ class SingleSelectExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST); $this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
} }
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {
return $conn->executeQuery($this->_sqlStatements, $params, $types); return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile);
} }
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
@ -45,7 +42,10 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST); $this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST);
} }
} }
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {
return $conn->executeUpdate($this->_sqlStatements, $params, $types); return $conn->executeUpdate($this->_sqlStatements, $params, $types);

View File

@ -57,7 +57,7 @@ abstract class Base
public function add($arg) public function add($arg)
{ {
if ( $arg !== null ) { if ( $arg !== null || ($arg instanceof self && $arg->count() > 0) ) {
// If we decide to keep Expr\Base instances, we can use this check // If we decide to keep Expr\Base instances, we can use this check
if ( ! is_string($arg)) { if ( ! is_string($arg)) {
$class = get_class($arg); $class = get_class($arg);

View File

@ -90,10 +90,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCache() public function testUseResultCache()
{ {
$cache = new \Doctrine\Common\Cache\ArrayCache(); $cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$query->useResultCache(true); $query->useResultCache(true);
$query->setResultCacheDriver($cache);
$query->setResultCacheId('testing_result_cache_id'); $query->setResultCacheId('testing_result_cache_id');
$users = $query->getResult(); $users = $query->getResult();
@ -108,11 +108,11 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCacheParams() public function testUseResultCacheParams()
{ {
$cache = new \Doctrine\Common\Cache\ArrayCache(); $cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$sqlCount = count($this->_sqlLoggerStack->queries); $sqlCount = count($this->_sqlLoggerStack->queries);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1');
$query->setParameter(1, 1); $query->setParameter(1, 1);
$query->setResultCacheDriver($cache);
$query->useResultCache(true); $query->useResultCache(true);
$query->getResult(); $query->getResult();
@ -149,10 +149,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
} }
/** /**
* @param <type> $query * @param string $query
* @depends testNativeQueryResultCaching * @depends testNativeQueryResultCaching
*/ */
public function testResultCacheDependsOnQueryHints($query) public function testResultCacheNotDependsOnQueryHints($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache); $cacheCount = $this->getCacheSize($cache);
@ -160,7 +160,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setHint('foo', 'bar'); $query->setHint('foo', 'bar');
$query->getResult(); $query->getResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache)); $this->assertEquals($cacheCount, $this->getCacheSize($cache));
} }
/** /**
@ -182,7 +182,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
* @param <type> $query * @param <type> $query
* @depends testNativeQueryResultCaching * @depends testNativeQueryResultCaching
*/ */
public function testResultCacheDependsOnHydrationMode($query) public function testResultCacheNotDependsOnHydrationMode($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache); $cacheCount = $this->getCacheSize($cache);
@ -190,7 +190,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode()); $this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
$query->getArrayResult(); $query->getArrayResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache)); $this->assertEquals($cacheCount, $this->getCacheSize($cache));
} }
/** /**

View File

@ -0,0 +1,69 @@
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\UnitOfWork;
require_once __DIR__ . '/../../../TestInit.php';
/**
* @group DDC-1454
*/
class DDC1454Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454File'),
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1454Picture'),
));
} catch (\Exception $ignored) {
}
}
public function testFailingCase()
{
$pic = new DDC1454Picture();
$this->_em->getUnitOfWork()->getEntityState($pic);
}
}
/**
* @Entity
*/
class DDC1454Picture extends DDC1454File
{
}
/**
* @Entity
* @InheritanceType("JOINED")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"picture" = "DDC1454Picture"})
*/
class DDC1454File
{
/**
* @Column(name="file_id", type="integer")
* @Id
*/
public $fileId;
public function __construct() {
$this->fileId = rand();
}
/**
* Get fileId
*/
public function getFileId() {
return $this->fileId;
}
}