Приоритеты резолвера
Каждое итоговое значение SEO — заголовок, описание, канонический URL, robots, изображения — формируется SEOResolver при объединении шести слоёв. Более высокие слои имеют приоритет, а null никогда не перезаписывает значение нижнего слоя, поэтому каждая страница всегда получает осмысленные значения.
Шесть слоёв
От самого нижнего (присутствует всегда) к самому верхнему (всегда побеждает):
| № | Слой | Источник | Типичное применение |
|---|---|---|---|
| 1 | Конфигурация сайта | config/seo.php (site_name, title_suffix, default_og_image, default_robots, …) | Общие для бренда значения по умолчанию |
| 2 | Глобальные значения по умолчанию в БД | Строки seo_defaults без типа модели | Редактируемые значения для всего сайта без развёртывания |
| 3 | Значения по умолчанию для типа модели | Строки seo_defaults, привязанные к классу модели | «Все товары получают это OG-изображение» |
| 4 | Значения по умолчанию для маршрута | Строки seo_defaults, привязанные к имени маршрута | Статические страницы (home, contact) без модели |
| 5 | Вычисляемые значения | Получаются из собственных атрибутов модели | Резервный заголовок из title, описание из excerpt/body, … |
| 6 | Явные значения | Строка seo_meta модели (saveSEO()) | Значения, заданные редакторами вручную |
use Rankbeam\Seo\Facades\SEO;
$seo = SEO::resolve($post); // model page: layers 1-3 + 5-6
$seo = SEO::forRoute('contact'); // route page: layers 1-2 + 4Результат — неизменяемый объект-значение SEOData, который используют все средства вывода (Blade, массив, Inertia).
Вычисляемые резервные значения (слой 5)
Если явное значение отсутствует, резолвер получает его из модели:
- Заголовок — атрибут
title/nameмодели. - Описание — первый атрибут из
seo.computed.description_fields(цепочка по умолчанию:excerpt,summary,description,intro,lead,teaser,content,body,text,article), содержащий осмысленный текст. HTML удаляется, сущности декодируются, а текст обрезается по границе слова (seo.computed.description_max_length, по умолчанию 160 — без многоточия). - Robots — из метода модели
getSEORobots()или атрибутаis_indexable(см. управление robots и возможностью индексации). - Значения из URL — канонический URL и
og:urlизgetUrlForSEO().
Управление robots и возможностью индексации
Настройка noindex для отдельной модели уже встроена — дополнительные пакеты или специальные столбцы не нужны. Трейт HasSEO не объявляет метод robots (он необязателен), поэтому эту возможность легко не заметить. Однако резолвер уже учитывает три источника, начиная с самого приоритетного:
| Приоритет | Источник | Пример |
|---|---|---|
| 1 | Явное seo_meta.robots | $page->saveSEO(['robots' => 'noindex,follow']) |
| 2 | Метод getSEORobots(): ?string в модели | Вернуть 'noindex, nofollow' или null, чтобы перейти к следующему источнику |
| 3 | Атрибут is_indexable (столбец или аксессор) | Значение, приводимое к false, ⇒ noindex, nofollow; приводимое к true, ⇒ index, follow |
class Page extends Model
{
use HasSEO;
// Option A: let the resolver derive robots from a boolean flag.
// Schema::table('pages', fn ($t) => $t->boolean('is_indexable')->default(true));
// Option B: compute it from your own state.
public function getSEORobots(): ?string
{
return $this->status === 'draft' ? 'noindex, nofollow' : null;
}
}
// Option C: set it explicitly per page (wins over A and B).
$page->saveSEO(['robots' => 'noindex, follow']);Что фактически выводится
Перед попаданием в <head> полученная директива проходит политику вывода: тег <meta name="robots"> выводится только тогда, когда директива отличается от default_robots (по умолчанию index,follow). Поэтому:
- страница, разрешающая индексацию (результат
index, follow), не выводит тег robots — его отсутствие поисковый робот трактует как index,follow; - страница, запрещающая индексацию, выводит
<meta name="robots" content="noindex, nofollow">; - любая отличающаяся директива (
noindex,max-snippet:-1,unavailable_after, …) выводится без изменений, с сохранением введённых пробелов.
Задайте seo.robots.emit_default = true, чтобы тег выводился всегда. Подробности — в политике вывода robots.
Политики после определения итоговых значений
Они применяются независимо от того, какой слой задал значение:
- Суффикс заголовка —
title_suffixдобавляется, если итоговый заголовок ещё не заканчивается им. Если шаблон значения по умолчанию для маршрута уже содержит бренд, завершите шаблон суффиксом, чтобы избежать «Brand — X | Brand». - Удаление параметров запроса из канонического URL — у вычисленных канонических URL (URL модели / текущий URL) удаляется строка запроса, кроме ключей из
canonical.query_whitelist(например,pageдля архивов с пагинацией), которые сохраняются; явно заданные канонические URL сохраняются без изменений. - Абсолютные URL изображений для соцсетей —
og:imageиtwitter:imageвсегда выводятся как абсолютные URL (этого требует спецификация Open Graph), даже если сохранён относительный путь.
Как узнать, какой слой победил
Пакет Filament показывает это для каждого поля (Вручную / Из контента / Значение по умолчанию для типа модели / Глобальное значение по умолчанию / Конфигурация сайта / Получено из URL). В коде SEOWarningEvaluator предоставляет такое же различие между ручным и резервным значением, чтобы вы могли создавать собственные индикаторы в панели администратора.