Hello everyone!

We have been investing plenty of personal time and energy for many years to share our knowledge with you all. However, we now need your help to keep this blog running. All you have to do is just click one of the adverts on the site, otherwise it will sadly be taken down due to hosting etc. costs. Thank you.

This example shows us how we can use Memcached as "cache pool" in Symfony applications. You can clear all caches in given pool in one go without touching cache items that present in other pools. There is a non-pool version as well but this is the preferred approach.


If you want to use Redis instead, all you have to do is add config below. Afterwards run sudo apt-get install redis-server and sudo apt-get install php7.2-redis commands and restart your web/php services.


framework:
cache:
default_redis_provider: 'redis://localhost:6379'
pools:
app.cache.countries:
adapter: cache.adapter.redis
public: false
default_lifetime: 60

Installation


Memcached


$ sudo apt-get install memcached
$ sudo apt-get install -y libapache2-mod-php
$ sudo apt-get install -y php7.2-memcached

$ sudo systemctl restart nginx
$ sudo service php7.2-fpm restart

Symfony


$ composer require symfony/cache

Commands


# Clear cache for given pool only in terminal
$ bin/console cache:pool:clear app.cache.countries
$ bin/console cache:pool:clear app.cache.products

# Delete a cache from given pool in terminal
$ bin/console cache:pool:delete app.cache.countries key

Files


config/packages/framework.yaml


framework:
cache:
default_memcached_provider: 'memcached://localhost:11211'
pools:
app.cache.countries:
adapter: cache.adapter.memcached
public: false
default_lifetime: 60
app.cache.products:
adapter: cache.adapter.memcached
public: false
default_lifetime: 120

config/services.yaml


services:
App\Controller\CountryController:
arguments:
$cachePool: '@app.cache.countries'
tags: ['controller.service_arguments']

App\Controller\ProductController:
arguments:
$cachePool: '@app.cache.products'
tags: ['controller.service_arguments']

Controller/CountryController.php


declare(strict_types=1);

namespace App\Controller;

use App\Util\CacheInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
* @Route("/countries")
*/
class CountryController
{
private $cachePool;
private $cacheUtil;

public function __construct(
MemcachedAdapter $cachePool,
CacheInterface $cacheUtil
) {
$this->cachePool = $cachePool;
$this->cacheUtil = $cacheUtil;
}

/**
* @Route("/save-item", methods={"GET"})
*/
public function saveItem(): JsonResponse
{
return new JsonResponse($this->cacheUtil->saveItem($this->cachePool, 'key', 'country'));
}

/**
* @Route("/get-item", methods={"GET"})
*/
public function getItem(): JsonResponse
{
return new JsonResponse($this->cacheUtil->getItem($this->cachePool, 'key'));
}

/**
* @Route("/delete-item", methods={"GET"})
*/
public function deleteItem(): JsonResponse
{
return new JsonResponse($this->cacheUtil->deleteItem($this->cachePool, 'key'));
}

/**
* @Route("/delete-all", methods={"GET"})
*/
public function deleteAll(): JsonResponse
{
return new JsonResponse($this->cacheUtil->deleteAll($this->cachePool));
}
}

Controller/ProductController.php


declare(strict_types=1);

namespace App\Controller;

use App\Util\CacheInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
use Symfony\Component\HttpFoundation\JsonResponse;

/**
* @Route("/products")
*/
class ProductController
{
private $cachePool;
private $cacheUtil;

public function __construct(
MemcachedAdapter $cachePool,
CacheInterface $cacheUtil
) {
$this->cachePool = $cachePool;
$this->cacheUtil = $cacheUtil;
}

/**
* @Route("/save-item", methods={"GET"})
*/
public function saveItem(): JsonResponse
{
return new JsonResponse($this->cacheUtil->saveItem($this->cachePool, 'key', 'product'));
}

/**
* @Route("/get-item", methods={"GET"})
*/
public function getItem(): JsonResponse
{
return new JsonResponse($this->cacheUtil->getItem($this->cachePool, 'key'));
}

/**
* @Route("/delete-item", methods={"GET"})
*/
public function deleteItem(): JsonResponse
{
return new JsonResponse($this->cacheUtil->deleteItem($this->cachePool, 'key'));
}

/**
* @Route("/delete-all", methods={"GET"})
*/
public function deleteAll(): JsonResponse
{
return new JsonResponse($this->cacheUtil->deleteAll($this->cachePool));
}
}

Util/CacheInterface.php


declare(strict_types=1);

namespace App\Util;

use Symfony\Component\Cache\Adapter\MemcachedAdapter;

interface CacheInterface
{
public function saveItem(MemcachedAdapter $cachePool, string $key, string $value): bool;

public function getItem(MemcachedAdapter $cachePool, string $key): ?string;

public function deleteItem(MemcachedAdapter $cachePool, string $key): bool;

public function deleteAll(MemcachedAdapter $cachePool): bool;
}

Util/MemcachedUtil.php


declare(strict_types=1);

namespace App\Util;

use App\Exception\CacheException;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Cache\Adapter\MemcachedAdapter;

class MemcachedUtil implements CacheInterface
{
public function saveItem(MemcachedAdapter $cachePool, string $key, string $value): bool
{
$item = $this->fetch($cachePool, $key);
$item->set($value);

return $cachePool->save($item);
}

public function getItem(MemcachedAdapter $cachePool, string $key): ?string
{
$result = null;

$item = $this->fetch($cachePool, $key);
if ($item->isHit()) {
$result = $item->get();
}

return $result;
}

public function deleteItem(MemcachedAdapter $cachePool, string $key): bool
{
return $cachePool->deleteItem($key);
}

public function deleteAll(MemcachedAdapter $cachePool): bool
{
return $cachePool->clear();
}

private function fetch(MemcachedAdapter $cachePool, string $key): CacheItemInterface
{
try {
return $cachePool->getItem($key);
} catch (InvalidArgumentException $e) {
throw new CacheException($e->getMessage());
}
}
}

Exception/CacheException.php


declare(strict_types=1);

namespace App\Exception;

use RuntimeException;

class CacheException extends RuntimeException
{
}

References