Урок 14: Кэширование в Symfony. Twig, HTTP, Redis, Memcached

В 14-ом уроке мы разберем одну из важных тем для оптимизации ваших Symfony-приложений, это кэширование. Кэширование позволяет ускорить загрузку страниц, снизить нагрузку на сервер и улучшить пользовательский опыт. В Symfony есть множество инструментов для работы с кэшем и в этом уроке мы подробно изучим их все, от кэширования шаблонов Twig до настройки Redis и Memcached.

Зачем нужно кэширование?

Допустим ваш сайт обрабатывает тысячи запросов в минуту. Каждый раз сервер выполняет одни и те же операции генерирует шаблоны, запрашивает данные из базы, вычисляет сложную логику. Это ресурсоемко и медленно. Кэширование решает эту проблему, сохраняя «тяжелые» вычисления и отдавая их из быстрого хранилища при повторных запросах. Результат — страницы грузятся быстрее, сервер «дышит свободнее», пользователи довольны.

Типы кэширования в Symfony

Symfony поддерживает несколько уровней кэширования. Давайте разберем каждый из них.

1. Кэш приложения

Этот тип кэша хранит результаты дорогостоящих операций, например, результаты запросов к базе данных или вычислений. Symfony использует компонент Cache из Symfony Contracts, который поддерживает различные хранилища: файлы, Redis, Memcached, базы данных.

Пример конфигурации в config/packages/cache.yaml:

yaml
framework:
    cache:
        app: cache.adapter.redis
        default_redis_provider: 'redis://localhost:6379'

2. HTTP-кэширование

HTTP-кэширование работает на уровне протокола HTTP. Сервер отправляет клиенту (браузеру или прокси-серверу) заголовки, указывающие, как долго можно хранить копию страницы. Это уменьшает количество запросов к вашему серверу.

3. Кэширование данных

Используется для хранения часто запрашиваемых данных, например, результатов API-запросов или списка товаров. Symfony позволяет гибко настраивать время жизни кэша (TTL) и зависимости между данными.

Кэширование шаблонов Twig

Twig автоматически кэширует скомпилированные шаблоны в виде PHP-кода. Но вы можете вручную управлять кэшированием блоков внутри шаблонов.

Базовое кэширование блоков

Используйте тег {% cache %}, чтобы закэшировать часть шаблона. Например, кэшируем список статей:

twig
{% cache 'article_list' 3600 %}
    <ul>
        {% for article in articles %}
            <li>{{ article.title }}</li>
        {% endfor %}
    </ul>
{% endcache %}
  • 'article_list' — уникальный ключ кэша.
  • 3600 — время жизни кэша в секундах (1 час).

Динамический кэш с зависимостями

Если данные зависят от пользователя, добавьте в ключ уникальный идентификатор:

twig
{% cache ['user_profile', user.id] 600 %}
    <div>Профиль: {{ user.username }}</div>
{% endcache %}

Практическая задача

Задача 1: Добавьте кэширование блока с последними комментариями в шаблоне templates/blog/comments.html.twig. Кэш должен обновляться каждые 30 минут.

Решение:

twig
{# templates/blog/comments.html.twig #}
{% cache 'latest_comments' 1800 %}
    <div class="comments">
        {% for comment in comments %}
            <p>{{ comment.text }}</p>
        {% endfor %}
    </div>
{% endcache %}

HTTP-кэширование с заголовками

Symfony упрощает работу с HTTP-кэшированием через методы класса Response. Рассмотрим основные подходы.

Заголовок Cache-Control

Управляет тем, как кэшируется контент. Пример настройки в контроллере:

php
use Symfony\Component\HttpFoundation\Response;

public function index(): Response
{
    $response = new Response($content);
    $response->setSharedMaxAge(3600); // Кэшировать на 1 час для всех пользователей
    return $response;
}
  • setSharedMaxAge() — указывает максимальное время хранения кэша для публичных ресурсов (например, списка статей).
  • setMaxAge() — для приватных ресурсов (например, персональной страницы пользователя).

Валидация кэша с ETag и Last-Modified

Если данные изменились, клиент должен получить новую версию. Для этого используются:

  • ETag — хэш контента.
  • Last-Modified — дата последнего изменения.

Пример с ETag:

php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

public function showArticle(Request $request, Article $article): Response
{
    $response = new Response($this->renderView('article/show.html.twig', [
        'article' => $article,
    ]));

    // Генерируем ETag на основе содержимого статьи
    $etag = md5($response->getContent());
    $response->setEtag($etag);

    // Проверяем, актуален ли кэш у клиента
    if ($response->isNotModified($request)) {
        return $response;
    }

    return $response;
}

Практическая задача

Задача 2: Реализуйте HTTP-кэширование для страницы профиля пользователя. Используйте ETag на основе даты обновления профиля.

Решение:

php
public function userProfile(Request $request, User $user): Response
{
    $response = new Response($this->renderView('user/profile.html.twig', [
        'user' => $user,
    ]));

    // ETag на основе времени последнего обновления
    $etag = md5($user->getUpdatedAt()->format('Y-m-d H:i:s'));
    $response->setEtag($etag);

    if ($response->isNotModified($request)) {
        return $response;
    }

    return $response;
}

Redis и Memcached для кэша данных

Файловый кэш подходит для небольших проектов, но для высоконагруженных сайтов лучше использовать Redis или Memcached. Они хранят данные в оперативной памяти, что ускоряет чтение/запись.

Настройка Redis в Symfony

  1. Установите пакеты:
bash
composer require symfony/cache predis/predis
  1. Настройте подключение в .env:
env
REDIS_URL=redis://localhost:6379
  1. Используйте Redis для кэша в config/packages/cache.yaml:
yaml
framework:
    cache:
        app: cache.adapter.redis
        default_redis_provider: '%env(REDIS_URL)%'

Пример кэширования данных

Кэшируем результат запроса к базе данных:

php
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Component\Cache\Adapter\RedisAdapter;

class ArticleRepository extends ServiceEntityRepository
{
    public function findCachedArticles(): array
    {
        $cache = new RedisAdapter(
            RedisAdapter::createConnection('redis://localhost:6379'),
            'articles_'
        );

        return $cache->get('latest_articles', function (ItemInterface $item) {
            $item->expiresAfter(3600); // Время жизни 1 час
            return $this->createQueryBuilder('a')
                ->orderBy('a.publishedAt', 'DESC')
                ->setMaxResults(10)
                ->getQuery()
                ->getResult();
        });
    }
}

Практическая задача

Задача 3: Перенесите кэш данных для списка товаров из файлового хранилища в Redis. Установите TTL = 2 часа.

Решение:

php
// В ProductRepository
public function findCachedProducts(): array
{
    $cache = new RedisAdapter(
        RedisAdapter::createConnection('%env(REDIS_URL)%'),
        'products_'
    );

    return $cache->get('featured_products', function (ItemInterface $item) {
        $item->expiresAfter(7200);
        return $this->createQueryBuilder('p')
            ->where('p.isFeatured = true')
            ->getQuery()
            ->getResult();
    });
}

Итоги 14-го урока

Сегодня мы разобрали:

  • Типы кэширования в Symfony.
  • Работу с кэшем Twig-шаблонов.
  • Настройку HTTP-кэширования через заголовки.
  • Интеграцию Redis и Memcached.

Не кэшируйте всё подряд. Анализируйте медленные части приложения (например, через Symfony Profiler) и применяйте кэш точечно.

Хотите освоить Symfony? Перейти к полному курсу по Symfony для начинающих.

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий