How do I make all validate() calls to execute in a controller? - laravel

In a controller I am using two validation like this:
public function update(Request $request){
if( $request->hasFile('img1') ){
$request->validate(
[
'img1'=>'image'
]
);
}
if( $request->hasFile('img2') ){
$request->validate(
[
'img2'=>'image'
]
);
}
}
Now if I upload incorrect file types for both img1 & img2 Only the first validation is checked and laravel redirects user to the original form page. This way message for only first validation is displayed. Even though second file type was also incorrect. I want to make sure all validate methods are checked are executed before I get redirected to the page I came from i.e., the page containing form.
Also I can't put validate method for file in one if statement as img1 and img2 might not be present at the same time. Because user might just want to upload one file.

You need not invoke validate method more than once for validating a single request. In the validate method you can validate all the inputs from the request, and if the validation fails, then it will automatically return the page which the request came from. And also you need not check whether the request has the input or not, before the validation. It will automatically be managed by laravel.
In the following validation required is used to specify the field is mandatory, and it will only accept file types specified in mimes, and max is used to specify the maximum file size in kilobytes.
public function update(Request $request){
$request->validate([
'img1' => 'required | mimes:jpeg,jpg,png | max:1000',
'img2' => 'required | mimes:jpeg,jpg,png | max:1000',
]);
}

you can try
public function update(Request $request){
$request->validate([
'img1' => 'required_without:img2',
'img2' => 'required_without:img1'
]);
}
If have more than two fields and only one is required, use required_without_all:foo,bar,...

Related

Custom login property Laravel 8

