Why is it passing a param when it should not - Laravel Routing - laravel

Bizarre issue, lets see some routes:
Route::get('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController#index']);
Route::get('/admin/races/{race}', ['as' => 'races.race', 'uses' => 'RacesController#show']);
Route::get('/admin/races/create', ['as' => 'races.create', 'uses' => 'RacesController#create']);
Route::get('/admin/races/{race}/edit', ['as' => 'races.edit', 'uses' => 'RacesController#edit']);
Seems normal enough, lets see the controller:
class RacesController extends Controller {
public function index() {
return view('admin.races.list');
}
public function show(GameRace $race) {
return view('admin.races.race', [
'race' => $race,
]);
}
public function create() {
return view('admin.races.manage', [
'race' => null,
]);
}
public function edit(GameRace $race) {
return view('admin.races.manage', [
'race' => $race,
]);
}
}
Seems normal enough. The issue is:
When I go to /admin/races/create I get a 404. The reason being is because, exception:
Illuminate\Database\Eloquent\ModelNotFoundException^ {#851
#model: "App\Flare\Models\GameRace"
#ids: array:1 [
0 => "create"
]
#message: "No query results for model [App\Flare\Models\GameRace] new"
#code: 0
#file: "./vendor/laravel/framework/src/Illuminate/Routing/ImplicitRouteBinding.php"
#line: 47
trace: {
.....
Why is calling:
<li>Create Race</li>
Causing Laravel to take the word create and inject it in as a model? No other route that I have, that is similar does this. For context, here's how we create items:
Route::get('/admin/items/create', ['as' => 'items.create', 'uses' => 'ItemsController#create']);
Same concept, just instead of races its items. So how laravel messing this up?
I have run all the cache clears and route clears and everything. Same issue. Even tests are failing on this. No where am I calling this with a param (especially not one called create) so it should not be assuming there is a param.

It's because you have defined Route::get('/admin/races/{race}' .. first, so it'll hit that route regardless of what the value is. Simply moving the create route before the show route will solve your problem.
Route::get('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController#index']);
Route::get('/admin/races/create', ['as' => 'races.create', 'uses' => 'RacesController#create']);
Route::get('/admin/races/{race}', ['as' => 'races.race', 'uses' => 'RacesController#show']);
Route::get('/admin/races/{race}/edit', ['as' => 'races.edit', 'uses' => 'RacesController#edit']);
That said, you can simplify this a lot more with a simple resource-route.
Route::resource('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController'])->only("index", "show", "create", "edit");

An alternative solution to the accepted answer is to specify conditions for the parameter in the parametrised route. For example if {race} needs to be numeric you can do:
Route::get('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController#index']);
Route::get('/admin/races/{race}', ['as' => 'races.race', 'uses' => 'RacesController#show'])->where('race', '\d+');
Route::get('/admin/races/create', ['as' => 'races.create', 'uses' => 'RacesController#create']);
Route::get('/admin/races/{race}/edit', ['as' => 'races.edit', 'uses' => 'RacesController#edit']);
This is useful in the cases where you can't control the order of the routes (e.g. there's a package registering routes that conflict).

Related

Laravel route gets wrong controller

