Skip to main content

Using the new Laravel missing function to prevent link rot

Taylor Otwell recently introduced a new route helper function, eloquently named missing.
Using the new method it is possible to catch ModelNotFoundExceptions and change the default 404 behavior to something more suitable.

You can read more about the new function on the Laravel docs: Customizing missing model behavior.

Preventing link rot when changing slugs

Let's assume we recently published a new blog post. It's actively being linked to, it's getting shared on Twitter, everything is great. However, what if we need to change the existing slug? All those links are going to be broken.

Here's what I came up with to prevent this:

Route::get('blog/{post}', [PostController::class, 'show'])->name('posts.show')
    ->missing(function (\Illuminate\Http\Request $request) {
        return Redirect::fromRequest($request); // Note: NOT the Redirect facade
    });

Whenever a slug cannot be found, I forward the request to a custom Redirect model to handle the request.

This is what the Redirect model looks like:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class Redirect extends Model
{
    public $timestamps = false;

    public function post()
    {
        return $this->belongsTo(Post::class);
    }

    public static function fromRequest(Request $request): RedirectResponse
    {
        $slug = $request->route()->parameter('post');

        $redirect = static::with('post')->where('slug', $slug)->first();

        // No redirect was found
        if ($redirect === null) {
            abort(404);
        }

        // A redirect with an unpublished post was found
        if ($redirect->post === null) {
            abort(404);
        }

        return redirect()->route('posts.show', $redirect->post);
    }
}

Let's demystify the code snippet by having a closer look at the fromRequest method: First, I grab the value of the post parameter from the route I defined earlier.
Then I check in the database whether a redirect for this slug exists. When checking, I already eager load the related post to prevent unnecessary database queries. Now the only thing that is left to do, is check whether a 'redirect' and its related post were found and to return a redirect response accordingly.

Conclusion

By adding old slugs to the redirects table and utilizing the missing method in combination with the fromRequest method we can prevent link rot by redirecting outdated links to the correct location.

Published on March 3, 2021
Last modified on March 15, 2021

Did you like what you read? Feel free to share! Make sure to follow me on Twitter to stay in the loop.