Laravel - Validator::make fails my unique rule - php

I'm trying to use a validation on 'name' as unique, but to be ignored if the objected that has that same name is the same object being updated.
The Validation keeps failing, can you help me figuring out why?
Validator Function - I'm using same validator function to both create and update and only need to apply the rule on the update.
protected function validator(array $data, Wharehouse $wharehouse = null)
{
//different validations for create and edit
if($wharehouse != null){
return Validator::make($data, [
'name' => ['required', 'string', 'max:255', Rule::unique('wharehouses')->ignore($wharehouse)],
'espacoTotal' => ['required', 'numeric', 'max:60000']
]);
}else{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255', Rule::unique('wharehouses')],
'espacoTotal' => ['required', 'numeric', 'max:60000']
]);
}
}
Validation Call
protected function editById(Request $request)
{
$wharehouse = Wharehouse::find($request->wharehouse_id);
$validation = $this->validator($request->all(),$wharehouse);
if ($validation->fails()) {
return Redirect::to('/wharehouses')->withInput()->withErrors($validation);
} else {
$wharehouse->name = $request->input('name');
$wharehouse->espacoTotal = $request->input('espaco');
$wharehouse->save();
return back()->with('create.success','Armazem actualizado com sucesso.');
}
}

Append the id of the instance currently being updated to the validator.
Pass the id of your instance to ignore the unique validator.
In the validator, use a parameter to detect if you are updating or
creating the resource.
If updating, force the unique rule to ignore a given id: unique:table,column,except,idColumn
//rules
'name' => ['required', 'string', 'max:255', 'unique:users,name,' . $userId],

Related

Laravel request date validation

I am beginner webdeveloper / php laravel developer.
I have this request:
class ProductRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
$id = $this->route('product');
return array_merge(
[
'name' => ['required', 'max:255', Rule::unique('products')->ignore($id)->whereNull('deleted_at')],
'slug' => ['required', 'max:255', Rule::unique('products')->ignore($id)->whereNull('deleted_at')],
'price' => ['required', 'numeric'],
'preorder_release_date' => ['required', 'numeric'],
],
$this->generateTranslationsRules()
);
}
private function generateTranslationsRules(): array
{
$rules = [];
foreach (LaravelLocalization::getSupportedLanguagesKeysExceptDefault() as $locale) {
$rules[$locale . '.name'] = ['required', 'max:255'];
$rules[$locale . '.slug'] = ['required', 'max:255'];
}
return $rules;
}
}
It's work fine.
I need to add a requirement for preorder_release_date - the date cannot be earlier than today. How can I write it down?
Please help me
use Laravel validation before
'preorder_release_date' => ['required','date','date_format:Y-m-d','before:today'],
NOTE : date format you need to adjust based on your request
ref link https://laravel.com/docs/8.x/validation#rule-before

dont want to edit particular field which is used in store and update functions

