Добавления JSON-LD к статьям в MODX

Добавления JSON-LD к статьям в MODX
Содержание:
  1. Что такое JSON-LD
  2. Как проверить микроразметку
  3. Как сформировать структурированные данные и добавить их на страницу
  4. Инструкция по добавлению JSON-LD для CMS MODX
  5. Комментарии

В этой статье приведена инструкция для CMS MODX Revolution по добавлению структурированных данным на страницы, материал на которых соответствует объекту «Article» из «schema.org». В качестве способа разметки будем использовать JSON-LD.

Что такое JSON-LD

JSON-LD – это формат описания контента (связанных данных) с помощью JSON. Данные в этом формате представляют собой набор пар ключ-значение. Описание данных в JSON-LD можно осуществлять с помощью различных словарей, но в этой статье будем использовать «schema.org».

Для указания словаря используется ключ «@context», а типа описываемой сущности - «@type».

Например:

{
  "@context": "http://schema.org",
  "@type": "Article"
}

Это зарезервированные ключи. После них нужно описать сам этот объект. Осуществляется это аналогичным образом, т.е. с помощью набора пар ключ-значение. Имена ключей и формат значений, которые они должны содержать в этом случае нужно брать для типа «Article» из словаря «schema.org».

Например:

{
  "@context": "https://schema.org",
  "@type": "Article",
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://myblog.ru/post-1"
  },
  "headline":"Нзвание статьи",
  "image":[
    "https://myblog.ru/images/1.jpg",
    "https://myblog.ru/images/2.jpg"
  ],
  "datePublished": "2020-01-19T12:30:00+03:00",
  "dateModified": "2020-01-20T15:17:00+03:00",
  "author": {
    "@type": "Person",
    "name": "Имя автора статьи"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Мой блог",
    "logo": {
      "@type": "ImageObject",
      "url": "https://myblog.ru/images/logo.png"
    }
  },
  "description": "Описание статьи..."}
}

Проверить структурированные данные можно с помощью соответствующего инструмента Google:

Проверка структурированных данных с помощью инструмента Google

Познакомиться с различными элементами структурированных данных можно на ресурсе Google для разработчиков..

Вставка JSON-LD на страницу выполняется следующим образом:

<script type="application/ld+json">
{
  // здесь должны находиться структурированные данные...
}
</script>

Поместить эту конструкцию согласно рекомендациям Google лучше всего в элемент head.

Добавление структурированных данных на страницу сделает её контент более понятным для поисковых систем, а также может улучшить представление её сниппета в поисковой выдаче.

А чем поисковая система лучше поймёт контент страницы, тем она будет более релевантной соответствующему запросу. В результате это приведёт к возрастанию количества целевых пользователей, которые будут переходить из поисковой выдачи на данный сайт.

В настоящее время поисковые системы Google и Яндекс понимают формат JSON-LD и рекомендуют его к внедрению.

Как проверить микроразметку

Проверка структурированных данных на странице обычное выполняется с помощью соответствующих инструментов Яндекса и Google.

Результат проверки структурированных данных с помощью валидатора микроразметки «Яндекс.Вебмастер»:

Проверка структурированных данных с помощью валидатора микроразметки Яндекса

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

Как сформировать структурированные данные и добавить их на страницу

Формирование структурированных данных на стороне сервера (с помощью PHP) выполняется довольно просто.

Для этого нужно создать ассоциативный массив и наполнить его определёнными данными.

Например:

<?php

// данные, которые мы получили из базы данных или каким-то другим способом
$url = 'https://myblog.ru/post-1';
$title = 'Название статьи';
$description = 'Описание статьи...';
$images = [
  'https://myblog.ru/images/1.jpg',
  'https://myblog.ru/images/2.jpg'
];
$datePublished = '2020-01-19T12:30:00+03:00';
$dateModified = '2020-01-20T15:17:00+03:00';
$author = 'Имя автора статьи';
$siteName = 'Мой блог';
$siteLogo = 'https://myblog.ru/images/logo.png';

