Перейти к основному содержимому

Карты сайта (Sitemaps)

Предупреждение

Чтобы использовать эту функцию, необходимо включить PimcoreSeoBundle в файле bundle.php и установить его с помощью следующей команды:

bin/console pimcore:bundle:install PimcoreSeoBundle

Pimcore включает пакет presta/sitemap-bundle, который предоставляет простой, но мощный API для генерации XML-карт сайта. Ознакомьтесь с документацией пакета, чтобы понять, как он работает и как добавлять в него карты сайта. Пакет предоставляет возможность добавлять записи в карту сайта с помощью генерации события, которое вы можете обработать, добавляя записи в UrlContainerInterface. Для простых сценариев можно напрямую реализовать обработчик события и начать добавлять записи.

Отображение карт сайта

Карты сайта можно либо формировать «на лету», либо предварительно дампить в статические файлы. Выбор зависит от размера вашего сайта (например, размеров дерева документов, которые нужно обработать). В общем случае рекомендуется генерировать статические файлы — это снижает накладные расходы на создание sitemap при каждом запросе поискового робота. Если вы хотите отдавать sitemap напрямую (on-the-fly), нужно зарегистрировать маршруты sitemap в конфиге роутинга (см. документацию PrestaSitemapBundle для деталей).

PrestaSitemapBundle:  
resource: "@PrestaSitemapBundle/config/routing.yml"
prefix: /

После регистрации маршрута вы сможете получить доступ к sitemap по адресам /sitemap.xml и /sitemap.<section>.xml.

Чтобы дампить карты сайта в статические файлы, используйте команду presta:sitemaps:dump:

$ bin/console presta:sitemaps:dump
Dumping all sections of sitemaps into public directory
Created/Updated the following sitemap files:
sitemap.default.xml
sitemap.xml

Настройка схемы и хоста для URL в sitemap

Контекст командной строки не знает, какую схему (http/https) и хост использовать для абсолютных URL в sitemap, поэтому эти значения нужно настроить. Symfony позволяет задать эти параметры в Request Context. Если настроено, Pimcore использует домен, указанный в системных настройках как основной домен, в качестве хоста по умолчанию. Эти параметры будут переопределяться текущим запросом в веб‑контексте при использовании метода on-the-fly (через маршрут). При запуске команды presta:sitemaps:dump вы можете переопределить эти параметры, передав опцию --base-url:

$ bin/console presta:sitemaps:dump --base-url=https://pimcore.com/

Ссылки для деталей:

Генераторы sitemap

Pimcore добавляет возможность подключить один или несколько генераторов в процесс генерации sitemap. Такие генераторы регистрируются, упорядочиваются по приоритету и могут быть включены/отключены через конфигурацию. Базовый интерфейс генератора определяет единственный метод populate(), который должен добавлять записи в URL-контейнер:

<?php  

namespace Pimcore\Bundle\SeoBundle\Sitemap;

use Presta\SitemapBundle\Service\UrlContainerInterface;

interface GeneratorInterface
{
/**
* Populates the sitemap
*/
public function populate(UrlContainerInterface $urlContainer, string $section = null): void;
}

Когда пакет sitemap запускает событие SitemapPopulateEvent::class, Pimcore выполняет итерацию по каждому зарегистрированному генератору и вызывает метод populate(). Чтобы сделать генератор доступным для обработчика событий, его необходимо зарегистрировать через config. generator_id в приведенной ниже конфигурации ссылается на генератор, который ранее был зарегистрирован как сервис. Как вы можете видеть, генераторы можно включать/отключать и упорядочивать по приоритету.

pimcore_seo:  
sitemaps:
generators:
app_news:
enabled: true
priority: 50
generator_id: App\Sitemaps\NewsGenerator

# Pimcore предоставляет генератор дерева документов по умолчанию, который включен по умолчанию,
# но вы можете легко отключить его здесь.
pimcore_documents:
enabled: false

Генераторы sitemap элементов

