Added multi-get support for second level cached collections
This commit is contained in:
parent
8f097ab304
commit
74e93e4cad
@ -21,6 +21,7 @@
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\Common\Cache\MultiGetCache;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
@ -29,6 +30,8 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Cache\Region\DefaultRegion;
|
||||
use Doctrine\ORM\Cache\Region\FileLockRegion;
|
||||
use Doctrine\ORM\Cache\Region\UpdateTimestampCache;
|
||||
use Doctrine\ORM\Cache\Region\DefaultMultiGetRegion;
|
||||
use Doctrine\ORM\Cache\Persister\CachedPersister;
|
||||
use Doctrine\ORM\Persisters\Entity\EntityPersister;
|
||||
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister;
|
||||
@ -178,6 +181,19 @@ class DefaultCacheFactory implements CacheFactory
|
||||
*/
|
||||
public function buildCollectionHydrator(EntityManagerInterface $em, array $mapping)
|
||||
{
|
||||
if ($mapping['cache']) {
|
||||
$targetPersister = $em->getUnitOfWork()->getEntityPersister($mapping['targetEntity']);
|
||||
|
||||
if ($targetPersister instanceof CachedPersister) {
|
||||
|
||||
$targetRegion = $targetPersister->getCacheRegion();
|
||||
|
||||
if ($targetRegion instanceof MultiGetRegion) {
|
||||
return new MultiGetCollectionHydrator($em);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new DefaultCollectionHydrator($em);
|
||||
}
|
||||
|
||||
@ -202,7 +218,11 @@ class DefaultCacheFactory implements CacheFactory
|
||||
|
||||
$cacheAdapter->setNamespace($cache['region']);
|
||||
|
||||
$region = new DefaultRegion($cache['region'], $cacheAdapter, $this->regionsConfig->getLifetime($cache['region']));
|
||||
if ($cacheAdapter instanceof MultiGetCache) {
|
||||
$region = new DefaultMultiGetRegion($cache['region'], $cacheAdapter, $this->regionsConfig->getLifetime($cache['region']));
|
||||
} else {
|
||||
$region = new DefaultRegion($cache['region'], $cacheAdapter, $this->regionsConfig->getLifetime($cache['region']));
|
||||
}
|
||||
|
||||
if ($cache['usage'] === ClassMetadata::CACHE_USAGE_READ_WRITE) {
|
||||
|
||||
|
@ -81,15 +81,34 @@ class DefaultCollectionHydrator implements CollectionHydrator
|
||||
$targetRegion = $targetPersister->getCacheRegion();
|
||||
$list = array();
|
||||
|
||||
$keys = array();
|
||||
foreach ($entry->identifiers as $index => $identifier) {
|
||||
$keys[$index] = new EntityCacheKey($assoc['targetEntity'], $identifier);
|
||||
}
|
||||
|
||||
$entityEntry = $targetRegion->get(new EntityCacheKey($assoc['targetEntity'], $identifier));
|
||||
|
||||
if ($entityEntry === null) {
|
||||
if ($targetRegion instanceof MultiGetRegion) {
|
||||
$entityEntries = $targetRegion->getMulti($keys);
|
||||
|
||||
if ($entityEntries === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
|
||||
foreach ($entityEntries as $index => $entityEntry) {
|
||||
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->data, self::$hints);
|
||||
}
|
||||
|
||||
} else {
|
||||
foreach ($entry->identifiers as $index => $identifier) {
|
||||
|
||||
$entityEntry = $targetRegion->get(new EntityCacheKey($assoc['targetEntity'], $identifier));
|
||||
|
||||
if ($entityEntry === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
|
||||
}
|
||||
}
|
||||
|
||||
array_walk($list, function($entity, $index) use ($collection) {
|
||||
|
106
lib/Doctrine/ORM/Cache/MultiGetCollectionHydrator.php
Normal file
106
lib/Doctrine/ORM/Cache/MultiGetCollectionHydrator.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* Collection hydrator that expects a target region to be instance of {Doctrine\ORM\Cache\MultiGetRegion},
|
||||
* to enable loading the entire collection with one cache request.
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Asmir Mustafic <goetas@gmail.com>
|
||||
*/
|
||||
class MultiGetCollectionHydrator implements CollectionHydrator
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\EntityManagerInterface
|
||||
*/
|
||||
private $em;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
private $uow;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $hints = array(Query::HINT_CACHE_ENABLED => true);
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\EntityManagerInterface $em The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->uow = $em->getUnitOfWork();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, $collection)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
foreach ($collection as $index => $entity) {
|
||||
$data[$index] = $this->uow->getEntityIdentifier($entity);
|
||||
}
|
||||
|
||||
return new CollectionCacheEntry($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadCacheEntry(ClassMetadata $metadata, CollectionCacheKey $key, CollectionCacheEntry $entry, PersistentCollection $collection)
|
||||
{
|
||||
$assoc = $metadata->associationMappings[$key->association];
|
||||
$targetPersister = $this->uow->getEntityPersister($assoc['targetEntity']);
|
||||
$targetRegion = $targetPersister->getCacheRegion();
|
||||
$list = array();
|
||||
|
||||
$keys = array();
|
||||
foreach ($entry->identifiers as $index => $identifier) {
|
||||
$keys[$index] = new EntityCacheKey($assoc['targetEntity'], $identifier);
|
||||
}
|
||||
|
||||
$entityEntries = $targetRegion->getMulti($keys);
|
||||
|
||||
if ($entityEntries === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($entityEntries as $index => $entityEntry) {
|
||||
$list[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->data, self::$hints);
|
||||
}
|
||||
|
||||
array_walk($list, function ($entity, $index) use ($collection) {
|
||||
$collection->hydrateSet($index, $entity);
|
||||
});
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
43
lib/Doctrine/ORM/Cache/MultiGetRegion.php
Normal file
43
lib/Doctrine/ORM/Cache/MultiGetRegion.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache;
|
||||
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
|
||||
/**
|
||||
* Defines a region that supports multi-get reading.
|
||||
*
|
||||
* With one method call we can get multipe items.
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Asmir Mustafic
|
||||
*/
|
||||
interface MultiGetRegion extends Region
|
||||
{
|
||||
/**
|
||||
* Get all items from the cache indentifed by $keys.
|
||||
* It returns NULL if some elements can not be found.
|
||||
*
|
||||
* @param CacheKey[] $key The keys of the items to be retrieved.
|
||||
* @return array The cached entries or NULL if one or more entries can not be found
|
||||
*/
|
||||
public function getMulti(array $keys);
|
||||
}
|
61
lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php
Normal file
61
lib/Doctrine/ORM/Cache/Region/DefaultMultiGetRegion.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Cache\Region;
|
||||
|
||||
use Doctrine\ORM\Cache\Lock;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\CacheKey;
|
||||
use Doctrine\ORM\Cache\CacheEntry;
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\ORM\Cache\MultiGetRegion;
|
||||
use Doctrine\Common\Cache\CacheMultiGet;
|
||||
|
||||
/**
|
||||
* A cache region that enables the retrieval of multiple elements with one call
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Asmir Mustafic <goetas@gmail.com>
|
||||
*/
|
||||
class DefaultMultiGetRegion extends DefaultRegion implements MultiGetRegion
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMulti(array $keys)
|
||||
{
|
||||
$keysToRetrieve = array();
|
||||
foreach ($keys as $index => $key) {
|
||||
$keysToRetrieve[$index] = $this->name . '_' . $key->hash;
|
||||
}
|
||||
|
||||
$items = $this->cache->fetchMultiple($keysToRetrieve);
|
||||
if (count($items) !== count($keysToRetrieve)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$returnableItems = array();
|
||||
foreach ($keysToRetrieve as $index => $key) {
|
||||
$returnableItems[$index] = $items[$key];
|
||||
}
|
||||
|
||||
return $returnableItems;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user