Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use both ID and slug in Laravel 4 route URL? (resource/id/slug)

Tags:

php

seo

laravel-4

I would like to use both ID and slug in my articles route. So instead of /articles/ID I want /articles/ID/slug.

I don't actually need the slug variable for anything; it's just there to make the URL more readable and SEO, so I'll be using ID as identifier for retrieving the articles.

If the URL /articles/ID is entered I want to redirect to /articles/ID/slug. There has to be an exception for /articles/ID/edit since this opens the form for editing the article.

I've googled and looked this site, but I've only found examples of replacing ID with slug, not include both.

How can I achieve this? And can I use the URL class to get the full URL (/articles/ID/slug) for an article?

Current route config:

Route::resource('articles', 'ArticlesController');
like image 337
Thomas Jensen Avatar asked Aug 29 '14 12:08

Thomas Jensen


1 Answers

So, here is what I ended up doing:

routes.php, created a custom route for show and edit. Used a resource for the rest:

Route::pattern('id', '[0-9]+');

Route::get('articles/{id}/{slug?}', ['as' => 'articles.show', 'uses' =>   'ArticlesController@show']);
Route::get('articles/edit/{id}', ['as' => 'articles.edit', 'uses' => 'ArticlesController@edit']);
Route::resource('articles', 'ArticlesController', ['except' => ['show', 'edit']]);

Controller, added a slug input parameter with a default value. Redirect the request if the slug is missing or incorrect, so it will redirect if the title is changed and return a HTTP 301 (permanently moved):

public function show($id, $slug = null)
{
    $post = Article::findOrFail($id);

    if ($slug != Str::slug($post->title))
        return Redirect::route('articles.show', array('id' => $post->id, 'slug' => Str::slug($post->title)), 301);

    return View::make('articles.show', [
        'article' => Article::with('writer')->findOrFail($id)
    ]);
}

View presenter, I originally had something in my model class. But moved it to a view presenter class on the basis of this answer: https://stackoverflow.com/a/25577174/3903565, installed and used this: https://github.com/laracasts/Presenter

public function url()
{
    return URL::route('articles.show', array('id' => $this->id, 'slug' => Str::slug($this->title)));
}

public function stump()
{
    return Str::limit($this->content, 500);
}

View, get the url from the view presenter:

@foreach($articles as $article)
    <article>
        <h3>{{ HTML::link($article->present()->url, $article->title) }} <small>by {{ $article->writer->name }}</small></h3>
        <div class="body">
            <p>{{ $article->present()->stump }}</p>
            <p><a href="{{ $article->present()->url }}"><button type="button" class="btn btn-default btn-sm">Read more...</button></a></p>
        </div>
    </article>
@endforeach
like image 106
Thomas Jensen Avatar answered Oct 13 '22 01:10

Thomas Jensen