Adding Parameter From Controller to Validation Request - php

I want to devide my controller to several service layer, validation layer, and logical layer.
But I got stuck when I want to send new variable to validation request, my schenario is, if there is a new sent it indicates for new data, and if new variable is not exists it indicates it's updating data.
Here is my Controller:
public function store(AlbumRequest $request, AlbumService $service)
{
$request->add(['new' => true])
try{
$service->store($request);
return redirect(route('admin.web.album.index'));
}catch(\Exception $err){
return back()->withInput()->with('error',$err->getMessage());
}
}
Here is my Request
class AlbumRequest extends FormRequest
{
public function rules()
{
dd($this->request->get('new')
}
}
I want to catch variable I have sent from Controller to Request. How to do that?
Thank you.

You can add new parameter in request from controller like that
$request->merge(array('new' => true));

before your request reaches your controller , it has to go through AlbumRequest class . so you have to merge that field in AlbumRequest class by using method prepareForValidation :
protected function prepareForValidation()
{
$this->merge([
'new' => true,
]);
}
add this method in your AlbumRequest class and see if it works

I am afraid you cannot do that because the incoming form request is validated before the controller method is called. Now if you want to know whether the request is for creating something new or updating something, you can do it by accessing the route parameters and method type
public function rules()
{
$rules = [
'something' => 'required',
];
if (in_array($this->method(), ['PUT', 'PATCH'])) {
//it is an edit request
//you can also access router parameter here, $this->route()->parameter('other thing');
$rules['somethingelse'] = [
'required',
];
}
return $rules;
}

You can enter the requested class as URL params.
class AlbumRequest extends FormRequest
{
public function rules()
{
dd(request()->get('new')
}
}

Related

Laravel9: route parameter always missing in validation

I am using Laravel v9.2.1 + Laravel Sanctum v2.14.1
I got a route
DELETE /api/v1/auth/tokens/{token}
for example (the token is an uuid)
DELETE http://example.com/api/v1/auth/tokens/5fcfa274-81d8-4e9f-8feb-207db77531af
And I am sure it works as expected via php artisan route:list
Before handling by the Controller, it should be validated by a FormRequest
app/Http/Controllers/V1/Auth/TokensController.php
namespace App\Http\Controllers\V1\Auth;
use App\Http\Requests\V1\Auth\Tokens\{
DestroyRequest,
};
class TokensController extends Controller
{
public function destroy(DestroyRequest $request) {
$request->user()->tokens()->where('id', $request->token)->first()->delete();
return response()->noContent();
}
}
app/Http/Requests/V1/Auth/Tokens/DestroyRequest.php
class DestroyRequest extends FormRequest
{
public function rules()
{
return [
'token' => [
'required',
'string',
'regex:/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
Rule::exists('personal_access_tokens')->where(function ($query) {
return $query->where('tokenable_id', $this->user()->id);
}),
]
];
}
}
But what I only got is The token field is required
I had already pass the token, why the 'required' rule still working?
What I tried
Only if I pass the token parameter like below, it will work
DELETE /api/auth/tokens/something?token=test_regex_is_working
I try to dd($this->token) in app/Http/Requests/V1/Auth/Tokens/DestroyRequest.php, it works as expected.
i might try going about it differently as the token isn't really user input
In the routes file:
Route::delete('/api/v1/auth/tokens/{token}', [TokensController::class, 'destroy'])->whereUuid('token');
In the FormRequest something maybe like this:
public function authorize()
{
return DB::table('personal_access_tokens')
->where('tokenable_id', Auth::id())
->where('token', $this->route('token'))
->exists()
}
You might need to add the following in the FormRequest class:
protected function prepareForValidation()
{
$this->merge(['token' => $this->route('token')]);
}
I believe URL parameters are not included in the request directly.
With the help of both #RawSlugs and #Aaron T, thank them a lot!
app/Http/Requests/V1/Auth/Tokens/DestroyRequest.php
protected function prepareForValidation() {
$this->merge(['token' => $this->route('token')]);
}
public function authorize() {
return $this->user()->tokens()->where('id', $this->token)->exists();
}
// But since the authorize() will validate the request before rules(), this will be useless
public function rules() {
return [
'token' => [
'required',
'string',
'regex:/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
]
];
}
I'm using Laravel Sanctum.

Laravel Validator Not Returning Key

I am creating a new API call for our project.
We have a table with different locales. Ex:
ID Code
1 fr_CA
2 en_CA
However, when we are calling the API to create Invoices, we do not want to send the id but the code.
Here's a sample of the object we are sending:
{
"locale_code": "fr_CA",
"billing_first_name": "David",
"billing_last_name": "Etc"
}
In our controller, we are modifying the locale_code to locale_id using a function with an extension of FormRequest:
// This function is our method in the controller
public function createInvoice(InvoiceCreateRequest $request)
{
$validated = $request->convertLocaleCodeToLocaleId()->validated();
}
// this function is part of ApiRequest which extend FormRequest
// InvoiceCreateRequest extend ApiRequest
// So it goes FormRequest -> ApiRequest -> InvoiceCreateRequest
public function convertLocaleCodeToLocaleId()
{
if(!$this->has('locale_code'))
return $this;
$localeCode = $this->input('locale_code');
if(empty($localeCode))
return $this['locale_id'] = NULL;
$locale = Locale::where(Locale::REFERENCE_COLUMN, $localeCode)->firstOrFail();
$this['locale_id'] = $locale['locale_id'];
return $this;
}
If we do a dump of $this->input('locale_id') inside the function, it return the proper ID (1). However, when it goes through validated();, it doesn't return locale_id even if it's part of the rules:
public function rules()
{
return [
'locale_id' => 'sometimes'
];
}
I also tried the function merge, add, set, etc and nothing work.
Any ideas?
The FormRequest will run before it ever gets to the controller. So trying to do this in the controller is not going to work.
The way you can do this is to use the prepareForValidation() method in the FormRequest class.
// InvoiceCreateRequest
protected function prepareForValidation()
{
// logic here
$this->merge([
'locale_id' => $localeId,
]);
}

Newbie question about controller and request in Laravel in my case

I am new in Laravel, what I try to achieve is very simple thing, I would like to use FormRequest provided by Laravel to do validation of the request, but I encounter some puzzles (which I am sure is easy things to solve if you are experienced in Laravel).
Here is what I tried:
I have route maps to controller:
Route::put('user/{name}', 'UserController#show');
I can get the name parameter in show function:
class UserController {
public function show($name)
{
// validtion rules to apply
...
}
}
I have validation rules to apply to the request, so I decided to create form request by php artisan make:request ShowRequest, which creates the form request class:
class ShowRequest extends FormRequest {
public function authorize()
{
return true;
}
public function rules()
{
return [
// my validation rules here
];
}
}
Since I have above request class, so I refactored the show function in controller to receive the ShowRequest .
class UserController {
public function show(ShowRequest $request)
{
// now I don't need validtion rules in this function
// but how can I access the 'name' parameter now
...
}
}
I have two questions to ask:
Inside the refactored show function, how can I now access the route parameter name ?
If we forget about the parameter is a name (please don't focus on what to validate for name, imaging it is an object or value to validate in general). How to add custom logic for handling validation error instead of using Laravel default behaviour. I want to inject code like dummy code below:
if (!$validator->pass())
{
//my custom code for handling validation failure
}
Where to put my custom code for handling validation error now? I mean I don't know where to have this logic, in controller? in the request class? how?
You still can add the parameter $name in the show() method of your controller as it's part of the routed url more than the validated form/data. (recommanded)
class UserController {
public function show(ShowRequest $request, $name)
{
//...
}
}
You can also access it from the request object
class UserController {
public function show(ShowRequest $request)
{
$request->input('name');
}
}
As for the error messages (not the exception) you can add the messages() method to your ShowRequest::class
class ShowRequest extends FormRequest
{
/**
* #return array
*/
public function messages()
{
return [
'name.required' => 'The name is required',
'name.numeric' => 'The name must be a number',
//...
];
}
}
If you instead need to validate that the name catched by the route is only composed of letter OR really exists as a field in your DB (like a slug of a post) you need to add some validation in your route declaration.
Setup a route that catches request only if it is composed of letters.
Route::get('/user/{name}', 'Controller#show')->where(['name' => '[a-z]+']);
Setup a route that catches request only if the "name" exists in DB:
User.php
Class User //..
{
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'name';
}
}
web.php
//
Route::get('/user/{user:name}', 'Controller#show');
And adapt your controller to take a user directly
class UserController {
public function show(ShowRequest $request, User $user)
{
//...
}
}
You can access the values of the Form Request using this
$validated = $request->validated();
The $validated will have all the values which had been validated by the FormRequest.
To answer your second question, if you want to throw custom validation, you can always use the following
throw ValidationException::withMessages(['name' => 'Something is wrong']);

Specify different set of rules based on which function makes the request

I wish to create a Request in a Laravel 5.1 application which has a specific set of rules based on the function that calls it.
For example, say, there are two functions in UsersController namely, login() and register(). The login() function requires only two inputs- username and password, whereas the register() function requires three inputs- username, password and email among other constraints. How can I create a single Request, call it UserRequest, that can handle the rules corresponding to both the above functions based on whichever makes the Request?
I am not sure if what I'm attempting could be done, or whether it is a good practice. Please suggest regarding the same as well.
You have a lot of flexibility since you can do whatever in rules() method, you just need a way to distinguish who is using the Request.
For this example, i would simply use the route() method(it tells you what route was called), you can do something like this:
class MyRequest extends Request {
/*
* Request rules
*/
protected $rules = [
// login rules
'login_route' => [
'login' => 'required',
],
// register rules
'register_route' => [
'login' => 'sometimes',
],
// depends if you need it
'default' => [
'login' => 'sometimes',
]
];
public function authorize()
{
return true; // or whatever
}
public function rules()
{
// where did this request come from?
$route = $this->route();
if(array_key_exists($route, $this->rules))
return $this->rules[$route];
return $this->rules['default'];
}
}
There are other ways, it depends on your problem, you could check the request method (GET, POST..) using getMethod() or check a segment, or instanciate an object on construct (dependency injection) to check if a user is logged in or not for example, really, it depends.
However, if the use case is complex, it is better to seperate in two requests.
Hope this helps.
You mean form requests. Before your method will be excecuted, the validator checks the rules. If not valid, the not-valid messages array will be returned with http status 422. See example below from https://mattstauffer.co/blog/laravel-5.0-form-requests
<?php namespace App\Http\Controllers;
use App\Http\Requests\FriendFormRequest;
use Illuminate\Routing\Controller;
use Response;
use View;
class FriendsController extends Controller
{
public function getAddFriend()
{
return view('friends.add');
}
public function postAddFriend(FriendFormRequest $request)
{
return Response::make('Friend added!');
}
}
And then your form request class:
<?php namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Response;
class FriendFormRequest extends FormRequest
{
public function rules()
{
return [
'first_name' => 'required',
'email_address' => 'required|email'
];
}
public function authorize()
{
// Only allow logged in users
// return \Auth::check();
// Allows all users in
return true;
}
// OPTIONAL OVERRIDE
public function forbiddenResponse()
{
// Optionally, send a custom response on authorize failure
// (default is to just redirect to initial page with errors)
//
// Can return a response, a view, a redirect, or whatever else
return Response::make('Permission denied foo!', 403);
}
// OPTIONAL OVERRIDE
public function response()
{
// If you want to customize what happens on a failed validation,
// override this method.
// See what it does natively here:
}
}
You can make different 'standalone' form requests and use them for your controller methods.
Official documentation here: http://laravel.com/docs/master/validation#form-request-validation

Laravel custom redirection after validation errors

Can I ask what have I done wrong in my LoginRequest.php where I've set a condition to redirect to a custom login page if there is any sort of error in the login process? I have my codes as below:
<?php namespace App\Http\Requests;
use App\Http\Requests\Request;
class LoginRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'login_email' => 'required',
'login_password' => 'required'
];
}
public function messages()
{
return [
'login_email.required' => 'Email cannot be blank',
'login_password.required' => 'Password cannot be blank'
];
}
public function redirect()
{
return redirect()->route('login');
}
}
The code is supposed to redirect users who login from a nav bar login form to the main login page, if there are any errors, but it doesn't seem to redirect.
if you want to redirect to a specific url, then use protected $redirect
class LoginRequest extends Request
{
protected $redirect = "/login#form1";
// ...
}
or if you want to redirect to a named route, then use $redirectRoute
class LoginRequest extends Request
{
protected $redirectRoute = "session.login";
// ...
}
If you do not want to use the validate method on the request, you may create a validator instance manually using the Validator facade. The make method on the facade generates a new validator instance: Refer to Laravel Validation
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
Found a solutions. All I need to do is to override the initial response from
FormRequest.php
like such and it works like a charm.
public function response(array $errors)
{
// Optionally, send a custom response on authorize failure
// (default is to just redirect to initial page with errors)
//
// Can return a response, a view, a redirect, or whatever else
if ($this->ajax() || $this->wantsJson())
{
return new JsonResponse($errors, 422);
}
return $this->redirector->to('login')
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
This works in Lara 7
Add an anchor to jump to the comment form if validation fails
protected function getRedirectUrl()
{
return parent::getRedirectUrl() . '#comment-form';
}
If you are using the validate() method on the Controller
$this->validate($request, $rules);
then you can overwrite the buildFailedValidationResponse from the ValidatesRequests trait present on the base Controller you extend.
Something along this line:
protected function buildFailedValidationResponse(Request $request, array $errors)
{
if ($request->expectsJson()) {
return new JsonResponse($errors, 422);
}
return redirect()->route('login');
}
Variations on this answer have already been offered, but overriding the getRedirectUrl() method in a custom request can enable you to define the route parameters, rather than just the name that the $redirectRoute property offers.

Categories