Making Laravel 8 validation rules optional to allow empty fields - laravel

I have a Laravel 8 application. In a form I have two fields that are both optional.
I set the validation rules like so:
class ValidateAddEmptyTopic extends FormRequest {
public function rules() {
return [
'title' => 'string|max:255',
'init_url' => 'url'
];
}
}
However it still requires the fields to be included, even without the required attribute. How can I make the fields optional while still having the validation rules applied when data is submitted from the fields?

You need to add nullable validation too.
return [
'title' => 'nullable|string|max:255',
'init_url' => 'nullable|url'
];
The field under validation may be null. This is particularly useful when validating primitive such as strings and integers that can contain null values

Hope it will slove your problem
class ValidateAddEmptyTopic extends FormRequest {
public function rules() {
return [
'title' => 'nullable|string|max:255',
'init_url' => 'nullable|url'
];
}
}

Related

FormRequest messages() function does not translate all rules [duplicate]

I'm working on a Laravel 5.8 project and trying to show custom validation messages for a validation which uses the requiredIf validation rule.
Here is how I have it set up:
$validation = Validator::make(
$request->all(),
[
...
'sum' => [
Rule::requiredIf(function() use ($request){
$model = Model::find($request->id);
return $model->is_special; //returns a boolean value
}),
'numeric'
],
...
],
[
...
'sum.required_if' => 'This cannot be blank',
'sum.numeric' => 'Must use a number here',
...
]
);
Now the validation is working correctly and the custom message for the numeric validation shows as should, but the message I get for the requiredIf() method is Laravel's default error message.
I also tried using 'sum.requiredIf' => '...' but that didn't work either and can't seem to find any documentation or example for this scenario.
I was tinkering with this for a while and noticed that for this to work I needed to define
'sum.required' => 'This cannot be blank'
and not 'sum.required_if' => 'This cannot be blank',.
Not sure if this is expected behavior or just a workaround but my deduction is that with the callback Rule::requiredIf(function() use ($request){...}) the parameters :other and :value are not passed so it falls back onto required messaging and I guess this makes sense since required_if and required would not be used on the same :attribute.
Hope this helps anyone who comes across this problem.
First, create a rule name isSpecial or whatever
php artisan make:rule isSpecial
Go to App\Rules\isSpecial.php
private $id;
public function __construct($id) // pass id or what you need
{
//
$this->id=$id;
}
public function passes($attribute, $value) // customize your rules here
{
//
return Model::find($request->id)->is_special;
}
public function message() // here is answer for your question
{
return 'The validation error message.'; // your message
}
in your controller
use App\Rules\isSpecial;
\Validator::make($request->all(), [
'sum' => new isSpecial() ,
])->validate();
another idea :
Specifying Custom Messages In Language Files
In most cases, you will probably specify your custom messages in a language file instead of passing them directly to the Validator. To do so, add your messages to custom array in the resources/lang/xx/validation.php language file.
'custom' => [
'email' => [
'required' => 'We need to know your e-mail address!',
],
],
Simple notice:
- I suggest using HTTP Requests instead use validation in your controller and function direct
Looks like as of Laravel 8, using required_if works as expected, and alternatively will not fall back on required as mentioned previously:
'sum.required_if' => 'This cannot be blank',

Laravel avoid duplicate entry from model

I'm building a Laravel API. I have a models called Reservations. I want to avoid that a user creates two reservations for the same product and time period.
I have the following:
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
Edit after comments:
I'm also using validation
$validator = Validator::make($request->all(), [
'listing_id' => 'required|exists:listings,id',
'user_id_from' => 'required|exists:users,id',
'start_date' => 'required|date_format:"Y-m-d"|after:today',
'end_date' => 'required|date_format:"Y-m-d"|after:start_date'
]);
if ($validator->fails()) {
return response()->json(['error' => 'Validation failed'], 403);
}
Validation is working properly.
End of Edit
In my model I have casted the start_date and end_date as dates.
class Reservation extends Model
{
protected $fillable = ['listing_id', 'start_date', 'end_date'];
protected $dates = [
'start_date',
'end_date'
];
....
....
Documentation says:
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs
However I notice that I'm still able to insert entries with the same attributes.
Any idea what I'm doing wrong or suggestions to fix it?
Probably there's a better way than this, but you can create an static method on Reservation to do this, like:
public static function createWithRules($data) {
$exists = $this->where('product_id', $data['product_id'])->whereBetween(*date logic that i don't remember right now*)->first();
if(!$exists) {
* insert logic *
} else {
* product with date exists *
}
}
So you can call Reservation::createWithRules($data)
You can achieve this using Laravel's built in ValidateRequest class. The most simple use-case for this validation, is to call it directly in your store() method like this:
public function store(){
$this->validate($request, [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
], $this->messages);
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
}
With this, you're validating users $request with by saying that specified columns are required and that they need to be unique, in order for validation to pass.
In your controller, you can also create messages function to display error messages, if the condition isn't met.
private $messages = [
'listing_id.required' => 'Listing_id is required',
'title.unique' => 'Listing_id already exists',
//... and so on
];
You can also achieve this by creating a new custom validation class:
php artisan make:request StoreReservation
The generated class will be placed in the app/Http/Requests directory. Now, you can add a few validation rules to the rules method:
public function rules()
{
return [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
];
}
All you need to do now is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
public function store(StoreReservation $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
}
If you have any additional question about this, feel free to ask. Source: Laravel official documentation.

TimestampBehavior does not work because of failing validation

I have the following class with a TimestampBehaviour:
/**
* #property int $id
* #property string $name
* #property int $created_at
*/
class Workspace extends yii\db\ActiveRecord {
public static function tableName() {
return 'workspace';
}
public function behaviors() {
return [
[
'class' => TimestampBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => 'created_at',
ActiveRecord::EVENT_BEFORE_UPDATE => false,
],
'value' => date('Y-m-d H:i:s')
],
];
}
...
}
For some reason the behavior does not populate the property. It is always empty when I try to save the model ($workspace->save()). I cannot save it since validation fails ("created_at cannot be blank"). There is nothing special with this class. Nothing is overridden. What could be the problem?
It turned out that the validation rules caused the troubles. Unexpected, since I thought all is correct. These were my rules:
public function rules() {
return [
[['id', 'name', 'created_at'], 'required'],
[['id'], 'int'],
[['name'], 'string', 'max' => 100],
[['created_at' ], 'datetime'],
];
}
created_at must not be required - that was the problem.
It is even documented:
Because attribute values will be set automatically by this behavior,
they are usually not user input and should therefore not be validated,
i.e. created_at and updated_at should not appear in the rules() method
of the model.
When $workspace->save() gets executed then the first step is the validation. And only after that step the EVENT_BEFORE_INSERT/EVENT_BEFORE_UPDATE gets triggered which causes TimestampBehaviour to populate the specified fields. And this happens only if the validation was successful! (if you var_dump you will indeed see an empty created_at.) Too late, validation has taken place already and I've got the validation error.
Recommended solution is to remove created_at from the required rule. Other approaches are also possible, of course (e.g. turn off validation or pass the properties that should be validated when save() gets called).
Add behaviour like bellow
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'create_time',
'updatedAtAttribute' => 'update_time',
'value' => new Expression('NOW()'),
],
];
}
and add it to safe records in your model class.
public function rules()
{
return array(
array('create_time,update_time', 'safe'),
);
}

