Why this Implicit Binding is not working? - Laravel - laravel

I'm using Laravel 9. I have a problem, i wil appreciate any help.
I Have a Model named Entity, a controller EntityControler, and a FormRequest UpdateEntityRequest.
My API Routes looks like:
Route::apiResource('entities', EntityController::class);
so i have show, create, store, update, delete... routes.
This is muy update method in EntityController (without the code/not important now)
public function update(UpdateEntityRequest $request, Entity $entity)
{
return $entity;
}
the update method works perfect. But I want another update method for only a section and here starts the problem.
This is my new API Routes:
Route::apiResource('entities', EntityController::class);
Route::patch('/entities/{id}/{section}',[EntityController::class, 'updateSection' ]);
And this is the new method in the controller(without code yet):
public function updateSection( UpdateEntityRequest $request,Entity $entity, $section)
{
return $entity;
}
But this last method return [] insted of the Entity and the update method works. WHY?
I change de uri in Postman for update PUT {{baseUrl}}/entities/1 and for updateSection {{baseUrl}}/entities/1/1 .
Why does work in update and not in updateSection?
PD:
This method work, and give the id, and I can create a Entity from this:
public function updateSection( UpdateEntityRequest $request, $entity, $section)
{
return $entity;
}
But this is not what I want. Any idea why this happen?

please make sure your uri segment is same as the variable name in the controller, in your case replace id with entity
Route::patch('/entities/{entity}/{section}',[EntityController::class, 'updateSection' ]);
for more please see documentation

Make the route param name consistent in your route api.php and your function updateSection in EntityController
Route::patch('/entities/{entity}/{section}',[EntityController::class, 'updateSection' ]);
and
public function updateSection( UpdateEntityRequest $request,Entity $entity, $section)
{
return $entity;
}

Related

Laravel API routes and Controller variable