// создадим ассоциативный массив из данных
$data = [
  '@context' => 'https://schema.org',
  '@type' => 'Article',
  'mainEntityOfPage' => [
    '@type' => 'WebPage',
    '@id' => $url
  ],
  'headline' => $title,
  'image' => $images,
  'datePublished' => $datePublished,
  'dateModified' => $dateModified,
  'author' => [
    '@type' => 'Person',
    'name' => $author
  ],
  'publisher' => [
    '@type' => 'Organization',
    'name' => $siteName,
    'logo' => [
      '@type' => 'ImageObject',
      'url' => $siteLogo
    ]
  ],
  'description' => $description
];

Затем полученный массив необходимо перевести в формат JSON и возвратить его в качестве результата:

echo json_encode($data, JSON_UNESCAPED_UNICODE);

После этого результат вышеприведенного скрипта необходимо поместить в элемент script с типом «application/ld+json»:

<!-- data_in_json.php - это скрипт, который возвращает данные в формате JSON-LD для этой статьи -->
<script type="application/ld+json"><?php include 'data_in_json.php'; ?></script>

В зависимости от того, как у вас организована бэкенд-часть сайта, вышеприведённый пример в большинстве случаев нужно будет каким-то определённым образом адаптировать под неё.

Данная инструкция применительно к CMS MODX Revolution будет иметь следующий вид.

Инструкция по добавлению JSON-LD для CMS MODX

Добавить структурированные данные в формате JSON-LD на страницу со статьями в CMS MODX можно следующим образом.

1. Создать сниппет и поместить в него следующий код:

Создание сниппета для формирования структурированных данных в формате JSON-LD
<?php

// если на этой странице контент не является объектом "Article", то завершаем работу (на сайте статьи имеют шаблон с id = 2)
if ($modx->resource->get('template') !== 2) {
  return;
}

// URL сайта
$siteUrl = $modx->getOption('site_url');
// id страницы
$id = $modx->resource->get('id');
// id главной страницы
$startId = $modx->getOption('site_start');

// url страницы
// 1 способ
// $url = $modx->makeUrl($id, '', '', 'full');
// 2 способ
if ($id == $startId) {
  $url = $siteUrl;
} else {
  $url = $siteUrl . $modx->resource->get('uri');
}

// заголовок страницы
$title = mb_strimwidth($modx->resource->get('pagetitle'), 0, 110, '...');
// описание страницы
$description = $modx->resource->get('description');
// все изображения, расположенные в контенте страницы
$images = [];
preg_match_all('/<img\s+.*?src=[\"\']?([^\"\' >]*)[\"\']?[^>]*>/i', $modx->resource->get('content'), $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
  $image = $match[1];
  if (mb_strpos($image, '/') === 0) {
    $image = $siteUrl . mb_substr($image, 1);
  } elseif (mb_strpos($image, $siteUrl) !== 0) {
    $image = $siteUrl . $image;
  }
  $images[] = $image;
}
// дата публикации статьи
$datePublished = date("c", strtotime($modx->resource->get('publishedon')));
// дата последнего изменения статьи
if ($modx->resource->get('editedon')) {
  $dateModified = date("c", strtotime($modx->resource->get('editedon')));
} else {
  $dateModified = $datePublished;
}
// автор статьи
$author = $modx->getObject('modUserProfile', $modx->resource->get('publishedby'))->get('fullname') ;
// имя сайта
$siteName = $modx->getOption('site_name');
// логотип сайта
$siteLogo = '/assets/images/logo.png';

// создадим ассоциативный массив из данных
$data = [
  '@context' => 'https://schema.org',
  '@type' => 'Article',
  'mainEntityOfPage' => [
    '@type' => 'WebPage',
    '@id' => $url
  ],
  'headline' => $title,
  'image' => $images,
  'datePublished' => $datePublished,
  'dateModified' => $dateModified,
  'author' => [
    '@type' => 'Person',
    'name' => $author
  ],
  'publisher' => [
    '@type' => 'Organization',
    'name' => $siteName,
    'logo' => [
      '@type' => 'ImageObject',
      'url' => $siteLogo
    ]
  ],
  'description' => $description
];

