Laravel Validation between not converting comma separated string to array? - php

Just trying to do some tags validation. First checking for number of tags (as comma separated list of id's).
class SomePostRequest extends Request
{
public function rules()
{
return [
'tags' => 'between:2,5'
];
}
public function all()
{
$input = parent::all();
$input['tags'] = explode(',', #$input['tags']);
return $input;
}
}
It keeps spititing out the message as
The tags must be between 2 and 5 characters.
Instead of the proper array message:
The :attribute must have between :min and :max items.

Try to use custom validation rule for you requirement:
$this->app['validator']->extend('tag', function ($attribute, $value, $parameters)
{
$tags = explode(',', $value);
if(count($tags) >= 2 || count($tags) <= 5)){
return false;
}
});
and then rules would be
public function rules(){
return [
'tags' => ['tag']
];
}
and message can update by
public function messages() {
return [
'tags.tag' => 'The :attribute must have between :min and :max items.'];
}
I hope, you get basic idea to achieve this requirement, let me know if its helps.

Related

How to create custom validation rule for dependent input fields in laravel

In my input form, I have two fields; momentFrom & momentTo. I need to put a validation which gives error message if any of the following criteria fails.
momentFrom is greater than or equal to momentTo.
momentFrom is less than now.
My code for storing the data:
public function store(Request $request, Requisition $requisitionObj) {
$momentFrom = strtotime($request->txtTravelDate . " " . $request->txtTimeFrom);
$momentTo = strtotime($request->txtTravelDate . " " . $request->txtTimeTo);
$timeValidation = $requisitionObj->validateTiming($momentFrom, $momentTo);
if ($timeValidation['error']) {
echo 'ERROR: ' . $timeValidation['message'];
return view('requisitions.create');
} else {
/* store form data into requisition object */
$requisitionObj->travel_date = $request->txtTravelDate;
$requisitionObj->moment_from = $momentFrom;
$requisitionObj->moment_to = $momentTo;
$requisitionObj->save();
return redirect()->route('requisitions.index');
}
}
I have seen laravel custom validation rules where only one field can be validated at a time. But in my scenario I need to check both fields at a time depending on each other. How can I achieve this?
Thanks for any help in advance!
Creating new Rule Class
You can create your custom rule with the artisan command: php artisan make:rule YourRuleNamethis will create a new Rule Class file into the Rules folder.
By default the created file contains a constructor, a passes method and a message method.
Rules Logic
If you have some complicated rules where you need the request or some models, you can pass them via the constructor.
public function __construct(Request $request, User $user, ....)
{
//save them into class variables to access them later
$this->request = $request;
$this->user = $user;
}
Otherwise you can directly put your validation logic into the passes method:
public function passes($attribute, $value){
//some code
return #myCondition
}
Last you are able to specify the message if the validation fails.
public function message()
{
return 'Your message';
}
To use your rule simply add it to your rules array:
$rules = [
'my_attribute' => [new MyCustomRule(),...],
]
At last, I have solved this problem using FormRequest and AppServiceProvider. Thought this would help others who come to this place.
First I have created FormRequest validator using following artisan command.
php artisan make:request StoreRequisition
Then added primary validation rules and messages into it.
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreRequisition extends FormRequest {
public function authorize() {
return true;
}
public function rules() {
$rules = [
'txtTravelDate' => 'required|date_format:Y-m-d|after_or_equal:today',
'txtTimeFrom' => 'required|date_format:H:i|travel_time_validate',
'txtTimeTo' => 'required|date_format:H:i',
];
return $rules;
}
public function messages() {
return [
'txtTravelDate.required' => 'Travel date is required!',
'txtTravelDate.date_format' => 'Invalid format for Travel Date!',
'txtTravelDate.after_or_equal' => 'Travel Date should be today or later!',
'txtTimeFrom.required' => 'Time From is required!',
'txtTimeFrom.date_format' => 'Invalid format for Time From!',
'txtTimeFrom.travel_time_validate' => 'Invalid time selected!',
'txtTimeTo.required' => 'Time To is required!',
'txtTimeTo.date_format' => 'Invalid format for Time To!',
'listFunction.required' => 'Department to be selected!',
'txtPickLoc.required' => 'Pickup Location is required!',
'txtDropLoc.required' => 'Drop Location is required!',
'listPurpose.required' => 'Travel Purpose to be selected!'
];
}
}
Then inside app\Providers\AppServiceProvider, added the extra validation logic.
public function boot() {
Validator::extend(
'travel_time_validate',
function ($attribute, $value, $parameters, $validator) {
$inputs = $validator->getData();
/* convert time to moments */
$momentFrom = strtotime($inputs['txtTravelDate'] . " " . $inputs['txtTimeFrom']);
$momentTo = strtotime($inputs['txtTravelDate'] . " " . $inputs['txtTimeTo']);
$result = true;
if ($momentFrom >= $momentTo) {
$result = false;
}
return $result;
}
);
}
My Controller:
public function store(StoreRequisition $request, Requisition $requisitionObj) {
$validatedData = $request->validated();
/* store form data into requisition object */
$requisitionObj->requester_id = Auth::user()->id;
$requisitionObj->travel_date = $request->txtTravelDate;
$requisitionObj->time_from = $request->txtTimeFrom;
$requisitionObj->time_to = $request->txtTimeTo;
$requisitionObj->purpose_id = $request->listPurpose;
/* Finally save the record into the database */
$requisitionObj->save();
return redirect()->route('requisitions.index');
}
Example how make custom rule for validation in Laravel 8.x / Lumen 8.x.
public static function rules(){
return [
'number' => [
'required', 'min:1', 'max:30', 'string', self::testNumber(),
],
];
}
public static function testNumber(){
return function($attribute, $value, $fail){
if ($value === 'foo'){
$fail('The '.$attribute.' is invalid.');
}
};
}

Laravel form validation: same as field a or b

I have 3 fields: player1, player2 and winner. I want to validate that winner is equal to either player1 or player 2.
I tried looking for a way to add an 'or' clause to the validation but couldn't find anything, only workarounds for different problems.
You can use a custom validation rule to do this. There's nothing built in that allows comparison to one of many other fields. The closest is the same check, but it only checks against one other field.
(I've added dd() to dump data, you can remove them)
The $values will be what comes from your input.
The $validationRules can be adjusted for your needs.
Validator::extend('equals_one_of', function($attribute, $value, $parameters, \Illuminate\Validation\Validator $validator) {
$fields = $validator->getData(); // all posted values
foreach($parameters as $param) { // this is each of the comma separated fields in the validationRules array
if ($value == $fields[$param]) {
dd("matched");
return true;
}
}
dd("no match");
return false;
});
$values = [
'player1' => 'test1',
'player2' => 'test2',
'winner' => 'test1'
];
$validationRules = [
'player1' => 'required',
'player2' => 'required',
'winner' => 'required|equals_one_of:player1,player2'
];
$validate = Validator::make($values, $validationRules);
// use your validator as normal.
dd($validate->validate());
Actually this rule should be registered with extendDependent to correctly resolve parameter names when used with arrays.
Final code:
class OneOfRule
{
public function validate($attribute, $value, $parameters, $validator) {
if(is_null($value)) return true;
foreach($parameters as $param) {
$other = Arr::get($validator->getData(), $param);
if ($value === $other) {
return true;
}
}
return false;
}
}
public function boot()
{
Validator::extendDependent('one_of', 'App\Http\Validation\Rules\OneOfRule#validate');
}

How to deal with conditioned based validation in laravel?

I have a situation and unfortunately not sure how to sort it out in proper way. I have below script
$validator = Validator::make(
$request->all(),
[
'game_id' => 'required|integer'
],
$messages
);
if ($validator->fails()) {
$response = $validator->messages();
}else{
$response = $gameService->setStatus($request);
}
Now each game has different type, I wanted to add validation on behalf of type. For example if a game is Task Based then I would add validation for time which would be mandatory only for Task based game otherwise it would be an optional for other types.
I have three types of games
1 - level_based
2 - task_based
3 - time_based
In the type table, each game has type.
So is there any way to add validation? I want to do it, inside validation function.
Thank you so much.
You can write your conditions before the validation.
$data = $request->all();
if ($data['game_id'] == 1) {
$rules = [
// level_based validation
];
} else if($data['game_id'] == 2) {
$rules = [
// task_based validation
];
} else {
$rules = [
// time_based validation
];
}
$validator = Validator::make($data, $rules);
Hope it helps. Cheers.
I would go with the required_if validation rule.
So in your case, will send two fields, the type can be a hidden field for example, then on the game_id you will add
'game_id' => 'required_if:type,1'
and so on.. And of course you can customize the error messages.
Try this code snippet
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class CreateGameRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
try {
$request = $this->request->all();
$rule_array = collect();
$rule1 = [
'game_id' => 'required|integer'
]
$rule_array = $rule_array->merge($rule1);
if(isset($request->task_id))
{
$rule2 = [
'task_id' => 'required|integer'
]
}
$rule_array = $rule_array->merge($rule2);
return $rule_array->all();
} catch (Exception $e) {
return $e;
}
}
public function messages(){
return [
'game_id' => 'Please select valid game',
'task_id' => 'Please select valid task'
];
}
}
then invoke this request class in controller function as
use App\Http\Requests\CreateGameRequest;
public function game(CreateGameRequest $request)
{
}

