I saw there are a few other questions in which they asked what the difference was between $this->validate and Validator::make(). They didnt really answer the conceptual question I was wondering though.
Is there a proper use for each of these? Like when to use one versus the other?
How I am currently using it is in my API classes I use a if else with $validator::make() (like below) while in my Web portion of the program I use $this->validate() (also below)
Is this a proper way to use this?
$validator::make:
public function store(Request $request)
{
$validator = Validator::make($request->all(),[
'name' => 'required',
'url' => 'required',
'isPublic' => 'required'
]);
if($validator->fails()){
return response($validator->messages(), 200);
} else {
Helpers::storeServer($request);
return response()->json([
'message'=> ['Server Stored']
]);
}
}
$this->validate:
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'url' => 'required',
'isPublic' => 'required'
]);
Helpers::storeServer($request);
return redirect('dashboard')->with('success', 'Server stored');
}
Nope, they do the same exact thing two different ways. I mean that literally, $this->validate() calls the make() method on the validation class. If you look at ValidatesRequests.php, implemented by Controller.php which your controller extends.
The validate() method calls:
$validator = $this->getValidationFactory()
->make($request->all(), $rules, $messages, $customAttributes);
So it ends up using the make() method eventually. There is a difference in how it is handled, since $this->validate() calls:
if ($validator->fails()) {
$this->throwValidationException($request, $validator);
}
So using Validator::make() will allow you to handle the exception yourself, instead of $this->validate() auto throwing the validation exception for you. This is useful for doing something before your redirect. You show this in your first example, since you check if the validation fails before deciding how to handle it.
In the second example you know that if validation fails it will automatically refuse the request...
if fail any rules on $this->validate, will auto throw an error
$validator::make: you can easy handle the errors and may be you want wrap any data to array() and pass to $validator::make: for Validation
and also if you want to use FormRequest + Route Model Binding
your store() look like this
public function store(YourFormRequestRules $request)
{
Helpers::storeServer($request);
return redirect('dashboard')->with('success', 'Server stored');
}
public function update(YourFormRequestRules $request, Post $post)
{
$post->title = $request->input('title');
$post->save();
return redirect()->route('example.index');
}
thats all
Using $this->validate(): YourController extends a Controller class which uses ValidatesRequests trait and it looks like this:
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
The ValidatesRequests trait itself is made up of several methods that the base Controller gets and then your own controller and which includes:
validateWith($validator, Request $request = null)
...
validate(Request $request, array $rules, array $messages = [], array $customAttributes = [])
...
validateWithBag($errorBag, Request $request, array $rules, array $messages = [], array $customAttributes = [])
... and so on...
These methods helps makes some validation and request errors handling much convenient and in fact considering the mentioned validate() method.
When there's validation error in the request passed, it helps you handle the response without litering your controller with unneeded logic; i.e when it's an ajax call it returns a 422 response with a json body while it returns populates the error bag and fill the $errors variable for use in the blade template for non-ajax.
In summary, this only helps you incase you don't want to do manual validation by creating an instance of Validator, so to keep the developer focus on doing the real work ;)
UPDATE:
$validator = Validator::make() //creates an instance of the validator for further operations
// You can even do after the creating the instance of validator:
$this->validateWith($validator, $request)
//Lets do both steps above i.e the validation and trigger the Validation Exception if there's failure.
$this->validate($request, [...rules...], [...messages..])
Check: https://laravel.com/docs/5.6/validation#quick-writing-the-validation-logic
Validator helper makes me more happy
$validator = validator()->make(request()->all(), [
'type' => 'required|integer'
]);
if ($validator->fails())
{
redirect()->back()->with('error', ['your message here']);
}
Laravel give helper that makes developing more convince.
Related
In my method update() has some block of error conditions Call to a member function failed on array precisely that checks if $validations fails. I noticed that all the store() and updated() methods work correctly without the need for the conditional block. I just thought it would be nice to insert these conditions.
namespace App\Http\Controllers;
use App\Http\Requests\HunterRequest;
use Illuminate\Http\Request;
use App\Models\HunterModel;
class HunterController extends Controller {
public function store(Request $request)
{
$validations = $request->validate();
if ($validations->fails()){
return redirect()->back();
}
HunterModel::create($validations);
return redirect()->to('/');
}
public function update(Request $request, $id)
{
$validations = $request->validate();
if ($validations->fails()){ // Call to a member function fails() on array
return redirect()->back();
}
HunterModel::where('id',$id)->update($validations);
return redirect()->to('/');
}
}
$request->validate() expects an array of validation rules to validate the incoming request data against. You're not currently supplying anything so aren't actually performing any validation.
If validation is successful, the return value of that call will be an array of validated data, it is not an object and so doesn't have a fails() method (or any methods for that matter).
// $validations is an array
$validations = $request->validate([
'field' => ['required'],
]);
If validation is not successful, Laravel will automatically redirect back to the caller sending all failuer errors messages.
Therefore, the following lines are redundant and should be removed as Laravel will handle failure scenarios for you:
if ($validations->fails()){
return redirect()->back();
}
you are using validate method for the validation logic, this method return an array ...
you should use make method to check validation's fails
$validator = Validator::make($request->all(), [
'smoeFiled' => 'required|.....',
]);
you should also provide the validator with validation rules.
see Laravel doc for more details.
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')
}
}
I found myself stuck or lost in docs!
I'm using Laravel 5.4 and I'm trying to create validation in a controller which requires a request object.
My problem is my route passes parameters, I can't find in the docs an example how you include the Request $request argument and the $id parameter in a Controller method.
Here is my example:
1: SomeController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
...
public function edit($id) {
$request = Request; // <-- Do I use that instead of passing an arg?
}
2: Routes with a Paramter in -- routes/web.php
Route::match(['GET', 'POST'], '/some/edit/{id}', 'SomeController#edit');
In their example # https://laravel.com/docs/5.4/requests#accessing-the-request they have Request $request this in their controller, but what happens to Route parameters?:
public function store(Request $request) {}
Question: Do I need to edit the route or is the first parameter ignored if its a request?
A: Or would I do this in **SomeController.php?**
public function edit(Request $request, $id)
{
// I would get the $request, but what happens to $id?
$this->validate($request, [
'title' => 'required|unique:posts|max:255',
]);
}
B: Perhaps, something like this?:
public function edit($id)
{
// Or Request::getInstance()?
$this->validate(Request, [
'title' => 'required|unique:posts|max:255',
]);
}
Any advice would help, or even an example!
Doing
public function edit(Request $request, $id)
should match the route you already have. So it's ok to do that. Request $request here is not a required parameter that you pass. Laravel will take care of it.
It works this way because everytime you send a request, well Laravel 'sends' a request for you (GET, POST). So, it always exist. But you can use use it within your controller if you dont pass it to the controller function as a parameter.
However, you can access it through the global helper function request() if you dont want to pass it as a function parameter.
$request = request();
$name = $request->name; // or
$name = request('name');
Let's say I'm building a small application, where a small part of it is responsible for sending an email when a contact form is submitted. I only want to do that if the contact form passes some simple validation.
In the following controller the ContactRequest $request parameter is unused inside the method, although Laravel used the type-hinting to automatically apply the ContactRequest logic to the request.
Is it possible to achieve the same thing without leaving an unused variable in the controller method?
// Route
Route::post( 'contact', 'PageController#submitContactForm' );
// PageController
public function submitContactForm( ContactRequest $request ) {
sendContactFormEmail();
return redirect()->back();
}
// ContactRequest
public function authorize() {
return hasNotSubmittedContactFormRecently();
}
public function rules() {
return [ 'message' => 'required' ];
}
Yes, you can write your controller method like so:
// PageController
public function submitContactForm()
{
app()->make(ContactRequest::class);
sendContactFormEmail();
return redirect()->back();
}
and it will have same effect. However for me it's better to use it as you used it before.
Also probably you somehow use data you receive, so it might be more reasonable to use it like this:
sendContactFormEmail($request);
instead of probably injecting request into sendContactFormEmail method.
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;
});
}