Hello,
is there known way to cache page or template fragment with slim ?
Entire page cache: is there a way with a middleware to grab the entire response, store it into cache, then serve it from cache on the subsequent call ?
Fragment caching: I found this that works with twig: https://github.com/asm89/twig-cache-extension . Not sure if it works with slim + twig. Or are there alternative, or easier way to do it with other template engine than twig ?
Hi @claude.samuelson, I’m trying to cache my Slim responses in the same way you suggest (doing it in the middleware), however, I’m having trouble due to the way Slim responses use a TEMP stream. My response object caches fine but after retrieving the cached response and attempting to display it the stream reference no longer points to a valid stream and no body is returned. I was wondering if you may have an idea on how to address this as your post makes it seem like you’ve gotten this method to work once already.
Thanks for the reply @claude.samuelson but I already figured it out and it turns out to be very close to your suggestion. Here’s my solution:
<?php
namespace App\Middleware;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Http\Headers;
class CacheMiddleware
{
/** @var \Slim\Container $container The Slim application container */
protected $container;
/**
* Create a Middleware object.
*
* @param \Slim\Container $container The Slim application container
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* Cache responses to speed up page loading.
*
* @param \Slim\Http\Request $request Incoming request object
* @param \Slim\Http\Response $response Outgoing response object
* @param callable $next The next middleware
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function __invoke(Request $request, Response $response, callable $next)
{
if (! $this->container->config->get('cache.enabled', false)) {
return $next($request, $response);
}
$key = $request->getUri()->getPath();
if ($this->container->cache->has($key)) {
[$response, $body] = $this->container->cache->get($key);
$headers = new Headers;
foreach ($response->getHeaders() as $header => $value) {
$headers->set($header, $value);
}
return (
new Response($response->getStatusCode(), $headers)
)->write($body);
}
$response = $next($request, $response);
if ($response->isOk()) {
$this->container->cache->forever($key, [$response, (string) $response->getBody()]);
}
return $response;
}
}
One of the key differences is that my solution caches the whole response so I can use other cached properties like the status code. Also, instead of creating a new Body object I just use Response::write() to write the cached body to the newly created Response object.
The Twig template loader can cache the compiled templates on the filesystem for future reuse. It speeds up Twig a lot as templates are only compiled once. The performance boost is extreme, thanks to the PHP OPcache. See the cache and auto_reload options of Twig_Environment for more information.
Here is an example how to enable the Twig “OPCache”:
// composer require slim/views
use Slim\Views\Twig;
$container['view'] = function (Container $container) {
$settings = $container->get('settings');
$viewPath = $settings['twig']['path'];
// To enable the cache, just pass the cache path
// To disable the cache set the cache value to false.
$twig = new Twig($viewPath, [
'cache' => $settings['twig']['cache_enabled'] ? $settings['twig']['cache_path'] : false
]);
// ...
return $twig;
};
That’s great information @odan and I’ll definitely take it into consideration as I’m about to start working with Twig but for my specific purposes I’m caching rendered images and not Twig templates or HTML. Therefor caching the raw response is a much better approach.