Skip to content

Commit 3eed794

Browse files
feat: improved invalidator service and added mixin to the generated cached services; (#29)
* feat: improved invalidator service and added mixin to the generated cached services;
1 parent 4920482 commit 3eed794

8 files changed

Lines changed: 152 additions & 15 deletions

File tree

‎src/CachedServiceGenerator/Dto/MethodCallObject.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Tbessenreither\MultiLevelCache\CachedServiceGenerator\Dto;
66

7+
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Service\KeyGeneratorService;
8+
79
class MethodCallObject
810
{
911
public function __construct(
@@ -41,7 +43,7 @@ public function getAdditionalCacheKey(): ?string
4143

4244
public function getCachePrefix(): string
4345
{
44-
return str_replace(['\\', ':', ' '], ['_', '-', ''], $this->getClass());
46+
return KeyGeneratorService::namespaceToKeyString($this->getClass());
4547
}
4648

4749
public function getCallable(): callable

‎src/CachedServiceGenerator/Service/InvalidatorService.php‎

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,47 @@ public function __construct(
1919
$this->directRedisCacheService = $multiLevelCacheFactory->getImplementationRedisWithPrefix('mlc');
2020
}
2121

22-
public function invalidateCacheForClass(string $classString): bool
22+
public function invalidateEverything(): bool
2323
{
24-
try {
25-
$generateKey = KeyGeneratorService::getKeyPatternForClass(new MethodCallObject($classString, '', []));
24+
return $this->deleteByPattern('*');
25+
}
2626

27-
$this->directRedisCacheService->deleteByPattern($generateKey);
27+
public function invalidateCacheForNamespace(string $namespace): bool
28+
{
29+
$completedWithoutError = true;
30+
$invalidateKeyPatterns = [
31+
KeyGeneratorService::namespaceToKeyString($namespace, null, true) . ':*',
32+
KeyGeneratorService::namespaceToKeyString($namespace, null, false) . ':*',
33+
];
34+
foreach ($invalidateKeyPatterns as $pattern) {
2835

29-
return true;
30-
} catch (Throwable $e) {
31-
return false;
36+
$result = $this->deleteByPattern($pattern);
37+
if ($result === false) {
38+
$completedWithoutError = false;
39+
}
3240
}
41+
42+
return $completedWithoutError;
43+
}
44+
45+
public function invalidateCacheForClass(string $classString): bool
46+
{
47+
$generateKey = KeyGeneratorService::getKeyPatternForClass(new MethodCallObject($classString, '', []));
48+
49+
return $this->deleteByPattern($generateKey);
3350
}
3451

3552
public function invalidateCacheForMethod(string $classString, string $method): bool
3653
{
37-
try {
38-
$generateKey = KeyGeneratorService::getKeyPatternForMethod(new MethodCallObject($classString, $method, []));
54+
$generateKey = KeyGeneratorService::getKeyPatternForMethod(new MethodCallObject($classString, $method, []));
3955

40-
$this->directRedisCacheService->deleteByPattern($generateKey);
56+
return $this->deleteByPattern($generateKey);
57+
}
58+
59+
public function deleteByPattern(string $pattern): bool
60+
{
61+
try {
62+
$this->directRedisCacheService->deleteByPattern($pattern);
4163

4264
return true;
4365
} catch (Throwable $e) {

‎src/CachedServiceGenerator/Service/KeyGeneratorService.php‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
class KeyGeneratorService
1616
{
17+
public const string CACHED_SERVICE_GENERATOR_KEY_PREFIX = 'CachedService';
18+
1719
/**
1820
* @var array<string, string>
1921
*/
@@ -28,6 +30,20 @@ public static function getKey(MethodCallObject $methodCallObject, bool $throw =
2830
return self::defaultKeyGenerator($methodCallObject);
2931
}
3032

33+
public static function namespaceToKeyString(string $fqcn, ?string $method = null, bool $addCsgPrefix = false): string
34+
{
35+
$parts = [];
36+
if ($addCsgPrefix) {
37+
$parts[] = self::CACHED_SERVICE_GENERATOR_KEY_PREFIX;
38+
}
39+
$parts[] = str_replace(['\\', ':', ' '], ['_', '-', ''], $fqcn);
40+
if ($method) {
41+
$parts[] = $method;
42+
}
43+
44+
return implode(':', $parts);
45+
}
46+
3147
public static function getKeyPatternForMethod(MethodCallObject $methodCallObject): string
3248
{
3349
$generatedFullKey = self::getKey($methodCallObject);

‎src/CachedServiceGenerator/Service/MakeCachedServiceService.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class: $interfaceClass,
129129
$classCode = RenderTemplateService::render('Class/CachedService', [
130130
'ServiceNamespace' => $namespace,
131131
'ServiceName' => $shortName,
132+
'CacheKeyPrefix' => KeyGeneratorService::namespaceToKeyString(fqcn:$class, addCsgPrefix:true),
132133
'ClassDotSeparated' => $classDotSeparated,
133134
'ClassUnderscoreSeparated' => $classUnderscoreSeparated,
134135
'ClassHyphenSeparated' => $classHyphenSeparated,

‎src/CachedServiceGenerator/Service/MlcTemplates/Class/CachedServiceTemplate.txt‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,19 @@ use Throwable;
2626
*
2727
* Cache enabled wrapper for Service /*{ServiceName}*/.
2828
*
29-
* This Wrapper provides all public methods from the origin service and was generated using the make command
30-
* You can regenerate/update using php vendor/tbessenreither/multi-level-cache/bin/make --service=/*{ClassDotSeparated}*/
31-
* Or you can update all services with php vendor/tbessenreither/multi-level-cache/bin/update
29+
* This Wrapper provides all public methods from the origin service and was generated using the make command.
30+
* You can regenerate/update using php vendor/tbessenreither/multi-level-cache/bin/make --service=/*{ClassDotSeparated}*/.
31+
* Or you can update all services with php vendor/tbessenreither/multi-level-cache/bin/update.
3232
* @codeCoverageIgnore
33+
* @mixin /*{ServiceName}*/
3334
*/
3435
#[MlcCachedService(
3536
originalServiceClass: /*{ServiceName}*/::class,
3637
allowSync: true,
3738
)]
3839
class /*{ServiceName}*/Cached implements /*{InterfacesString}*/
3940
{
40-
public const CACHE_KEY_PREFIX = 'CachedService:/*{ClassUnderscoreSeparated}*/';
41+
public const CACHE_KEY_PREFIX = '/*{CacheKeyPrefix}*/';
4142

4243
/**
4344
* @var array<string, MlcCacheableMethod|null>

‎tests/CachedServiceGenerator/Dto/MethodCallObjectTest.php‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
use Closure;
88
use PHPUnit\Framework\Attributes\CoversClass;
9+
use PHPUnit\Framework\Attributes\UsesClass;
910
use PHPUnit\Framework\TestCase;
1011
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Dto\MethodCallObject;
12+
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Service\KeyGeneratorService;
1113

1214
#[CoversClass(MethodCallObject::class)]
15+
#[UsesClass(KeyGeneratorService::class)]
1316
class MethodCallObjectTest extends TestCase
1417
{
1518
public function testSetupAndGetter(): void

‎tests/CachedServiceGenerator/Service/InvalidatorServiceTest.php‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,53 @@
3131
#[UsesClass(RedisClientFactory::class)]
3232
class InvalidatorServiceTest extends TestCase
3333
{
34+
public function testInvalidateEverything(): void
35+
{
36+
$redisMock = $this->createMock(Redis::class);
37+
$redisMock->method('isConnected')->willReturn(true);
38+
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
39+
['key:1', 'key:2'],
40+
[]
41+
);
42+
$redisMock
43+
->expects($this->atLeastOnce())
44+
->method('del');
45+
46+
$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));
47+
48+
$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
49+
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
50+
->willReturn($directRedisCacheService);
51+
52+
$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
53+
$result = $invalidatorService->invalidateEverything();
54+
$this->assertTrue($result);
55+
}
56+
57+
public function testInvalidateEverythingWithError(): void
58+
{
59+
$redisMock = $this->createMock(Redis::class);
60+
$redisMock->method('isConnected')->willReturn(true);
61+
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
62+
['key:1', 'key:2'],
63+
[]
64+
);
65+
$redisMock
66+
->expects($this->atLeastOnce())
67+
->method('del')
68+
->willThrowException(new Exception('random error'));
69+
70+
$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));
71+
72+
$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
73+
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
74+
->willReturn($directRedisCacheService);
75+
76+
$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
77+
$result = $invalidatorService->invalidateEverything();
78+
$this->assertFalse($result);
79+
}
80+
3481
public function testClassInvalidation(): void
3582
{
3683
$redisStub = $this->createStub(Redis::class);
@@ -100,4 +147,47 @@ public function testMethodInvalidationError(): void
100147
$result = $invalidatorService->invalidateCacheForMethod(TestServiceA::class, 'testMethod');
101148
$this->assertFalse($result);
102149
}
150+
151+
public function testNamespaceInvalidation(): void
152+
{
153+
$redisMock = $this->createMock(Redis::class);
154+
$redisMock->method('isConnected')->willReturn(true);
155+
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
156+
['key:1', 'key:2'],
157+
[]
158+
);
159+
$redisMock->expects($this->atLeastOnce())
160+
->method('del');
161+
$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));
162+
163+
$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
164+
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
165+
->willReturn($directRedisCacheService);
166+
167+
$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
168+
$result = $invalidatorService->invalidateCacheForNamespace(TestServiceA::class);
169+
$this->assertTrue($result);
170+
}
171+
172+
public function testNamespaceInvalidationWithError(): void
173+
{
174+
$redisMock = $this->createMock(Redis::class);
175+
$redisMock->method('isConnected')->willReturn(true);
176+
$redisMock->method('scan')->willReturnOnConsecutiveCalls(
177+
['key:1', 'key:2'],
178+
[]
179+
);
180+
$redisMock->expects($this->atLeastOnce())
181+
->method('del')
182+
->willThrowException(new Exception('random error'));
183+
$directRedisCacheService = new DirectRedisCacheService(redisClientProvider: RedisClientFactoryTest::wrapClient($redisMock));
184+
185+
$multiLevelCacheFactoryStub = $this->createStub(MultiLevelCacheFactory::class);
186+
$multiLevelCacheFactoryStub->method('getImplementationRedisWithPrefix')
187+
->willReturn($directRedisCacheService);
188+
189+
$invalidatorService = new InvalidatorService($multiLevelCacheFactoryStub);
190+
$result = $invalidatorService->invalidateCacheForNamespace(TestServiceA::class);
191+
$this->assertFalse($result);
192+
}
103193
}

‎tests/Commands/MakeTest.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use PHPUnit\Framework\Attributes\CoversNothing;
88
use PHPUnit\Framework\TestCase;
9+
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Service\KeyGeneratorService;
910

1011
#[CoversNothing]
1112
class MakeTest extends TestCase
@@ -69,6 +70,7 @@ public function testMake(): void
6970
$this->assertStringContainsString('public function exampleMethod(): void', $interfaceFileContent);
7071
$this->assertStringContainsString('public function exampleMethodWithArguments(array $thing, bool $bool, int $int, string $string): void', $interfaceFileContent);
7172

73+
$this->assertStringContainsString("CACHE_KEY_PREFIX = '" . KeyGeneratorService::CACHED_SERVICE_GENERATOR_KEY_PREFIX . ":", $cachedFileContent);
7274
}
7375

7476
public function testMakeWithInterfaces(): void

0 commit comments

Comments
 (0)