$output = $modx->getChunk('jsonld.tpl', array(
    'output' => json_encode($data, JSON_UNESCAPED_UNICODE)
));
return $output;

В этом снимаете необходимо вместо цифры 2 указать id шаблона, который у вас используется для вывода ресурсов со статьями.

2. Создать чанк с именем «jsonld.tpl» и следующим содержимым:

<script type="application/ld+json">[[+output]]</script>

3. Вставить вызов только что созданного сниппета в шаблон:

...
<head>
  ...
  <!-- getJSONLD - это имя только что созданного сниппета -->
  [[getJSONLD]]
  ...

Вариант формирования JSON-LD для описания статьи и строки навигации

Ещё один пример сниппета для CMS MODX. В этом сниппете добавим структурированные данные не только для описания статьи, но и для строки навигации.

<?php

// если на этой странице контент не является объектом "Article", то завершаем работу (на сайте статьи имеют шаблон с id = 2)
if ($modx->resource->get('template') !== 2) {
  return;
}

// URL сайта
$siteUrl = $modx->getOption('site_url'); //$modx->getOption('http_host'); //$modx->getOption('site_url');
// id страницы
$id = $modx->resource->get('id');
// id главной страницы
$startId = $modx->getOption('site_start');

// url страницы
// 1 способ
// $url = $modx->makeUrl($id, '', '', 'full');
// 2 способ
if ($id == $startId) {
  $url = $siteUrl;
} else {
  $url = $siteUrl . $modx->resource->get('uri');
}

// заголовок страницы
$title = mb_strimwidth($modx->resource->get('pagetitle'), 0, 110, '...');
// описание страницы
$description = $modx->resource->get('description');
// все изображения, расположенные в контенте страницы
$images = [];
preg_match_all('/<img\s+.*?src=[\"\']?([^\"\' >]*)[\"\']?[^>]*>/i', $modx->resource->get('content'), $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
  $image = $match[1];
  if (mb_strpos($image, '/') === 0) {
    $image = $siteUrl . mb_substr($image, 1);
  } elseif (mb_strpos($image, $siteUrl) !== 0) {
    $image = $siteUrl . $image;
  }
  $images[] = $image;
}
// дата публикации статьи
$datePublished = date("c", strtotime($modx->resource->get('publishedon')));
// дата последнего изменения статьи
if ($modx->resource->get('editedon')) {
  $dateModified = date("c", strtotime($modx->resource->get('editedon')));
} else {
  $dateModified = $datePublished;
}
// автор статьи
$author = $modx->getObject('modUserProfile', $modx->resource->get('publishedby'))->get('fullname');
// имя сайта
$siteName = $modx->getOption('site_name');
// логотип сайта
$siteLogo = '/assets/images/logo.png';


// хлебные крошки
$breadcrumbs = [];
$crumbs = [];

// добавлем текущую страницу
$crumbs[] = ['name' => $title, 'item' => $url];
// получаем родительский ресурс
$parent = $modx->getObject('modResource', $modx->resource->get('parent'));
// пока родительский ресурс не равен null выполняем...
while ($parent != null) {
  $item = $parent->get('id') == $startId ? $siteUrl : $siteUrl . $parent->get('uri');
  $crumbs[] = ['name' => $parent->get('menutitle'), 'item' => $item];
  if ($parent->get('id') == $startId) {
    break;
  }
  $parent = $modx->getObject('modResource', $parent->get('parent'));
  if ($parent == null) {
    $parent = $modx->getObject('modResource', $startId);
  }
}
$index = 1;
for ($i = count($crumbs) - 1; $i >= 0; $i--) {
  $bredcrumbs[] = [
    '@type' => 'ListItem',
    'position' => $index++,
    'name' => $crumbs[$i]['name'],
    'item' => $crumbs[$i]['item']
  ];
}

