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.
Related
I am trying to access a data called "section" in my rules() function inside my FormRequest Validator Class. What I am trying to do is check what is the value of "section" and return different rules. But apparently, I am not able to access the value of the data/payload.
I have checked the answer from here, but it didn't work for me : Laravel Form Request Validation with switch statement
Here is my code :
FormController.php
class FormController extends Controller
{
public function verify(DynamicFormRequest $request , $section)
{
return response()->json(['success' => 'everything is ok']);
}
}
DynamicFormRequest.php
class DynamicFormRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
error_log($this->request->get('section'));//retruns null and depricated
error_log($this->request->input('section')); //this is absolutely wronge but , i tried it anyways
error_log($this->request->section); //retruns null
error_log($this->section); //retruns null
switch ($this->request->get('section')) {
case '1':
return [
'item_name' => 'required',
];
break;
case 'Type2':
return [
'item_favorite' => 'required',
];
break;
}
}
}
Please help to make me understand whats wronge
If you are using route model binding you can use $this->section right off the bat.
So this should work assuming your routes are set up in the correct format:
error_log($this->section);
Route (Something like....):
Route::post('section/{section}', [SectionController::class, 'update']);
This question could help: Stack Q: Laravel: Access Model instance in Form Request when using Route/Model binding
Lastly, Hard to know it's working without your other code. Have you dumped out $request to check section is in there?
you can use required_if:anotherfield,value to check the value for other fields
return [
'item_name' => 'required_if:section,1',
'item_favorite' => 'required_if:section,Type2'
]
https://laravel.com/docs/9.x/validation#rule-required-if
I hope it's helpful
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')
}
}
on my controller I have:
public function store(ProductRequest $request)
The request:
class ProductRequest extends Request
{
public function rules()
{
return [
'name' => 'required|min:3',
'perTypeTime' => 'sometimes|required',
'per_type_id' => 'required'
];
}
}
I want to change the perTypeTime rule above to be conditional depending on if per_type_id field == 1.
If I initiated the validator in the controller I believe I could do something like the below:
$v = Validator::make($data, [
'per_type_id' => 'required|email'
]);
$v->sometimes('perTypeTime', 'required|max:500', function($input)
{
return $input->per_type_id == 1;
});
Is there a way to do this, while keeping my custom request. I like how this approach keeps the controller cleaner.
I can't seem to access the validator on the request object. Is there a way to specify this condition inside the request itself, or to access the validator from the request object, or is there another way?
You can do that
I want to change the perTypeTime rule above to be conditional depending on if per_type_id field == 1.
within your rules() method in your ProductRequest.
For details see required_if:anotherfield,value in the Laravel documentation validation rules.
public function rules()
{
return [
'name' => 'required|min:3',
'perTypeTime' => 'required_if:per_type_id,1',
'per_type_id' => 'required'
];
}
Laravel 5.3, in your request file you can add:
use Illuminate\Validation\Factory;
...
public function validator(Factory $factory)
{
$validator = $factory->make($this->input(), $this->rules());
$validator->sometimes('your_sometimes_field', 'your_validation_rule', function($input) {
return $input->your_sometimes_field !== null;
});
return $validator;
}
Actually this answer https://stackoverflow.com/a/41842050/3922975 is not the best.
We don't have to replace default validator with our own (because we are not changing anything). In that solution we hope validation factory will always require only two attributes ($this->input(), $this->rules()) which is actually not true even in time of writing.
This is a default validator used by Laravel:
$factory->make(
$this->validationData(),
$this->container->call([$this, 'rules']),
$this->messages(),
$this->attributes()
);
As you can see it is much different from that Artem Verbo used.
Better solution is to create withValidator method in your ProductRequest class:
use Illuminate\Contracts\Validation\Validator;
...
public function withValidator(Validator $validator)
{
$validator->sometimes('your_sometimes_field', 'your_validation_rule', function ($input) {
return $input->your_sometimes_field !== null;
});
}
I am creating Rest Full Api for mobile application, I am validating request it redirects me to the login page with errors.
Here is my ApiController (I have created for all api):
use App\User as UserModel;
use App\Fb_friend as FbFriendsModel;
use App\Http\Requests\UserRequest;
class ApiController extends Controller
{
/**
* Create a new movie model instance.
*
* #return void
*/
public function __construct(UserModel $user, FbFriendsModel $fb_friends){
$this->user = $user;
$this->fb_friends = $fb_friends;
}
public function createUser (UserRequest $request) {
// some code here
}
Route:
Route::post('createUser', ['as' => 'createUser', 'uses' => 'ApiController#createUser']);
UserRequest.php:
public function rules()
{
return [
'fb_id' => 'required|unique:users',
'username' => 'required|unique:users',
'email' => 'required|unique:users',
'image' => 'required',
'device_id' => 'required',
'status' => 'required',
];
}
I have override a function Request.php for error formatting:
abstract class Request extends FormRequest
{
protected function formatErrors(Validator $validator)
{
return [$validator->messages()->toJson()];
}
}
When I try to call service via postman, it returns me error in json format but it also print the login page, I m not getting why?
If you are using Postman for testing API's, it is not necessary to override the response() in Request class, One can follow the following steps,
make return type in authorize() in your custom Request as true,
public function authorize()
{
//make it true
return true;
}
Go to headers section in your Postman and define Accept type,
Accept:application/json
Now hit the endpoint of your API and bam..working fine for me.
It has been done by override the response method in app/Http/Requests/Request.php
public function response(array $errors) {
if ($this->ajax() || $this->wantsJson() || Request::isJson()) {
$newError = [];
$newError['result'] = false;
$newError['errors'] = $errors;
// in the above three lines I have customize my errors array.
return new JsonResponse($newError, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors);
}
We also need to use JsonResponse class at the top
use Illuminate\Http\JsonResponse;
Source: https://laracasts.com/discuss/channels/general-discussion/laravel-5-validation-formrequest
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.