ProductController
public function store()
{
$product = Product::create($this->validateRequest());
return redirect('/product');
}
public function update(Product $product)
{
$product->update($this->validateRequest());
return redirect('/product');
}
private function validateRequest()
{
return request()->validate([
'sub_category_id' => ['required'],
'name' => ['required', 'min:4'],
'code' => ['required', 'alpha_dash','unique:products'],
'description' => ['required', 'min:4'],
'color' => ['required', 'min:3'],
'price' => ['required', 'integer'],
]);
}
here code has unique value from table products. but whenever I edit the form it says code has already taken. so how to execute this without edit the 'code'(its unique).
You can ignore certain ids during the unique check:
use Illuminate\Validation\Rule;
public function store()
{
Product::create($this->validateRequest(new Product()));
return redirect('/product');
}
public function update(Product $product)
{
$product->update($this->validateRequest($product));
return redirect('/product');
}
private function validateRequest(Product $product)
{
return request()->validate([
'sub_category_id' => ['required'],
'name' => ['required', 'min:4'],
'code' => ['required', 'alpha_dash', Rule::unique('products')->ignore($product)],
'description' => ['required', 'min:4'],
'color' => ['required', 'min:3'],
'price' => ['required', 'integer'],
]);
}
Here you either pass the existing model when updating or a new model instance when storing, so the call to $product->id either returns null when storing a new product so no product in the database is ignored, or the id when updating and then only that product is ignored.
From the docs:
Forcing A Unique Rule To Ignore A Given ID:
Sometimes, you may wish to ignore a given ID during the unique check.
For example, consider an "update profile" screen that includes the
user's name, e-mail address, and location. You will probably want to
verify that the e-mail address is unique. However, if the user only
changes the name field and not the e-mail field, you do not want a
validation error to be thrown because the user is already the owner of
the e-mail address.
To instruct the validator to ignore the user's ID, we'll use the Rule
class to fluently define the rule. In this example, we'll also specify
the validation rules as an array instead of using the | character to
delimit the rules:
use Illuminate\Validation\Rule;
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
Be aware of the following though:
You should never pass any user controlled request input into the
ignore method. Instead, you should only pass a system generated unique
ID such as an auto-incrementing ID or UUID from an Eloquent model
instance. Otherwise, your application will be vulnerable to an SQL
injection attack.
So the problem here is you are using the same rule for creating the product and updating the product.
If you are using the latest Laravel, you may want to read the documentation about form request https://laravel.com/docs/7.x/validation#form-request-validation and create different form request for store and update.
If you still want to use your way, you can try as below
private function validateRequest()
{
$rules = [
'sub_category_id' => ['required'],
'name' => ['required', 'min:4'],
'description' => ['required', 'min:4'],
'color' => ['required', 'min:3'],
'price' => ['required', 'integer'],
];
if (request()->isMethod('store')) {
$rules['code'] = ['required', 'alpha_dash'];
}
return request()->validate($rules);
}
Please tell me whether it works!
Try this, proper and clean code
ProductController
use Illuminate\Validation\Rule; // add this
public function store()
{
$product = Product::create($this->validateRequest());
return redirect('/product'); //use route instead of URL
}
public function update(Product $product)
{
$product->update($this->validateRequest($product->id));
return redirect('/product'); //use route instead of URL
}
private function validateRequest($id = null)
{
return request()->validate([
'sub_category_id' => 'required',
'name' => 'required|min:4',
'code' => 'required|alpha_dash|' . Rule::unique('products')->ignore($id),
'description' => 'required|min:4',
'color' => 'required|min:3',
'price' => 'required|integer',
]);
}
Suggest to use Form request validation

How can I reject user registration with certain username in default laravel register form?