// создадим ассоциативный массив из данных
$data = [
  [
    '@context' => 'https://schema.org',
    '@type' => 'Article',
    'mainEntityOfPage' => [
      '@type' => 'WebPage',
      '@id' => $url
    ],
    'headline' => $title,
    'image' => $images,
    'datePublished' => $datePublished,
    'dateModified' => $dateModified,
    'author' => [
      '@type' => 'Person',
      'name' => $author
    ],
    'publisher' => [
      '@type' => 'Organization',
      'name' => $siteName,
      'logo' => [
        '@type' => 'ImageObject',
        'url' => $siteLogo
      ]
    ],
    'description' => $description
  ], [
    '@context' => 'https://schema.org',
    '@type' => 'BreadcrumbList',
    'itemListElement' => $bredcrumbs
  ]
];

$output = $modx->getChunk('jsonld.tpl', array(
    'output' => json_encode($data, JSON_UNESCAPED_UNICODE)
));
return $output;

Комментарии: 12

Nik
Nik

Доброго дня Александр! Обнаружил что валидатор google слегка ругается если картинки нету в посте.

Описание картинки

Например раздел "вопросы", если пользователь не загрузил картинку а просто написал текст.

Добавил в сниппет после "логтип сайта" строчку, теперь если нет картинки то выводим логотип.
// логотип сайта
$siteLogo = '/images/logo.png';
if (empty($images)) {$images = $siteLogo;}
// создадим ассоциативный массив из данных
......
Кстати у вас две незначительные проблемы "Отсутствует поле img и url" в разделе Вопросы.

И еще вопросик как правильно добавить Comment в данный снипет?

Александр Мальцев
Александр Мальцев

Привет! Картинка не является обязательной. Если её добавить, то можно просто получить более красивый сниппет в Goggle или Yandex поиске.

А смысл структурировать комментарии? Они же не поддерживаются в Google или Yandex поиске для расширенной выдачи. А так они в любом случае анализируются поисковыми роботами. Google поддерживает только эти структурированные данные.

Например, есть смысл структурировать страницы с вопросами и ответами, так на выходе мы можем получить более специфичный сниппет в поисковой выдаче. Здесь на сайте для раздела с вопросами хотел такое сделать, но никак руки до этого не дойдут. Всегда находится что-то другое)

А так если есть желание структурировать комментарии, то можно сделать так как это описано в этой статье. Такое дублировать в JSON-LD мне кажется так себе решение.

Nik
Nik

Ясно, не знал про это, тогда действительно смысла никакого нет. А дублировать нет желания))) Просто у меня аналогичный раздел "вопрос - ответ" (на tickets), структурировал его данным сниппетом, только type написал BlogPosting.

Спасибо за информацию!
Amsterdam
Amsterdam
Александр, приветствую!

Не подскажите с таким вопросом: пытаюсь в pdoResources произвести сравнение pagetitle статей в разделе, посредством:
&where=`{"pagetitle:LIKE":"%Совпадающие слова%"}`
Цель — вывести статьи, в заголовках которых встречается хотя бы одно из перечисленных в условии слов.

Через LIKE можно вывести только с точным совпадением всех слов. Что можно использовать вместо LIKE для достижения цели?
Александр Мальцев
Александр Мальцев
Привет, можно LIKE с OR:
[[pdoResources?
    &parents=`0`
    &limit=`5`
    &select=`id,pagetitle`
    &where=`{"pagetitle:LIKE": "%html%", "OR:pagetitle:LIKE": "%css%"}`
    &showLog=`1`
]]
С использованием REGEXP так:
[[pdoResources?
    &parents=`0`
    &limit=`5`
    &select=`id,pagetitle`
    &where=`{"pagetitle:REGEXP": "html|css"}`
    &showLog=`1`
]]
Amsterdam
Amsterdam
Александр, здравствуйте!

