72 lines
2 KiB
Markdown
72 lines
2 KiB
Markdown
|
---
|
||
|
title: Caching with decorators
|
||
|
date: 2025-04-06
|
||
|
permalink: daily/2025/04/06/caching
|
||
|
tags:
|
||
|
- software-development
|
||
|
- drupal
|
||
|
- php
|
||
|
cta: ~
|
||
|
snippet: |
|
||
|
TODO
|
||
|
---
|
||
|
|
||
|
As well as [working with different versions of an API][0], I was able to use the same technique I wrote about yesterday to easily add a cacheable version of the API client.
|
||
|
|
||
|
As they all implement the same `ApiClientInterface`, I can inject and decorate a client with another client, making one solely responsible for caching the result from the API whilst keeping the API interaction logic separate (aka [the Decorator design pattern][1]).
|
||
|
|
||
|
Here's an example based on the code I wrote:
|
||
|
|
||
|
```php
|
||
|
final class CacheableApiClient implements ApiClientInterface {
|
||
|
|
||
|
/**
|
||
|
* The cache duration in seconds.
|
||
|
*/
|
||
|
private const CACHE_DURATION = 3600;
|
||
|
|
||
|
public function __construct(
|
||
|
private readonly ApiClientInterface $client,
|
||
|
private readonly TimeInterface $time,
|
||
|
private readonly CacheBackendInterface $cache,
|
||
|
) {
|
||
|
}
|
||
|
|
||
|
public function getResults(): Collection {
|
||
|
$key = $this->getCacheKey();
|
||
|
|
||
|
$cache = $this->cache->get($key);
|
||
|
|
||
|
if ($cache !== FALSE) {
|
||
|
return $cache->data;
|
||
|
}
|
||
|
|
||
|
$result = $this->client->getResults();
|
||
|
|
||
|
$this->cache->set(
|
||
|
cid: $key,
|
||
|
data: $result,
|
||
|
expire: $this->time->getRequestTime() + self::CACHE_DURATION,
|
||
|
);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Nothing in this instance is specific to either version of the API.
|
||
|
|
||
|
This client is only concerned with retrieving and saving cache data, and delegating any other logic to the original version.
|
||
|
|
||
|
With this approach, I can switch between `V1ApiClient`, `V2ApiClient` or any other version with the same methods without having to reimplement caching as that's handled within the `CacheableApiClient`.
|
||
|
|
||
|
But what if I don't want to interact with the API at all?
|
||
|
|
||
|
For local development, I have a `FakeApiClient` that returns a static response that I can work with.
|
||
|
|
||
|
The possibilities are endless.
|
||
|
|
||
|
[0]: {{site.url}}/daily/2025/04/05/strategies
|
||
|
[1]: {{site.url}}/daily/2022/12/08/the-decorator-design-pattern
|