2
votes

I'm having a little problem trying out Laravel 5.

I followed the whole tutorial in laracasts and tried to do everything the same exact way they do it there, only I changed the name of the model, table and controller.

At some point something with Route::bind() stopped working and because of that, now when I try to access a route with a Wild Card, the view will come up, but without the data.

This is my routes.php

Route::bind('singers', function($slug, $route){
return App\Singer::whereSlug($slug)->first();
});

Route::resource('singers', 'SingerController', [
                                            'names' => [
                                                    'index' => 'singers_index',
                                                    'show' => 'singers_show',
                                                    'edit' => 'singers_edit',
                                                    'update' => 'singers_update',
                                                    'create' => 'singers_create',
                                                    'store' => 'singers_store',
                                                    'destroy' => 'singers_destroy',
                                                        ],
                                                ]);

These are some snippets of my SingerController

namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use Illuminate\Http\Request;
use App\Http\Requests\CreateSingerRequest;
use App\Singer;

class SingerController extends Controller {

    public $restful = true;

    public function __construct(Singer $singer){
        $this->singer = $singer;
    }

    public function index()
    {
        $singers = $this->singer->orderBy('id', 'DESC')->get();
        return view('singers.list',compact('singers'));
    }

    public function show(Singer $singer){
        return view('singers.show', compact('singer'));
    }

    public function edit(Singer $singer){
        return view('singers.edit', compact('singer'));
    }

    public function update(Singer $singer, Request $request){
        $singer->fill($request->input());
        $singer->save();
        return view('singers.show', compact('singer'));
    }

    public function create(){
        return view('singers.new');
    }

    public function store(Singer $singer, CreateSingerRequest $request){
        $singer->create($request->all());
        return redirect()->route('singers_index');
    }

    public function destroy(Singer $singer){
        $singer->delete();
        return redirect()->route('singers_index');
    }
}

Now. The reason I try to bind the variable 'singers' in routes.php, is because they do it in the videos, and that way, the code in the Controller is shorter. And it WAS actually working. And then I just added the destroy function and it all stopped working. As I said, I can see the views, the tags, and any other text in them, but not the data that I pass, except for the index function, since I actually do the Eloquent search in the function itself.

Now here's a snippet of my show.blade.php

<b>Full Name: </b> {{ $singer->name.' '.$singer->lastname }}
<br>
<b>Age: </b> {{ $singer->age }}
<br>
<b>Country: </b> {{ $singer->country }}
<br>
<br>
<p>
    <b>Bio: </b> {!! nl2br($singer->bio) !!}
</p>

{!! HTML::linkRoute('singers_edit', 'Update', [$singer->slug], ['class' => 'btn btn-primary']) !!}

{!! Form::open(['method'=>'DELETE', 'route'=>['singers_destroy', $singer->slug]]) !!}
    <div class="form-group">
        {!! Form::submit('Delete', ['class'=>'btn btn-danger']) !!}
    </div>
{!! Form::close() !!}

My index view works fine, and the other views are just forms, but any view that I'm passing variables to isn't working, whether I pass it by doing this:

return view('singers.show',compact('singer'));

or this:

return view('singers.show')->with('singer',$singer);

So to recap:

  • index -> fine.
  • show -> won't show any data.
  • create -> actually works and the new record is saved.
  • edit -> error comes up because the wildcard isn't sent to the controller.

Edit

Paths

  • index (GET): /singers
  • show (GET): /singers/{singers}
  • create (GET): /singers/create
  • store (POST): /singers
  • edit (GET): /singers/{singers}/edit
  • update (PATCH): /singers/{singers}
  • destroy (DELETE): /singers/{singers}

Keep in mind that the {singers} wildcard is actually $singer->slug in each case, but the Route::bind() function doesn't allow me to call it whatever I want. And of course before the first slash comes myserver/myproject/public

2
What exactly url do you use for show and update?Marcin Nabiałek
show route -> localhost/project/public/singers/{singers} and edit route -> localhost/project/public/singers/{singers}/edit I will edit my post so you can see them betterarrigonfr

2 Answers

1
votes

I've just tested it.

Singer model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Singer extends Model {

}

SingerController - exact code you provided (this file was placed of course in app\Http\Controllers path.

In routes.php :

Route::bind('singers', function($slug, $route){
   return App\Singer::whereSlug($slug)->first();
});

Route::resource('singers', 'App\Http\Controllers\SingerController', [
    'names' => [
        'index' => 'singers_index',
        'show' => 'singers_show',
        'edit' => 'singers_edit',
        'update' => 'singers_update',
        'create' => 'singers_create',
        'store' => 'singers_store',
        'destroy' => 'singers_destroy',
    ],
]);

As you see I've added here full namespace path: 'App\Http\Controllers\SingerController' and not only SingerController.

I'ce created simple Singer table, with fields just id and slug - just for test (so I have only those 2 columns) and added 2 records:

id slug
1  abc
2  def

Views I've created:

list.blade.php:

@foreach ($singers as $s)
{{ $s->slug }}<br />
@endforeach

edit.blade.php and show.blade.php (exact same code):

{{ $singer->slug }}
{{ $singer->id }}

Now when I run:

http://testpro/singers I get result:

def
abc

as expected.

When I run:

http://testpro/singers/abc/edit

I get:

abc 1

as expected.

And when I run:

http://testpro/singers/def

I get:

def 2

as expected.

Everything seems to work fine here. Make sure you are using it the way I showed and it should work without any problem

0
votes

I'd be curios to see know if when you edited it back did you actually edit it or use a copy of the original. I just did that same tutorial and was totally stumped by Jeffrey's Magic, but looking at the API specifically on model binding here

http://laravel.com/docs/master/routing#route-model-binding

the critical point is your "wild card" has to be triggered in the active route for the bind anonymous function to fire.

In my case, using the original nominclature I had a wildcard of {slug}, but earlier and later he changes it to {song}. Somehow, I had left mine at song, which had no effect when I was re-assigning the variable.

$song = $this->song->whereSlug($slug)->first();

However, once you do the router bind, it will never catch because you are using the slug as your wildcard.

$router->get('songs/{slug}', 'SongsController@show');

So when you bind that bad boy to Route::bind('song', function($slug) //The first literal "slug" MUST be a literal { //in the route we are searching!!! return \App\Song::whereSlug($slug)->first(); }); It fails, becasue my wildcard filter is never matched, however change that song to slug and magic!

Route::bind('slug', function($slug) //The first literal "slug" MUST be a literal { //in the route we are searching!!! return \App\Song::whereSlug($slug)->first(); });

So, my guess is you had a type-O somewhere and corrected it in your edits. It's subtle, especially using so many literals, and with no code type hinting there. But thats ok. We can't all be Jeffrey Way. So if your bind fails, double check that first.

Good Luck.