Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can route model binding be used with RESTful controllers?

I have been using RESTful controllers in my Laravel project. By including:

Route::controller('things', 'ThingController')

in my routes.php, I can define functions in the ThingController like:

public function getDisplay($id) {
    $thing = Thing::find($id)
    ...
}

so that GETting the URL "...things/display/1" would automatically be directed to the controller function. This seems pretty handy and has been working great for me so far.

I noticed many of my controller functions start with getting a model by id from the url, and I thought it would be nice to be able to use route model binding to do this for me instead. So I updated my routes.php to

Route::model('thing', 'Thing');
Route::controller('things', 'ThingController')

and changed the ThingController functions to

public function getDisplay($thing) {
    ...
}

I assumed this would magically work the way I wanted it to (like everything else I've tried so far in Laravel has) but unfortunately I get "Trying to get property of non-object" when I attempt to use $thing in the function. Is this something that should be able to work and I have just done it wrong, or can route model binding only work with routes explicitly named in routes.php?

like image 339
Don't Panic Avatar asked Jun 24 '14 22:06

Don't Panic


2 Answers

If you don't mind with URI path, method name and just work only show, edit and update method, you can use Resource Controller to generate URI string which can define model binding.

In routes.php change to

Route::model('things', 'Thing');
Route::resource('things', 'ThingController');

You can use php artisan routes command to see all URIs

$ artisan routes | grep ThingController
GET|HEAD things                | things.index               | ThingController@index
GET|HEAD things/create         | things.create              | ThingController@create
POST things                    | things.store               | ThingController@store
GET|HEAD things/{things}       | things.show                | ThingController@show
GET|HEAD things/{things}/edit  | things.edit                | ThingController@edit
PUT things/{things}            | things.update              | ThingController@update
PATCH things/{things}          |                            | ThingController@update

After that you can threat parameter as Thing object without explicitly name route.

/**
 * Display the specified thing.
 *
 * @param  Thing  $thing
 * @return mixed
 */
public function show(Thing $thing)
{
    return $thing->toJson();
}

If you want to access ThingController@show, pass your model ID and Laravel will retrieve it automatically.

http://example.com/things/1

{"id":1,"type":"Yo!"}
like image 147
kunemata Avatar answered Oct 08 '22 19:10

kunemata


You can use Route:resource and still provide other methods. Place the route you need just before that particular Route::resource line. Eg:

Route::model('things', 'Thing');
Route::get('things/{things}/owner', 'ThingController@getOwner');
Route::resource('things', 'ThingController');

Then create the corresponding method in your controller.

public function getOwner($things) {
    return Response::json($things->owner()->get());
}

Here is the official documentation from the Laravel 4.2 docs:

Source: http://laravel.com/docs/controllers#resource-controllers

Adding Additional Routes To Resource Controllers

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:

Route::get('photos/popular');
Route::resource('photos', 'PhotoController');
like image 37
Kwabena Adu Avatar answered Oct 08 '22 20:10

Kwabena Adu