Для более сложных случаев использования, связанных с моделями Pimcore, Pimcore определяет AbstractElementGenerator, который можно расширять с помощью подключаемых фильтров и процессоров. Это позволяет определить поведение для повторного использования в фильтре/процессоре, которое может использоваться из нескольких генераторов. Фильтр определяет, может ли элемент быть добавлен в карту сайта и может ли он обрабатывать дочерние элементы (запрос этой информации зависит от генератора). Например, элемент PropertiesFilter исключает элементы со свойством sitemaps_exclude. Обработчик может улучшить запись перед ее добавлением в контейнер. Например, ModificationDateProcessor добавляет дату изменения элемента в URL-адрес.

Какие фильтры и процессоры следует использовать, можно определить на уровне генератора. Например, параметр DocumentTreeGenerator , который включен по умолчанию, определяется следующим образом:

services:  
Pimcore\Bundle\SeoBundle\Sitemap\Document\DocumentTreeGenerator:
arguments:
$filters:
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Filter\PublishedFilter'
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Filter\PropertiesFilter'
- '@Pimcore\Bundle\SeoBundle\Sitemap\Document\Filter\DocumentTypeFilter'
- '@Pimcore\Bundle\SeoBundle\Sitemap\Document\Filter\SiteRootFilter'
$processors:
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Processor\ModificationDateProcessor'
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Processor\PropertiesProcessor'
$options:
handleMainDomain: true
handleCurrentSite: false
handleSites: true

Если вам нужно повлиять на поведение генератора по дереву документов, вы можете либо переопределить определение core-сервиса, либо определить собственный сервис-генератор и подключить его в конфигурации (см. выше). Выбирая, какие фильтры и процессоры использовать, вы можете изменить и расширить поведение генератора. Pimcore определяет набор стандартных реализаций, которые зарегистрированы как сервисы и могут быть использованы напрямую.

ФильтрОписание
Pimcore\Bundle\SeoBundle\Sitemap\Element\Filter\PropertiesFilterИсключает элементы с логическим свойством sitemaps_exclude, равным true, и исключает дочернюю обработку элементов с логическим свойством sitemaps_exclude_children, равным true.
Pimcore\Bundle\SeoBundle\Sitemap\Element\Filter\PublishedFilterИсключает неопубликованные элементы.
Pimcore\Bundle\SeoBundle\Sitemap\Document\Filter\DocumentTypeFilterИспользуется DocumentTreeGenerator. Исключает документы, не соответствующие списку определенных типов, и обрабатывает дочерние элементы только для определенных типов.
Pimcore\Bundle\SeoBundle\Sitemap\Document\Filter\SiteRootFilterИспользуется DocumentTreeGenerator. Исключает документы, которые являются корневыми документами сайта, если обрабатываемый в данный момент сайт не соответствует документу. Например, если документ является корневым сайтом, а основной сайт в данный момент обрабатывается, он будет исключен для основного сайта, но позже будет использован для фактического сайта.
ПроцессорОписание
Pimcore\Bundle\SeoBundle\Sitemap\Element\Processor\ModificationDateProcessorДобавляет дату изменения элемента в качестве свойства lastmod.
Pimcore\Bundle\SeoBundle\Sitemap\Element\Processor\PropertiesProcessorСчитывает свойства sitemaps_changefreq и sitemaps_priority, если они установлены для элемента, и добавляет их в запись карты сайта, чтобы легко установить эти свойства на уровне элемента.

DocumentTreeGenerator

Pimcore поставляет генератор по умолчанию для документов, реализованный в DocumentTreeGenerator. Этот генератор выполняет итерацию всего дерева документов и добавляет записи для каждого документа, заботясь при этом об обработке сайтов и жестких ссылок. Он использует имена хостов, настроенных в качестве основного домена /сайта, и возвращается к хосту контекста запроса с помощью генератора url service. Вы можете либо полностью отключить генератор по умолчанию, как показано в примере выше, либо определить свой собственный сервис, используя класс DocumentTreeGenerator с вашими собственными фильтрами/процессорами. Определение сервиса по умолчанию можно найти в sitemaps.yaml в CoreBundle.

