You will need to change your controller to also look them up by username instead of id
using username on url instead of id
Good morning
right now i have this in my route to show each profile page
Route::get('/{id}', 'ProfileController@show');
i tried to change the id to username in my route but im getting error
Route::get('/{username}', 'ProfileController@show');
i am trying to make the user's url from
domain.com/id
to
domain.com/username
i will try to do that, thanks
In show method you need to find users by their username instead:
public function show($username)
{
$user = User::whereUsername($username)->firstOrFail();
return View::make('users.show', compact('user'));
}
You could change your app/Providers/RouteServiceProvider.php's boot method:
public function boot( Router $router )
{
parent::boot( $router );
$router->bind( 'username',
function ( $username )
{
return \App\User::where( 'username', $username )->firstOrFail( );
}
);
}
Then you can type-hint the User model as an argument in your controller's show method
public function show( App\User $user )
{
return view('users.show', compact('user'));
}
Laravel will bind the user model with the given username automatically for you in any route you specify the {username} placeholder and type-hint it in the controller's method.
thanks! uhm which one is ideal guys? to use binding or direct to controller?
@Ci,
@Ci binding would be better because, then you will not have to execute the findOrFail() or whereUsername() manually. :)
what about show image by id and username who uploaded it like /{username}/image/{id} ?
Binding allows you to delegate the task of looking for the model to the framework (Laravel), and not repeating yourself every time, so if you implement binding and have these routes:
Route::get('/profile/{username}', 'ProfileController@show');
Route::get('/posts/{username}', 'PostController@show');
As long you type-hint the User class in both controller's methods, Laravel will automatically bind the right model for you, so you don't need to repeat the same line in both of them.
Also, imagine your requirements change and there is a user with a username root that does not want his information avaiable in this app, you could go to the RouteServiceProvider and add this condition just there, like this:
public function boot( Router $router )
{
parent::boot( $router );
$router->bind( 'username',
function ( $username )
{
return \App\User::where( 'username', $username )->where('username', '<>', 'root')->firstOrFail( );
}
);
}
And all routes that use the {username} placeholder will have this new condition automatically. I actually use a similar approach in one app to hide the root (super-admin) user from the app, but it is the database for authentication and password management.
No problem, add a route model binding to your RouteServiceProvider:
public function boot( Router $router )
{
parent::boot( $router );
$router->bind( 'username',
function ( $username )
{
return \App\User::where( 'username', $username )->where('username', '<>', 'chief')->firstOrFail( );
}
);
// the ->model(...) will look for the model by its primary key, by convention an id attribute
$router->model( 'image', 'App\Image' );
}
Then create the route in your routes.php file
Route::get('/{username}/image/{image}', 'ImageController@show')
And in your ImageController@show method type-hint both models:
public function show (User $user, Image $image)
{
return view( 'images.show', compact( 'user', 'image' ));
}
Note that this time I did not specify the full namespace when type-hinting, so you should import them on your controller's file
@rodrigo.pedra thanks bro! but it display a profile page even if there is no username exist in database example i dont have username johndoe in my database and access
domain.com/johndoe
Did you use ->first() or ->firstOrFail() ?
@rodrigo.pedra here is the code that i used
public function boot(Router $router)
{
parent::boot($router);
$router->bind('username',
function($username)
{
return \App\User::where('username', $username)->firstorFail();
}
);
}
i tried both ->first() and ->firstorFail() but whatever i place after the url as username it displays the profile page
->firstOrFail() should have the O capitalized, but just in case, see if your app/Exceptions/Handler.php does automatically redirects back on exceptions.
I tested here and it looks fine.
Can you show your controller's method?
@rodrigo.pedra here is my controller's method, i also tried to capitalized the O in ->firstorFail() but still the same
public function show(User $user)
{
return view('user.setup_profile', compact('user'));
}
do i still need an if else logic there even thou i have ->firstOrFail() ?
Did you make any changes to your app/Exceptions/Handler.php file?
If you look on the implementation on Illuminate\Database\Eloquent\Builder [ https://github.com/illuminate/database/blob/master/Eloquent/Builder.php#L147 ]
public function firstOrFail($columns = array('*'))
{
if ( ! is_null($model = $this->first($columns))) return $model;
throw (new ModelNotFoundException)->setModel(get_class($this->model));
}
It should raise a ModelNotFoundException exception. The only reason I imagine possible is if you are handling exceptions by your own. I was redirecting back within mine.
no i did not made any changes to my handler.php file
here is my RouterServiceProvider
<?php namespace App\Providers;
use Illuminate\Routing\Router;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider {
/**
* This namespace is applied to the controller routes in your routes file.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
public function boot(Router $router)
{
parent::boot($router);
$router->bind('username',
function($username)
{
return \App\User::where('username', $username)->firstOrFail();
}
);
}
/**
* Define the routes for the application.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
public function map(Router $router)
{
$router->group(['namespace' => $this->namespace], function($router)
{
require app_path('Http/routes.php');
});
}
}
here is my handler.php file
<?php namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler {
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
'Symfony\Component\HttpKernel\Exception\HttpException'
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
* @return void
*/
public function report(Exception $e)
{
return parent::report($e);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
return parent::render($request, $e);
}
}
It seems fine... really weird, check these laracasts:
https://laracasts.com/series/laravel-5-fundamentals/episodes/18
around 6:20 @JeffreyWay uses the ->bind(...) method
and
https://laracasts.com/series/laravel-5-from-scratch/episodes/8
around 8:50 he builds a bind in a similar way than yours
Also the docs [ http://laravel.com/docs/5.0/routing#route-model-binding ] states:
Note: If a matching model instance is not found in the database, a 404 error will be thrown.
If you wish to specify your own "not found" behavior, pass a Closure as the third argument to the
modelmethod:Route::model('user', 'User', function() { throw new NotFoundHttpException; });If you wish to use your own resolution logic, you should use the
Router::bindmethod. The Closure you pass to thebindmethod will receive the value of the URI segment, and should return an instance of the class you want to be injected into the route:Route::bind('user', function($value) { return User::where('name', $value)->first(); });
@rodrigo.pedra yeah it is weird i dont know why it is not using the firstOrFail() i will check the laracast video again to see if i missed some code
Try doing this in your RouteServiceProvider and paste the results after accessing your route:
public function boot(Router $router)
{
parent::boot($router);
$router->bind('username',
function($username)
{
$query = \App\User::where('username', $username);
$user = $query->firstOrFail();
dd($query->toSql(), $user); // die and dump to check the returned record
return $user;
}
);
}
@rodrigo.pedra nothing happens i can still see the profile page whatever i type after domainname.com/
@rodrigo.pedra bro i tried this and still i can see whatever i type after domainname.com/ why is that like that? that means the bind() method is not working :(
public function boot(Router $router)
{
parent::boot($router);
}
route
Route::get('/{username}', 'UserController@show');
@rodrigo.pedra man this thing works now! it ruined my night spend 4hours before i find out why.. upon searching on google i have to run this php artisan optimize --force to make it work but i wonder why is it like that..... oh my god why is that it was not mentioned on the laracast video hahahaha
I'm new to laravel and just searched how to get to a user profile using username instead of id as key and found this in laravel documentation : https://laravel.com/docs/8.x/routing#customizing-the-default-key-name
Hope this helps !
Really? Try restarting your server, although it is not needed...
Something is preventing Laravel to trigger the binding. In which order are your declaring your routes? It could be have the routes conflicting.
I find this stackoverflow question similar to your question: [ http://stackoverflow.com/questions/26460171/issues-with-laravel-5s-routebind ].
And the answer is basically the same we are discussing here :(
In one of the comments a user suggests to clear the route cache, you can do this by running php artisan route:clear in your terminal.
Also, did you change your RouteServiceProvider? The boot method is empty in your last comment.
One thing we can try to check if something is conflicting is using a different parameter name in your RouteServiceProvider and in your route definition, like this:
// RouteServiceProvider.php
public function boot( Router $router )
{
parent::boot( $router );
$router->bind( 'username2',
function ( $username2 )
{
return \App\User::where( 'username', $username2 )->firstOrFail();;
}
);
}
And in your routes.php
// routes.php
Route::get( '/profile/{username2}', 'UserController@show');
Also, check if the RouteServiceProvider is registered in the config/app.php providers key, it should be there by default [ https://github.com/laravel/laravel/blob/master/config/app.php#L146 ].
im still wondering why it respond like that, my RouteServiceProvider is registered in config/app.php
here is my current boot
public function boot(Router $router)
{
parent::boot($router);
//$router->bind('username',function($username)
//{
// return \App\User::published()-findOrFail($username);
//});
//$router->model('username','App\User');
$router->bind('username', function($username)
{
return \App\User::where('username', $username)->firstOrFail();
}
);
}
i tried to add if else to redirect the user on a 404 page but it just gave me error, i think the route cache is not auto updating when change is made on RouteServiceProvider
No query results for model [App\User].
Nice! :D
Please or to participate in this conversation.