DEV Community

Discussion on: How to Create a Sitemap in Laravel for a Website that Contains Thousands of Records.

Collapse
 
lito profile image
Lito • Edited

The idea is great!

Here my version with months instead alphabet:

# Routes
Route::get('/sitemap.xml', SitemapIndexController::class)->name('sitemap.index');
Route::get('/sitemap/posts.xml', SitemapPostsIndexController::class)->name('sitemap.posts.index');
Route::get('/sitemap/posts/{date}.xml', SitemapPostsShowController::class)->name('sitemap.posts.show');
Enter fullscreen mode Exit fullscreen mode

<?php declare(strict_types=1)

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Response;

class SitemapIndexController extends Controller
{
    public function __invoke(): Response
    {
        $post = Post::select('published_at')
            ->orderBy('published_at', 'DESC')
            ->first();

        return response()->view('sitemap.index', [
            'post' => $post,
        ])->header('Content-Type', 'text/xml');
    }
}
Enter fullscreen mode Exit fullscreen mode

<?xml version="1.0" encoding="UTF-8"?>

<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    @if ($post)
    <sitemap>
        <loc>{{ route('sitemap.posts.index') }}</loc>
        <lastmod>{{ $post->updated_at->tz('UTC')->toAtomString() }}</lastmod>
    </sitemap>
    @endif
</sitemapindex>
Enter fullscreen mode Exit fullscreen mode

<?php declare(strict_types=1)

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Response;

class SitemapPostsIndexController extends Controller
{
    public function __invoke(): Response
    {
        $months = Post::selectRaw('DATE_FORMAT(`published_at`, "%Y-%m") AS `date`, MAX(`published_at`) AS `published_at`')
            ->groupBy('date')
            ->get();

        return response()->view('sitemap.posts.index', [
            'months' => $months,
        ])->header('Content-Type', 'text/xml');
    }
}
Enter fullscreen mode Exit fullscreen mode

<?xml version="1.0" encoding="UTF-8"?>

<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    @foreach ($months as $month)
    <sitemap>
        <loc>{{ route('sitemap.posts.show', $month->date) }}</loc>
        <lastmod>{{ $month->published_at->tz('UTC')->toAtomString() }}</lastmod>
    </sitemap>
    @endforeach
</sitemapindex>
Enter fullscreen mode Exit fullscreen mode

<?php declare(strict_types=1)

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class SitemapPostsShowController extends Controller
{
    public function __invoke(Request $request): Response
    {
        $posts = Post::select('id', 'published_at')
            ->whereRaw('DATE_FORMAT(`published_at`, "%Y-%m") = ?', [$request->input('date')])
            ->get();

        return response()->view('sitemap.posts.show', [
            'posts' => $posts,
        ])->header('Content-Type', 'text/xml');
    }
}
Enter fullscreen mode Exit fullscreen mode

<?xml version="1.0" encoding="UTF-8"?>

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    @foreach ($posts as $post)
    <url>
        <loc>{{ route('posts.show', $post->id) }}</loc>
        <lastmod>{{ $post->updated_at->tz('UTC')->toAtomString() }}</lastmod>
        <changefreq>weekly</changefreq>
        <priority>0.6</priority>
    </url>
    @endforeach
</urlset>
Enter fullscreen mode Exit fullscreen mode

Thanks for sharing!

Collapse
 
cwraytech profile image
Christopher Wray

I really like your method! Thank you!