Required If validation for Laravel array with empty value - php

I have the following input
{
"password":"password",
"environment_roles":[
{
"environment_id":"",
"role_id":""
}
],
"admin":true
}
and have a Request class with following rules :
public function rules()
{
return [
'password' => 'required|min:6|regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/',
'environment_roles' => 'array',
'environment_roles.*.role_id' => 'required_if:admin,false|exists:roles,role_id',
'environment_roles.*.environment_id' => 'required_if:admin,false|exists:environment,environment_id',
'admin' => 'sometimes'
];
}
But it is showing the following validation error if I give the above input, which has admin as true.
"validation": {
"environment_roles.0.role_id": [
"The selected environment_roles.0.role_id is invalid."
],
"environment_roles.0.environment_id": [
"The selected environment_roles.0.environment_id is invalid."
]
},
How can I fix this. I need to validate the environment_roles.*.role_id and environment_roles.*.environment_id when the value for admin is true.

If you are always sending the admin prop it would be more suitable to make it nullable not required. You could try with that:
public function rules()
{
return [
'password' => 'required|min:6|regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/',
'environment_roles' => 'array',
'environment_roles.*.role_id' => 'nullable|required_if:admin,false|exists:roles,role_id',
'environment_roles.*.environment_id' => 'nullable|required_if:admin,false|exists:environment,environment_id',
'admin' => 'bool|sometimes'
];
}
But your error shows that the role and the environment id's does not exist in the database ( The exists rule ). Setting those two fields to nullable means it will not trigger the exists rule.

Related

How to add required if validation rule if a file has been uploaded

I'm working with Laravel 5.8 and here is my validation request form:
public function rules()
{
return [
'art_audio_file' => ['nullable', 'file', 'mimes:audio/mpeg,mpga,mp3,wav,aac'],
'art_audio_file_title' => ['required_if: IF USER HAS UPLOADED FILE']
];
}
I wonder, how can I make art_audio_file_title required if art_audio_file is not empty.
So how can I do that?
You can add your logic like below:
return [
'art_audio_file' => ['nullable', 'file', 'mimes:audio/mpeg,mpga,mp3,wav,aac'],
'art_audio_file_title' => [
Rule::requiredIf(function() {
return !empty($this->request->get('art_audio_file'));
})
]
];
or in your case simply:
'art_audio_file_title'=>'required_with:art_audio_file'

Yii2 prevent TimestampBehavior

I am creating a custom Identity interface without created_at property. I got an error :
"name": "Unknown Property",
"message": "Setting unknown property: api\\common\\models\\User::created_at",
I tried to comment the TimestampBehavior, but I got the following error:
"name": "PHP Warning",
"message": "Invalid argument supplied for foreach()",
I want to know where is the problem.
Model class:
class User extends ActiveRecord implements IdentityInterface
{
public static function tableName()
{
return '{{%user}}';
}
public function behaviors()
{
// return [
// TimestampBehavior::className(),
// ];
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['purpose'], 'required'],
[['status'], 'integer'],
];
}
}
for the rest controller the action is
public function actionLogin(){
.
.
.
$api_user = new User();
$api_user->purpose="app";
$api_user->status=User::STATUS_ACTIVE;
if($api_user->save()){
$success = true;
}
}
This will automatically resolve the issue. BlameableBehavior and TimestampBehavior
// Include these on the start
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;
use Carbon\Carbon;
// Paste this function inside the class.
/**
* #return array
*/
public function behaviors()
{
return [
'blameable' => [
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'created_by',
'updatedByAttribute' => 'updated_by',
],
'timestamp' => [
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => Carbon::now(),
],
];
}
NOTE: If you are not using updated_at or updated_by then remove it
form the above code
change your Behavior in your model to:
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
],
'value' => new Expression('NOW()'),
],
];
}
if you haven't updated_at also delete it from attributes.
You were getting following warning because you've completely removed the return in the behaviors() method.
"name": "PHP Warning",
"message": "Invalid argument supplied for foreach()",
The behaviors method must return an array. If you don't want to use any behavior your behaviors() method should return empty array like this:
public function behaviors()
{
return [];
}
This is also default implementation of behaviors() method in yii\base\Component so if you don't need to use any behavior you can simply remove the behaviors() method from your model.
Attaching TimestampBehavior to your model when you are not using it means that you add unnecessary overhead.
Example: Rename and prevent time recording or remove properties. Also change the value
Rename or delete properties or change value.
public function behaviors()
{
return [
[
'class' => \yii\behaviors\TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
// 'createdAtAttribute' => 'c_time', //Change the name of the field
'updatedAtAttribute' => false, //false if you do not want to record the creation time.
// 'value' => new Expression('NOW()'), // Change the value
],
];
}
Or
'class' => \yii\behaviors\TimestampBehavior::className(),
'attributes' => [
\yii\db\ActiveRecord::EVENT_BEFORE_INSERT => ['created_at'],
// \yii\db\ActiveRecord::EVENT_BEFORE_UPDATE => [],
],
$createdAtAttribute: The attribute that will receive timestamp value Set this property to false if you do not want to record the creation time.
$attributes: List of attributes that are to be automatically filled with the value specified via $value. The array keys are the ActiveRecord events upon which the attributes are to be updated, and the array values are the corresponding attribute(s) to be updated. You can use a string to represent a single attribute, or an array to represent a list of attributes. For example,
[
ActiveRecord::EVENT_BEFORE_INSERT => ['attribute1', 'attribute2'],
ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
]

