Динамическая генерация XML-карты сайта
Динамический sitemap формируется на лету из базы данных — корректно отражает актуальное состояние сайта, не требует ручного обновления. Критически важен для интернет-магазинов, новостных порталов и любых сайтов с постоянно меняющимся контентом.
Архитектура для больших каталогов
Для сайтов с сотнями тысяч URL sitemap нельзя генерировать за один запрос. Используют chunked подход: каждый раздел — отдельный файл.
// routes/web.php
Route::get('/sitemap.xml', [SitemapController::class, 'index']);
Route::get('/sitemap-products-{page}.xml', [SitemapController::class, 'products']);
Route::get('/sitemap-categories.xml', [SitemapController::class, 'categories']);
Route::get('/sitemap-articles.xml', [SitemapController::class, 'articles']);
Индексный sitemap с подсчётом страниц
public function index(): Response
{
$productPages = ceil(Product::where('is_active', true)->count() / 1000);
$sitemaps = [];
for ($i = 1; $i <= $productPages; $i++) {
$sitemaps[] = [
'loc' => route('sitemap.products', ['page' => $i]),
'lastmod' => now()->format('Y-m-d'),
];
}
$sitemaps[] = ['loc' => route('sitemap.categories'), 'lastmod' => now()->format('Y-m-d')];
$sitemaps[] = ['loc' => route('sitemap.articles'), 'lastmod' => now()->format('Y-m-d')];
return response()
->view('sitemap.index', compact('sitemaps'))
->header('Content-Type', 'application/xml; charset=UTF-8');
}
Чанкированная генерация товаров
public function products(int $page): Response
{
$products = Product::where('is_active', true)
->select(['slug', 'updated_at', 'main_image'])
->orderBy('id')
->forPage($page, 1000)
->get();
return response()
->view('sitemap.products', compact('products'))
->header('Content-Type', 'application/xml; charset=UTF-8')
->header('Cache-Control', 'public, max-age=3600');
}
{{-- resources/views/sitemap/products.blade.php --}}
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
@foreach ($products as $product)
<url>
<loc>{{ url('/products/' . $product->slug) }}</loc>
<lastmod>{{ $product->updated_at->format('Y-m-d') }}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
@if ($product->main_image)
<image:image>
<image:loc>{{ $product->main_image }}</image:loc>
</image:image>
@endif
</url>
@endforeach
</urlset>
Кэширование
public function products(int $page): Response
{
$cacheKey = "sitemap:products:{$page}";
$content = Cache::remember($cacheKey, now()->addHour(), function () use ($page) {
$products = Product::where('is_active', true)
->select(['slug', 'updated_at', 'main_image'])
->orderBy('id')
->forPage($page, 1000)
->get();
return view('sitemap.products', compact('products'))->render();
});
return response($content)->header('Content-Type', 'application/xml; charset=UTF-8');
}
Инвалидация кэша при изменении товара:
// Product observer
public function saved(Product $product): void
{
$page = ceil($product->getKey() / 1000);
Cache::forget("sitemap:products:{$page}");
}
Sitemap для мультиязычных сайтов (hreflang)
<url>
<loc>https://example.ru/products/laptop</loc>
<xhtml:link rel="alternate" hreflang="ru" href="https://example.ru/products/laptop"/>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/products/laptop"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/products/laptop"/>
</url>
Автоматическая отправка в Search Console
После каждого деплоя или по расписанию:
// Artisan command
Http::get('https://www.google.com/ping', [
'sitemap' => url('/sitemap.xml')
]);
Яндекс.Вебмастер — аналогично через https://webmaster.yandex.ru/ping?sitemap=URL.
Исключение из sitemap
Страницы, которые не должны индексироваться: noindex страницы, страницы пагинации, фильтры с параметрами, дубли. Фильтруются на уровне запроса к БД через дополнительные флаги или по паттернам URL.
Срок настройки: 1–2 дня с кэшированием и поддержкой больших каталогов.