I'm building project with Laravel and Vue and i want my categories and tags urls to be like that:
domain.com/some-tag
domain.com/some-category
My web.php:
Route::get('/', ['uses' => '\App\Http\Controllers\IndexController#index']);
Route::get('/{category}', ['as' => 'category', 'uses' => '\App\Http\Controllers\CategoryController#index']);
Route::get('/{tag}', ['as' => 'tag', 'uses' => '\App\Http\Controllers\TagController#index']);
Route::get('/{category}/{article}', ['as' => 'category.article', 'uses' => '\App\Http\Controllers\ArticleController#index']);
I'm getting 404 error on my tags links and i know its because router matches "category" first and uses CategoryController.
What should I do? I don't want to make them unique by adding something like domain.com/tags/tag-name
I've tried to use named routes for my vue component (with Ziggy-js lib) so my link looks like
<a class="tags-block__link" :href="route('tag', {tag: tag.slug}).url()" v-for="tag in tags" :key="tag.id">
But it doesn't help
Why it should not be mixing?
You define Route::get('/{category}' and Route::get('/{tag}'. So if you open /1 in your browser it will always run the first route it is able to find that matches the pattern. So it is always running CategoryController#index yes?
Your routes should be:
Route::get('/category/{category}', ['as' => 'category', 'uses' => '\App\Http\Controllers\CategoryController#index']);
Route::get('/tag/{tag}', ['as' => 'tag', 'uses' => '\App\Http\Controllers\TagController#index']);
Read more at https://laravel.com/docs/7.x/routing
The remaining route should do fine, cause you define it last.

Laravel how can make a query string but not like url path?

I'm trying to make an url query string with laravel but this method:
url()->to('categories', ['id' => 1, 'name' => 'cars']);
Returns me:
http://localhost/categories/1/cars
But I need this:
http://localhost/categories?id=1&name=cars
First of all, you'll need to create a named route without parameters:
Route::get('categories', ['as' => 'categories', 'uses' => 'SomeController#showCategories']);
Then use route() helper:
route('categories', ['id' => 1, 'name' => 'cars']);
This will generate:
'/categories?id=1&name=cars'
The answer is:
Route::any(Request::path(), ['as' => Request::path(), 'uses' => 'Extender#xRun']);

How to make differently route name in Laravel?

I have two the same controller in different directories.
And routing is for both:
Route::resource('dashboard/statistic', 'Admin\StatisticController');
Route::resource('statistic', 'StatisticController');
When I run php artisan route:list
I see, that these routes have the same route name as: statistic:
statistic.index
statistic.destroy
statistic.edit
How can I make this diffrently?
You could to create each route explicitly (Route::resource creates multiple routes to handle a variety of RESTful actions on the resource), for example
Route::get('dashboard/statistic', ['as' => 'dashboard-statistic.index', 'uses' => 'Admin\StatisticController#index']);
Route::delete('dashboard/statistic', ['as' => 'dashboard-statistic.destroy', 'uses' => 'Admin\StatisticController#destroy']);
Route::put('dashboard/statistic', ['as' => 'dashboard-statistic.edit', 'uses' => 'Admin\StatisticController#edit']);
Maybe
Route::resource('dashboard/statistic', 'Admin\StatisticController',
['admin' => ['create', 'store', 'update', 'destroy']]);
Route::resource('statistic', 'StatisticController');
Hopefully this will solve your problem
maybe this can solve the problem
Route::group(['prefix' => 'dashboard', 'as' => 'dashboard.'], function() {
Route::resource('statistic', 'Admin\StatisticController');
});
will return name eg:
dashboard.statistic.store

Get a parameter from a route?

I have the following route:
Route::get('news', [
'as' => 'news',
'uses' => 'ArticleController#getIndex',
]);
There are also other routes that use the same resource controller, e.g:
Route::get('travel', [
'as' => 'travel',
'uses' => 'ArticleController#getIndex',
]);
In the article controller, how can I get the category of the articles. e.g. travel or news?
I can't turn it into a route param e.g.
Route::get('{travel}', [
'as' => 'travel',
'uses' => 'ArticleController#getIndex',
]);
As there are other sections such as contact, faqs, etc that do not use the article controller.
I know I could put it all on one route:
Route::get('/article/{category}', [
'as' => 'travel',
'uses' => 'ArticleController#getIndex',
]);
But I want pretty URLS like:
mydomain.com/category-slug/article-slug
Ideally if I could use a resource controller:
Route::resource('news', ArticleController');
With some sort of array:
Route::resource(['news', 'travel'], ArticleController');
So my questions:
How can I get the name of the resource. e.g. news, travel in my controller?
Is there an easy way to specify varying routes to the same resource?
For Example Keep your routes like this as your static routes will be limited and need to be unique so they will not get conflict between routes
demo routes :
Route::get('contact', [
'as' => 'contact',
'uses' => 'contactController#getIndex',
]);
Route::get('{travel}', [
'as' => 'travel',
'uses' => 'ArticleController#getIndex',
]);
In order to achieve a route like that:
mydomain.com/category-slug/article-slug
Use the following:
Route::get('/{category}/{article}', 'ArticleController#getArticle');
Then in your controller, you will have 2 parameters in your getArticle method.
public function getArticle($category, $article)
{
//Your code here
}
In order to have pretty urls you need to make a few steps:
1)
First of all you need to add a column to your articles table called for example 'slug'.
2) Then you need to specify your route key in your article model. Like this:
Article model
public function getRouteKeyName()
{
return 'slug';
}
3) When you add an article you should create the slug by your own in order to be unique. so in your controller that you create the article add this code.
$slug = str_slug($article['title'], '-');
$slugs_count = DB::table('articles')->where('name',$article['title'])- >count();
if($slugs_count>1){
$slug = $slug.'-'.($slugs_count-1);
}
$article->slug = $slug;
$article->update();
4) Now we have to set the route
Route::get('/article/{slug}', [
'as' => 'travel',
'uses' => 'ArticleController#getIndex'
])->where(['slug','[\w\d\-\_]+']);
5) Finally in order to get the article you need
$article = Article::where('slug','=',$slug)->first();
If you want to use categories etc you could pass more parameters in your route and manipulate it in your controller. like this
Route::get('/article/{category}/{slug}', [
'as' => 'travel',
'uses' => 'ArticleController#getIndex'
])->where(['slug','[\w\d\-\_]+','category','[\w\d\-\_]+']);
The where clause is to restrict the parameter with some regular expressions(its optional).
I forgot to mention that the routekeyname in your model works in laravel 5.2.