Laravel 5 validation rules

previously I have used validation within a Request class e.g.
public function rules()
{
return [
'userName' => 'required', 'min:3',
'userEmail' => 'required|email',
'departmentId' => 'required',
'slug' => 'required',
];
}
But I now have another form but I can't see any options within the documentation that might help me.
Basically, lets say I have a form with the same fields as the validation above. The only time validation should fail is if ALL fields contain absolutely no data. So if I put something like "hi" within the slug input and submit, it should pass the validation.
Would something like this be possible?
Thanks
You can probably use the required_without_all validation rule.
http://laravel.com/docs/5.1/validation#rule-required-without-all
The field under validation must be present only when all of the other
specified fields are not present.
It would give you something like
public function rules()
{
return [
'userName' => 'required_without_all:userEmal,departmentId,slug','min:3',
'userEmail' => 'required_without_all:userName,departmentId,slug|email'
...
];
}
But it's not very handy if you have a lot of fields.
If you have to deal with many fields, creating a custom validator might be a better solution.
http://laravel.com/docs/5.1/validation#custom-validation-rules

Using form request specific custom validation attributes

Using Laravel's localization (http://laravel.com/docs/5.1/localization) I have created some custom validation attributes to provide friendlier validation errors (for instance, 'First Name' instead of first name etc).
I am using form requests (http://laravel.com/docs/5.1/validation#form-request-validation) in order to validate user submissions and there are scenarios where I would like to provide store-specific custom validation attributes (for instance, I may have a 'name' field that is Brand Name in one context, and Product Name in another).
The messages() method allows me to specify validation rule specific message overrides, but that isn't ideal as it's not the validation message as such we need to override, just the attribute name (for example, if we have 5 validation rules for 'email', we have to provide 5 overrides here, rather than one override for, let's say, Customer Email).
Is there a solution to this? I note references to formatValidationErrors() and formatErrors() in the Laravel documentation, but there is not really any information on how to correctly override these, and I've not had much luck in trying.
You can override the attribute names, which is defaulting to whatever the field name is.
With form request
In your form request class override the attributes() method:
public function attributes()
{
return [
'this_is_my_field' => 'Custom Field'
];
}
With controller or custom validation
You can use the 4th argument to override the field names:
$this->validate($request, $rules, $messages, $customAttributes);
or
Validator::make($data, $rules, $messages, $customAttributes);
Simple working example
Route::get('/', function () {
// The data to validate
$data = [
'this_is_my_field' => null
];
// Rules for the validator
$rules = [
'this_is_my_field' => 'required',
];
// Custom error messages
$messages = [
'required' => 'The message for :attribute is overwritten'
];
// Custom field names
$customAttributes = [
'this_is_my_field' => 'Custom Field'
];
$validator = Validator::make($data, $rules, $messages, $customAttributes);
if ($validator->fails()) {
dd($validator->messages());
}
dd('Validation passed!');
});
As detailed in my question, I was looking for a way to provide specific form request stores (http://laravel.com/docs/5.1/validation#form-request-validation) with custom attribute names.
The Laravel documentation only covers two methods for Requests in this context - rules() and authorize(). I was aware there is a messages() method to provide validation specific custom error messages, but it also appears there is an attributes() method, which fits my requirements exactly:
public function attributes()
{
return [
'name' => 'Product Name'
]
}
This overrides the attribute name in the context of my store.

Resources