I have this custom function for atempting to login in Laravel 8
protected function attemptLogin(Request $request)
{
$credentials = $this->credentials($request);
$credentials['estado']=1;
return $this->guard()->attempt(
$credentials, $request->filled('remember')
);
}
How I can make to accept the login atempt when $credentials['estado'] also has 2 as value.
Don't know how to make it accept multiple values.
I managed to make the custom function accept the value of 1 but dunno how to make it accept multiple $credentials['estado'] values.
You don't need to change anything in attemptLogin() method, instead you can customize the crededentials() method in LoginController like this:
// login, if user have like a following described data in array
protected function credentials(Request $request)
{
$username = $this->username();
return [
$username => $request->get($username),
'password' => $request->get('password'),
'estado' => [ 1, 2 ], // OR condition
];
}
Answer for comments:
Honestly in my experience I didn't have that case, but if you want to redirect to the another view on failed login (for specific field 'estado'), you can customize the "sendFailedLoginResponse" method, and add some additional if-condition for checking the 'estado'.
As the "sendFailedLoginResponse" method will be called only for getting failed login response instance, then you can check: is that fail comes from 'estado' field actually. Something like this:
protected function sendFailedLoginResponse(Request $request)
{
// custom case, when login failed and estado is 2
if ($request->get('estado') == 2) {
return view('some.specific.view');
}
// laravel by default implementation
else {
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
}
Remember, in this case (when we're redirecting the user to some page) we actually not redirecting as for always, but instead we're just returning a view. We do that because I think you don't want to let the users to open that specific view page anytime their want, as you need to let them see that page only for specific case. But when you'll do the actual redirect, then you will let the users to visit that page with some static URL.
Of course, you can do some additional stuff (add something in DB or the Session, and check is the request comes actually from 'estado' fails, but not from any user), but this could be a headeche for you, and in my opinion that will not be a real laravel-specific code.
Anyway, this is the strategy. I don't think, that this is mandatory, but this can be do your work easy and secure.
Note: I've got this deafault implementations from "AuthenticatesUsers" trait (use use Illuminate\Foundation\Auth\AuthenticatesUsers;). In any time you can get some available methods from there and override them in your LoginController, as the LoginController used that as a trait.

What is the correct order of form validation and retrieving input values?

I wonder if I should do form validation before retrieving input values or vice versa.
I usually do validation first as I see no benefit in trying to access input values that might not be valid. However, a coworker looked at my code recently and found it strange. Is there any correct order for these steps?
public function createGroups(Request $request)
{
$this->validate($request, [
'courses' => 'required_without:sections',
'sections' => 'required_without:courses',
'group_set_name' => 'required',
'group_number' => 'required|integer|min:1'
]);
$courses = $request->input('courses');
$sections = $request->input('sections');
$group_set_name = $request->input('group_set_name');
$group_number = $request->input('group_number');
Positioning the validation for your controller logic at the beginning of a method is probably the way to go here, as you have required parameters defined. If you receive data that does not fully satisfy the requirements, you produce a validation error back to the user. This follows the productive "Fail Fast" line of thinking: https://en.wikipedia.org/wiki/Fail-fast
It's also important that you're not using any data that hasn't passed your stringent requirements from validation. Data that fails validation should no longer be trusted. Unless there's some other reason you need to be, say, logging any incoming data from the frontend, the order here looks good to me.
I totally agree with #1000Nettles response, to elaborate a little bit more on his/her answer (who should be the accepted one): There isn't any need to continue with your business logic when the data doens't comply with your specifications. Let's say you expected a string of a N characters long, because you defined your database with that limitation (in order to optimize the db desing), will you try to persist it even when it'll throw an exception? Not really.
Besides, Laravel has a particular way to extract validation classes: Form Request. This are injected in controllers. When a call reach the controller it means that already passed the validation, if not, an 422error be returned.
Create a custom request and keep the mess out of your controller, it doesn't even hit your controller function if validation failed and can just grab the data in your controller if validation passed.
php artisan make:request GroupRequest
In app/Http/Requests/GroupRequest.php:
public function authorize()
{
// return true;
return request()->user()-isAdmin; // <-- example, but true if anyone can use this form
}
public function rules()
{
return [
'courses' => ['required_without:sections'],
'sections' => ['required_without:courses'],
'group_set_name' => ['required'],
'group_number' => ['required', 'integer', 'min:1'],
];
}
The best part is you can even manipulate the data in here (GroupRequest.php) after it has been validated:
public function validated()
{
$validated = $this->getValidatorInstance()->validate();
// EXAMPLE: hash password here then just use new hashed password in controller
$validated['password'] = Hash::make($validated['password']);
return $validated;
}
In your controller:
public function createUser(UserRequest $request) // <- in your case 'GroupRequest'
{
$validated = $request->validated(); // <-- already passed validation
$new_user = User::create($validated); // <-- password already hashed in $validated
return view('dashboard.users.show')->with(compact('user'));
}
In your case, if you use my GroupRequest block above, you can return to view in 1 line of code:
public function createGroups(GroupRequest $request)
{
return view('example.groups.show')->with($request->validated()); // <-- already an array
}
In you blade view file, you can then use your variables like {{ $group_set_name }} and {{ $group_number }}

Validating Image Uploads

Yo! I am working on a form where I attach some image.
Form:
{{ Form::file('attachments[]', array('multiple')) }}
Validation:
$this->validate($response, array(
'attachments' => 'required | mimes:jpeg,jpg,png',
));
I have also tried 'image' as validator rule but whenever I post the form with jpg image I get back errors:
The attachments must be a file of type: jpeg, jpg, png.
Working with Laravel 5.3
Since you defined an input name of attachments[], attachments will be an array containing your file. If you only need to upload one file, you might want to rename your input name to be attachments, without the [] (or attachment would make more sense in that case). If you need to be able to upload multiple, you can build an iterator inside your Request-extending class that returns a set of rules covering each entry inside attachments[]
protected function attachments()
{
$rules = [];
$postedValues = $this->request->get('attachments');
if(null == $postedValues) {
return $rules;
}
// Let's create some rules!
foreach($postedValues as $index => $value) {
$rules["attachments.$index"] = 'required|mimes:jpeg,jpg,png';
}
/* Let's imagine we've uploaded 2 images. $rules would look like this:
[
'attachments.0' => 'required|mimes:jpeg,jpg,png',
'attachments.1' => 'required|mimes:jpeg,jpg,png'
];
*/
return $rules;
}
Then, you can just call that function inside rules() to merge the array returned from attachments with any other rules you might want to specify for that request:
public function rules()
{
return array_merge($this->attachments(), [
// Create any additional rules for your request here...
]);
}
If you do not yet have a dedicated Request-extending class for your form, you can create one with the artisan cli by entering: php artisan make:request MyRequestName. A new request class will be created inside app\Http\Requests. That is the file where you would put the code above in. Next, you may just typehint this class inside the function signature of your controller endpoint:
public function myControllerEndpoint(MyRequestName $request)
{
// Do your logic... (if your code gets here, all rules inside MyRequestName are met, yay!)
}

laravel 5 double validation and request

I did this validation and works:
public function salvar(CreateEquipamento $Vequip, CreateLocalizacao $VLocal)
{
$this->equipamento->create($Vequip->all());
$equipamento = $this->equipamento->create($input);
return redirect()->route('equipamento.index');
}
what I want is to also do something like get the last created equipment ID and include in the array to validate and create for Local validation (CreateLocalizacao $VLocal) because i've two tables, one for the equipment and another one who stores all the places where my equipment was in.
$input['equipamento_id'] = $equipamento->id;
$this->localizacao->create($VLocal->all());
How could I do something like this?? thx in advance !
I do a "workarround" solution ;)
$localizacao = [
'equipamento_id' => $id,
'centrocusto_id' => $input['centrocusto_id'],
'projeto' => $input['projeto'],
'data_movimentacao' => $input['data_movimentacao']
];
$this->localizacao->create($VLocal->all($localizacao));
I dont know if this is the best way to do it but works, but if somebody has the right way to do post please!
Are you using Laravel 5?
If yes, use form Requests, they make everything easier. If you need to validate two things from one form, you just put two requests in the controller method. I use this when I register an user for an ecommerce page. I need to validate the user data and the address data, like this:
public function store(UserRegisterRequest $user_request, AddressCreateRequest $add_request)
{
//if this is being executed, the input passed the validation tests...
$user = User::create(
//... some user input...
));
Address::create(array_merge(
$add_request->all(),
['user_id' => $user->id]
));
}}
Create the request using artisan: php artisan make:request SomethingRequest, it generates an empty request (note the authorize function always returns false, change this to true or code that verifies that the user is authorized to make that request).
Here's an example of a Request:
class AddressCreateRequest extends Request {
public function authorize()
{
return true;
}
public function rules()
{
return [
"fullname" => "required",
//other rules
];
}
}
More on that on the docs:
http://laravel.com/docs/5.0/validation#form-request-validation

