Поначалу я думал, что сделаю карту с помощью модуля. Для типовых проектов — лучшего не придумаешь. Достал последнюю версию модуля xmlsitemap под 6-ю версию друпала, настроил типы публикаций и запустил rebuild ссылок.
Но, видимо, публикаций оказалось слишком много (около 50к), и модуль начал что то «пережевывать», показывая прогресс по 100-150 публикаций в секунду. Через какое то время он вовсе прекратил свою работу, так и не создав мне карту.
Если ищите решение для 7-й версии Drupal — то здесь я выложил модифицированный код для неё. Но сначала стоит прочитать статью :).
Зачем делать свою карту, если есть готовые модули?
Неудача с xmlsitemap — лишь повод избавиться еще от одного громоздкого модуля из моей сборки Drupal :).
Карта сайта нужна мне для проекта ГенийМеста. Для него типовая процедура совсем не подходит, так как сайт имеет множество программируемых разделов. Потому для меня это не простая прихоть, а необходимость.
Здесь я не буду показывать код, который подходит только для Гения. А сформулирую задачу для типового проекта.
Постановка задачи
Дефиниций всего три:
- Карта сайта должна находится в корне сайта под именем sitemap.xml.
- В карту должны попасть только заданные типы публикаций, имеющие статус — «опубликовано».
- Карта должна разбиваться на несколько файлов, включающих в себя не более 1000 адресов.
К поставленной задаче я добавил ещё создание специальной страницы в админке, где бы можно было запустить перегенерацию всего списка.
Начнем с функции, которая выводит HTML код этой админ страницы в Drupal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function _admin_xmlsitemap() { //типы материалов, для которых нужна карта //у меня это к примеру вот такие: $types = array('news', 'page', 'works'); $out = '<p>Чтобы перегенерировать xml файлы - нажмите: <a href="?rebuildXML=1">генерация</a></p>'; if (!empty($_GET['rebuildXML'])) { //создали объект-картостроитель, задали типы нод, перестроили! $builder = new xmlsitemap_builder(); $builder->types = $types; $builder->rebuild(); drupal_set_message('Генерация XML карты сайта заверщена. <a href="/sitemap.xml">Посмотреть карту сайта</a>'); } return $out; } |
Функция демонстрирует как использовать класс, код которого я приведу ниже. Вы можете использовать этот код на своей кастомной странице в админке и включить его в hook_cron. И никто не помешает вам дополнить класс необходимым для вашего случая функционалом.
У класса всего один публичный метод — rebuild() — и несколько свойств, которые можно задать перед генерацией xml карты сайта.
$domain — часть урла, содержащая доменное имя вашего сайта. При инициализации берется значение из $_SERVER[‘HTTP_HOST’].
$recordsPerFile — максимально число ссылок в одном файле (1000)
$outputDir — каталог, куда будут записаны файлы со ссылками. Класс пытается выяснить значение public каталога для хранения файлов в Drupal. В нем будет создан подкаталог ‘sitemaps’. Если значение получить не удаётся, то создаётся папка ‘sitemaps’ в корне сайта.
$types — массив типов публикаций, которые нужно включить в карту сайта. По умолчанию, это только ‘page’ — базовый тип для drupal — разделы сайта.
Код класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
/* класс, который займется рутиной */ class xmlsitemap_builder { //домен инициализируется по данным сервера var $domain; //кол-во записей в одном файле var $recordsPerFile = 1000; //каталог для хранения xml-карты, инициализируется //по public хранилищу файлов (обычно: sites/default/files) var $outputDir = ''; //типы публикаций, которые надо включить в карту сайта var $types = array('page'); /* а это массив урлов файлов xml карты, он будет заполнен после генерации */ var $files = array(); //конструктор function xmlsitemap_builder() { $this->domain = 'http://' . $_SERVER['HTTP_HOST']; $this->outputDir = variable_get('file_directory_path', ''); if ($this->outputDir === '') $this->outputDir = 'sitemaps'; else $this->outputDir .= '/sitemaps'; } //генерация карты сайта function rebuild() { if (!file_exists($_SERVER['DOCUMENT_ROOT'] . '/' . $this->outputDir)) mkdir($_SERVER['DOCUMENT_ROOT'] . '/' . $this->outputDir); //получим список нод для xml карты $res = $this->getNodeList(); //главная страница сразу в списке $items = array(' <url> <loc>' . $this->domain . '</loc> <changefreq>daily</changefreq> <priority>1</priority> </url>'); //перебираем список while ($r = mysql_fetch_object($res)) { $items[] = ' <url> <loc>' . $this->domain . '/' . ($r->dst ? $r->dst : 'node/' . $r->nid) . '</loc> <changefreq>weekly</changefreq> <lastmod>' . date('c', $r->changed). '</lastmod> <priority>0.5</priority> </url>'; //пишем XML файл if (count($items) >= $this->recordsPerFile) $this->__buildfile($items); } //пишем остатки if (count($items)) $this->__buildfile($items); //пишем реестр if (!empty($this->files)) $this->__buildreestr(); } private function getNodeList() { //мы "подшиваем" к публикациям их алиасы, //чтобы выбрать всю информацию за один запрос return db_query("SELECT UA.dst, node.nid, node.changed FROM node LEFT JOIN url_alias UA ON UA.src = CONCAT('node/', node.nid) WHERE node.status <> 0 AND node.type IN ('" . implode("','", $this->types) . "')"); } //создание файла-списка private function __buildfile(&$items) { $N = count($this->files); $filepath = $_SERVER['DOCUMENT_ROOT'] . '/' . $this->outputDir . '/sitemap-' . $N . '.xml'; $handle = fopen($filepath, "w"); fwrite($handle, '<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ' . implode('', $items). ' </urlset> '); fclose($handle); //очищаем список $items = array(); //запишем url файла $this->files[] = $this->domain . '/' . $this->outputDir . '/sitemap-' . $N . '.xml'; } //создание файла-реестра private function __buildreestr() { $reestr = array(); foreach ($this->files as $fileurl) { $reestr[] = ' <sitemap> <loc>' . $fileurl . '</loc> <lastmod>' . date('c') . '</lastmod> </sitemap>'; } $filepath = $_SERVER['DOCUMENT_ROOT'] . '/sitemap.xml'; $handle = fopen($filepath, "w"); fwrite($handle, '<?xml version="1.0" encoding="UTF-8"?> <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ' . implode("\n", $reestr). ' </sitemapindex> '); fclose($handle); } } |
Результат мне понравился — скорость создания реестра около секунды (для почти 50к ссылок).