12

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');
Thomas Jensen
  • 2,138
  • 2
  • 25
  • 48
  • http://stackoverflow.com/questions/21949298/how-to-add-slug-and-id-url-to-laravel-4-route – JofryHS Aug 29 '14 at 12:35
  • I've seen that post, and he says: "My goal is to continue using Numbers **but also to use Slugs** for better SEO URL's". I want to use both. – Thomas Jensen Aug 29 '14 at 12:37
  • 1
    It'll probably still be very close to the first answer to that post. In this case you'll probably be best off without `::resource`, since you need to specify extra parameter for your slug. Something like `::controller('articles/{id}/{slug?}', ArticlesController');` might do the trick – JofryHS Aug 29 '14 at 12:40
  • Thanks, I'll give that a try. From the documentation it seems that I can keep the `::resource` route: _If it becomes necessary for you to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to Route::resource:_ http://stackoverflow.com/questions/16661292/add-new-methods-to-a-resource-controller-in-laravel-4/16661375#16661375 – Thomas Jensen Aug 29 '14 at 12:53
  • That is correct, depending on where you need those slug; if it's just for when viewing them, then that'll do. If you need the slug on all your `ArticlesController` method, then you'll end up writing all the `Route` definition before your `::resource`, in that instance `::controller` is the more obvious choice. So it comes back to your case. – JofryHS Aug 29 '14 at 12:59
  • I just need it for viewing a single article, all the rest is admin stuff so SEO is not so important. I am a bit curious to how `/articles/ID/edit` will be handled though... I'll do some testing when I get home and post my final solution :) – Thomas Jensen Aug 29 '14 at 13:04

1 Answers1

12

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
Community
  • 1
  • 1
Thomas Jensen
  • 2,138
  • 2
  • 25
  • 48