# Кэш в проекте

## Что используется

Один общий компонент `cache` класса `yii\caching\FileCache`. Файлы кладутся в `runtime/cache/` и раскладываются по двухсимвольным подпапкам (`runtime/cache/0a`, `runtime/cache/0b`, ...).

Конфиг продублирован в двух местах — это важно, потому что часть ключей пишется из CLI (`commands/CacheManagerController.php`), а читается из веба и наоборот:

- `config/web.php:84` — `'class' => 'yii\caching\FileCache'`
- `config/console.php:25` — то же самое

Никаких отдельных компонентов (`cache2`, `frontendCache` и т.п.) нет — везде один `Yii::$app->cache`.

В `composer.json` пакета `yiisoft/yii2-redis` нет, в `docker-compose.yml` redis-сервиса нет — то есть кэш чисто файловый.

## Где используется

### Меню сайта — `components/MenuTab.php:68`

Главный dropdown-меню по домену.

- Ключ: `['MenuTab', $domain->id, $type]` (`$type === 'mainDropdown'`).
- TTL: `86400` (24 часа).
- Зависимость: `DbDependency` по `SELECT dateUpdated FROM menu WHERE type = :type` — кэш сам инвалидируется, как только меняется `menu.dateUpdated`.
- Получение: `Yii::$app->cache->getOrSet(...)`, внутри `(new Menu)->getMenuByType($type)`.

Единственное место в проекте, где правильно используется TTL + зависимость.

### URL-маппинг — `components/UrlRule.php:34-100`

Кэш маппинга `urlStorage` (`category` ↔ `url`, `product` ↔ `url`, `tag` ↔ `url`) для доменов с `useUrlStorage = 1` и `redirectUrlStorage = 0`. Используется в `createUrl()` при построении ссылок в шаблонах.

- Ключи:
  - `url-storage-{domainId}-{params.url}` для маршрутов `category/detail` и `product/index`.
- TTL: **нет** (живёт до ручного удаления).
- Особенность: «отрицательный» кэш — если в `urlStorage` ничего не нашлось, в кэш кладётся строка `"empty"`, чтобы при повторном вызове не делать SQL.
- Дополнительно есть защита: если в кэше лежит url с `https` (битые данные с прошлых версий), он игнорируется и перезаписывается.
- Инвалидация: вручную через `php yii cache-manager/clear-domain {domainId}` (см. ниже).

### URL товара по домену — `components/Utility.php::getProductUrl` (строки 56-90)

Используется в фидах/выгрузках, чтобы быстро получить URL товара для конкретного домена.

- Ключ: `productUrl-{domainId}-{domainId}-{productId}` (двойной `domainId` в ключе — историческая опечатка, не баг логики).
- TTL: **нет**.
- Логика: если у домена `useUrlStorage && redirectUrlStorage` — кэш не используется, URL строится по шаблону. Если `!useUrlStorage` — тоже без кэша. Кэшируется только случай `useUrlStorage && !redirectUrlStorage`.
- Аналогично UrlRule: значения с `https` в кэше считаются битыми.

### Снапшот товара для XML/фидов — `components/Utility.php::getProductXml` + `getProductParam` (строки 92-227)

Это «тяжёлый» кэш товара — массив с полями `id`, `type`, `title`, `price`, `subproductCount`, `url`, `category`, `pictures`, `factory`, `params`. Используется для генерации Yandex/Google-фидов и в фильтрах.

- Ключ: `product-{productId}`.
- TTL: **нет**.
- Заполнение: только через CLI-команду `php yii cache-manager/product` (см. `commands/CacheManagerController.php::actionProduct`). На лету в вебе ключ не пишется — `getProductXml` просто `return false`, если кэша нет.
- Чтение: `getProductXml`, `getProductParam` и `actionCheckProduct` (отладочная команда).

То есть для XML-фидов считается, что кэш заранее «прогрет» через крон/руками.

### Min/max диапазоны фильтров — `controllers/CategoryController.php:442-468`

