Peter Amo
Peter Amo

Reputation: 221

How to change Route Model Binding finding data from id to slug

I have a route like the following.

Route::get('/articles/{articleSlug}' , 
    [App\Http\Controllers\ArticleController::class, 'single']);

And the method of single() at ArticleController class goes here:

public function single($slug)
{
    $article = Article::where('slug',$slug)->first();
    $article->increment('viewCount');

    return view('home.article',compact('article'));
}

Now I wish to use Route Model Binding for finding this data from the articles table based on the column slug. But as I know, Route Model Binding finds data based on the id. So how to change Route Model Binding finding data from id to slug ONLY for ArticleController.php (meaning that the other Controller classes can work with id as route model binding)?

Upvotes: 4

Views: 3551

Answers (5)

Wael Khalifa
Wael Khalifa

Reputation: 935

you can add bind method to your model boot() like this

public function boot()
{
    Route::bind('article', function ($value) {
        return Article::where('slug', $value)->firstOrFail();
    });
 
}

to learn more about it read this section in the Laravel docs https://laravel.com/docs/9.x/routing#customizing-the-resolution-logic

Upvotes: 0

Muhammad Huzaifa
Muhammad Huzaifa

Reputation: 101

you can customize route model bindings directly in the route definition:

  1. past given code in app/model/Article.php:

    public function getRouteKeyName() { return 'slug'; }

2.when you use slug change route to

 Route::get('/articles/{article:slug}' , [App\Http\Controllers\ArticleController::class, 'single']);
  1. to use id sample change slug to id

    Route::get('/articles/{article:id}' , [App\Http\Controllers\ArticleController::class, 'single']);

Upvotes: 0

Yves Kipondo
Yves Kipondo

Reputation: 5623

In case you want to use other model field as the biding attribute instead of id you can define a getRouteKeyName which return the name of the field which must be use

class Article extends Model {
    // other methods goes here
    public function getRouteKeyName() {
        return 'slug';
    }
}

Or you can pass the field name directly when you define the route like this

Route::get('/articles/{article:slug}' , [App\Http\Controllers\ArticleController::class, 'single']);

With this code inside of your controller you must ensure that the name provide as parameter in the route definition match the name of the controller argument

public function single(Article $article)
{
    $article->increment('viewCount');

    return view('home.article',compact('article'));
}

Upvotes: 7

Behzad
Behzad

Reputation: 572

change your route to this:

Route::get('/articles/{article:slug}' , [App\Http\Controllers\ArticleController::class, 'single']);

and then inject the Article model to your controller function and let laravel do the rest for you:

public function single(Article $article)
    {
        $article->increment('viewCount');
        return view('home.article',compact('article'));
    }

Upvotes: 1

gowl
gowl

Reputation: 334

Your controller is already set up, all you need to do is change your variable name to $slug in the route, and I believe that should be enough:

Route::get('/articles/{slug}' , [App\Http\Controllers\ArticleController::class, 'single']);

Upvotes: 0

Related Questions