Laravel / nested validation

I am building a component-driven API for a basic page-builder system and have hit a stumbling block when it comes to validation.
First, I want to explain the use-case.
if we have a component (for example in Vue) in /components/ProfileCard.vue
<script>
export default {
props: {
name: String,
age: Number,
avatar: String
}
}
</script>
I am creating a component in the backend components.php config:
<?php
return [
'profile' => [
'component' => 'ProfileCard',
'rules' => [
'name' => [
'required',
],
'age' => [
'required',
'number',
],
'avatar' => [
'required',
]
],
],
];
Which checks and validates every time a profile card component is submitted.
Creating a custom validation rule for Component, I am able to say "the ProfileCard component is not valid" but I am not able to merge / nest the validation rules:
Component.php
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Validator;
class Component implements Rule
{
protected $validator = null;
public function passes($attribute, $value)
{
$components = config('components');
$component = $value['component'];
if (isset($components[$component])) {
return false;
}
$c = $components[$component];
$this->validator = Validator::make($value['data'], $c['rules'], $c['messages'] ?? '');
return $this->validator->passes();
}
public function message()
{
if (is_null($this->validator)) {
return 'The component does not exist';
}
return $this->validator->errors();
}
}
Has anybody got any experience doing anything like this or can anybody point me in the right direction towards a solution?
I am ideally looking for a solution which is applicable while using Laravel's FormRequest validation, like so:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Unique;
use App\Rules\Component;
class CreateUserRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => [
'required',
'email',
new Unique('users', 'email'),
],
'profile' => [
'required',
new Component(),
]
];
}
}
The data would come in like so:
{
"email": "test#example.com",
"profile": {
"component": "ProfileCard",
"data": {
"name": "Test",
"age": 49,
"avatar": "https://example.com/avatar.png"
}
}
}
I have updated the question with the progress I have made myself, you can return a MessageBag in the messages method on the rule, however, this creates a slight problem, the response comes back as follows:
"message": "The given data was invalid.",
"errors": {
"profile": [
{
"name": [
"The name field is required."
],
"age": [
"The age field is required."
],
"avatar": [
"The avatar field is required."
],
},
":message"
]
}
Clearly this is an improvement but it's still not as usable, we don't have a ':message' and the validation errors are nested in an object in the "profile" array.
Your approach seems like you are over complicating a simple problem. I would never do a validation in a validation rule. Instead do rules that is dependent on the component and adjust it in the form request accordingly. You can easily do nested rules like so.
[
'profile.name' => 'string',
]
Do the rest of the logic in the form request. The strategy is to have rules based on the request input and your config file, based on what you already tried.
public function rules()
{
// i do not know how you determine this key
$componentKey = 'profile';
$rules = [
...,
$componentKey => [
'required',
]
];
$inputComponent= $this->input('profile')['component'];
$components = config('components');
// based on your data this seems wrong, but basically fetch the correct config entry
$component = $components[$inputComponent];
foreach ($component['rules'] as $key => $value) {
$rules[$componentKey . '.' . $key] => $value;
}
return $rules;
}
There is some parts of your code where i can't figure out what your data means, i do not know how you get the component key profile and your code based on the config and the component field seems wrong and should instead do a loop with a where condition. I think that this can get you in the right direction, this solution will solve your message problem and be way simpler.

Laravel array validation in Form Request

I can't validate a field, which contains array elements, in Form Request class.
Rules method:
public function rules()
{
return [
"state" => 'required',
"state.0" => 'required',
"state.*" => 'required',
];
}
There is an array in request->all()
"state" => array:1 [
0 => ""
]
Zero element is empty. But validation is successful.
What am I doing wrong?
In order to handle the dynamic fields, you will need to loop through all the posted "items" and add a rule for each.
Here is an updated method demonstrating this:
public function rules() {
$rules = [
'state' => 'required',
];
foreach($this->request->get('state') as $key => $val) {
$rules['state.'.$key] = 'required';
}
return $rules;
}

Laravel custom validation message

I'm using laravel 5.4 to make a custom form validation. But why is the custom error message isn't displayed?
Validator::extend('myCustomeValidator', function ($attribute, $value, $parameters, $validator) {
//some code here
return false;
});
return Validator::make($data, [
'myField' => 'myCustomeValidator',
]);
and added the following to the file : ressources\lang\en\validation.php as the documentation advises:
'custom' => [
'myField' => [
'myCustomeValidator' => 'You made an error.',
],
],
The error is correctly triggered but instead of my custom error message, I get this:
validation.my_custome_validator
What am I missing?
You need to use the snake-cased name of your custom validation rule. The following should the trick:
'custom' => [
'myField' => [
'my_custome_validator' => 'You made an error.',
],
],

Categories