Создание пользовательского генератора

Чтобы создать свой собственный генератор, начните с реализации GeneratorInterface. В этом разделе мы расширим AbstractElementGenerator для создания записей для моделей Pimcore. Генератор для добавления записей "BlogArticle` на карту сайта может выглядеть следующим образом:

<?php  

namespace App\Sitemaps;

use Pimcore\Model\DataObject\BlogArticle;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\AbstractElementGenerator;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\GeneratorContext;
use Presta\SitemapBundle\Service\UrlContainerInterface;
use Presta\SitemapBundle\Sitemap\Url\UrlConcrete;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class BlogGenerator extends AbstractElementGenerator
{
public function populate(UrlContainerInterface $urlContainer, string $section = null): void
{
if (null !== $section && $section !== 'blog') {
// не добавлять записи, если раздел не соответствует
return;
}

$section = 'blog';

$list = new BlogArticle\Listing();
$list->setOrderKey('date');
$list->setOrder('DESC');

// контекст содержит метаданные для фильтров/процессоров
// он содержит, по крайней мере, URL-адрес контейнера, но вы можете добавить дополнительные данные
// с параметром params
$context = new GeneratorContext($urlContainer, $section, ['foo' => 'bar']);

/** @var BlogArticle $blogArticle */
foreach ($list as $blogArticle) {
// добавлять элемент только в том случае, если он не отфильтрован
if (!$this->canBeAdded($blogArticle, $context)) {
continue;
}

// используйте генератор ссылок для создания URL-адреса статьи
// вам нужно убедиться, что генератор ссылок генерирует абсолютный URL-адрес
$link = $blogArticle->getClass()->getLinkGenerator()->generate($blogArticle, [
'referenceType' => UrlGeneratorInterface::ABSOLUTE_URL
]);

// создайте запись для карты сайта
$url = new UrlConcrete($link);

// запуск URL-адреса через процессоры
$url = $this->process($url, $blogArticle, $context);

// обработчики могут возвращать значение null, чтобы исключить URL-адрес
if (null === $url) {
continue;
}

// добавьте URL-адрес в контейнер
$urlContainer->addUrl($url, $section);
}
}
}

AbstractElementGenerator предоставляет методы canBeAdded() и process() для прогонки записи через фильтры и процессоры. Если вы обрабатываете вложенные древовидные структуры, вы также можете использовать handlesChildren() для проверки, следует ли обрабатывать детей. Все три метода принимают объект GeneratorContextInterface, который вы можете использовать для передачи метаданных контекста фильтрам и процессорам. Например, DocumentTreeProcessor использует контекст для определения сайта, в котором находится документ.

В примере выше URL создаётся с помощью Link Generator.

Важно, чтобы ваш link generator мог генерировать абсолютный URL для данного объекта. Выше приведен только пример, но вы можете ознакомиться с демонстрацией для рабочего примера построения записей sitemap для объектов News.

После создания генератора зарегистрируйте его как сервис и добавьте в конфигурацию. Используйте фильтры и процессоры, чтобы переиспользовать уже реализованное поведение.

# services.yaml  

services:
_defaults:
autowire: true
autoconfigure: true
public: false

App\Sitemaps\BlogGenerator:
arguments:
$filters:
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Filter\PublishedFilter'
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Filter\PropertiesFilter'
$processors:
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Processor\ModificationDateProcessor'
- '@Pimcore\Bundle\SeoBundle\Sitemap\Element\Processor\PropertiesProcessor'

Сделайте генератор доступным для основного слушателя, зарегистрировав его в конфигурации:

# config.yaml  

pimcore_seo:
sitemaps:
generators:
app_blog:
generator_id: App\Sitemaps\BlogGenerator

Создание собственного фильтра

