PUT in laravel API - laravel

I'm studying api rest with laravel, I was able to implement all methods except PUT. Although the routes and controllers are correctly configured a response to a request using the PUT method is "laravel put Sorry, the page you are looking for could not be found.", As now image.
here is the method code in the controller in app/Http/Controllers/LivroController.php:
public function store(Request $request) {
$livro = $request->isMethod('put') ? Livro::findOrFail($request->livro_id) : new Livro;
$livro->id = $request->input('livro_id');
$livro->nome = $request->input('nome');
$livro->descricao = $request->input('descricao');
$livro->user_id = 1; //$request->user()->id;
if($livro->save()) {
return new LivroResource($livro);
}}
here is the route code in /routes/api.php:
Route::put('livro', 'LivroController#store');

change your postman method to POST and then add new parameter in your Body :
"_method" : PUT
This is because HTML forms do not support PUT, PATCH or DELETE actions. So, when defining PUT, PATCH or DELETE routes that are called from an HTML form, you will need to add a hidden _method field to the form

If you want to create new data, you should use post method,
Route::post('livro', 'LivroController#store');
public function store(Request $request) {
If you want to update exist data you should use put method,
Route::put('livro/{id}', 'LivroController#update');
public function update(Request $request, $id) {
You can use this package https://github.com/durmus-aydogdu/laravel-resource for rest calls. This package highly customizable for rest and resource calls.

Is better that you use controllers type resources and for this case the put method. Also you should validate the request. For example:
public function update(Request $request, $id)
{
$livro = Livro::findOrFail($id);
$validator = Validator::make($request->all(), [
'livro_id' => 'required',
'nome' => 'required',
'descricao' => 'required',
]);
if ($validator->fails()) {
return response()->json(['errors'=>$validator->messages()],Response::HTTP_UNPROCESSABLE_ENTITY);
}else{
$livo->update($request->all());
return response()->json(['livro'=>$livro], Response::HTTP_OK);
}
}

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 retrieve url parameter in custom Request?

I need to make custom request and use its rules. Here's what I have:
public function rules()
{
return [
'name' => 'required|min:2',
'email' => 'required|email|unique:users,email,' . $id,
'password' => 'nullable|min:4'
];
}
The problem is that I can't get $id from url (example.com/users/20), I've tried this as some forums advised:
$this->id
$this->route('id')
$this->input('id')
But all of this returns null, how can I get id from url?
When you are using resources, the route parameter will be named as the singular version of the resource name. Try this.
$this->route('user');
Bonus points
This sound like you are going down the path of doing something similar to this.
User::findOrFail($this->route('user'));
In the context of controllers this is an anti pattern, as Laravels container automatic can resolve these. This is called model binding, which will both handle the 404 case and fetching it from the database.
public function update(Request $request, User $user) {
}

How can I use Laravel Validation in the API, it's returning my view now?

I have this route in route/api.php:
Route::post('/register', 'LoginController#register');
My LoginController
class LoginController extends Controller {
public function register(Request $request) {
$this->validate($request, // <-- using this will return the view
// from **web.php** instead of the expected json response.
[
'email' => 'required|email',
'firstName' => 'required|alpha_dash',
'lastName' => 'required',
'password' => 'required|confirmed',
]);
$input = $request->all();
//$plain_password = $input['password'];
$input['uuid'] = uuid();
$input['password'] = Hash::make($input['password']);
$user = User::create($input);
dd($errors);
$response['succes'] = true;
$response['user'] = $user;
return response($response);
}
}
Why does adding a validation call change the behaviour to returning my view / the wrong route. I want the api to validate my request too, not just my "frontend".
When you use Laravel's validate method from controller it automatically handles/takes the step if the validation fails. So, depending on the required content type/request type, it determines whether to redirect back or to a given url or sending a json response. Ultimately, something like thos following happens when your validation fails:
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->expectsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->to($this->getRedirectUrl())
->withInput($request->input())
->withErrors($errors, $this->errorBag());
}
So, if the first if statement is true then you'll get a json response and it'll be true if you either send an ajax request or if you attach a accept header with your request to accept json response (When requesting from a remote server). So, make sure your request fulfills the requirements.
Alternatively, you can manually validate the request using the Validator component and return a json response explicitly if it fails.

How to change response in Laravel?

I have RESTful service that is available by endpoints.
For example, I request api/main and get JSON data from server.
For response I use:
return response()->json(["categories" => $categories]);
How to control format of response passing parameter in URL?
As sample I need this: api/main?format=json|html that it will work for each response in controllers.
One option would be to use Middleware for this. The below example assumes that you'll always be returning view('...', [/* some data */]) i.e. a view with data.
When the "format" should be json, the below will return the data array passed to the view instead of the compiled view itself. You would then just apply this middleware to the routes that can have json and html returned.
public function handle($request, Closure $next)
{
$response = $next($request);
if ($request->input('format') === 'json') {
$response->setContent(
$response->getOriginalContent()->getData()
);
}
return $response;
}
You can use for this Response macros. For example in AppServiceProvider inside boot method you can add:
\Response::macro('custom', function($view, $data) {
if (\Request::input('format') == 'json') {
return response()->json($data);
}
return view($view, $data);
});
and in your controller you can use now:
$data = [
'key' => 'value',
];
return response()->custom('your.view', $data);
If you run now for example GET /categories you will get normal HTML page, but if you run GET /categories?format=json you will get Json response. However depending on your needs you might need to customize it much more to handle for example also redirects.
With your format query parameter example the controller code would look something like this:
public function main(Request $request)
{
$data = [
'categories' => /* ... */
];
if ($request->input('format') === 'json') {
return response()->json(data);
}
return view('main', $data);
}
Alternatively you could simply check if the incoming request is an AJAX call via $request->input('format') === 'json' with $request->ajax()

Laravel - Change URL parameters using GET

I have RESTful API built on Laravel.
Now I'm passing parameter like
http://www.compute.com/api/GetAPI/1/1
but I want to pass parameter like
http://www.compute.com/api/GetAPI?id=1&page_no=1
Is there a way to change Laravel routes/functions to support this?
you can use link_to_route() and link_to_action() methods too.
(source)
link_to_route take three parameters (name, title and parameters). you can use it like following:
link_to_route('api.GetAPI', 'get api', [
'page_no' => $page_no,
'id' => $id
]);
If you want to use an action, link_to_action() is very similar but it uses action name instead of route.
link_to_action('ApiController#getApi', 'get api', [
'page_no' => $page_no,
'id' => $id
]);
href text
with these methods anything after the expected number of parameters is exceeded, the remaining arguments will be added as a query string.
Or you can use traditional concatination like following:
create a route in routes.php
Route::get('api/GetAPI', [
'as' => 'get_api', 'uses' => 'ApiController#getApi'
]);
while using it append query string like this. you can use route method to get url for required method in controller. I prefer action method.
$url = action('ApiController#getApi'). '?id=1&page_no=1';
and in your controller access these variables by following methods.
public function getApi(Request $request) {
if($request->has('page_no')){
$page = $request->input('page_no');
}
// ...your stuff
}
Or by Input Class
public function getApi() {
if(Input::get('page_no')){
$page = Input::get('page_no');
}
// ...your stuff
}
Yes you can use those parameters, then in your controllers you can get their values using the Request object.
public function index(Request $request) {
if($request->has('page_no')){
$page = $request->input('page_no');
}
// ...
}

Resources