Skip to content

Commit

Permalink
Use own cache to store configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
franmomu committed Dec 28, 2021
1 parent 3e372a5 commit 474ca92
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 102 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ a release.
- Loggable: Using only PHP 8 attributes.
- References: Avoid deprecations using LazyCollection with PHP 8.1

### Changed
- In order to use a custom cache for storing configuration of an extension, the user has to call `setCacheItemPool`
on the extension listener passing an instance of `Psr\Cache\CacheItemPoolInterface`.

## [3.4.0] - 2021-12-05
### Added
- PHP 8 Attributes support for Doctrine MongoDB to document & traits.
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
"doctrine/collections": "^1.0",
"doctrine/common": "^2.13 || ^3.0",
"doctrine/event-manager": "^1.0",
"doctrine/persistence": "^1.3.3 || ^2.0"
"doctrine/persistence": "^1.3.3 || ^2.0",
"psr/cache": "^1 || ^2 || ^3",
"symfony/cache": "^4.4 || ^5.3 || ^6.0"
},
"require-dev": {
"doctrine/cache": "^1.11 || ^2.0",
Expand All @@ -57,7 +59,6 @@
"phpstan/phpstan-doctrine": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.5",
"symfony/cache": "^4.4 || ^5.3 || ^6.0",
"symfony/console": "^4.4 || ^5.3 || ^6.0",
"symfony/yaml": "^4.4 || ^5.3 || ^6.0"
},
Expand Down
7 changes: 7 additions & 0 deletions example/em.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,32 @@
// Sluggable extension
$sluggableListener = new Gedmo\Sluggable\SluggableListener();
$sluggableListener->setAnnotationReader($annotationReader);
$sluggableListener->setCacheItemPool($cache);
$eventManager->addEventSubscriber($sluggableListener);

// Tree extension
$treeListener = new Gedmo\Tree\TreeListener();
$treeListener->setAnnotationReader($annotationReader);
$treeListener->setCacheItemPool($cache);
$eventManager->addEventSubscriber($treeListener);

// Loggable extension, not used in example
//$loggableListener = new Gedmo\Loggable\LoggableListener;
//$loggableListener->setAnnotationReader($annotationReader);
//$loggableListener->setCacheItemPool($cache);
//$loggableListener->setUsername('admin');
//$eventManager->addEventSubscriber($loggableListener);

// Timestampable extension
$timestampableListener = new Gedmo\Timestampable\TimestampableListener();
$timestampableListener->setAnnotationReader($annotationReader);
$timestampableListener->setCacheItemPool($cache);
$eventManager->addEventSubscriber($timestampableListener);

// Blameable extension
$blameableListener = new \Gedmo\Blameable\BlameableListener();
$blameableListener->setAnnotationReader($annotationReader);
$blameableListener->setCacheItemPool($cache);
$blameableListener->setUserValue('MyUsername'); // determine from your environment
$eventManager->addEventSubscriber($blameableListener);

Expand All @@ -111,11 +116,13 @@
$translatableListener->setTranslatableLocale('en');
$translatableListener->setDefaultLocale('en');
$translatableListener->setAnnotationReader($annotationReader);
$translatableListener->setCacheItemPool($cache);;
$eventManager->addEventSubscriber($translatableListener);

// Sortable extension, not used in example
//$sortableListener = new Gedmo\Sortable\SortableListener;
//$sortableListener->setAnnotationReader($annotationReader);
//$sortableListener->setCacheItemPool($cache);
//$eventManager->addEventSubscriber($sortableListener);

// Now we will build our ORM configuration.
Expand Down
37 changes: 22 additions & 15 deletions src/Mapping/ExtensionMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
namespace Gedmo\Mapping;

use Doctrine\Bundle\DoctrineBundle\Mapping\MappingDriver as DoctrineBundleMappingDriver;
use Doctrine\Common\Cache\Cache;
use Doctrine\Persistence\Mapping\Driver\DefaultFileLocator;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
Expand All @@ -21,6 +20,7 @@
use Gedmo\Mapping\Driver\AttributeDriverInterface;
use Gedmo\Mapping\Driver\AttributeReader;
use Gedmo\Mapping\Driver\File as FileDriver;
use Psr\Cache\CacheItemPoolInterface;

/**
* The extension metadata factory is responsible for extension driver
Expand Down Expand Up @@ -59,18 +59,18 @@ class ExtensionMetadataFactory
protected $annotationReader;

/**
* Initializes extension driver
*
* @param string $extensionNamespace
* @param object $annotationReader
* @var CacheItemPoolInterface|null
*/
public function __construct(ObjectManager $objectManager, $extensionNamespace, $annotationReader)
private $cachePoolItem;

public function __construct(ObjectManager $objectManager, string $extensionNamespace, object $annotationReader, ?CacheItemPoolInterface $cachePoolItem = null)
{
$this->objectManager = $objectManager;
$this->annotationReader = $annotationReader;
$this->extensionNamespace = $extensionNamespace;
$omDriver = $objectManager->getConfiguration()->getMetadataDriverImpl();
$this->driver = $this->getDriver($omDriver);
$this->cachePoolItem = $cachePoolItem;
}

/**
Expand Down Expand Up @@ -110,14 +110,7 @@ public function getExtensionMetadata($meta)
$config['useObjectClass'] = $useObjectName;
}

$cacheDriver = $cmf->getCacheDriver();

if ($cacheDriver instanceof Cache) {
// Cache the result, even if it's empty, to prevent re-parsing non-existent annotations.
$cacheId = self::getCacheId($meta->getName(), $this->extensionNamespace);

$cacheDriver->save($cacheId, $config);
}
$this->storeConfiguration($meta->getName(), $config);

return $config;
}
Expand All @@ -132,7 +125,7 @@ public function getExtensionMetadata($meta)
*/
public static function getCacheId($className, $extensionNamespace)
{
return $className.'\\$'.strtoupper(str_replace('\\', '_', $extensionNamespace)).'_CLASSMETADATA';
return str_replace('\\', '_', $className).'_$'.strtoupper(str_replace('\\', '_', $extensionNamespace)).'_CLASSMETADATA';
}

/**
Expand Down Expand Up @@ -201,4 +194,18 @@ protected function getDriver($omDriver)

return $driver;
}

private function storeConfiguration(string $className, array $config): void
{
if (null === $this->cachePoolItem) {
return;
}

// Cache the result, even if it's empty, to prevent re-parsing non-existent annotations.
$cacheId = self::getCacheId($className, $this->extensionNamespace);

$item = $this->cachePoolItem->getItem($cacheId);

$this->cachePoolItem->save($item->set($config));
}
}
66 changes: 44 additions & 22 deletions src/Mapping/MappedEventSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Doctrine\Common\EventSubscriber;
use Doctrine\Persistence\ObjectManager;
use Gedmo\Mapping\Event\AdapterInterface;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

/**
Expand Down Expand Up @@ -78,6 +79,11 @@ abstract class MappedEventSubscriber implements EventSubscriber
*/
private static $defaultAnnotationReader;

/**
* @var CacheItemPoolInterface|null
*/
private $cacheItemPool;

/**
* Constructor
*/
Expand All @@ -97,32 +103,33 @@ public function __construct()
*/
public function getConfiguration(ObjectManager $objectManager, $class)
{
$config = [];
if (isset(self::$configurations[$this->name][$class])) {
$config = self::$configurations[$this->name][$class];
} else {
$factory = $objectManager->getMetadataFactory();
$cacheDriver = $factory->getCacheDriver();
if ($cacheDriver) {
$cacheId = ExtensionMetadataFactory::getCacheId($class, $this->getNamespace());
if (false !== ($cached = $cacheDriver->fetch($cacheId))) {
self::$configurations[$this->name][$class] = $cached;
$config = $cached;
} else {
// re-generate metadata on cache miss
$this->loadMetadataForObjectClass($objectManager, $factory->getMetadataFor($class));
if (isset(self::$configurations[$this->name][$class])) {
$config = self::$configurations[$this->name][$class];
}
}
return self::$configurations[$this->name][$class];
}

$objectClass = $config['useObjectClass'] ?? $class;
if ($objectClass !== $class) {
$this->getConfiguration($objectManager, $objectClass);
}
$config = [];

$cacheItemPool = $this->getCacheItemPool();

$cacheId = ExtensionMetadataFactory::getCacheId($class, $this->getNamespace());
$cacheItem = $cacheItemPool->getItem($cacheId);

if ($cacheItem->isHit()) {
$config = $cacheItem->get();
self::$configurations[$this->name][$class] = $config;
} else {
// re-generate metadata on cache miss
$this->loadMetadataForObjectClass($objectManager, $objectManager->getClassMetadata($class));
if (isset(self::$configurations[$this->name][$class])) {
$config = self::$configurations[$this->name][$class];
}
}

$objectClass = $config['useObjectClass'] ?? $class;
if ($objectClass !== $class) {
$this->getConfiguration($objectManager, $objectClass);
}

return $config;
}

Expand All @@ -142,7 +149,8 @@ public function getExtensionMetadataFactory(ObjectManager $objectManager)
$this->extensionMetadataFactory[$oid] = new ExtensionMetadataFactory(
$objectManager,
$this->getNamespace(),
$this->annotationReader
$this->annotationReader,
$this->getCacheItemPool()
);
}

