Sitemap.xml и robots.txt в CakePHP 3

Немаловажным при подготовке сайта к продвижению является создание специальных файлов sitemap.xml и robots.txt. В этой статье я хочу показать на сколько просто их разработать при работе с CakePHP.

Приведённые примеры реализованы на CakePHP 3.6.

Немного о протоколе Sitemap

Файлы Sitemap применяются для оповещения поисковых систем о всех страницах сайта, подлежащих индексированию. В них хранятся URL-адреса страниц и связанные с ними метаданные. К таким метаданным относятся:

  • Дата последнего изменения;
  • Частота изменения (как часто страница обновляется);
  • Приоритетность на уровне сайта (главная страница сайта приоритетней статьи в блоге).

При оформлении файлов Sitemap необходимо следовать синтаксическим правилам языка разметки XML. Я не стану изобретать велосипед, описывая Sitemap, а просто поделюсь ссылкой на официальную документацию.

Индексирование страниц сайта, поисковыми система, происходит за счёт сканирования ссылок на самом сайте и сайтах, ссылающихся на него. В дополнение к найденной информации, поисковые системы получают URL-адреса из присланных файлов Sitemap, чтобы максимально полно охватить все URL-адреса сайта. Но использование этих файлов не гарантирует попадание страниц в индекс, а несёт только рекомендательный характер. Впрочем, это не значит, что стоит этим пренебрегать.

В качестве примера, приведу файл sitemap.xml, используемый на данном сайте. Его требуется располагать в корне сайте. В моём случае, файл доступен по ссылке https://dallin.uz/sitemap.xml.

Совсем немного о robots.txt

Файл robots.txt – это стандарт исключений для поисковых роботов, предназначенный для ограничения доступа к содержимому на сайте. Как и файл sitemap.xml, robots.txt должен располагаться в корне сайта. В моём случае, этот файл располагается по адресу https://dallin.uz/robots.txt.

Файл robots.txt состоит из набора инструкций, с помощью которых ограничивается доступ поисковым роботам к файлам, страницам и даже целым каталогам сайта. Официальная документация доступна по ссылке. На русском языке можно ознакомиться здесь.

Использование robots.txt, для частичного управления индексированием страниц поисковыми системами, так же несёт рекомендательный характер.

Этапы разработки

Разработку страниц sitemap.xml и robots.txt рекомендую начать с маршрутизации. Наши файлы имеют свои расширения и должны располагаться в корне сайта, поэтому необходимо добавить несколько инструкций маршрутизации.

После этого, в контроллер PagesController, добавим методы sitemap() и robots().

На последнем этапе, подготовим макеты и шаблоны для представления типов контента.

Настройка маршрутизации

По умолчанию, все настройки маршрутизации проводятся в файле /config/routes.php. Для маршрутизации расширений файлов, в CakePHP, предусмотрено три способа.

Способ №1. На глобальном уровне

Статическим метод Cake\Routing\Router::extensions() работает на глобальном уровне, распространяя своё действие на все маршруты, объявленные после его инициализации.

<?php
use Cake\Routing\RouteBuilder;
use Cake\Routing\Router;
use Cake\Routing\Route\DashedRoute;

Router::defaultRouteClass(DashedRoute::class);

Router::extensions(['xml', 'txt']);

Router::scope('/', function (RouteBuilder $routes) {
    //Другие маршруты
    
    $routes->connect('/sitemap', ['controller' => 'Pages', 'action' => 'sitemap']);
    $routes->connect('/robots', ['controller' => 'Pages', 'action' => 'robots']);
    
    $routes->fallbacks(DashedRoute::class);
});

Способ №2. На уровне области маршрутизации

Для ограничения действия расширения конкретными областями (scope), можно воспользоваться методом Cake\Routing\RouteBuilder::setExtensions(). Подобно глобальному методу Router::extensions(), действие RouteBuilder::setExtensions() распространяется на все маршруты, объявленные после его инициализации, ограничиваясь заданной областью или повторным вызовом метода.

<?php
use Cake\Routing\RouteBuilder;
use Cake\Routing\Router;
use Cake\Routing\Route\DashedRoute;

Router::defaultRouteClass(DashedRoute::class);

Router::scope('/', function (RouteBuilder $routes) {
    //Другие маршруты
	
    $routes->setExtensions(['xml', 'txt']);
    $routes->connect('/sitemap', ['controller' => 'Pages', 'action' => 'sitemap']);
    $routes->connect('/robots', ['controller' => 'Pages', 'action' => 'robots']);
    
    $routes->fallbacks(DashedRoute::class);
});

Способ №3. На уровне маршрута

Когда необходимо ограничиться одним маршрутом, стоит воспользоваться быстрым методом построения.

<?php
use Cake\Routing\RouteBuilder;
use Cake\Routing\Router;
use Cake\Routing\Route\DashedRoute;

Router::defaultRouteClass(DashedRoute::class);

Router::scope('/', function (RouteBuilder $routes) {
    //Другие маршруты
	
    $routes->connect('/sitemap', ['controller' => 'Pages', 'action' => 'sitemap'])
        ->setExtensions(['xml']);
		
    $routes->connect('/robots', ['controller' => 'Pages', 'action' => 'robots'])
        ->setExtensions(['txt']);
    
    $routes->fallbacks(DashedRoute::class);
});

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

Что я имею ввиду? Например, маршрут, определяющий Sitemap, всегда будет срабатывать, когда пользователь перейдёт по URL-адресам /sitemap.xml и /sitemap. То есть, методы, указанные выше, только разрешают заданные расширения, но не ограничивают шаблон URL-адреса. Поэтому, при обращении пользователя к адресу /sitemap, приложение будет выдавать 500 ошибку, потому что не созданы шаблоны представления, вместо выдачи 404 ошибки. Чтобы этого избежать, в методах, на которые ссылаются маршруты, необходимо добавить проверки условий запроса. О них пойдёт речь далее.

Методы контроллера

В методах sitemap() и robots() проведём проверки условий запроса с помощью класса Cake\Http\ServerRequest.

Если условия не будут выполняться, будем выбрасывать исключение. Для этого используем класс Cake\Http\Exception\NotFoundException.

Метод sitemap()

Например, необходимо вывезти в файле sitemap.xml все URL-адреса статей. Для этого используем класс Cake\ORM\TableRegistry и проведём поиск статей в таблице Posts.

Чтобы проверить есть в ли в запросе расширение .xml, воспользуемся методом ServerRequest::is('xml').

<?php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Http\Exception\NotFoundException;
use Cake\ORM\TableRegistry;

class PagesController extends AppController
{
    public function beforeFilter(\Cake\Event\Event $event)
    {
        parent::beforeFilter($event);
    }
	
    // Другие методы контроллера

    public function sitemap()
    {
        if (!$this->request->is('xml')) {
            throw new NotFoundException(__('Not found page'));
        }
		
        $postsTable = TableRegistry::getTableLocator()->get('Posts');
        $posts = $postsTable->find('all');
			
        $this->set('posts', $posts);
    }
}	

Метод robots()

Передавать данные из метода в представление не нужно, потому что всё содержимое файла robots.txt будет формироваться в соответствующем шаблоне.

В методе robots() проведём только проверку условия запроса. А для этого придётся расширить доступный набор детекторов, потому что, по умолчанию, нет детектора на проверку запроса на наличие расширения .txt. В разделе «Request & Response Objects», документации по CakePHP, подробно описан процесс создания детекторов.

<?php
namespace App\Controller;

use App\Controller\AppController;
use Cake\Http\Exception\NotFoundException;

class PagesController extends AppController
{
    public function beforeFilter(\Cake\Event\Event $event)
    {
        parent::beforeFilter($event);
    }
	
    // Другие методы контроллера
	
    public function robots()
    {
        $this->request->addDetector('extTxt',
            function ($request) {
                return $request->getParam('_ext') === 'txt';
            }
        );
        
        if (!$this->request->is('extTxt')) {
            throw new NotFoundException(__('Not found page'));
        }
    }
}	

Макеты файлов

При использовании компонента RequestHandlerComponent можно в разы упростить работу с представлением. В сочетании с Cake\Routing\Router::extensions(), компонент RequestHandler будет автоматически переключать макеты и шаблоны на те, которые соответствуют типам файлов, отличным от HTML. Подключим компонент в AppController.php:

<?php
namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('RequestHandler');

        // Загрузка других компонентов
    }
}	

Определив расширение файла, класс Вида будет ожидать макет типа /Template/Layout/ext/default.ctp. Соответственно, создадим необходимые макеты /Template/Layout/xml/default.ctp и /Template/Layout/txt/default.ctp, со следующем содержимым:

<?php
echo $this->fetch('content');

Шаблоны файлов

Следуя соглашениям по представлению, шаблоны файлов должны располагаться в директории /Template/Pages/, в соответствующих расширениям поддиректориях.

Шаблон sitemap.ctp

Пример с текущего сайта. Путь к файлу /Template/Layout/Pages/xml/sitemap.ctp

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc><?php echo $this->Url->build(['controller' => 'Pages', 'action' => 'display'], true); ?></loc>
        <priority>1.0</priority>
    </url>
    <url>
        <loc><?php echo $this->Url->build(['controller' => 'Posts', 'action' => 'index'], true); ?></loc>
        <priority>0.8
    </url>
    <?php foreach ($posts as $post): ?>
    <url>
        <loc><?php echo $this->Url->build(['controller' => 'Posts', 'action' => 'view', 'alias' => h($post->alias)], true); ?></loc>
    </url>
    <?php endforeach; ?>
</urlset>

Шаблон robots.ctp

Пример с текущего сайта. Путь к файлу /Template/Layout/Pages/txt/robots.ctp

User-agent: *
Allow: /
Disallow: /admin/

Sitemap: <?php echo $this->Url->build(['controller' => 'Pages', 'action' => 'sitemap', '_ext' => 'xml'], true); ?>

Host: <?php echo $this->Url->build(['controller' => 'Pages', 'action' => 'display'], true); ?>

Обратите внимание на пятую строку кода, где указывается путь до файла Sitemap. При создании URL-адреса файла с расширением, используется параметр _ext.

Итог

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

Рекомендую ознакомиться с плагинами Sitemap для CakePHP, например, CakePHP-Sitemap. В большинстве своём, собственной разработки будет достаточно, но для развития навыков программирования с помощью CakePHP, будет полезно.