Числовые фильтры (`int`-параметры) на странице категории. Считаются через `MIN/MAX` по `paramValueInt` для всех товаров категории — дорогой запрос, поэтому кэшируется.

- Ключ: `paramValueInt-minmax-{domainId}-{categoryId}-{pageType}` (`pageType` это `category` или `tag`).
- TTL: **нет**.
- Особенность: проверка идёт через `=== false` (а не `!`), то есть пустой массив `[]` считается валидным закэшированным значением и второй раз запрос не выполняется.
- Инвалидация: только ручным `cache:flush`. Это значит: после массовых правок цен/параметров фильтр может показать устаревшие границы.

## CLI: `commands/CacheManagerController.php`

Команды для управления тяжёлыми ключами:

- `php yii cache-manager/product` — `actionProduct`. Прогревает `product-{id}` для **всех** активных и неактивных товаров (`Product::find()->all()`). Внутри собирает категорию, тип, цену, картинки (для `simpleOption` — `ppvPresetImages`, иначе — `images[].publicImage`, не более 10), фабрику и `params` (объединение `int` и `var` фильтров). Для товаров без категории кэш удаляется. Время выполнения выводится в конце.
- `php yii cache-manager/clear-domain {domainId}` — `actionClearDomain`. Идёт по всем активным товарам и удаляет ключи `url-storage-{domainId}-{product.url}`. Используется при пересборке `urlStorage` для конкретного домена.
- `php yii cache-manager/check-product` — `actionCheckProduct`. Отладочная: выводит `print_r` для первых 10 товарных ключей.

Регулярного крона на `cache-manager/product` в репозитории нет — прогрев инициируется руками или внешним планировщиком.

## Сводка по ключам

| Ключ | TTL | Где пишется | Где читается | Инвалидация |
|---|---|---|---|---|
| `['MenuTab', domainId, 'mainDropdown']` | 24 ч | `MenuTab::run()` | `MenuTab::run()` | автомат: `DbDependency` по `menu.dateUpdated` |
| `url-storage-{domainId}-{url}` | ∞ | `UrlRule::createUrl` | `UrlRule::createUrl` | вручную: `cache-manager/clear-domain` |
| `productUrl-{domainId}-{domainId}-{productId}` | ∞ | `Utility::getProductUrl` | `Utility::getProductUrl` | только `cache:flush` |
| `product-{productId}` | ∞ | `cache-manager/product` (CLI) | `Utility::getProductXml`, `Utility::getProductParam` | перезапись через `cache-manager/product` |
| `paramValueInt-minmax-{domainId}-{categoryId}-{pageType}` | ∞ | `CategoryController::actionDetail` | `CategoryController::actionDetail` | только `cache:flush` |

## Особенности и подводные камни

1. **TTL почти везде отсутствует.** Из 5 групп ключей только меню имеет ограничение и зависимость. Остальные живут до ручного сброса — это устраивает, пока товары/`urlStorage`/фильтры меняются нечасто и есть прогрев из CLI, но при массовых изменениях цен/параметров надо помнить про `php yii cache/flush-all`.

2. **`product-*` пишется только из CLI.** В вебе при холодном кэше `getProductXml` возвращает `false`, то есть товар выпадает из фида. Для нового товара надо запускать `cache-manager/product`.

3. **«Отрицательный» кэш как строка `"empty"`** в `UrlRule` — если решите менять формат, надо обработать оба случая (`null` от Redis и старый `"empty"` от FileCache).

4. **Конфиг кэша дублируется** в `web.php` и `console.php`. При смене драйвера (например, на Redis) править надо оба файла, иначе CLI и веб начнут смотреть в разные хранилища и `cache-manager/product` не будет виден из контроллеров.

5. **Никаких `TagDependency`** в коде нет. Если потребуется групповая инвалидация (например, «сбросить все ключи товара X»), это надо проектировать отдельно.

## Очистка вручную

Стандартные команды Yii2 работают:

```bash
docker exec php74_mebel php yii cache/flush-all   # снести всё
docker exec php74_mebel php yii cache/flush cache # только компонент `cache`
```

Физически — это просто `rm -rf runtime/cache/*`.