Expand All @@ -165,6 +173,11 @@ public function setAnnotationReader($reader)
$this->annotationReader = $reader;
}

final public function setCacheItemPool(CacheItemPoolInterface $cacheItemPool): void
{
$this->cacheItemPool = $cacheItemPool;
}

/**
* Scans the objects for extended annotations
* event subscribers must subscribe to loadClassMetadata event
Expand Down Expand Up @@ -264,4 +277,13 @@ private function getDefaultAnnotationReader(): Reader

return self::$defaultAnnotationReader;
}

private function getCacheItemPool(): CacheItemPoolInterface
{
if (null === $this->cacheItemPool) {
$this->cacheItemPool = new ArrayAdapter();
}

return $this->cacheItemPool;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,33 @@

namespace Gedmo\Tests\Mapping;

use Doctrine\Common\EventManager;
use Doctrine\ORM\Mapping\Driver\YamlDriver;
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
use Gedmo\Loggable\Entity\LogEntry;
use Gedmo\Loggable\LoggableListener;
use Gedmo\Mapping\ExtensionMetadataFactory;
use Gedmo\Tests\Mapping\Fixture\Yaml\Category;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

/**
* These are mapping tests for tree extension
*
* @author Gediminas Morkevicius <[email protected]>
*/
final class LoggableMappingTest extends \PHPUnit\Framework\TestCase
final class LoggableORMMappingTest extends ORMMappingTestCase
{
public const YAML_CATEGORY = Category::class;

/**
* @var \Doctrine\ORM\EntityManager
*/
private $em;

protected function setUp(): void
{
$config = new \Doctrine\ORM\Configuration();
$config->setMetadataCache(new ArrayAdapter());
$config->setQueryCache(new ArrayAdapter());
$config->setProxyDir(TESTS_TEMP_DIR);
$config->setProxyNamespace('Gedmo\Mapping\Proxy');
parent::setUp();

$config = $this->getBasicConfiguration();
$chainDriverImpl = new MappingDriverChain();
$chainDriverImpl->addDriver(
new YamlDriver([__DIR__.'/Driver/Yaml']),
Expand All @@ -48,18 +50,18 @@ protected function setUp(): void
'memory' => true,
];

//$config->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());

$evm = new \Doctrine\Common\EventManager();
$evm->addEventSubscriber(new LoggableListener());
$evm = new EventManager();
$loggableListener = new LoggableListener();
$loggableListener->setCacheItemPool($this->cache);
$evm->addEventSubscriber($loggableListener);
$this->em = \Doctrine\ORM\EntityManager::create($conn, $config, $evm);
}

public function testLoggableMapping(): void
{
$meta = $this->em->getClassMetadata(self::YAML_CATEGORY);
$cacheId = ExtensionMetadataFactory::getCacheId(self::YAML_CATEGORY, 'Gedmo\Loggable');
$config = $this->em->getMetadataFactory()->getCacheDriver()->fetch($cacheId);
$config = $this->cache->getItem($cacheId)->get();

static::assertArrayHasKey('loggable', $config);
static::assertTrue($config['loggable']);
Expand Down
41 changes: 41 additions & 0 deletions tests/Gedmo/Mapping/ORMMappingTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Doctrine Behavioral Extensions package.
* (c) Gediminas Morkevicius <[email protected]> http://www.gediminasm.org
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Gedmo\Tests\Mapping;

use Doctrine\ORM\Configuration;
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

abstract class ORMMappingTestCase extends TestCase
{
/**
* @var CacheItemPoolInterface
*/
protected $cache;

protected function setUp(): void
{
$this->cache = new ArrayAdapter();
}

final protected function getBasicConfiguration(): Configuration
{
$config = new Configuration();
$config->setMetadataCache(new ArrayAdapter());
$config->setQueryCache(new ArrayAdapter());
$config->setProxyDir(TESTS_TEMP_DIR);
$config->setProxyNamespace('Gedmo\Mapping\Proxy');

return $config;
}
}
Loading

0 comments on commit 474ca92

Please sign in to comment.