Категории
Sitemap XML для Joomla с ЧПУ
09.07.2026 | коды из категории: Joomla
🗺️ Sitemap XML для Joomla
Версия: финальная | PHP 7.0+ | Joomla CMS
📋 Общее описание
Скрипт генерирует XML-карту сайта для Joomla с правильными ЧПУ-адресами на основе структуры меню и категорий. Автоматически определяет приоритеты и частоту обновления для каждой страницы.
🏠 Главная страница
Приоритет 1.0
Частота: daily
📄 Статьи
Динамический приоритет 0.5-0.9
Частота: daily / weekly / monthly
📁 Категории
Приоритет 0.6
Частота: weekly
🔍 Основной функционал
1. Главная страница
- URL:
https://www.doctorneiro.ru/ - Приоритет: 1.0 (максимальный)
- Частота обновления: daily
2. Статьи
- Только опубликованные (
state = 1) - С доступом для всех (
access = 1) - Категория должна быть опубликована
- Автоматический расчет приоритета и частоты обновления:
| Возраст статьи | Приоритет | Частота обновления |
|---|---|---|
| Младше 7 дней | 0.9 | daily |
| Младше 30 дней | 0.7 | weekly |
| Старше 30 дней | 0.5 | monthly |
3. Категории
- Только категории с активными статьями
- Приоритет: 0.6
- Частота обновления: weekly
🗺️ Формирование URL
Приоритеты построения ЧПУ:
-
Путь из меню (самый приоритетный)
✅ Пример: /o-neirologii/kak-lechit-golovu
-
Путь из категории (второй приоритет)
✅ Пример: /golovnye-boli/lechenie-golovnoj-boli
-
Запасной вариант (если нет ЧПУ)
⚠️ Пример: /component/content/article/ID-alias
function buildArticleUrl($article, $menuArticleMap, $db) {
// Приоритет 1: путь из меню
if (isset($menuArticleMap[$article->id])) {
return 'https://www.doctorneiro.ru/' . $menuArticleMap[$article->id];
}
// Приоритет 2: путь из категории
$categoryPath = getCategoryPath($article->catid, $db);
if (!empty($categoryPath)) {
return 'https://www.doctorneiro.ru/' . $categoryPath . '/' . $article->alias;
}
// Приоритет 3: запасной вариант
return 'https://www.doctorneiro.ru/component/content/article/' . $article->id;
}
📊 Структура данных
Основные таблицы Joomla:
📋 #__menu
Пункты меню для получения ЧПУ
id
alias
link
path
📄 #__content
Статьи
id
alias
catid
state
access
📁 #__categories
Категории
id
alias
parent_id
published
🎯 Особенности реализации
🧠 Умный выбор пути
Если статья имеет несколько пунктов в меню — выбирается самый короткий путь
🌳 Рекурсивное построение
Полная иерархия категорий от корня до текущей
🔒 Фильтрация
Проверка дат публикации, доступа и состояния
⚙️ Технические детали
Зависимости
- Joomla Framework (используется Factory::getDbo())
- PHP 7.0+
HTTP заголовки
Обработка ошибок
- Отключены все ошибки (
error_reporting(0)) - Игнорирование исключений в try-catch
📝 Пример вывода
<loc>https://www.doctorneiro.ru/o-neirologii/kak-lechit-golovu</loc>
<lastmod>2026-01-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
🚀 Рекомендации по использованию
💾 Кеширование
Рекомендуется кэшировать результат на 1-24 часа для снижения нагрузки
🔄 Обновление
Запускать при добавлении или изменении статей
🤖 SEO
Обязательно указать в robots.txt: Sitemap: https://www.doctorneiro.ru/sitemap.xml
🔧 Возможные улучшения
- 🌍 Добавить поддержку многоязычности
- 📦 Включить другие типы материалов (компоненты, модули)
- 🖼️ Добавить теги
<image:image>для статей с изображениями - 📄 Реализовать пагинацию для больших сайтов
- ⚡ Добавить кеширование в файл для снижения нагрузки на БД
<?php
/**
* Sitemap XML - с правильными ЧПУ на основе структуры меню и категорий
* Версия: финальная
*/
error_reporting(0);
ini_set('display_errors', 0);
define('_JEXEC', 1);
define('JPATH_BASE', __DIR__);
require_once JPATH_BASE . '/includes/defines.php';
require_once JPATH_BASE . '/includes/framework.php';
use Joomla\CMS\Factory;
header('Content-Type: application/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.doctorneiro.ru/</loc>
<lastmod><?php echo date('Y-m-d'); ?></lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<?php
try {
$db = Factory::getDbo();
// ============================================
// 1. ПОЛУЧАЕМ ВСЕ ПУНКТЫ МЕНЮ (для определения правильных ЧПУ)
// ============================================
$query = $db->getQuery(true);
$query->select('id, alias, link, path')
->from('#__menu')
->where('published = 1')
->where('client_id = 0');
$db->setQuery($query);
$menuItems = $db->loadObjectList();
// Строим карту: ID статьи => путь из меню
$menuArticleMap = [];
foreach ($menuItems as $item) {
if (strpos($item->link, 'view=article&id=') !== false) {
preg_match('/id=(\d+)/', $item->link, $matches);
if (isset($matches[1])) {
$articleId = (int)$matches[1];
$menuPath = trim($item->path, '/');
if (!empty($menuPath)) {
// Если для одной статьи несколько пунктов меню — берём самый короткий (обычно главный)
if (!isset($menuArticleMap[$articleId]) || strlen($menuPath) < strlen($menuArticleMap[$articleId])) {
$menuArticleMap[$articleId] = $menuPath;
}
}
}
}
}
// ============================================
// 2. ФУНКЦИЯ ДЛЯ ПОЛУЧЕНИЯ ПУТИ КАТЕГОРИИ (с родителями)
// ============================================
function getCategoryPath($catId, $db) {
$path = [];
$currentId = (int)$catId;
while ($currentId > 0) {
$query = $db->getQuery(true);
$query->select('id, alias, parent_id')
->from('#__categories')
->where('id = ' . $currentId);
$db->setQuery($query);
$category = $db->loadObject();
if (!$category || empty($category->alias)) {
break;
}
array_unshift($path, $category->alias);
if ($category->parent_id <= 1) {
break;
}
$currentId = $category->parent_id;
}
return implode('/', $path);
}
// ============================================
// 3. ФУНКЦИЯ ДЛЯ ПОСТРОЕНИЯ URL СТАТЬИ
// ============================================
function buildArticleUrl($article, $menuArticleMap, $db) {
// Приоритет 1: если есть путь из меню — используем его
if (isset($menuArticleMap[$article->id])) {
return 'https://www.doctorneiro.ru/' . $menuArticleMap[$article->id];
}
// Приоритет 2: строим из категории
$categoryPath = getCategoryPath($article->catid, $db);
if (!empty($categoryPath) && !empty($article->alias) && $article->alias != 'index.php') {
return 'https://www.doctorneiro.ru/' . $categoryPath . '/' . $article->alias;
}
// Приоритет 3: запасной вариант
return 'https://www.doctorneiro.ru/component/content/article/' . $article->id . '-' . ($article->alias ?: 'article');
}
// ============================================
// 4. ВСЕ АКТИВНЫЕ СТАТЬИ
// ============================================
$query = $db->getQuery(true);
$query->select('a.id, a.alias, a.catid, a.created, a.modified, a.publish_up, a.publish_down, a.state, a.access, c.alias AS category_alias, c.published AS category_published')
->from('#__content AS a')
->leftJoin('#__categories AS c ON a.catid = c.id')
->where('a.state = 1')
->where('a.access = 1')
->where('c.published = 1')
->where('c.access = 1')
->order('a.id DESC');
$db->setQuery($query);
$articles = $db->loadObjectList();
if ($articles) {
foreach ($articles as $article) {
$now = date('Y-m-d H:i:s');
if ($article->publish_up && $article->publish_up > $now) continue;
if ($article->publish_down && $article->publish_down < $now) continue;
if (empty($article->catid) || empty($article->category_alias)) continue;
$date = $article->modified ?: ($article->publish_up ?: $article->created);
$lastmod = date('Y-m-d', strtotime($date));
$days = (time() - strtotime($date)) / 86400;
$priority = $days < 7 ? '0.9' : ($days < 30 ? '0.7' : '0.5');
$freq = $days < 7 ? 'daily' : ($days < 30 ? 'weekly' : 'monthly');
$fullUrl = buildArticleUrl($article, $menuArticleMap, $db);
// Убираем дублирующие слеши на всякий случай
$fullUrl = str_replace('//', '/', $fullUrl);
?>
<url>
<loc><?php echo $fullUrl; ?></loc>
<lastmod><?php echo $lastmod; ?></lastmod>
<changefreq><?php echo $freq; ?></changefreq>
<priority><?php echo $priority; ?></priority>
</url>
<?php
}
}
// ============================================
// 5. ВСЕ КАТЕГОРИИ (только с активными статьями)
// ============================================
// Получаем ID категорий, в которых есть активные статьи
$validCategoryIds = array_unique(array_column($articles, 'catid'));
if (!empty($validCategoryIds)) {
$query = $db->getQuery(true);
$query->select('DISTINCT c.id, c.alias, c.parent_id')
->from('#__categories AS c')
->where('c.id IN (' . implode(',', $validCategoryIds) . ')')
->where('c.published = 1')
->where('c.access = 1');
$db->setQuery($query);
$categories = $db->loadObjectList();
if ($categories) {
foreach ($categories as $cat) {
$categoryPath = getCategoryPath($cat->id, $db);
$fullUrl = 'https://www.doctorneiro.ru/' . ($categoryPath ?: 'category-' . $cat->id);
?>
<url>
<loc><?php echo $fullUrl; ?></loc>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<?php
}
}
}
} catch (Exception $e) {
// Игнорируем ошибки
}
?>
</urlset>