I'm a new user of Laravel, and i'm a bit confused with Laravel route API and the name of variable in the controller.
Here an example to explain :
An API route
Route::middleware('auth:sanctum')->group( function () {
Route::resource('cepage', CepageController::class);
});
For a PUT or PATCH, i have this function in the CepageController :
public function update(Request $request, Cepage $cepage)
{
$input = $request->all();
$validator = Validator::make($input, [
'libelle' => 'required',
'abrege' => 'required'
]);
if($validator->fails()){
return $this->sendError($validator->errors());
}
$cepage->libelle = $input['libelle'];
$cepage->abrege = $input['abrege'];
$cepage->save();
return $this->sendResponse(new CepageResource($cepage), 'Cépage mis à jour');
}
If you see my route name "cepage" have the same name of the $cepage variable of the function declaration in the controller, Laravel update the record in the database.
If they are no identical, Laravel create a new record in the database.
Why they need to be exactly the same ?
I think i miss something in the documenation of Laravel.
Thanks for your explanations.
It needs to be the same, for laravel to know what object does he needs to create for us.
Route::resource does a few routes for you, with the base url give into it (https://laravel.com/docs/8.x/controllers#actions-handled-by-resource-controller)
So once you have defined Route::resource('cepage', CepageController::class)
You will have the following routes defined:
Verb URI Action Route Name
GET /cepage CepageController#index cepage.index
GET /cepage/create CepageController#create cepage.create
POST /cepage CepageController#store cepage.store
GET /cepage/{cepage_id} CepageController#show cepage.show
GET /cepage/{cepage_id}/edit CepageController#edit cepage.edit
PUT/PATCH /cepage/{cepage_id} CepageController#update cepage.update
DELETE /cepage/{cepage_id} CepageController#destroy cepage.destroy
And in the controller you need to follow the naming, because in the url you have only ids of the object. But if you follow the naming, laravel will fetch the object for you by its id. See:
public function update(Request $request, $cepage_id)
{
$cepage = Cepage::find($cepage_id);
//here you have to fetch the object for yourself to access it
}
public function update(Request $request, Cepage $cepage)
{
//here you can already access $cepage variable
}

Laravel - How to not pass unused parameter in controller

I have a route for example -
Route::get('/api/{user}/companies/{company}', [CompanyController::class, 'getCompanies'])
and a function in this controller
public function getCompanies(User $user, Company $company) {
$companies = $company->all();
return response()->json(['companies' => $companies]);
}
I am not using the $user instance in the function and I would like to not pass a User $user param in it, but I want that the route has user id as a param for clarity on the frontend.
I found a solution of using middleware with the forgetParameter() method but I don't want to add new middleware or declare it only for this route.
I can just leave that unused param in my function and everything will work just fine, but I am curious is there some elegant solution for this case.
public function getCompanies(Company $company, ?User $user = null)
{
return response()->json(['companies' => $company->all()]);
}
Pass $user to the last position and give it a default value
I think you can put a _ instead of passing a parameter, but I could be wrong.

recover the slug of a category linked to another category Laravel

I would like to recover the slug of 2 categories from my routes but can’t write the Controller.
My Route
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
My Controller
public function viewoccupationcity($slug)
{
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
Your controller will accept the parameters from your route as variables by order
public function viewoccupationcity($ocupation, $city)
{
...
}
Example:
URL: technicians/o/foo/c/bar
public function viewoccupationcity($ocupation, $city)
{
// $ocupation will be 'foo'
// $city will be 'bar
}
Ok, you would need to retrieve 2 variables as that is what you are passing
public function viewoccupationcity($occupation, $city)
If you want the whole slug to do another search then you would use the $request object. So like so
public function viewoccupationcity(Request $request, $occupation, $city){ // You also need to include the Request decleration
$slug = $request->path();
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
EDIT: We are having to do a lot of guesswork as your question isn't very clear. I think what you are trying to achieve is probably this
public function viewoccupationcity($occupation, $city){
$technicians = TechnicianResource::collection(occupation::where('city',$city)->where('occupation',$occupation)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
If you need something more then you need to give more details

not working #extends() use query in laravel

i try to display data with id, but problem is when just:
public function category()
{
return view('font.category.category');
}
#extends() blade is working. I try to query use this:
function public function category($id)
{
$pCategoryById = Menu::where('id', $id)->get();
return view('font.category.category', 'pCategoryById'=>$pCategoryById]);
}
#extends() blade is not working how to solve it? url is {{url('/category/'.$result->id)}} web is: Route::get("/category/{id}",'fontController#category');.
in url
{{url('/category',$result->id)}}
in controller
$pCategoryById=Menu::find($id);
return view('font.category.category',compact('pCategoryById'));
In the url,
{{url('/category',$result->id)}}
Or you can use as like,
{{url('/')}}/{{$result->id}}
In the Controller,
$pCategoryById=Menu::where('id',$id)->get();
return view('font.category.category',compact($pCategoryById));
If #extends() is not working, then you have to check the right path which is extended.

Laravel 5: How to create a router model binding on multiple parameters

So far I know how to create a router model binding on single parameters like so:
// RouteServiceProvider.php
$router->model('subject_slug', 'App\Subject', function($slug) {
return Subject::where('slug', $slug)->firstOrFail();
});
The above can then be used like this:
// routes.php
Route::get('/{subject_slug}', 'MenuController#showSubject');
And in the controller:
public function showSubject(Subject $subject) {
....
}
But sometimes I need to specify multiple parameters in order to get the right model.
For example consider the following route:
Route::get('/{subject_slug}/{topic_slug}/', 'MenuController#showTopic');
and the corresponding controller:
public function showTopic(Subject $subject, Topic $topic) {
....
}
However to get the correct model for Topic I need to know the Subject. For example:
// !!! Invalid laravel code !!!
$router->model('topic_slug', 'App\Topic, function($subject_slug, $topic_slug) {
// ERROR: $subject_slug is obviously not defined!
return Topic::where([
'subject_slug' => $subject_slug,
'slug' => $topic_slug,
])->firstOrFail();
});
How can I make a router model binding for Topic bearing in mind I need to know the Subject parameter before it in order to fetch the correct Topic.
Is there an alternative better way of doing this?
UPDATE
Currently my showTopic method in my controller is like this:
public function showTopic(Subject $subject, $topic_slug) {
$topic = Topic::where([
'subject_slug' => $subject_slug,
'slug' => $topic_slug,
])->firstOrFail();
// ...
}
and I have no router model binding for topic_slug.
This works as expected, but I would like to take advantage of router model bindings!
It turns out the way I was doing it was a bit flawed. I was unnessarily using model bindings when instead it would be better to have used a normal binding like so:
$router->bind('topic_slug', function($slug, Route $route) {
$subject = $route->parameter('subject_slug');
return Topic::where([
'subject_slug' => $subject->slug,
'slug' => $slug,
])->firstOrFail();
});
Also I was using model bindings completely wrong before as the 3rd function should be the "not found behaviour" (not for additional logic)!

Resources