How to Create Dynamic XML Sitemap in Laravel

Hello artisans, today I’ll discuss about sitemap in Laravel. We will see step by step how to create sitemap xml without any packages/plugins. And if you heard for a first time about sitemap, I request please check this article on Wikipedia. So, lets get started.

Note: Tested on Laravel 8.67.

Table of Contents

  1. Create Models
  2. Prepare Migrations
  3. Prepare Controller
  4. Create Routes
  5. Create View

Creates Model

Create models, for that run the following command in your terminal.

php artisan make:model Post -m
php artisan make:model Category -m
php artisan make:model SubCategory -m

Prepare Migration

Prepare migrations by modifying the generated migrations file.

database/migrations/2021_10_25_191854_create_posts_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('slug');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

database/migrations/2021_10_25_191910_create_categories_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCategoriesTable extends Migration
{
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('slug');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('categories');
    }
}

database/migrations/2021_10_25_191920_create_sub_categories_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateSubCategoriesTable extends Migration
{

    public function up()
    {
        Schema::create('sub_categories', function (Blueprint $table) {
            $table->id();
            $table->string('slug');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('sub_categories');
    }
}

Finally, run the migration

php artisan migrate

Prepare Controller

Create a controller, for that run the following command in your terminal.

php artisan make:controller SitemapController

Now open the SitemapController and paste the below code.

app/Http/Controllers/SitemapController.php
<?php

namespace App\Http\Controllers;

use App\Models\Category;
use App\Models\Post;
use App\Models\SubCategory;

class SitemapController extends Controller
{
    public function index() {
        $posts = Post::first();
        $categories = Category::all()->first();
        $subcategories = Subcategory::all()->first();
        return response()->view('sitemaps.index', [
            'posts' => $posts,
            'category' => $categories,
            'subcategory' => $subcategories,
        ])->header('Content-Type', 'text/xml');
    }

    public function articles() {
        $post = Post::latest()->get();
        return response()->view('sitemaps.article', [
            'post' => $post,
        ])->header('Content-Type', 'text/xml');
    }

    public function categories() {
        $category = Category::all();
        return response()->view('sitemaps.category', [
            'categories' => $category,
        ])->header('Content-Type', 'text/xml');
    }

    public function subcategories() {
        $subcategory = Subcategory::all();
        return response()->view('sitemaps.subcategory', [
            'subcategories' => $subcategory,
        ])->header('Content-Type', 'text/xml');
    }
}

Create Routes

Create necessary routes for that.

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});


Route::get('/sitemap.xml', [\App\Http\Controllers\SitemapController::class,'index'])->name('sitemap.xml');
Route::get('/sitemap.xml/post', [\App\Http\Controllers\SitemapController::class,'posts'])->name('post.xml');
Route::get('/sitemap.xml/category', [\App\Http\Controllers\SitemapController::class,'category'])->name('category.xml');
Route::get('/sitemap.xml/subcategory', [\App\Http\Controllers\SitemapController::class,'subcategory'])->name('subcategory.xml');


Create Views

Now create a folder inside resources/views named sitemaps and create 4 blade files

  1. index.blade.php
  2. post.blade.php
  3. category.blade.php
  4. subcategory.blade.php

Now paste the following codes into their corresponding view files:

resources/views/sitemaps/index.blade.php
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>

<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <sitemap>
        <loc>http://127.0.0.1:8000/sitemap.xml/post</loc>
    </sitemap>
    <sitemap>
        <loc>http://127.0.0.1:8000/sitemap.xml/category</loc>
    </sitemap>
    <sitemap>
        <loc>http://127.0.0.1:8000/sitemap.xml/subcategory</loc>
    </sitemap>
</sitemapindex>
resources/views/sitemaps/category.blade.php
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    @foreach ($categories as $category)
        <url>
            <loc>http://127.0.0.1:8000/article/category/{{ $category ->slug }}</loc>
            <lastmod>{{ $category->created_at->tz('UTC')->toAtomString() }}</lastmod>
            <changefreq>weekly</changefreq>
            <priority>0.9</priority>
        </url>
    @endforeach
</urlset>
resources/views/sitemaps/subcategory.blade.php
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    @foreach ($subcategories as $subcategory )
        <url>
            <loc>http://127.0.0.1:8000/article/subcategory/{{ $subcategory ->slug }}</loc>
            <lastmod>{{ $subcategory->created_at->tz('UTC')->toAtomString() }}</lastmod>
            <changefreq>weekly</changefreq>
            <priority>0.9</priority>
        </url>
    @endforeach
</urlset>

resources/views/sitemaps/post.blade.php
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    @foreach ($posts as $content)
        <url>
            <loc>http://127.0.0.1:8000/article/{{ $content->slug }}</loc>
            <lastmod>{{ $content->created_at->tz('UTC')->toAtomString() }}</lastmod>
            <changefreq>weekly</changefreq>
            <priority>0.9</priority>
        </url>
    @endforeach
</urlset>

Now just visit the following slug to justify our sitemap content:

  1. http://127.0.0.1:8000/sitemap.xml
  2. http://127.0.0.1:8000/sitemap.xml/post
  3. http://127.0.0.1:8000/sitemap.xml/category
  4. http://127.0.0.1:8000/sitemap.xml/subcategory

That’s all for today. You can download this project from GitHub. Thanks for reading.