# Ядро wordpress, hooks

# Filter document_title_parts

`document_title_parts` — это **низкоуровневый фильтр WordPress**, который управляет **частями заголовка `<title>`**, *до* того как они будут склеены в одну строку.

Разберём подробно.

---

#### 🧠 Как WordPress формирует `<title>`

Современный WordPress (4.4+) делает так:

1. Собирает массив частей заголовка
2. Пропускает его через фильтр `document_title_parts`
3. Склеивает части через разделитель
4. Пропускает итог через `pre_get_document_title`
5. Выводит `<title>`

Схема:

```text
document_title_parts  →  pre_get_document_title  →  <title>

```

---

#### 🔎 Что такое `document_title_parts`

Это фильтр, который возвращает **массив**:

```php
array(
    'title'   => 'Заголовок страницы',
    'page'    => 'Page 2',
    'tagline' => 'Описание сайта',
    'site'    => 'Название сайта',
);

```

WordPress сам решает, какие части включать.

---

#### 📌 Пример стандартного массива

##### Для поста:

```php
array(
    'title' => 'Название поста',
    'site'  => 'Название сайта',
);

```

##### Для главной:

```php
array(
    'site'    => 'Название сайта',
    'tagline' => 'Описание сайта',
);

```

---

#### ✅ Как использовать `document_title_parts`

##### Пример: заменить только title

```php
add_filter('document_title_parts', function ($parts) {

    if (is_singular()) {
        $seo_title = get_field('seo_title');

        if ($seo_title) {
            $parts['title'] = $seo_title;
        }
    }

    return $parts;
});

```

✔ меняется только заголовок  
✔ имя сайта остаётся  
✔ слоган остаётся (если включён)

---

#### 🔥 Пример: убрать название сайта

```php
add_filter('document_title_parts', function ($parts) {

    unset($parts['site']);

    return $parts;
});

```

Результат:

```html
<title>Заголовок страницы</title>

```

---

#### 🔥 Пример: кастомный порядок

```php
add_filter('document_title_parts', function ($parts) {

    return array(
        'title' => $parts['title'],
        'site'  => $parts['site'],
    );
});

```

---

#### 🆚 document\_title\_parts vs pre\_get\_document\_title

<table id="bkmrk-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80-%D0%A7%D1%82%D0%BE-%D0%BC%D0%B5%D0%BD%D1%8F%D0%B5%D1%82-%D0%9A%D0%BE"><thead><tr><th>Фильтр</th><th>Что меняет</th><th>Когда использовать</th></tr></thead><tbody><tr><td>`document_title_parts`</td><td>части title</td><td>тонкая настройка</td></tr><tr><td>`pre_get_document_title`</td><td>весь title строкой</td><td>полный контроль</td></tr></tbody></table>

👉 **SEO-плагины используют оба.**

---

#### ⚠ Важно помнить

- `document_title_parts` не выводит `<title>`
- он **НЕ строка**, а массив
- `pre_get_document_title` срабатывает позже

---

#### ✅ Рекомендация для SEO

Для кастомного SEO:

- использовать `document_title_parts` → управлять логикой
- использовать `pre_get_document_title` → крайний приоритет

---

#### 🎯 Итог

✔ `document_title_parts` — управляет частями `<title>`  
✔ Работает до склейки  
✔ Позволяет убрать site/tagline/page  
✔ Идеален для SEO без дублирования

# Action wp_head

### 🔹 Добавление SEO Description

#### ✅ Правильный способ в WordPress (через фильтр)

❌ **НЕ** делаем `echo <meta>` в шаблоне  
✔ используем `wp_head`

---

#### 🧩 Универсальный код (посты, страницы, таксономии)

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk--1"><div class="sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs">  
</div></div></div><div class="overflow-y-auto p-4" dir="ltr">  
</div></div>```php
add_action('wp_head', function () {

    $description = '';

    // Посты и страницы
    if (is_singular()) {
        $description = get_field('seo_desc');
    }
    // Категории и метки
    elseif (is_category() || is_tag()) {
        $term = get_queried_object();
        $description = get_field('seo_desc', $term);
    }

    // Фолбэк — excerpt или описание термина
    if (!$description) {
        if (is_singular()) {
            $description = get_the_excerpt();
        }
        elseif (is_category() || is_tag()) {
            $term = get_queried_object();
            $description = term_description($term);
        }
    }

    if (!$description) {
        return;
    }

    // Чистим HTML и лишние пробелы
    $description = wp_strip_all_tags($description);
    $description = trim(preg_replace('/\s+/', ' ', $description));

    echo '<meta name="description" content="' . esc_attr($description) . '">' . "\n";

});

```

<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary" id="bkmrk--2"><div class="overflow-y-auto p-4" dir="ltr">  
</div></div>---

#### 🔎 Почему именно так

✔ Работает для:

- постов
- страниц
- категорий
- меток

✔ Без дублей  
✔ Без XSS  
✔ Совместимо с темами и плагинами

---

#### 🎯 Лучшие практики SEO Description

- 120–160 символов (RU)
- 70–155 (моб)
- уникальный
- без кавычек
- **не повторять title**
- коммерческий посыл (если уместно)

---

#### 🧠 Частые ошибки

❌ несколько `<meta description>`  
❌ HTML внутри  
❌ слишком длинный текст  
❌ одинаковый на всех страницах

---

#### 🚀 Если нужно улучшить

Можно добавить:

- `seo_desc_en` для EN версии
- авто-обрезку до 160 символов
- приоритет для главной
- проверку `noindex`
- фильтр `wpseo_metadesc` (если используешь Yoast)

---

#### ✅ Итог

✔ Meta description добавляется через `wp_head`  
✔ Используется ACF  
✔ Есть fallback  
✔ Без конфликтов