Ако някога сте поставяли фрагмент от „schema“ в заглавката и сте виждали Google да игнорира вашите богати резултати, проблемът често произтича от две неща: непълен (или невалиден) JSON-LD и инжектиране на грешното място в цикъла. WordPressЩе поправим това правилно, в код, за WordPress 6.9.4 и PHP 8.1+.
Какво ще изградим
Ще имплементирате структуриран слой данни (Schema.org) в JSON-LD инжектиран в <head>, без плъгин „бюрократичен кошмар“, с:
- Статия/Публикация в блога върху статиите (заглавие, изображение, автор, дати, издател).
- Списък с навигационна пътека последователно (полезно дори ако темата ви не показва навигационна пътека).
- организация + Уеб сайт + Действие при търсене (поле за търсене на връзки към сайта).
- Механизъм на дедупликация (избягвайте дублиращи се схеми с Yoast/RankMath/SEOPress).
- Des филтри за персонализиране (лого, социални мрежи, типове съдържание).
Проектиран е за редакционни уебсайтове (блогове, медийни издания, уебсайтове за представяне с блог) и остава съвместим с Divi 5. Elementor и Avada, защото разчитаме на основните куки (не на конструктора).
В крайна сметка ще знаете: къде да инжектирате JSON-LD, как да изградите стабилна схематична графика, как да избегнете дублиране и как да тествате правилно.
Бързо обобщение
- JSON-LD се инжектира чрез
wp_headс MU-плъгин (зарежда се рано, стабилно). - Ние изграждаме @граф Уебсайт + Организация + Навигационен списък + Публикуване в блог.
- Нормализираме URL адреси, дати по ISO 8601, изображения и добавяме... @документ за самоличност стабилен.
- Избягваме конфликти с SEO плъгини, като откриваме техния изход (и деактивираме блокировките си, ако е необходимо).
- Потвърждаваме с Тест за богати резултати et Le Валидатор на маркиране на схеми.
Кога да използвате това решение
- Vous voulez да овладеят вашите структурирани данни (независими от плъгин, който променя логиката).
- Вашата тема/конструктор не създава нищо (или създава непълна схема) и искате чиста основа.
- Имате специфични нужди: гост-автор, множество лога, персонализирани breadcrumbs, CPT.
- Вече имате SEO плъгин, но искате remplacer част от диаграмата му (или я допълнете) с ясна логика.
Според моя опит, това е особено полезно при мигрирани „стари“ Divi/Avada сайтове: SEO оптимизацията е налице, но схемата или липсва, или е дублирана, или е несъвместима между шаблоните.
Кога НЕ трябва да използвате това решение
- Вие сте начинаещ в PHP и нямате тестова среда: забравена скоба може да наруши работата на сайта.
- Вече използвате Yoast/RankMath/SEOPress и сте доволни от богатите резултати: не създавайте втора система.
- Имате разширена схема за стратегия (Продукт, ЧЗВ, Как да, Местен бизнес), задвижвана от бизнес плъгин: по-добре е да разширите този плъгин чрез неговите куки.
- Не можете да контролирате скривалище (Агресивна CDN): рискувате да тествате остарели версии и да „преследвате призрак“.
Преди да започнете (предварителни изисквания)
Технически предпоставки
- WordPress 6.9.4 (април 2026 г.) или по-висока.
- PHP 8.1 + (Препоръчва се 8.2/8.3, ако вашият хостинг доставчик го поддържа).
- FTP/SSH достъп или файлов мениджър на хостинг доставчика.
Архивиране и среда
- Направете резервно копие на файловете и базата данни (или моментна снимка, ако сте на услуга за управляван хостинг).
- Първо тествайте на сайт в предпроизводствена/подготовка. Често съм виждал „прост“ фрагмент да повреди сайт, защото е бил поставен в грешен файл.
Полезни инструменти
- Плъгин за дебъгване: Query Monitor (по избор, но полезен).
- Достъп до Search Console (ако искате да измерите въздействието).
Официални източници (препратки)
- Hook wp_head (Ресурси за разработчици на WordPress)
- wp_json_encode()
- get_the_post_thumbnail_url()
- json_encode (PHP)
- разработка в wordpress (GitHub)
Стъпка 1: Създайте мини MU плъгин за чисто инжектиране на JSON-LD
Повечето от „счупените“ фрагменти от схема, които отстранявам, идват от лошо място: заседнали са header.phpТова може да се случи в конструктор или в плъгин за фрагменти, който се изпълнява твърде късно. MU-плъгинът решава това: той се зарежда автоматично, преди обикновените плъгини.
Къде да кликна / къде да създам файла
- Отворете сайта си чрез FTP/SSH.
- отивам
wp-content/. - Създайте папката
mu-pluginsако не съществува:wp-content/mu-plugins/. - Създайте файла:
wp-content/mu-plugins/bpcab-schema-jsonld.php.
Код (стъпка 1)
Поставете този код такъв, какъвто е. Той все още не извежда никаква схема; той настройва „двигателя“: събиране на данни, защитено JSON кодиране и инжектиране в wp_headи филтри.
<?php
/**
* Plugin Name: BPCAB - Schema JSON-LD (MU)
* Description: Injection contrôlée de données structurées JSON-LD (Schema.org) pour WordPress 6.9.4+.
* Author: Votre nom / agence
* Version: 1.0.0
*
* Emplacement: wp-content/mu-plugins/bpcab-schema-jsonld.php
*/
defined('ABSPATH') || exit;
final class BPCAB_Schema_JSONLD {
/**
* Stocke les nœuds du graphe Schema.
*
* @var array<array>
*/
private array $graph = [];
/**
* Singleton simple.
*/
private static ?self $instance = null;
public static function instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Injection dans le <head> : priorité 20 pour passer après certains plugins/thèmes.
add_action('wp_head', [$this, 'render_jsonld'], 20);
// Point d'extension : on construit le graphe quand WP est prêt (requête principale résolue).
add_action('wp', [$this, 'build_graph'], 20);
}
/**
* Construit le graphe Schema en fonction du contexte (single, page, home, etc.).
*/
public function build_graph(): void {
// Permet de désactiver totalement via filtre (utile si conflit avec un plugin SEO).
$enabled = (bool) apply_filters('bpcab_schema_jsonld_enabled', true);
if (!$enabled) {
return;
}
// Reset à chaque requête.
$this->graph = [];
/**
* On laisse les étapes suivantes remplir $this->graph via des méthodes dédiées.
* Ici, on ne fait rien de plus.
*/
do_action('bpcab_schema_jsonld_build', $this);
}
/**
* Ajoute un nœud au graphe.
*
* @param array $node
*/
public function add_node(array $node): void {
if (empty($node['@type'])) {
return;
}
$this->graph[] = $node;
}
/**
* Rend le JSON-LD dans le head.
*/
public function render_jsonld(): void {
if (empty($this->graph)) {
return;
}
$payload = [
'@context' => 'https://schema.org',
'@graph' => array_values($this->graph),
];
/**
* Filtre final pour ajuster le payload complet.
* Attention : ne mettez pas d'objets non sérialisables.
*/
$payload = apply_filters('bpcab_schema_jsonld_payload', $payload);
// Encodage JSON sûr côté WP (gère mieux certains cas que json_encode direct).
$json = wp_json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if (!$json) {
return;
}
echo "n" . '<script type="application/ld+json" id="bpcab-schema-jsonld">' . "n";
echo $json; // JSON-LD : sortie volontairement non-échappée (c'est du JSON, pas du HTML).
echo "n" . '</script>' . "n";
}
}
BPCAB_Schema_JSONLD::instance();
Очакван резултат
- Няма видими промени отпред (засега).
- Няма PHP грешки.
- Кука
bpcab_schema_jsonld_buildготов за употреба в следващата стъпка.
Стъпка 2: Добавете статия/публикация в блога към статиите (с автор, изображение, дати)
Ще добавим възел Публикуване в блог на отделни публикации. Ключовата разлика: @id конюшниISO дати и правилно оразмерено изображение. Често съм виждал „валидни“, но безполезни JSON-LD файлове, защото изображението е празно или авторът сочи към неканоничен URL адрес.
Добавете този код към същия MU-plugin файл
Поставете следните методи в класната стая BPCAB_Schema_JSONLD (След render_jsonld() например), след което добавете действието, което ги извиква.
/**
* Enregistre les builders par défaut.
*/
private function register_builders(): void {
add_action('bpcab_schema_jsonld_build', [$this, 'add_blogposting_for_single'], 20);
}
/**
* Petit helper : URL canonique de la requête.
*/
private function get_canonical_url(): string {
// wp_get_canonical_url() existe sur WP modernes, mais on garde une approche simple et fiable.
if (is_singular()) {
$url = get_permalink(get_queried_object_id());
return is_string($url) ? $url : home_url('/');
}
// Pour home/blog page/archives, on évite les constructions trop “magiques”.
return home_url(add_query_arg([]));
}
/**
* Ajoute BlogPosting sur les articles.
*/
public function add_blogposting_for_single(self $schema): void {
if (!is_singular('post')) {
return;
}
$post_id = get_queried_object_id();
$post = get_post($post_id);
if (!$post instanceof WP_Post) {
return;
}
$canonical = get_permalink($post_id);
if (!is_string($canonical) || '' === $canonical) {
return;
}
// Image : privilégiez une taille large (évite les warnings “image too small”).
$image_url = get_the_post_thumbnail_url($post_id, 'full');
if (!is_string($image_url)) {
$image_url = '';
}
$author_id = (int) $post->post_author;
$author_url = get_author_posts_url($author_id);
$site_name = get_bloginfo('name');
$site_url = home_url('/');
$logo_id = (int) get_theme_mod('custom_logo');
$logo_url = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : '';
// @id stables : on s'appuie sur l'URL canonique + fragment.
$webpage_id = $canonical . '#webpage';
$blogposting_id = $canonical . '#blogposting';
$author_id_uri = is_string($author_url) ? $author_url . '#author' : $site_url . '#author';
$publisher_id = $site_url . '#organization';
$node = [
'@type' => 'BlogPosting',
'@id' => $blogposting_id,
'mainEntityOfPage' => [
'@type' => 'WebPage',
'@id' => $webpage_id,
],
'headline' => get_the_title($post_id),
'description' => wp_strip_all_tags(get_the_excerpt($post_id)),
'datePublished' => get_the_date(DATE_W3C, $post_id),
'dateModified' => get_the_modified_date(DATE_W3C, $post_id),
'author' => [
'@type' => 'Person',
'@id' => $author_id_uri,
'name' => get_the_author_meta('display_name', $author_id),
'url' => is_string($author_url) ? $author_url : '',
],
'publisher' => [
'@type' => 'Organization',
'@id' => $publisher_id,
'name' => $site_name,
],
'isPartOf' => [
'@type' => 'Blog',
'@id' => $site_url . '#blog',
'name' => $site_name,
'url' => $site_url,
],
'url' => $canonical,
];
if ('' !== $image_url) {
$node['image'] = [
'@type' => 'ImageObject',
'url' => $image_url,
];
}
if (is_string($logo_url) && '' !== $logo_url) {
$node['publisher']['logo'] = [
'@type' => 'ImageObject',
'url' => $logo_url,
];
}
/**
* Filtre par post : permet d'ajuster le node BlogPosting (ajouter keywords, articleSection, etc.).
*/
$node = apply_filters('bpcab_schema_jsonld_blogposting_node', $node, $post_id);
$schema->add_node($node);
}
Добавете обаждането към регистъра на строителите
В строителя __construct()Добавете ред:
private function __construct() {
add_action('wp_head', [$this, 'render_jsonld'], 20);
add_action('wp', [$this, 'build_graph'], 20);
// Enregistre nos builders.
$this->register_builders();
}
Очакван резултат
- В дадена статия ще видите
<script type="application/ld+json" id="bpcab-schema-jsonld">в изходния код. - JSON съдържа
@graphс Objet"@type":"BlogPosting".
Стъпка 3: Добавяне на breadcrumbs в JSON-LD (BreadcrumbList)
Навигационните хлебни трохи са добър „структурен сигнал“. Капанът: генериране на непоследователни URL адреси (http/https, наклонена черта в края) или включване на таксономии, които се променят в зависимост от статията. Предпочитам просто правило: Начало → Блог (ако е страницата със статии) → Главна категория (по избор) → Статия.
Код (навигационна пътека) за добавяне към класа
/**
* Ajoute BreadcrumbList.
*/
public function add_breadcrumbs(self $schema): void {
// On ne met pas de breadcrumbs sur la page d'accueil “front page”.
if (is_front_page()) {
return;
}
$items = [];
$pos = 1;
$home_url = home_url('/');
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => 'Accueil',
'item' => $home_url,
];
// Si une page “Articles” est définie (Réglages > Lecture).
$page_for_posts = (int) get_option('page_for_posts');
if ($page_for_posts && !is_home()) {
$blog_url = get_permalink($page_for_posts);
if (is_string($blog_url) && '' !== $blog_url) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => get_the_title($page_for_posts),
'item' => $blog_url,
];
}
}
if (is_singular('post')) {
// Catégorie “principale” : on prend la première, stable, sans prétendre faire du SEO.
$cats = get_the_category(get_queried_object_id());
if (!empty($cats) && $cats[0] instanceof WP_Term) {
$cat = $cats[0];
$cat_url = get_term_link($cat);
if (is_string($cat_url)) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => $cat->name,
'item' => $cat_url,
];
}
}
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => get_the_title(get_queried_object_id()),
'item' => get_permalink(get_queried_object_id()),
];
} elseif (is_page()) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => get_the_title(get_queried_object_id()),
'item' => get_permalink(get_queried_object_id()),
];
} elseif (is_category()) {
$term = get_queried_object();
if ($term instanceof WP_Term) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => single_cat_title('', false),
'item' => get_term_link($term),
];
}
} elseif (is_tag()) {
$term = get_queried_object();
if ($term instanceof WP_Term) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => single_tag_title('', false),
'item' => get_term_link($term),
];
}
} elseif (is_search()) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => 'Recherche',
'item' => home_url('/?s=' . rawurlencode(get_search_query(false))),
];
}
$items = apply_filters('bpcab_schema_jsonld_breadcrumb_items', $items);
if (count($items) < 2) {
return;
}
$node = [
'@type' => 'BreadcrumbList',
'@id' => home_url('/') . '#breadcrumbs',
'itemListElement' => array_values($items),
];
$schema->add_node($node);
}
Активирайте този конструктор
категория register_builders(), добавете:
add_action('bpcab_schema_jsonld_build', [$this, 'add_breadcrumbs'], 30);
Очакван резултат
- В статия JSON-LD съдържа обект
"@type":"BreadcrumbList". - Списъкът има позиции 1..N без пропуски.
Стъпка 4: Добавете Организация + Уебсайт (и SearchAction) на ниво сайт
без организация et Уеб сайтВъзлите ви „Статия“ често се носят без ясна връзка. Google се справя, но губите последователност. Ще добавим и Действие при търсене (полезно, ако вътрешното ви търсене е достъпно чрез ?s=).
Код за добавяне към класа
/**
* Ajoute Organization + WebSite.
*/
public function add_site_entities(self $schema): void {
$site_url = home_url('/');
$site_name = get_bloginfo('name');
$org_type = (string) apply_filters('bpcab_schema_jsonld_org_type', 'Organization');
$logo_id = (int) get_theme_mod('custom_logo');
$logo_url = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : '';
// Réseaux sociaux : filtre pour éviter de coder en dur.
$same_as = (array) apply_filters('bpcab_schema_jsonld_same_as', []);
$org = [
'@type' => $org_type,
'@id' => $site_url . '#organization',
'name' => $site_name,
'url' => $site_url,
];
if (is_string($logo_url) && '' !== $logo_url) {
$org['logo'] = [
'@type' => 'ImageObject',
'url' => $logo_url,
];
}
// sameAs doit être un tableau d'URLs publiques (Facebook, LinkedIn, etc.).
$same_as = array_values(array_filter(array_map('esc_url_raw', $same_as)));
if (!empty($same_as)) {
$org['sameAs'] = $same_as;
}
$website = [
'@type' => 'WebSite',
'@id' => $site_url . '#website',
'url' => $site_url,
'name' => $site_name,
'publisher' => [
'@id' => $site_url . '#organization',
],
'inLanguage' => get_bloginfo('language'),
'potentialAction' => [
'@type' => 'SearchAction',
'target' => [
'@type' => 'EntryPoint',
'urlTemplate' => $site_url . '?s={search_term_string}',
],
'query-input' => 'required name=search_term_string',
],
];
$schema->add_node(apply_filters('bpcab_schema_jsonld_org_node', $org));
$schema->add_node(apply_filters('bpcab_schema_jsonld_website_node', $website));
}
Активирайте този конструктор
категория register_builders() :
add_action('bpcab_schema_jsonld_build', [$this, 'add_site_entities'], 10);
Очакван резултат
- На всички страници (освен ако не филтрирате) имате Организация + Уебсайт в
@graph. - Le
publisherвашите статии сочат към#organization.
Стъпка 5: Управление на конкретни страници (начална страница, страница, архив, WooCommerce, ако има такава)
Ако инжектирате една и съща схема навсякъде, ще създадете шум. Ще го коригираме: никакви breadcrumbs на началната страница, никакви BlogPosting на която и да е страница и ще добавим прост WebPage възел за страниците.
Код: WebPage за страници (и резервен вариант)
/**
* Ajoute un nœud WebPage sur les pages et en complément sur les singulars.
*/
public function add_webpage_node(self $schema): void {
if (is_front_page()) {
// Sur l'accueil, on peut aussi sortir WebPage, mais je préfère éviter les graph trop bavards.
return;
}
if (!is_singular()) {
return;
}
$post_id = get_queried_object_id();
$url = get_permalink($post_id);
if (!is_string($url) || '' === $url) {
return;
}
$type = is_page() ? 'WebPage' : 'WebPage';
$node = [
'@type' => $type,
'@id' => $url . '#webpage',
'url' => $url,
'name' => get_the_title($post_id),
'inLanguage' => get_bloginfo('language'),
'isPartOf' => [
'@id' => home_url('/') . '#website',
],
];
$schema->add_node(apply_filters('bpcab_schema_jsonld_webpage_node', $node, $post_id));
}
Активирайте този конструктор
add_action('bpcab_schema_jsonld_build', [$this, 'add_webpage_node'], 15);
WooCommerce (по избор)
Ако WooCommerce е активен, той често вече се отклонява от продуктовата схема чрез SEO разширения. Не го дублирайте. Ако искате да откриете WooCommerce и да деактивирате определени отклонения, можете да направите следното:
public function maybe_disable_on_products(): void {
// Exemple : désactiver notre schema sur les pages produit si un plugin SEO gère déjà Product.
if (function_exists('is_product') && is_product()) {
add_filter('bpcab_schema_jsonld_enabled', '__return_false', 1);
}
}
Ако използвате тази предпазна мярка, обадете се ѝ рано (в __construct() от add_action('wp', ... , 1)).
Пълният резултат
Ето една асемблирана (копируема) версия на MU-плъгина, с активирани конструктори. Можете да започнете отначало, като замените файла wp-content/mu-plugins/bpcab-schema-jsonld.php чрез това.
<?php
/**
* Plugin Name: BPCAB - Schema JSON-LD (MU)
* Description: Injection contrôlée de données structurées JSON-LD (Schema.org) pour WordPress 6.9.4+.
* Author: Votre nom / agence
* Version: 1.0.0
*/
defined('ABSPATH') || exit;
final class BPCAB_Schema_JSONLD {
private array $graph = [];
private static ?self $instance = null;
public static function instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action('wp_head', [$this, 'render_jsonld'], 20);
add_action('wp', [$this, 'build_graph'], 20);
$this->register_builders();
}
private function register_builders(): void {
add_action('bpcab_schema_jsonld_build', [$this, 'add_site_entities'], 10);
add_action('bpcab_schema_jsonld_build', [$this, 'add_webpage_node'], 15);
add_action('bpcab_schema_jsonld_build', [$this, 'add_blogposting_for_single'], 20);
add_action('bpcab_schema_jsonld_build', [$this, 'add_breadcrumbs'], 30);
}
public function build_graph(): void {
$enabled = (bool) apply_filters('bpcab_schema_jsonld_enabled', true);
if (!$enabled) {
return;
}
$this->graph = [];
do_action('bpcab_schema_jsonld_build', $this);
}
public function add_node(array $node): void {
if (empty($node['@type'])) {
return;
}
$this->graph[] = $node;
}
public function render_jsonld(): void {
if (empty($this->graph)) {
return;
}
$payload = [
'@context' => 'https://schema.org',
'@graph' => array_values($this->graph),
];
$payload = apply_filters('bpcab_schema_jsonld_payload', $payload);
$json = wp_json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if (!$json) {
return;
}
echo "n" . '<script type="application/ld+json" id="bpcab-schema-jsonld">' . "n";
echo $json;
echo "n" . '</script>' . "n";
}
public function add_site_entities(self $schema): void {
$site_url = home_url('/');
$site_name = get_bloginfo('name');
$org_type = (string) apply_filters('bpcab_schema_jsonld_org_type', 'Organization');
$logo_id = (int) get_theme_mod('custom_logo');
$logo_url = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : '';
$same_as = (array) apply_filters('bpcab_schema_jsonld_same_as', []);
$org = [
'@type' => $org_type,
'@id' => $site_url . '#organization',
'name' => $site_name,
'url' => $site_url,
];
if (is_string($logo_url) && '' !== $logo_url) {
$org['logo'] = [
'@type' => 'ImageObject',
'url' => $logo_url,
];
}
$same_as = array_values(array_filter(array_map('esc_url_raw', $same_as)));
if (!empty($same_as)) {
$org['sameAs'] = $same_as;
}
$website = [
'@type' => 'WebSite',
'@id' => $site_url . '#website',
'url' => $site_url,
'name' => $site_name,
'publisher' => [
'@id' => $site_url . '#organization',
],
'inLanguage' => get_bloginfo('language'),
'potentialAction' => [
'@type' => 'SearchAction',
'target' => [
'@type' => 'EntryPoint',
'urlTemplate' => $site_url . '?s={search_term_string}',
],
'query-input' => 'required name=search_term_string',
],
];
$schema->add_node(apply_filters('bpcab_schema_jsonld_org_node', $org));
$schema->add_node(apply_filters('bpcab_schema_jsonld_website_node', $website));
}
public function add_webpage_node(self $schema): void {
if (is_front_page()) {
return;
}
if (!is_singular()) {
return;
}
$post_id = get_queried_object_id();
$url = get_permalink($post_id);
if (!is_string($url) || '' === $url) {
return;
}
$node = [
'@type' => 'WebPage',
'@id' => $url . '#webpage',
'url' => $url,
'name' => get_the_title($post_id),
'inLanguage' => get_bloginfo('language'),
'isPartOf' => [
'@id' => home_url('/') . '#website',
],
];
$schema->add_node(apply_filters('bpcab_schema_jsonld_webpage_node', $node, $post_id));
}
public function add_blogposting_for_single(self $schema): void {
if (!is_singular('post')) {
return;
}
$post_id = get_queried_object_id();
$post = get_post($post_id);
if (!$post instanceof WP_Post) {
return;
}
$canonical = get_permalink($post_id);
if (!is_string($canonical) || '' === $canonical) {
return;
}
$image_url = get_the_post_thumbnail_url($post_id, 'full');
if (!is_string($image_url)) {
$image_url = '';
}
$author_id = (int) $post->post_author;
$author_url = get_author_posts_url($author_id);
$site_name = get_bloginfo('name');
$site_url = home_url('/');
$logo_id = (int) get_theme_mod('custom_logo');
$logo_url = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : '';
$webpage_id = $canonical . '#webpage';
$blogposting_id = $canonical . '#blogposting';
$author_id_uri = is_string($author_url) ? $author_url . '#author' : $site_url . '#author';
$publisher_id = $site_url . '#organization';
$node = [
'@type' => 'BlogPosting',
'@id' => $blogposting_id,
'mainEntityOfPage' => [
'@type' => 'WebPage',
'@id' => $webpage_id,
],
'headline' => get_the_title($post_id),
'description' => wp_strip_all_tags(get_the_excerpt($post_id)),
'datePublished' => get_the_date(DATE_W3C, $post_id),
'dateModified' => get_the_modified_date(DATE_W3C, $post_id),
'author' => [
'@type' => 'Person',
'@id' => $author_id_uri,
'name' => get_the_author_meta('display_name', $author_id),
'url' => is_string($author_url) ? $author_url : '',
],
'publisher' => [
'@type' => 'Organization',
'@id' => $publisher_id,
'name' => $site_name,
],
'isPartOf' => [
'@type' => 'Blog',
'@id' => $site_url . '#blog',
'name' => $site_name,
'url' => $site_url,
],
'url' => $canonical,
];
if ('' !== $image_url) {
$node['image'] = [
'@type' => 'ImageObject',
'url' => $image_url,
];
}
if (is_string($logo_url) && '' !== $logo_url) {
$node['publisher']['logo'] = [
'@type' => 'ImageObject',
'url' => $logo_url,
];
}
$node = apply_filters('bpcab_schema_jsonld_blogposting_node', $node, $post_id);
$schema->add_node($node);
}
public function add_breadcrumbs(self $schema): void {
if (is_front_page()) {
return;
}
$items = [];
$pos = 1;
$home_url = home_url('/');
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => 'Accueil',
'item' => $home_url,
];
$page_for_posts = (int) get_option('page_for_posts');
if ($page_for_posts && !is_home()) {
$blog_url = get_permalink($page_for_posts);
if (is_string($blog_url) && '' !== $blog_url) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => get_the_title($page_for_posts),
'item' => $blog_url,
];
}
}
if (is_singular('post')) {
$cats = get_the_category(get_queried_object_id());
if (!empty($cats) && $cats[0] instanceof WP_Term) {
$cat = $cats[0];
$cat_url = get_term_link($cat);
if (is_string($cat_url)) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => $cat->name,
'item' => $cat_url,
];
}
}
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => get_the_title(get_queried_object_id()),
'item' => get_permalink(get_queried_object_id()),
];
} elseif (is_page()) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => get_the_title(get_queried_object_id()),
'item' => get_permalink(get_queried_object_id()),
];
} elseif (is_category()) {
$term = get_queried_object();
if ($term instanceof WP_Term) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => single_cat_title('', false),
'item' => get_term_link($term),
];
}
} elseif (is_tag()) {
$term = get_queried_object();
if ($term instanceof WP_Term) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => single_tag_title('', false),
'item' => get_term_link($term),
];
}
} elseif (is_search()) {
$items[] = [
'@type' => 'ListItem',
'position' => $pos++,
'name' => 'Recherche',
'item' => home_url('/?s=' . rawurlencode(get_search_query(false))),
];
}
$items = apply_filters('bpcab_schema_jsonld_breadcrumb_items', $items);
if (count($items) < 2) {
return;
}
$node = [
'@type' => 'BreadcrumbList',
'@id' => home_url('/') . '#breadcrumbs',
'itemListElement' => array_values($items),
];
$schema->add_node($node);
}
}
BPCAB_Schema_JSONLD::instance();
Бързо персонализиране (примери)
Добавете тези филтри в класически плъгин или в дъщерната си тема (не в MU-плъгина, ако искате да можете да го актуализирате лесно).
<?php
// Exemple : ajouter vos réseaux sociaux à sameAs.
add_filter('bpcab_schema_jsonld_same_as', function(array $same_as): array {
$same_as[] = 'https://www.linkedin.com/company/votre-marque/';
$same_as[] = 'https://www.youtube.com/@votre-chaine';
return $same_as;
});
// Exemple : changer le type d'organisation.
add_filter('bpcab_schema_jsonld_org_type', function(string $type): string {
return 'Organization'; // Ou 'NewsMediaOrganization' si pertinent.
});
Адаптиране за Divi 5 / Elementor / Avada
Добра новина: докато инжектираме чрез wp_headОбикновено работи „без адаптация“. Строителите имат значително влияние. съдържание (HTML кодът), а не WP заявката.
Диви 5
- Divi може да добавя schema markup чрез модули/SEO инструменти на трети страни. Проверете за дубликати.
- Ако използвате Divi модул, за да покажете заглавието/откъса по различен начин, това не променя нищо: ние четем данните от WordPress (заглавие, откъс, изображение).
Съвет за Divi, който използвам: ако изображението, което ви е представено, често липсва (Divi вмъква изображения в съдържанието), добавете изображение с помощта на филтър. bpcab_schema_jsonld_blogposting_node чрез търсене на първото изображение на съдържанието (методът трябва да се прилага с повишено внимание за по-добра производителност).
Elementor
- Elementor може да обработва шаблони за една публикация, но
is_singular('post')остава правилен. - В някои сайтове съм виждал празни откъси, защото са „визуални“. В този случай, заменете
descriptionчрез извадка, генерирана от съдържанието (с ограничена дължина) чрез филтър.
Avada
- Avada/Fusion Builder понякога има SEO/схема опции. Деактивирайте ги, ако запазите този MU-плъгин, в противен случай ще имате два конкуриращи се JSON-LD.
- Avada често показва логото чрез опциите си за тема. Ако
custom_logoе празно, можете да инжектирате URL адреса на вашето лого чрезbpcab_schema_jsonld_org_node.
Последна проверка
1) Проверете изходния код
- Отворете статия в режим на частно сърфиране.
- Прегледайте изходния код (не инспектора, а „view-source“).
- Търсене
bpcab-schema-jsonld.
Трябва да видите валиден JSON файл, с @context et @graph.
2) Валидиране на схемата
- Тествайте с Тест за разширени резултати (Google).
- Валидирайте съответствието на общата схема с Валидатор на маркиране на схеми.
3) Проверете за дубликати
Ако видите множество JSON-LD блокове, това не е непременно „забранено“, но бързо става непоследователно. Потърсете:
- plusieurs
BlogPostingза същия URL адрес, - plusieurs
Organizationс различни имена, - на
@idкоито не сочат към вашия каноничен домейн.
Ако резултатът не е такъв, какъвто се очаква
| симптом | Вероятна причина | проверка | Решение |
|---|---|---|---|
| Нищо не се появява в главата | MU-плъгин файлът е на грешното място | Проверка wp-content/mu-plugins/ и името на файла |
Поставете файла директно в mu-plugins (не е в подпапка) |
| Грешка 500 след добавяне на код | PHP синтаксис (точка и запетая, къдрава скоба) | Проверете PHP лог файловете на хостинг доставчика | Връщане към предишното състояние, коригиране на маркирания ред. |
| Тестът на Google не открива актуализацията | Кеш (плъгин, сървър, CDN) | Сравнете „view-source“ с това, което Google извлича | Изчистете кеша на плъгина + кеша на сървъра + CDN, след което тествайте в режим „инкогнито“. |
| Предупреждения: „дублирана“ или непоследователна схема | SEO плъгинът също така инжектира JSON-LD | търсене application/ld+json в източника |
Деактивирайте схемата на SEO плъгина или деактивирайте този MU-плъгин чрез филтър |
| Липсва изображение в BlogPubling | Няма представено изображение | Проверете за наличие на представено изображение | Добавете препоръчително изображение или персонализирайте чрез филтър |
Контролен списък за бързи отстраняване на неизправности
- Поставихте кода правилно. в класната стая (и не след края на работното време)
})? - Имате ли твърде стара версия на PHP (поне 8.1)?
- Изчистихте ли кеша на браузъра си (или тествахте ли в частен режим)?
- Не си поставил стар фрагмент, използвайки остарели функции, нали?
Често срещани капани и грешки
| Грешка | Причина | Решение |
|---|---|---|
Поставете JSON-LD скрипта в header.php |
Темата/конструкторът може да го дублира, а актуализацията го презаписва. | Използвайте MU-плъгин или специален плъгин |
употреба json_encode() без опции |
Непоследователно екраниране, Unicode символи, наклонени черти | употреба wp_json_encode() (WP) с опции |
Неподходяща кука (напр. init) |
Контекстът на заявката не е разрешен | Постройте графиката върху wp, отидете на wp_head |
| Дублирана организация чрез SEO плъгин | Два източника на истината | Деактивирайте един от двата изхода (SEO плъгин или филтър) bpcab_schema_jsonld_enabled) |
| Дати, които не са по ISO | Използвайте локализиран формат | употреба DATE_W3C (ISO 8601) |
| Фрагмент, повреден от плъгин за фрагменти | Ред на зареждане / тихи грешки | Предпочитам MU-плъгин за основата |
Вариант / алтернатива
Алтернатива без код (SEO плъгин)
Ако вашите нужди са „стандартни“, често е достатъчен скорошен SEO плъгин. Трите, които виждам най-често в продукцията, са:
- Yoast SEO (доста изчерпателна схема, но понякога многословна)
- Ранг математика (много опции, пазете се от дубликати)
- SEOPress (добър компромис, по-лек в зависимост от конфигурацията)
Ако ще използвате плъгин, създайте правило: един схематичен генераторСмесването на плъгини и фрагменти почти винаги води до дублиране.
По-усъвършенствана алтернатива: Схема на тип съдържание (CPT)
Ако имате CPT-и (напр. „Подкаст“, „Рецепта“, „Събитие“), запазете този MU-плъгин като основа и добавете конструктори на условия (напр. is_singular('event')) със специфични възли на схемата. Не претоварвайте BlogPosting за всичко.
Съвети за безопасност, производителност и поддръжка
- Сигурност Никога не включвайте нефилтрирани потребителски данни (ACF полета, редактируеми от роли на ниско ниво, коментари и др.) в JSON-LD. JSON-LD е текст в секцията head: лошо управляваното инжектиране може да се превърне в XSS уязвимост, ако конкатенирате HTML. Тук разчитаме на функциите на WordPress и...
wp_json_encode(). - Изпълнение Избягвайте използването на тежки заявки (напр. извличане на 50 термина) за навигационни пътеки. Поддържайте ги прости. Заглавната секция се визуализира на всяка страница.
- Кеш Ако имате HTML кеш, всяка промяна на схемата изисква изчистване. Често съм губил време за „бъг“, който просто е бил Cloudflare, обслужващ по-стара версия.
- поддръжка Версия на този файл (Git). Неверсиран MU-плъгин бързо се превръща в „нещото, което вече не смееш да докоснеш“.
Деактивирайте бързо в случай на конфликт
Добавете това (плъгин или дъщерна тема), за да изрежете незабавно схемата:
<?php
add_filter('bpcab_schema_jsonld_enabled', '__return_false');
За да се отиде по-далеч
- Прибавям Човек попълнете за автори (изображение, jobTitle, sameAs) и свържете чрез
@id. - Прибавям Раздел на статията (категория) и ключови думи (етикети) чрез
bpcab_schema_jsonld_blogposting_node(внимавайте да не спамите). - Прибавям Говорим ако правите аудио/асистенти (специфичен случай на употреба).
- Добавете режим „редактор“: metabox, за да изберете основната категория на breadcrumb (избягва случайността на първата категория).
ресурси
- wp_head (кука)
- кука (wp)
- wp_json_encode()
- get_option ()
- get_permalink()
- json_encode (PHP)
- WordPress Core в GitHub
- Валидатор на маркиране на схеми
- Тест за богати резултати от Google
Често задавани въпроси
JSON-LD гарантира ли богати фрагменти?
Не. Вие предоставяте структурирани сигнали, но Google решава. Какво контролирате вие: валидност, последователност и липса на дубликати.
Защо JSON-LD, а не микроданни в HTML?
JSON-LD е по-лесен за поддръжка и по-малко крехък при работа с конструктори. Микроданните се повреждат бързо веднага щом модул промени HTML структурата.
Проблем ли е да има няколко JSON-LD скрипта?
Не е задължително. Проблемът е: няколко възела, които описват едно и също нещо с различни стойности (две организации, две публикации в блогове, две различни breadcrumbs).
Къде да конфигурирам логото, използвано в Организация?
Кодът използва custom_logo (Външен вид > Персонализиране > Идентичност на сайта). Ако вашата тема не предоставя тази информация, инжектирайте лого, като използвате филтъра. bpcab_schema_jsonld_org_node.
Защо да градите на куката wp и да се върнем на wp_head ?
wp гарантира, че основната заявка е решена (знаете дали сте на статия, страница, категория). wp_head е стандартното място за извеждане на метаданни. Смесването на двете създава гранични случаи.
Откъсът ми е празен с Elementor/Divi, какво трябва да направя?
Заменете description от bpcab_schema_jsonld_blogposting_node Генерирайте извадка от съдържанието (премахнете етикетите за strip, ограничение на дължината). Направете го правилно, за да избегнете вмъкването на огромни количества текст.
Съвместим ли е с многоезичен уебсайт?
Да, но трябва да проверите това home_url() et get_permalink() Те правилно връщат URL адреса на текущия език (това зависи от Polylang/WPML). Настройте inLanguage ако вашият плъгин не се актуализира get_bloginfo('language') по език.
Можем ли да добавим страница с ЧЗВ или „Как да“?
Да, но ви съветвам да го правите на конкретни страници, а не глобално. Добавете конструктор на условия (за всеки идентификатор на страница или шаблон), за да избегнете замърсяване на целия сайт.
Този код замества ли SEO плъгин?
Не. Той само замества частта „JSON-LD схема“. SEO плъгинът управлява също Sitemap, meta robots, разширени канонични тагове, социални интеграции и др.
Ами ако SEO плъгин вече инжектира схема и искам да запазя плъгина?
Или деактивирате схематичния изход на плъгина (ако съществува опцията), или деактивирате този MU-плъгин чрез bpcab_schema_jsonld_enabledСмесването на двете без стратегия рядко завършва добре.