Validate Arabic Numbers in Laravel 5

I have a form that contains a field for user to enter amount value of certain payment. This field is input of type number.
The validation rule in Laravel for this input is:
'amount' => 'required|numeric'
When I enter the amount in English as: 1500 => The validation passes and everything is OK.
But when I enter the amount in Arabic as: ١٥٠٠ => The validation fails with the following error message:
"validation.numeric"
Should I validate this field manually or is there another solution to this problem?
Maybe you can create your own validation type.
You can add something like this to your boot method in app/Providers/AppServiceProvider.php.
Validator::extend('arabic_numbers', function ($attributes, $value, $parameters, $validation) {
$arabic_numbers = [
'٥',
'١',
// add more
];
$input = $value;
if (!$input) {
return false;
}
$chars = preg_split('//u', $input, -1, PREG_SPLIT_NO_EMPTY);
foreach ($chars as $char) {
if (!in_array($char, $arabic_numbers)) {
return false;
}
}
return true;
});
You can add to your existing rule, e.g. required|arabic_numbers.
Or use something like this:
$input = '١';
$validator = Validator::make([
'user_input' => $input,
], [
'user_input' => 'required|arabic_numbers'
];
if ($validator->fails()) {
//
}
Also you can use in many other ways for example in a custom request:
public function rules()
{
return [
'something' => 'required|arabic_numbers',
];
}
Hope this helps.

Laravel validation modify request before validation. return original if failed

I have a WYSIWYG editor. When users keep pressing space in the editor, the input will be like this.
"<p> </p>"
To prevent this I modified the all method in Request class like this to remove whitespace and tags.
public function all()
{
$input = parent::all();
$input['body'] = strip_tags(preg_replace('/\s+/', '', str_replace(' ',"", $input['body'])));
//modify input here
return $input;
}
This works great.
However, the problem here is if other validation rules fail, the return value from old helper function is modified by the method.
So, if the original input is like this
"""
<p> <iframe src="//www.youtube.com/embed/Mb5xcH9PcI0" width="560" height="314" allowfullscreen="allowfullscreen"></iframe></p>\r\n
<p>This is the body.</p>
"""
And if other validation rules fail I get this as an old input.
"Thisisthebody."
So, is there any way to get original request inputs as an old input when validations failed?
Here is my form request.
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use Illuminate\Validation\Factory as ValidationFactory;
class ArticleRequest extends Request
{
public function all()
{
$input = parent::all();
$input['body'] = strip_tags(preg_replace('/\s+/', '', str_replace(' ',"", $input['body'])));
//modify input here
return $input;
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'title' => 'required|min:3|max:40',
'tags.*' => 'required',
'body' => 'required|min:50',
//up to 6mb
'thumbnail'=>'image|file|max:10240'
];
}
public function messages()
{
return [
'title.required' => 'タイトルを入力してください',
'title.min' => 'タイトルは3文字以上でお願いします',
'title.max' => 'タイトルは40文字以下でお願いします',
'body.min' => '本文は50文字以上お書きください',
'body.required' => '本文を入力してください',
'tags.*.required' => 'タグを選んでください',
'thumbnail.image' => '画像の形式はjpeg, bmp, png, svgのいずれかをアップロードできます',
'thumbnail.file' => 'フォームから画像をもう一度アップロードしてください',
'thumbnail.max' => 'ファイルは10MBまでアップロードできます',
];
}
}
Create a custom validator which strips the tags, counts characters but does not modify the content itself.
Validator::extend('strip_min', function ($attribute, $value, $parameters, $validator) {
$validator->addReplacer('strip_min', function($message, $attribute, $rule, $parameters){
return str_replace([':min'], $parameters, $message);
});
return strlen(
strip_tags(
preg_replace(
'/\s+/',
'',
str_replace(' ',"", $value)
)
)
) >= $parameters[0];
});
and in your validation.php lang file add:
'strip_min' => 'The :attribute must be at least :strip_min characters.'
source: https://stackoverflow.com/a/33414725/2119863

Categories