Вопрос не совсем по теме, но по json, т.к пытаюсь выводить в него данные из сниппета, но даже примеры из оф. документации, почему то не работают (приводят к ошибке 500). Пример вывода:
$object->set('name','Bob');
$object->set('email','pinkdaisies@gmail.com');
$json = $object->toJSON();
echo $json;
Не работает на 2.7.3 и 2.8.1. В чем причина, не подскажите?
Александр Мальцев
Александр Мальцев
Здравствуйте!
Так сначала нужно создать или получить xPDOObject, и только затем использовать метод set.
Например:
$object = $modx->newObject('modResource');
$object->set('name','Bob');
$object->set('email','pinkdaisies@gmail.com');
$json = $object->toJSON();
return $json;
Amsterdam
Amsterdam
Как обычно, четко и понятно! Если бы документация на modx была написана так же грамотно, как ваши ответы и материалы, Александр) Спасибо!!!
Юрий
Юрий
И еще хотел узнать ваше мнение по системам управления сайтами. Сейчас разные конструкторы типа Тильда, Викс и пр. довольно сильно «отжимают» нишу создания простых сайтов типа визиток. Цены, конечно, у них дороговатые. Но покупает то, что у них есть готовые блоки для контента, которые легко вставляются на любую страницу сайта в нужное место.
Так вот у MODX Revo, на сколько мне известно, кроме одного платного и довольно дорого компонента (Фред, если не ошибаюсь) нет аналогов, которые позволяют создавать свои блоки с простой вставкой их в контент сайта. Можно, вроде, сделать свой конструктор блоков на MIGX, даже видео есть об этом на Ютубе, но мне показалось это как-то сложным. По крайней мере я пока не разобрался с этой кухней.
У того же вордпресса в новом редакторе Гутенберг, вроде, есть возможность вставки своих заранее заготовленных блоков для контента. Не говоря уже о специфических плагинах для «сборки» страницы из блоков типа Elementor. Но мне вордпресс как-то не по душе. В MODX Revo нравится то, что весь код я легко могу контролировать.
Например, есть на сайте страницы с услугами. И нужно на каждой такой странице вставить блок с преимуществами этой услуги. Получается у каждой услуги это будут свои преимущества. Можно, конечно, завести для этого дополнительное поле того же MIGX. Но что делать, если вдруг возникает необходимость вставки на страницу не одного такого блока, а нескольких? Заводить несколько таких полей с запасом или как-то еще? Хотелось бы иметь возможность удобной вставки только нужных блоков, причем в нужных количествах.
Так вот, собственно, вопрос. Вы не в курсе, как на MODX Revo можно организовать удобную вставку на страницу контентных заранее подготовленных блоков со своим уникальным оформлением? Чтобы эти блоки были не заранее кем-то добавлены, а чтобы можно было самостоятельно добавлять свои блоки со своей версткой.
Быть может, вам известны какие-то другие системы управления похожие на модекс, у которых есть удобная возможность вставки своих блоков на страницу?
И еще попутно вопрос. Как вы относитесь к Evolution CMS (ранее MODX Evo)? Вроде эта cms шагнула далеко вперед и на данный момент обладает даже какими-то крутыми фишками, которых нет в MODX Revo?
Александр Мальцев
Александр Мальцев
Wix, MODX – это просто разные системы. Тем, кто использует MODX не нужны конструкторы. Поэтому их в этой системе нет. Тем, кому нужно собрать сайт из готовых модулей лучше использовать специальные предназначенные для этого платформы, например Wix или Tilda. Просто не вижу смыла это смешивать. Для меня это системы разные, и уровень, который требуется от разработчика для выполнения работ на этих системах тоже нужен разный. Кому нужно ещё больше возможностей и индивидуальности при написании проектов используют уже фреймворки, например Laravel или Symfony.

Не знаю, насколько крута это новая Evolution CMS и какую нишу она займёт. Насколько много у них активных разработчиков чтобы её двигать? Например, есть October CMS, которые тоже базируется на Laravel. Чем Evolution CMS лучше, чем October CMS, чтобы пользователи, которые активно её используют перешли на Evolution :)
Юрий
Юрий
Здравствуйте, Александр! Подскажите, пожалуйста, при такой вставке структурированных данных на страницу уже не нужно вносить правки в верстку самого сайта. А именно, в сами теги разметки не нужно внедрять itemprop, itemtype и пр.? Если я правильно понял, то саму верстку в таком случае можно делать в удобном для себя виде?
Александр Мальцев
Александр Мальцев
Здравствуйте! Да, в этом случае внедрять структурированные данные в верстку самого сайта не нужно. И следовательно, можно верстать страницы так, как вам это больше удобно.