Skip to content

Commit

Permalink
also update the cache when updating a setting entity directly
Browse files Browse the repository at this point in the history
  • Loading branch information
craue committed Aug 18, 2023
1 parent 2887285 commit 668619b
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 57 deletions.
26 changes: 1 addition & 25 deletions Controller/SettingsController.php
Expand Up @@ -2,7 +2,6 @@

namespace Craue\ConfigBundle\Controller;

use Craue\ConfigBundle\CacheAdapter\CacheAdapterInterface;
use Craue\ConfigBundle\Entity\SettingInterface;
use Craue\ConfigBundle\Form\ModifySettingsForm;
use Doctrine\ORM\EntityManagerInterface;
Expand All @@ -22,7 +21,7 @@
*/
class SettingsController extends AbstractController {

public function modifyAction(CacheAdapterInterface $cache, FormFactoryInterface $formFactory, Request $request,
public function modifyAction(FormFactoryInterface $formFactory, Request $request,
SessionInterface $session, Environment $twig, EntityManagerInterface $em, TranslatorInterface $translator) {
$repo = $em->getRepository($this->container->getParameter('craue_config.entity_name'));
$allStoredSettings = $repo->findAll();
Expand All @@ -37,14 +36,6 @@ public function modifyAction(CacheAdapterInterface $cache, FormFactoryInterface
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
// update the cache
foreach ($formData['settings'] as $formSetting) {
$storedSetting = $this->getSettingByName($allStoredSettings, $formSetting->getName());
if ($storedSetting !== null) {
$cache->set($storedSetting->getName(), $storedSetting->getValue());
}
}

$em->flush();

if ($session instanceof Session) {
Expand Down Expand Up @@ -80,19 +71,4 @@ protected function getSections(array $settings) {
return $sections;
}

/**
* @param SettingInterface[] $settings
* @param string $name
* @return SettingInterface|null
*/
protected function getSettingByName(array $settings, $name) {
foreach ($settings as $setting) {
if ($setting->getName() === $name) {
return $setting;
}
}

return null;
}

}
1 change: 1 addition & 0 deletions DependencyInjection/CraueConfigExtension.php
Expand Up @@ -24,6 +24,7 @@ class CraueConfigExtension extends Extension implements PrependExtensionInterfac
public function load(array $configs, ContainerBuilder $container) : void {
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('controller.xml');
$loader->load('event_listener.xml');
$loader->load('form.xml');
$loader->load('twig.xml');
$loader->load('util.xml');
Expand Down
43 changes: 43 additions & 0 deletions EventListener/SettingUpdateListener.php
@@ -0,0 +1,43 @@
<?php

namespace Craue\ConfigBundle\EventListener;

use Craue\ConfigBundle\CacheAdapter\CacheAdapterInterface;
use Craue\ConfigBundle\CacheAdapter\NullAdapter;
use Craue\ConfigBundle\Entity\SettingInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;

/**
* @author Christian Raue <christian.raue@gmail.com>
* @copyright 2011-2023 Christian Raue
* @license http://opensource.org/licenses/mit-license.php MIT License
*/
class SettingUpdateListener {

/**
* @var CacheAdapterInterface
*/
private $cache;

public function __construct(?CacheAdapterInterface $cache = null) {
$this->cache = $cache ?? new NullAdapter();
}

// TODO add `PostUpdateEventArgs` type-hint as soon as doctrine/orm >= 2.13 is required
public function postUpdate($eventArgs) : void {
assert($eventArgs instanceof LifecycleEventArgs);

$entity = $eventArgs->getObject();

if (!$entity instanceof SettingInterface) {
return;
}

$this->updateCache($entity);
}

private function updateCache(SettingInterface $setting) : void {
$this->cache->set($setting->getName(), $setting->getValue());
}

}
16 changes: 16 additions & 0 deletions Resources/config/event_listener.xml
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Author: Christian Raue <christian.raue@gmail.com>
Copyright: 2011-2023 Christian Raue
License: http://opensource.org/licenses/mit-license.php MIT License
-->
<container
xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Craue\ConfigBundle\EventListener\SettingUpdateListener" autowire="true">
<tag name="doctrine.event_listener" event="postUpdate" />
</service>
</services>
</container>
3 changes: 1 addition & 2 deletions Tests/Controller/SettingsControllerIntegrationTest.php
Expand Up @@ -132,9 +132,8 @@ public function testModifyAction_changeValue_cacheUsage($platform, $config, $req
]);

$this->assertTrue($cache->has('name1'));
$this->assertTrue($cache->has('name2'));
$this->assertFalse($cache->has('name2'));
$this->assertSame('new-value1', $cache->get('name1'));
$this->assertSame('value2', $cache->get('name2'));
}

public function dataModifyAction_changeValue_cacheUsage() {
Expand Down
22 changes: 0 additions & 22 deletions Tests/Controller/SettingsControllerUnitTest.php
Expand Up @@ -40,26 +40,4 @@ public function dataGetSections() {
];
}

/**
* @dataProvider dataGetSettingByName
*/
public function testGetSettingByName(array $settings, $name, $expectedResult) {
$controller = new SettingsController();
$method = new \ReflectionMethod($controller, 'getSettingByName');
$method->setAccessible(true);

$this->assertSame($expectedResult, $method->invoke($controller, $settings, $name));
}

public function dataGetSettingByName() {
$setting1 = Setting::create('name1');
$setting2 = Setting::create('name2');

return [
[[$setting1], 'name1', $setting1],
[[$setting1, $setting2], 'name2', $setting2],
[[$setting1, $setting2], 'name3', null],
];
}

}
68 changes: 68 additions & 0 deletions Tests/EventListener/SettingUpdateListenerTest.php
@@ -0,0 +1,68 @@
<?php

namespace Craue\ConfigBundle\Tests\EventListener;

use Craue\ConfigBundle\CacheAdapter\CacheAdapterInterface;
use Craue\ConfigBundle\Entity\SettingInterface;
use Craue\ConfigBundle\EventListener\SettingUpdateListener;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;
use PHPUnit\Framework\TestCase;

/**
* @author Christian Raue <christian.raue@gmail.com>
* @copyright 2011-2023 Christian Raue
* @license http://opensource.org/licenses/mit-license.php MIT License
*/
class SettingUpdateListenerTest extends TestCase {

public function testPostUpdate() : void {
$cache = $this->createMock(CacheAdapterInterface::class);
$listener = new SettingUpdateListener($cache);

$setting = $this->getMockBuilder(SettingInterface::class)->onlyMethods(['getName', 'getValue'])->getMockForAbstractClass();
$name = 'name';
$newValue = 'new-value';

$setting->expects($this->once())
->method('getName')
->willReturn($name)
;

$setting->expects($this->once())
->method('getValue')
->willReturn($newValue)
;

$cache->expects($this->once())
->method('set')
->with($name, $newValue)
;

$listener->postUpdate($this->getPostUpdateEventArgs($setting));
}

public function testPostUpdate_entityIsNotSetting() : void {
$cache = $this->createMock(CacheAdapterInterface::class);
$listener = new SettingUpdateListener($cache);

$cache->expects($this->never())
->method('set')
;

$listener->postUpdate($this->getPostUpdateEventArgs(new \stdClass()));
}

// TODO remove as soon as doctrine/orm >= 2.13 is required
private function getPostUpdateEventArgs(object $object) : LifecycleEventArgs {
$em = $this->createMock(EntityManagerInterface::class);

if (!class_exists(PostUpdateEventArgs::class)) {
return new LifecycleEventArgs($object, $em);
}

return new PostUpdateEventArgs($object, $em);
}

}
24 changes: 24 additions & 0 deletions Tests/Util/ConfigIntegrationTest.php
Expand Up @@ -75,6 +75,30 @@ public function dataCacheUsage() {
return $testData;
}

/**
* Ensure that the cache is updated when updating a setting entity directly.
*
* @dataProvider dataCacheUsage
*/
public function testCacheUpdateOnEntityUpdate($platform, $config, $requiredExtension, $environment) : void {
$this->initClient($requiredExtension, ['environment' => $environment . '_' . $platform, 'config' => $config]);

$cache = $this->getService('craue_config_cache_adapter');
$cache->clear();

$setting = Setting::create('name', 'value');
$this->persistSetting($setting);

$this->assertFalse($cache->has($setting->getName()));

// update the entity directly
$setting->setValue('new-value2');
$this->getEntityManager()->flush();

$this->assertTrue($cache->has($setting->getName()));
$this->assertSame('new-value2', $cache->get($setting->getName()));
}

/**
* Ensure that a custom config class can actually be used with a custom model class.
*
Expand Down
10 changes: 4 additions & 6 deletions Tests/Util/ConfigUnitTest.php
Expand Up @@ -101,9 +101,8 @@ public function testSet() {

$config->setEntityManager($this->createEntityManagerMock($this->createEntityRepositoryMock(['findOneBy' => $setting])));

$cache->expects($this->once())
->method('set')
->with($setting->getName(), $newValue)
$cache->expects($this->never())
->method($this->anything())
;

$setting->expects($this->once())
Expand Down Expand Up @@ -138,9 +137,8 @@ public function testSetMultiple() {
$setting->getName() => $newValue,
];

$cache->expects($this->once())
->method('setMultiple')
->with($settingsKeyValuePairs)
$cache->expects($this->never())
->method($this->anything())
;

$setting->expects($this->once())
Expand Down
4 changes: 2 additions & 2 deletions Util/Config.php
Expand Up @@ -99,7 +99,7 @@ public function set($name, $value) {
$setting->setValue($value);
$this->em->flush();

$this->cache->set($name, $value);
// cache is updated in SettingUpdateListener
}

/**
Expand All @@ -123,7 +123,7 @@ public function setMultiple(array $newSettings) {

$this->em->flush();

$this->cache->setMultiple($newSettings);
// cache is updated in SettingUpdateListener
}

/**
Expand Down

0 comments on commit 668619b

Please sign in to comment.