Фильтры можно создавать, реализуя FilterInterface. Пример фильтра, который исключает любой элемент с датой изменения старше года, может выглядеть так:

<?php  

namespace App\Sitemaps\Filter;

use Pimcore\Model\Element\ElementInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\FilterInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\GeneratorContextInterface;

class AgeFilter implements FilterInterface
{
private int $maxYears;

public function __construct(int $maxYears = 1)
{
$this->maxYears = $maxYears;
}

public function canBeAdded(ElementInterface $element, GeneratorContextInterface $context): bool
{
$modicationDate = \DateTimeImmutable::createFromFormat('U', (string)$element->getModificationDate());
$now = new \DateTimeImmutable();

$diff = $modicationDate->diff($now);

// исключить элемент, если число лет больше, чем задано в конфигурации
return $diff->y < $this->maxYears;
}

public function handlesChildren(ElementInterface $element, GeneratorContextInterface $context): bool
{
// несоответствие возрастному ограничению не означает, что не нужно обрабатывать детей
return true;
}
}

Теперь вы можете определить фильтр как сервис и использовать его в ваших генераторах:

services:  
_defaults:
autowire: true
autoconfigure: true
public: false

App\Sitemaps\Filter\AgeFilter: ~

App\Sitemaps\BlogGenerator:
arguments:
$filters:
- '@App\Sitemaps\Filter\AgeFilter'

Создание собственного процессора

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

<?php  

namespace App\Sitemaps\Processor;

use Pimcore\Model\Element\ElementInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\GeneratorContextInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\ProcessorInterface;
use Presta\SitemapBundle\Sitemap\Url\Url;
use Presta\SitemapBundle\Sitemap\Url\UrlConcrete;

class RandomPriorityProcessor implements ProcessorInterface
{
public function process(Url $url, ElementInterface $element, GeneratorContextInterface $context): Url
{
if ($url instanceof UrlConcrete) {
$url->setPriority(rand(0, 10) / 10);
}

// важно: вернуть экземпляр Url, который будет добавлен.
// если ваш процессор вернёт null, запись будет опущена.
return $url;
}
}

Важно, чтобы процессор возвращал экземпляр Url, иначе запись будет опущена. Вы можете использовать это в ваших процессорах для применения какой-либо фильтрации на уровне процессора или для возвращения другого экземпляра вместо оригинального Url. Типичный кейс — использование Url Decorator в процессоре и возврат его экземпляра вместо оригинала.

Определите процессор как сервис и начните использовать его в ваших генераторах:

services:  
_defaults:
autowire: true
autoconfigure: true
public: false

App\Sitemaps\Processor\RandomPriorityProcessor: ~

App\Sitemaps\BlogGenerator:
arguments:
$processors:
- '@App\Sitemaps\Processor\RandomPriorityProcessor'

Генерация абсолютных URL

Чтобы генерировать абсолютные URL, Pimcore определяет url generator, который, получив путь, формирует абсолютный URL на основе Request Context. См. core processors/generators и demo для подробностей. Пример использования URL-генератора в процессоре:

<?php  

namespace App\Sitemaps\Processor;

use Pimcore\Model\Element\ElementInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\GeneratorContextInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\Element\ProcessorInterface;
use Pimcore\Bundle\SeoBundle\Sitemap\UrlGeneratorInterface;
use Presta\SitemapBundle\Sitemap\Url\Url;
use Presta\SitemapBundle\Sitemap\Url\UrlConcrete;

class RandomPathProcessor implements ProcessorInterface
{
private UrlGeneratorInterface $urlGenerator;

public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}

public function process(Url $url, ElementInterface $element, GeneratorContextInterface $context): UrlConcrete
{
$path = $this->urlGenerator->generateUrl('/foo/bar');
$url = new UrlConcrete($path);

return $url;
}
}


Вы можете предложить улучшение документации или задать вопрос в комментариях.
Если вам нужна полноценная консультация — вы можете заказать её на нашем сайте.