简体   繁体   中英

Laravel 5: route-model-binding not recognizing object

I have a search() method in SubredditController

public function search(Request $request, Subreddit $subreddit)
{
    $query = $request->input('search');
    $subreddit = Subreddit::with('posts.votes')->with('moderators.user')->where('id', 24)->first();
    $posts = $subreddit->posts()->where('title', 'LIKE', '%' . $query . '%')->get();
    $isModerator = $subreddit->moderators()->where('user_id', Auth::id())->exists();
    $modList = Moderator::where('subreddit_id', '=', $subreddit->id)->get();

    return view('subreddit.search', compact('query', 'subreddit', 'posts', 'isModerator', 'modList'));
}

As you can see $subreddit is getting all posts from subreddit ID 24 and the query works.

But when I replace 24 with $subreddit->id the query fails and gives null.

EDIT: Looks like it works on $modList because I have hardcoded it 24 in the first variable $subreddit but that still doesn't answer why $subreddit->id is inaccessible when it works fine on all other methods of the same controller

EDIT 2: I have also tried changing the search route to accept a parameter, like this Route::post('search/{subreddit}') but that gives the error NotFoundHttpException and the URL redirects to localhost/reddit/public/search/%7Bsubreddit%7D it's not recognizing any parameter.

EDIT 3: if I change the search route to Route::post('subreddit/{id}/search', ...) I get no errors, but $subreddit->id remains inaccessible and the URL points to subreddit/%7Bid%7D/search (with %7B characters)

These are my bindings

$router->model('subreddit', 'App\Subreddit');
$router->model('posts', 'App\Post');
$router->model('moderators', 'App\Moderator');

My Routes

Route::resource('subreddit', 'SubredditController');

Route::resource('subreddit.moderators', 'ModeratorsController');

Route::get('mysubreddits', [
    'as' => 'mysubreddits',
    'uses' => 'SubredditController@mySubreddits'
]);

Route::post('search', ['as' => 'search', 'uses' => 'SubredditController@search']);

Route::resource('posts', 'PostsController');
Route::resource('votes', 'VotesController');
Route::resource('profile', 'ProfilesController');

The view (which is a partial) from where I'm sending the search request

<h4>Search {{ $subreddit->name }}</h4>
{!! Form::open(['action' => 'SubredditController@search']) !!}
<div id="custom-search-input">
    <div class="input-group col-md-12">
        <input type="text" name="search" class="search-query form-control" placeholder="Search" />
            <span class="input-group-btn">
                <button class="btn btn-danger" type="submit">
                    <span class=" glyphicon glyphicon-search"></span>
                </button>
            </span>
    </div>
</div>
{!! Form::close() !!}

You appear to be assuming that typehinting a model will automatically somehow give you the model in your route. However a few things need to be done in order for route model binding to work:

  1. You actually need to include the parameter in your route specification.
  2. You need to ensure you pass the ID (or whatever field you you in your model binding function) in the URL.

So, given your code the following changes are required:

Route:

Route::post('search/{subreddit}', 'SubredditController@search')->name('search');

This actually adds a the placeholder for the parameter, and names it the same as your model's route binding.

(I have also taken the liberty of rearranging the line to not have the awkward options-as-array syntax, though depending on your Laravel version this may not be doable for you.)

View:

{{ Form::open(['route' => ['search', $subreddit->getKey()]]) }}

This ensures that the first parameter to the route named search will contain the Subreddit 's ID (key) value.

These two things, together, will mean that the URI ends up being something like /search/24 which will be converted into the SubredditController@search with method parameter typehinted as Subreddit pre-filled with a real model (ID of 24).


Note: if you don't wish to use IDs in your URIs but instead something like a slug field, you can still do this - bind your route as normal, but on your model define a getRouteKeyName method:

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

And then just change the view's Form::open to this:

{{ Form::open(['route' => ['search', $subreddit->getRouteKey()]]) }}

Again, this may be 5.1 specific and you may be using 5.0 which may or may not support doing it this way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM