I have this route:
Route::get('{country}/{federalstate?}/{city?}', ['as' => 'regions.show', 'uses' => 'RegionsController#show']);
Next I have a model Country with a few countries in it.
I want this route only to take effect, when given {country} exists in the model Country. I want to do this, because don't wan to use a prefix for that route.
The ugly way would be a regular expression. Ugly, because I would have to update it every time I add a new country to the model.
So what's the best way to accomplish what I want to do?
Maybe this could help -> https://laravel.com/docs/5.4/routing#route-model-binding - but I am a beginner, and I can't get it to work.
edit:
this might work:
Route::get('{country?}/{federalstate?}/{city?}', ['as' => 'regions.show', 'uses' => 'RegionsController#show'])->where([
'country' => 'germany|usa|canada'
]);
But, as I said, I would have to change the Regex every time I add a new country to the database.
Here's my solution for now:
in AppServiceProvider in the boot() method:
$available_countries = Country::select('slug')->get();
$available_countries = $available_countries->toArray();
$tmp = array();
foreach ($available_countries as $country)
{
$tmp[] = $country['slug'];
}
Config::set('app.available_countries', $tmp);
and in my routes file:
Route::get('{country?}/{federalstate?}/{city?}', ['as' => 'regions', 'uses' => 'RegionsController#index'])->where([
'country' => implode('|', config('app.available_countries'))
]);
As you suggested, Route-Model binding may be your best bet. You can see from the docs that if the model you've bound to your route does not exist, the route will return a 404.
Just be aware that the default functionality in Route-Model binding expects an ID from the URL. If you want to override that, you can do the following:
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'name';
}
In my opinion, you could pass Country to your controller as model. If there is no such model in your DB, Laravel will produce 404 error.
Related
I have the id in a blade and I want to access to another column.
I have $article->country_id which gives access to the id of the foreign key country.
How can I find the record using id and then get access to country->name.
I have done the things below but it doesn't work as I have explained.
Here is the blade
#foreach($articles as $article)
<div class="col-md-12">
کشور:<h4 class ="post-title">{{route('article.whichCountry',$article->country_id)}}</h4>
</div>
#endforeach
here is web.php
Route::get('article/whichCountry/{country}',[
'uses' => 'ArticleController#whichCountry',
'as' => 'article.whichCountry'
]);
and here is whichCountry in ArticleController
public function whichCountry($id){
$country = country::where('id',$id)->first();
return $country->name;
}
The point is that it doesn't call the function whichCountry
and prints:
http://127.0.0.1:8000/article/whichCountry/10
Thank you very much for your response in advance!
Your route access needs to be like below, giving value for {country} parameter.
{{ route('article.whichCountry',['country' => $article->country_id]) }}
Second, instead of where in your controller method, if Id is the primary key, you can just do:
Country::find($country);
Also, change your function definition like below for the parameter value filling
public function whichCountry($country){
This is very simple to implement this. First check your Controller is getting the id properly. Then use find method
Your route should be like this
{{route('article.whichCountry',['id'=>$article->country_id])}}
And in your controller you have to use like this
public function whichCountry($id){
$country = country::where('id',$id)->first();
return $country->name;
}
In your web.php looks like this
Route::get('article/whichCountry/{id}',[
'uses' => 'ArticleController#whichCountry',
'as' => 'article.whichCountry'
]);
Currently I have these 4 resourceful routes in my web.php file.
Route::resource('campaigns', 'CampaignController')->except(['show']);
Route::resource('users', 'UserController')->except(['show']);
Route::resource('models', 'ModelController')->except(['show']);
Route::resource('trims', 'TrimController')->except(['show']);
And I can't help but wonder. Can't I add something to the Route::resources function to make it behave this way? This because they all have one thing in common. They except() the show() method.
It want to have something like this. (This example does not work because resources() does not have an except() method.
Route::resources([
'campaigns' => 'CampaignController',
'users' => 'UserController',
'models' => 'ModelController',
'trims' => 'TrimController'
])->except(['show']);
You can write your own class that extends Illuminate\Routing\Route and implement your resources method like that:
namespace App\Extends;
class Route extends Illuminate\Routing\Route {
public function resources($routes, array $excepts) {
foreach ($routes as $key => $value){
$this->resource($key, $value)->except($excepts);
}
}
}
After you will need to bind your class in your service provider like this:
public function register()
{
$this->app->bind('Illuminate\Routing\Route', 'App\\Extends\\Route');
}
and call resources in web.php like that:
Route::resources([
'campaigns' => 'CampaignController',
'users' => 'UserController',
'models' => 'ModelController',
'trims' => 'TrimController'
], ['show']);
[EDIT 1]
From laravel.com/docs/5.8/controllers#resource-controllers
You may register many resource controllers at once by passing an array to the resources method:
Route::resources([
'photos' => 'PhotoController',
'posts' => 'PostController'
]);
But I don't know if you can call ->except(['show']) like that
Route::resources([])->except(['show'])
This question is already pretty old, but i just faced the same problem and solved it this way:
Route::resources([
'campaigns' => 'CampaignController',
'users' => 'UserController',
'models' => 'ModelController',
'trims' => 'TrimController'
], [
'except' => ['show']
// you can set here other options e.g. 'only', 'except', 'names', 'middleware'
]);
According to source code, method resources is not chainable, because it returns void. But you still can pass the options into second argument of resources.
This is "Larawel-way" and you have not overwrite any vendor code.
Dig here to get more info, how it works.
Note that if you want to exclude show method, but try to reach /users/{user} (HTTP GET) in browser, router will throw The GET method is not supported for this route. Supported methods: PUT, PATCH, DELETE. error. It happens because router still has this route, but for PUT, PATCH, and DELETE. Then your application crashes.
So, maybe you want show HTTP 404 if someone accidentally goes to "show" page.
I prefer to add fallback route (this should be the last route of your routes!)
Route::fallback(function () {
abort(404);
});
If you talk about possibility then yes it's possible as above answer mentioned but by default you can't
Take a look at this file,
/vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php
* #method static \Illuminate\Routing\PendingResourceRegistration resource(string $name, string $controller, array $options = [])
* #method static void resources(array $resources)
Suppose I have these routes :
$api->group(['prefix' => 'Course'], function ($api) {
$api->group(['prefix' => '/{course}'], function ($api) {
$api->post('/', ['uses' => 'CourseController#course_details']);
$api->post('Register', ['uses' => 'CourseController#course_register']);
$api->post('Lessons', ['uses' => 'CourseController#course_lessons']);
});
});
As you can see all / , Register and Lessons route prefixed by a course required parameter.
course parameter is a ID of a Course model that I want to use for route model binding.
But In the other hand when I want use course parameter for example in course_details function, it returns null. like this :
public function course_details (\App\Course $course)
{
dd($course);
}
But if I use below, all things worked fine :
public function course_details ($course)
{
$course = Course::findOrFail($course);
return $course;
}
Seems that it can not bind model properly.
What is Problem ?
Update :
In fact I'm using dingo-api laravel package to create an API. all routes defined based it's configuration.
But there is an issue about route model binding where to support route model binding we must to add a middleware named binding to each route that need model binding. HERE is described it.
A bigger problem that exists is when I want to add binding middleware to a route group, it does not work and I must add it to each of routes.
In this case I do not know how can I solve the problem.
Solution:
After many Googling I found that :
I found that must to add bindings middleware in the same route group that added auth.api middleware instead adding it to each sub routes separately.
means like this :
$api->group(['middleware' => 'api.auth|bindings'], function ($api) {
});
add in kernel.php
use Illuminate\Routing\Middleware\SubstituteBindings;
protected $routeMiddleware = [
...
'bindings' => SubstituteBindings::class,
];
and in your group route:
Route::middleware(['auth:sanctum', 'bindings'])->group(function(){
... you routes here ...
});
this worked for me. thanks
As you said
course parameter is a ID of a Course
You can use Request to get id, try like this
public function course_details (Request $request)
{
return dd($request->course);
}
I came across a similar issue. I think you need to use the 'bindings' middleware on your routes.
See my answer here:
https://stackoverflow.com/a/55949930/2867894
Take a close look on:
// Here $course is the id of the Course
public function course_details ($course)
{
$course = Course::findOrFail($course);
return $course;
}
But here:
// Here $course is the object of the model \App\Course
public function course_details (\App\Course $course)
{
dd($course);
}
that should be
public function course_details ($course, \App\Course $_course)
{
// add your model here with object $_course
// now $course return the id in your route
dd($course);
}
After searching for 2 hours I got the issue details
For route binding to work, your type-hinted variable name must match the route placeholder name
For example my edit method
Here is my route URI for the edit
user/role/{role}/edit
As you can see there is {role} placeholder in the route definition, so the corresponding variable must be called $role.
public function edit(Role $role)
{
return view('role.edit',compact('role'));
}
Route::get('post/form/{id}', array('as' => 'admin.post.delete', 'uses' => "PostController#deleteForm"));
Route::get('post/form', array('as' => 'admin.post.create', 'uses' => "PostController#createForm"));
I want to combine two routes above in a route for two functions, create and delete. Because the both routes have only different id.
Route::get('post/form/{id}', array('as' => 'admin.post', 'uses' => "PostController#getForm"));
If I want to type without id, it redirects to create function. If I type with id, it redirect to delete function.
How can I use one route for two functions?
As mentioned by James this is not really practical but you could achieve this via the following.
Laravel gives you the possibility to define optional route parameters as shown below.
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
Laravel Documentation about Route Parameters: Laravel Route Parameters
So this means you could just make your route like this.
Route::get('post/form/{id?}', array('as' => 'admin.post', 'uses' => "PostController#getForm"))
In your controller you then need to check if the 'id' is present. If not you create the user. If the 'id' is present you delete the user.
You can't use 1 route for 2 methods.
The solution is using 1 method which fires concrete method eg.
routes.php
get('post/form/{id?}', 'PostConteoller#form');
PostController.php
public function form($id = null) {
return $id ? $this->deleteForm($id) : $this->createForm();
}
However using 2 routes is much simpler.
I am new to Laravel and I am doing the following, but I was wondering if there was a better way to do it.
Both do the same thing but return to a different view
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//Get all the services
$franchises = Franchise::all();
//Load the view and pass the services
return View::make('franchises.index')->with('franchises', $franchises);
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function indexTwo()
{
//Get all the services
$franchises = Franchise::all();
//Load the view and pass the services
return View::make('admin.usertemplate')->with('franchises', $franchises);
}
And this is my route.php
Route::get('admin/logout', array('as' => 'admin.logout', 'before' => 'user', 'uses' => 'AuthController#getLogout'));
Route::get('admin/login', array('as' => 'admin.login', 'before' => 'is_guest', 'uses' => 'AuthController#getLogin'));
Route::post('admin/login', array('as' => 'admin.login.post', 'before' => 'is_guest', 'uses' => 'AuthController#postLogin'));
//---- Backedn Index
Route::get('admin',array('as'=>'index','uses'=>'BackendController#getIndex'));
Your examples represents two controllers methods.In isolation, they don't do anything. They depends on Route (not provided) and Model(Franchise).
This can be improved in a number of ways, depending on your domain logic layer design.
You could for example do this:
return View::make("franchises.index",compact("franchises"));
or this:
return View::make("franchises.index",["franchise"=>$franchises]);
But it is all variation of same thing. Further it can be improved by applying repository pattern which gives you more flexibility when dealing with database. i.e. not depending on one ORM (eloquent).
As I sad, everything depends on your goals. Controller is just a door to you domain logic.
UPDATE TO YOUR UPDATE:
You can group your routes in one controller:
Route::resource("admin","AdminController");
And please move from laravel-3.