Caching in the PHP Delivery client library
Content types, environment and space data change rarely, but you want to access them very often. They are a perfect candidate for caching: you save API calls and speed up execution. Let’s take a look at how to locally cache their data using the PHP CDA client library.
Delivery client library and PSR-6
The client library supports any PSR-6 cache pool. We suggest using either the Symfony Cache component for a comprehensive solution, or one of the packages in the PHP cache organization to solve a specific need; for our example we’ll be using the latter, specifically the Filesystem adapter:
After this step is done, you have two options for triggering a cache warmup: using the CLI utility bundled in the client library, or manually handling the cache warmer for a better integration with your workflow. Let’s take a look at both.
CLI utility
Because PSR-6 cache pools can wrap almost anything, the client library can not know how to properly instantiate them; for this reason, the actual instantiation of the the cache pool must be handled by the user. The client library will, in fact, expect the user to create a class which implements Contentful\Delivery\Cache\CacheItemPoolFactoryInterface:
Let’s create a simple implementation:
Now that we have everything we need, let’s trigger a cache warmup:
A few notes about the command:
- The environment ID parameter is optional, and will default to
master. --factory-classexpects a fully-qualified class name (FQCN). The client library uses the system autoloader, so anything in your project should work.- The command takes an optional
--use-previewparameter, which will make the client library use the Preview API instead of the Delivery API. - Use
php vendor/bin/contentful help delivery:cache:warmupfor a complete description. - A corresponding
delivery:cache:clearcommand is provided, and it accepts the same parameters.
Manually calling the cache warmer
The CLI utility behind the scenes uses the class Contentful\Delivery\Cache\CacheWarmer. If you need to have a tighter integration in your code with cache warming/clearing, you can use yourself that class directly. The constructor expects two arguments: a Contentful\Delivery\Client object (configured without a cache setting) and a PSR-6 cache pool:
Configuring the client
Regardless how you configure the cache, you need to tell the client to use the cache pool. To do so, set the cache parameter to the PSR-6 cache pool in the options array of the client constructor:
Now, the client library will check the cache for space, environment, and content type information before querying the API. Bear in mind that when calling the collection endpoint for content types ($client->getContentTypes($query)) the cache can not be used, since the client library can’t reliably predict which content types will be returned.
Runtime, dynamic cache warm up
There is another option, which doesn’t require you to statically cache data beforehand, and instead delegates that process to runtime when a new space, environment, or content type is found. This is useful when working with very dynamic applications or cache stores, or where data about Contentful (such as space ID or environment ID) is not known at build time.
To enable dynamic cache warm up, pass true as second parameter to withCache:
When using this configuration, the client library will automatically save in the configured PSR-6 cache pool any new instance of space, environment, and content types that is found. While this is a convenient and easy option, we still recommend statically warming up the cache instead, to avoid increased runtime complexity and a possible performance hit (for instance if the cache is slow on write).
Caching actual entries and assets
The client library provides a method of caching content (entries and assets) besides structure (content types, etc). However, this method has a huge limitation: it works only when calling $client->getEntry($entryId), $client->getAsset($assetId) and when resolving a link. This means that calling $client->getEntries() and $client->getAssets() will skip the cache lookup altogether.
The reason for this is simple: when fetching a single entry or asset, the client library can reliably know if it is in the local cache. In fact, to identify a cache element, its ID (alongside other elements) is used. This means when fetching a collection of entries or assets, the client library can’t really know which IDs will be returned, so the local cache will not be used.
If you still want to enable caching of entries and assets (it might be useful in certain scenarios), pass true as the third parameter to withCache:
As a final note, remember one thing: should you make changes such as adding or removing a locale, or changing fields in a content type, you would need to regenerate the cache, as the client library behavior is to use the cache whenever that’s available, even at the cost of ignoring newer info. In the event of a new content type, its info would be picked up correctly — there would be nothing about it in the cache. Our suggestion is to warmup the cache at every change to the content types, so you can be sure to always have the best performance and never risk working with obsolete data. Should you make changes to a content type without purging its obsolete cache data, the client library will still try to build the entry, but you may find yourself in a situation where some field is not built correctly, and is instead returned “raw”, for instance a string rather than a Contentful\Core\Api\DateTimeImmutable object, or an array rather than a Contentful\Core\Api\Link object. For this reason, you should always try to do a cache purge on content type changes, either manually or using webhooks.