Laravel Routes Artisan

When I run artisan routes in laravel 4
auth/login/{v1}/{v2}/{v3}/{v4}/{v5}
Is this normal or is there something wrong. My routes work just wondering if there might be a bug or something. Below are my routes for auth. I'm using restful routes for auth.
Route::controller('auth','AuthController');
Route::get('AuthController/login', array('as' => 'login', 'uses' => 'AuthController#login'));
Route::get('auth/logout', array('as' => 'logout', 'uses' => 'auth#logout'));
Route::post('auth/login', array('uses' => 'auth#login'));
This is expected. When you register controllers with Route::controller() the controller inspector adds the URI wildcards. Consider the following example:
Route::controller('user', 'UserController');
You might then have a method like this on your UserController:
public function getProfile($username)
{
$user = User::where('username', $username)->first();
return View::make('profile')->with('user', $user);
}
You could then hit that method by going to localhost/yourapp/user/profile/jason
In a nut shell it allows you to pass extra parameters to a method. To me this is a very old school way of doing it, as it looks nicer like localhost/yourapp/user/jason/profile and in this case you'd need to use a route to map to the controller method.
I suggest you 2 improvements:
1 - keep a standard with URIs
You don't need Route::controller in this case. In order to mantain all routes with the same structure I would do:
Route::group( array('prefix'=>'auth,function(){ //make all auth routes starting by auth
Route::get('getLogin', array('as' => 'getLogin', 'uses' => 'AuthController#getLogin'));
Route::get('getLogin', array('as' => 'logout', 'uses' => 'AuthController#logout'));
Route::post('postLogin', array('as' => 'postLogin', 'uses' => 'AuthController#postLogin'));
});
It is not necessary to use group but if you app grows could be better. Without group code will be:
Route::get('auth/getLogin', array('as' => 'getLogin', 'uses' => 'AuthController#getLogin'));
Route::get('auth/getLogin', array('as' => 'logout', 'uses' => 'AuthController#logout'));
Route::post('auth/postLogin', array('as' => 'postLogin', 'uses' => 'AuthController#postLogin'));
2 - Protect your post routes
For every post and put request we've to prevent CSRF attacks like this:
Route::post('postLogin',array('before' => 'csrf','uses'=>AuthController#postLogin) );

Resources