laravel 5 double validation and request - laravel-5

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

Related

Making Laravel 9 validation rule that is unique on 2 columns

I am trying to update a row in the pages table.
The slug must be unique in the pages table on the slug and app_id field combined.
i.e. there can be multiple slugs entitled 'this-is-my-slug' but they must have unique app_id.
Therefore I have found that formula for the unique rule is:
unique:table,column,except,idColumn,extraColumn,extraColumnValue
I have an update method and getValidationRules method.
public function update($resource,$id,$request){
$app_id=22;
$request->validate(
$this->getValidationRules($id,$app_id)
);
// ...store
}
When I test for just a unique slug the following works:
public function getValidationRules($id,$app_id){
return [
'title'=> 'required',
'slug'=> 'required|unique:pages,slug,'.$id
];
}
However, when I try and add the app_id into the validation rules it returns server error.
public function getValidationRules($id,$app_id){
return [
'title'=> 'required',
'slug'=> 'required|unique:pages,slug,'.$id.',app_id,'.$app_id
];
}
I have also tried to use the Rule facade, but that also returns server error. Infact I can't even get that working for just the ignore id!
public function getValidationRules($id,$app_id){
return [
'title'=> 'required',
'slug'=> [Rule::unique('pages','slug')->where('app_id',$app_id)->ignore($id)]
];
}
Any help is much appreciated :)
Thanks for the respsonses. It turned out a couple of things were wrong.
Firstly if you want to use the Rule facade for the validation rules, make sure you've included it:
use Illuminate\Validation\Rule;
The other method for defining the validation rule seems to be limited to the following pattern:
unique:table,column,except,idColumn
The blog post that I read that showed you could add additional columns was for laravel 7, so i guess that is no longer the case for laravel 9.
Thanks for your responses and help in the chat!
I recommend you to add your own custom rule.
First run artisan make:rule SlugWithUniqueAppIdRule
This will create new file/class inside App\Rules called SlugWIthUniqueAppRule.php.
Next inside, lets add your custom rule and message when error occured.
public function passes($attribute, $value)
{
// I assume you use model Page for table pages
$app_id = request()->id;
$pageExists = Page::query()
->where('slug', $slug)
->where('app_id', $app_id)
->exists();
return !$pageExists;
}
public function message()
{
return 'The slug must have unique app id.';
}
Than you can use it inside your validation.
return [
'title'=> 'required|string',
'slug' => new SlugWithUniqueAppIdRule(),
];
You can try it again and adjust this custom rule according to your needs.
Bonus:
I recommend to move your form request into separate class.
Run artisan make:request UpdateSlugAppRequest
And check this newly made file in App\Http\Requests.
This request class by default will consists of 2 public methods : authorize() and rules().
Change authorize to return true, or otherwise this route can not be accessed.
Move your rules array from controller into rules().
public function rules()
{
return [
'title'=> 'required|string',
'slug' => new SlugWithUniqueAppIdRule(),
];
}
To use it inside your controller:
public function update(UpdateSlugAppRequest $request, $resource, $id){
// this will return validated inputs in array format
$validated = $request->validated();
// ...store process , move to a ServiceClass
}
This will make your controller a lot slimmer.

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.

Map controller functions into database records in Laravel

I try to implement a chain of approval methodology in my Laravel app.
Lets say that I have a standard CRUD controller with standard REST routes.
[URL]/products
In the controller I have 3 functions (Index, Store, Update)
I want that for Store and Update a certain condition will happen before, something like that (pseudo):
if (fucntion requires chain of approval) {
if (auth()->user !== one of the approvers){
email all approvers;
return 201 "pending approval";
}
}
// the logged in user is allowed to execute the function
rest of the code...
I'm struggling with a few things here:
The only thing I can think of something that might be similar to this inside Laravel is the $this->authorize() function but I don't think that it was meant to be used like this, it is meant to authorize or not to authorize, not for 201 codes, and using it means aborting with 201 and it doesn't sit right
I want to allow the admins to control which functions need approval and because I don't want to maintain my controller functions together with a seeder that will contain a list of all the functions I'm thinking about creating an artisan command to be run before production and mapping all the functions into a database table that will be used as a model and all the process will use a proper many to many relations between the functions and the approvers, but I don't know how to map the functions with artisan command and I don't know if this is even the right way to go
I want to avoid from writing a certain code in all the functions that might require approval, and don't know how and if it is even possible
The return of the functions should return a JsonResource of the specific model for example ProductResource, What should I return when I need approval? Just to mock a proper response with status pending?
Thanks for everybody who is willing to give me some guidance.
You can do something similar to below... just have a single class that defines how each user type (or even permission) is handled.
public function index(IndexRequest $request) // verify that use is authorized to do this action
{
$response = (new MyJobDirector)->handle(Auth::user());
.. handle response
}
MyJobDirector Class
class MyJobDirector
{
const STRATEGY = [
'user' => 'userHandler',
'manager' => 'managerHandler',
];
public function handle(User $user): array
{
return $this->{static::STRATEGY[$user->role]}();
}
protected function userHandler(): array
{
event(EmailApprovers::class);
return [
'code' => 201,
'response' => 'pending approval',
];
}
}
You can make it even more complicated by defining a separate class for each handler and specifying constants for status and a response that each class should return
class User extends BaseJobHandle
{
const CODE = 201;
const RESPONSE = 'pending approval';
}
abstract class BaseJobHandler
{
const CODE;
const RESPONSE;
public function handle(): array
{
$this->additionalProcesses();
return [
'code' => static::CODE,
'response' => static::RESPONSE,
];
}
protected function additionalProcesses(): void {}
}
class MyJobDirector
{
const STRATEGY = [
'user' => User::class,
'manager' => Manager::class,
];
public function handle(User $user): array
{
$class = static::STRATEGY[$user->role];
return (new $class)->handle();
}
}
Regarding The return of the functions should return a JsonResource of the specific model for example ProductResource, What should I return when I need approval? Just to mock a proper response with status pending?
You can have a ProductResouce class, in which you decide which specific resource to return depending on a use case. It's all depends on what data you need to return.

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 }}

How to validate params in REST Lumen/Laravel request?

Route:
$app->get('/ip/{ip}', GeoIpController::class . '#show');
How to validate ip's properly? I've tried to inject Request object in show method, but wasn't able to solve this. I want to stick with REST, so using URL parameters is not solution for me. I use it for API purposes, so status code as response would be appropriate.
Also tried that way:
$app->bind('ip', function ($ip) {
$this->validate($ip, [
'ip' => 'required|ip',
]);
});
EDIT:
The answer below is correct, I've found more info about requests in documentation:
Form requests are not supported by Lumen. If you would like to use form requests, you should use the full Laravel framework.
In other words, you cannot use custom requests via injection in constructors in Lumen.
The validate method takes the request object as the first parameter. Since you're passing the ip in the route, you need to create a custom validator.
public function show($ip)
{
$data = ['ip' => $ip];
$validator = \Validator::make($data, [
'ip' => 'required|ip'
]);
if ($validator->fails()) {
return $validator->errors();
}
return response()->json(['All good!']);
}
Edit : This is all laravel does under the hood. You could basically you this function directly to validate the ip and save a lot of effort.
protected function validateIp($ip)
{
return filter_var($ip, FILTER_VALIDATE_IP) !== false;
}

Resources