Skip to content

Commit

Permalink
Allow overriding cache expiry and cache key for cacheable commands
Browse files Browse the repository at this point in the history
  • Loading branch information
adamnicholson committed Mar 16, 2016
1 parent ee9284c commit 94e6de1
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
30 changes: 25 additions & 5 deletions src/Decorators/CachingDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Chief\Command;
use Chief\CommandBus;
use Chief\Decorator;
use Chief\HasCacheOptions;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;

Expand Down Expand Up @@ -55,7 +56,7 @@ public function execute(Command $command)
return $this->innerBus->execute($command);
}

$cached = $this->cache->getItem(self::createCacheKey($command));
$cached = $this->cache->getItem($this->getCacheKey($command));
if ($cached->isHit()) {
return $cached->get();
}
Expand All @@ -76,9 +77,9 @@ public function execute(Command $command)
*/
private function createCacheItem(CacheableCommand $command, $value)
{
return $this->cache->getItem($this->createCacheKey($command))
->set($value)
->expiresAfter($this->expiresAfter);
return $this->cache->getItem($this->getCacheKey($command))
->expiresAfter($this->getCacheExpiry($command))
->set($value);
}

/**
Expand All @@ -91,8 +92,27 @@ private function createCacheItem(CacheableCommand $command, $value)
* @param CacheableCommand $command
* @return string
*/
private function createCacheKey(CacheableCommand $command)
private function getCacheKey(CacheableCommand $command)
{
if ($command instanceof HasCacheOptions && $command->getCacheKey()) {
return $command->getCacheKey();
}

return md5(serialize($command));
}

/**
* Determine when this CachableCommand should expire, in terms of seconds from now.
*
* @param CacheableCommand $command
* @return int
*/
private function getCacheExpiry(CacheableCommand $command)
{
if ($command instanceof HasCacheOptions && $command->getCacheExpiry() > 0) {
return $command->getCacheExpiry();
}

return $this->expiresAfter;
}
}
19 changes: 19 additions & 0 deletions src/HasCacheOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Chief;

interface HasCacheOptions extends CacheableCommand
{
/**
* @return int|null
* In how many seconds from now should this cache item expire. Return null to use the default value specified
* in the CachingDecorator.
*/
public function getCacheExpiry();

/**
* @return string|null
* The cache key used when caching this object. Return null to automatically generate a cache key.
*/
public function getCacheKey();
}
61 changes: 61 additions & 0 deletions tests/Decorators/CachingDecoratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Chief\Command;
use Chief\CommandBus;
use Chief\Decorator;
use Chief\HasCacheOptions;
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Cache\CacheItemInterface;
Expand Down Expand Up @@ -68,6 +69,66 @@ public function test_execution_return_value_is_cached_when_command_implements_ca
$this->assertEquals(7, $result);
}

public function test_cache_expiry_can_be_overridden()
{
$decorator = $this->getDecorator();
$inner = $this->prophesize(CommandBus::class);
$decorator->setInnerBus($inner->reveal());

$commandProphecy = $this->prophesize(HasCacheOptions::class);
$commandProphecy->getCacheExpiry()->willReturn(60*60*24*365);
$commandProphecy->getCacheKey()->willReturn(null);

$command = $commandProphecy->reveal();

$notCachedItem = $this->prophesize(CacheItemInterface::class);
$notCachedItem->isHit()->willReturn(false);
$notCachedItem->getKey()->willReturn(md5(serialize(($command))));
$this->cache->getItem(Argument::any())->willReturn($notCachedItem->reveal());

$notCachedItem->set(7)->shouldBeCalled()->willReturn($notCachedItem->reveal());
$notCachedItem->expiresAfter(60*60*24*365)->shouldBeCalled()->willReturn($notCachedItem->reveal());

$this->cache->save(Argument::that(function (CacheItemInterface $item) use ($command) {
return !empty($item->getKey());
}))->shouldBeCalled();

$inner->execute($command)->shouldBeCalled()->willReturn(7);

$result = $decorator->execute($command);
$this->assertEquals(7, $result);
}

public function test_cache_key_can_be_overridden()
{
$decorator = $this->getDecorator();
$inner = $this->prophesize(CommandBus::class);
$decorator->setInnerBus($inner->reveal());

$commandProphecy = $this->prophesize(HasCacheOptions::class);
$commandProphecy->getCacheExpiry()->willReturn(null);
$commandProphecy->getCacheKey()->willReturn('custom-cache-key');

$command = $commandProphecy->reveal();

$notCachedItem = $this->prophesize(CacheItemInterface::class);
$notCachedItem->isHit()->willReturn(false);
$notCachedItem->getKey()->willReturn('custom-cache-key');
$this->cache->getItem('custom-cache-key')->willReturn($notCachedItem->reveal());

$notCachedItem->set(7)->shouldBeCalled()->willReturn($notCachedItem->reveal());
$notCachedItem->expiresAfter(3600)->shouldBeCalled()->willReturn($notCachedItem->reveal());

$this->cache->save(Argument::that(function (CacheItemInterface $item) use ($command) {
return $item->getKey() === 'custom-cache-key';
}))->shouldBeCalled();

$inner->execute($command)->shouldBeCalled()->willReturn(7);

$result = $decorator->execute($command);
$this->assertEquals(7, $result);
}

public function test_cache_item_value_is_returned_if_cached()
{
$decorator = $this->getDecorator();
Expand Down

0 comments on commit 94e6de1

Please sign in to comment.