I'm trying to block users from using certain names like 'admin' or 'operator'.
I've tried fiddling with both Controllers/Auth/RegisterController and Controller/RegisterController but failed.
What I've tried was something like this:
in Controllers/Auth/RegisterController,
if ($data['name'] === 'admin' || $data['name'] === 'operator') {
return redirect()->back()->withErrors(['Invalid username']);
}
else {
session()->flash('message', 'Welcome!');
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
The code above gave me this error:
Argument 1 passed to Illuminate\Auth\SessionGuard::login() must be an
instance of Illuminate\Contracts\Auth\Authenticatable, string given,
called in /var/www/vendor/laravel/ui/auth-backend/RegistersUsers.php
on line 36
I've also searched whether validators can block specific words but failed.
I know I can work this around by using JS, but I think Laravel would have some function like this.
Go to RegisterController.php.
There will be a validator(array $data) function that validates your registration input.
Add not_in validation check for the name field.
Something like this:
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255', 'not_in:admin,operator'],
'pan' => ['required', 'string', 'min:10', 'max:10', 'unique:users,username'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
Hopefully, this helps.
You can try to validate if requested name is not in the list of predefined blocked names:
use Illuminate\Validation\Rule;
Validator::make($data, [
'toppings' => [
'required',
Rule::notIn(['admin', 'superuser']),//etc..
],
]);
Hope it helps.
Here's what I did in Laravel 9 with the Breeze scaffolding.
I created a separate "Services" trait with the reserved usernames, so that I can easily implement and extend it.
Create a Services folder at app/Services
Create a trait Reserved at app/Services/Reserved.php
Reserved.php
<?php
namespace App\Services;
trait Reserved
{
public static function usernames()
{
return ['admin', 'operator', 'someBadWord'];
}
}
Next open app/Http/Controllers/Auth/RegisteredUserController.php and import the trait, then add it.
Now just add it to your validation rules using the notIn rule.
Rule::notIn(Reserved::usernames())
RegisteredUserController.php
use Illuminate\Validation\Rule;
use App\Services\Reserved;
class RegisteredUserController extends Controller
{
use Reserved;
...
public function store(Request $request)
{
$request->validate([
'username' => ['required', 'string', 'max:16', 'unique:users', Rule::notIn(Reserved::usernames())],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
...
}
}
Create RegisterControllerRequest using the following command:
php artisan make:request RegisterControllerRequest
find out your recent created file in app/Http/Requests, then update rules() to be:
public function rules()
{
return [
'name' => 'required|notIn:admin,operator',
];
}
Then update your controller to use the new validation:
After name space add:
use App\Http\Requests\RegisterControllerRequest;
And finally inject the validation as a parameter in your register method:
public function register (RegisterControllerRequest $request)
For more information check documentation: https://laravel.com/docs/7.x/validation#form-request-validation

"Trying to get property of non-object" from validate

for some reason that I do not know, when I try to pass a validation without request and try to use one if
public function save(Request $request){
$request = $request->validate([
'name' => ['string', 'max:255'],
'email' => ['string', 'email', 'max:255', 'unique:users'],
]);
if($request->name != null){
return $request;
}
return $request;
}
You are replacing the type of your $request with the result from validate()
The validation will handle the what you wish to, so no need to worry. If you say the variable name is required, it will enforce it to not be null or empty;
So, just replace the result to a specific variable instead of replacing $request by doing:
$validationResult = $request->validate([
'name' => ['string', 'required', 'max:255'],
'email' => ['string', 'required', 'email', 'max:255', 'unique:users'],
]);
Better option than this is to create a specific request type by running
php artisan make:request YourRequest`
Your new class will be ready at app/Http/Requests where you can specify not only your rules() as you have in your array, as the messages() you wish each to output.
Then all you need to do is to replace your save(Request $request)
for save(YourRequest $request) which will kick in the validation before it triggers the method, which means you are at ease within the controller method to do the logic instead of having to double check your variables
Examples for common rules() within your class, will be:
public function rules()
{
return [
'email' => 'required|email|unique:users|max:255',
'name' => 'required|min:2|max:200',
];
}
public function messages()
{
return [
'email.required' => 'The email field is required',
'email.email' => 'The email field needs to be an email type. Ex:. type#gmail.com',
....
];
}
Obviously adjust the rules and messages to your project and liking :)

How to: validate the existence of a database relationship in Laravel 4?

I have a model Product that belongs to a Trend:
class Product extends Eloquent {
public function trend()
{
return $this->belongsTo('Trend');
}
}
And as part of my validation rules I would like to check that this relationship exists, and if not trigger an error using:
$validator = Validator::make(Input::all(), $rules, $messages);
if ($validator->fails())
{
... some redirection code here
is called. I have tried to use the validation exists like below, but it never fires.
$rules = array(
'brand_name' => 'required|min:3',
'blurb' => 'required',
'link' => 'required',
'trend' => 'exists:trends'
);
I have also tried a few variations on the exists method, but nothing ever seems to fire. I know that the instance I am testing on definitely doesn't have a relationship set.
What am I doing wrong here?
EDIT:
I see now from typing this out that I am validating the input and not the models values. How would I actually validate a model instance's properties instead?
I have the following code in a ExchangeRate class:
/**
* Return the validator for this exchange rate.
*
* #return Illuminate\Validation\Validator A validator instance.
*/
public function getValidator()
{
$params = array(
'from_currency_id' => $this->from_currency_id,
'to_currency_id' => $this->to_currency_id,
'valid_from' => $this->valid_from,
'rate' => $this->rate,
'organization_id' => $this->organization_id,
);
$rules = array(
'from_currency_id' => ['required', 'exists:currencies,id'],
'to_currency_id' => ['required', 'exists:currencies,id', 'different:from_currency_id'],
'valid_from' => ['required', 'date'],
'rate' => ['required', 'numeric', 'min:0.0'],
'organization_id' => ['required', 'exists:organizations,id'],
);
return Validator::make($params, $rules);
}
Of course, this ExchangeRate class also have the associations defined:
public function from_currency()
{
return $this->belongsTo('Currency', 'from_currency_id');
}
public function to_currency()
{
return $this->belongsTo('Currency', 'to_currency_id');
}
And all this glued together works like a clock:
$validator = $exchangeRate->getValidator();
if ($validator->fails())
throw new ValidationException($validator);

Categories