non-static method basemodel::validate() should not be called statically,assuming $this from compatible context

Was tryin to validate my registration form calling the validation method from the basemodel in my controller
The method
public function postSIgnup ()
{
$validation = User::validate(Input::all());
}
Routes
Route::post('register', array('before=>'csrf', 'uses'=>'UsersController#postSignup'));
Help mi solve this problem
You can't just say 'validate my whole form'.
The reason this error occurs is because you are trying to use the validation method from Laravel.
Basic Form Validation in Laravel
First you want to grab all your form data/content.
$input = Input::all();
Secondly you can setup some rules. Those rules can be set in an array which Laravel will use.
Make sure the names are correctly spelled. (Those are the ones u used in your form-template.)
$rules = array(
'real_name' => 'Required|Min:3|Max:80|Alpha',
'email' => 'Required|Between:3,64|Email|Unique:users',
'age' => 'Integer|Min:18',
'password' =>'Required|AlphaNum|Between:4,8|Confirmed',
'password_confirmation'=>'Required|AlphaNum|Between:4,8'
);
To make use of the validator you have to create anew instance first:
You attach the form input and the rules you have set above.
When they match you can save your form to your database or what else you would like to do with it. That will probably look like:
$validator = Validator::make($input,$rules);
Cool,
We can check now if the Validator passes or not...
if($validator->fails()){
$messages = $validator->messages();
return Redirect::to('yourpage')->withErrors($messages);
}else{
// Handle your data...
}

Resources