Benjamin Crozat

Written by Benjamin Crozat on .

How to make your URLs unbreakable in a Laravel project

I just can’t remember where I learned this technique. But anyway, I’ll share it.

On this blog, the ID and the slug are part of posts URLs. It looks like https://hello-world.test/blog/1/foo. Whoever write blog posts and impatiently publish them knows that at some point, you’ll probably have to tweak the title, which is problematic if you already shared your post on social media, since the URL will be broken. Some people use this kind of solution. While it works perfectly, I don’t like it. it could be way simpler than that.

Let’s say I changed the title of my blog post from “Foo” to “Bar”. If someone access the old URL (https://hello-world.test/blog/1/foo), we should redirect to (https://hello-world.test/blog/1/bar). Here’s how I do it on this blog:

// routes/web.php

Route::get('/blog/{id}/{slug?}', [PostController::class, 'show’])->name(‘posts.show’);
// app/Http/Controllers/PostController.php

public function show($id, $slug = null)
{
    // Find the post or show a 404.
    $post = Post::findOrFail($id);

    // If someone is using an old slug, redirect to the correct one.
    if ($slug !== $post->slug) {
        return redirect()->route('posts.show', [$id, $post->slug], 301);
    }

    // If not, we present the view as expected.
    return view('posts.show')->with(compact('post'));
}
// tests/Feature/PostsTest.php

/** @test */
public function visitors_are_redirected_to_the_correct_slug() : void
{
    // Create a fake post.
    $post = Post::factory()->forUser()->create();

    // Try to access it with the wrong slug.
    $this->get(route('posts.show', [$post->id, ‘wrong-slug’]))
        // Make sure we’re redirected to the correct URL with the right slug.
	  	->assertStatus(301)
        ->assertLocation(route('posts.show', [$post->id, $post->slug]));
}

Until next time!