Laravel 5.1 Modify input before form request validation - php

Is there a way to modify input fields inside a form request class before the validation takes place?
I want to modify some input date fields as follows but it doesn't seem to work.
When I set $this->start_dt input field to 2016-02-06 12:00:00 and $this->end_dt to 2016-02-06 13:00:00 I still get validation error "end_dt must be after start_dt". Which means the input request values aren't getting changed when you update $this->start_dt and $this->end_dt inside the rules() function.
public function rules()
{
if ($this->start_dt){
$this->start_dt = Carbon::createFromFormat('d M Y H:i:s', $this->start_dt . ' ' . $this->start_hr . ':'. $this->start_min . ':00');
}
if ($this->end_dt){
$this->end_dt = Carbon::createFromFormat('d M Y H:i:s', $this->end_dt . ' ' . $this->end_hr . ':'. $this->end_min . ':00');
}
return [
'start_dt' => 'required|date|after:yesterday',
'end_dt' => 'required|date|after:start_dt|before:' . Carbon::parse($this->start_dt)->addDays(30)
];
}
Note: start_dt and end_dt are date picker fields and the start_hr, start_min are drop down fields. Hence I need to create a datetime by combining all the fields so that I can compare.

As of laravel 5.4 you can override the prepareForValidation method of the ValidatesWhenResolvedTrait to modify any input. Something similar should be possible for laravel 5.1.
Example in your Request
/**
* Modify the input values
*
* #return void
*/
protected function prepareForValidation() {
// get the input
$input = array_map('trim', $this->all());
// check newsletter
if (!isset($input['newsletter'])) {
$input['newsletter'] = false;
}
// replace old input with new input
$this->replace($input);
}

The FormRequest has a method validationData() that return the data to validate, so i'm overriding it in my form request:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyClassRequest extends FormRequest
{
...
/**
* Get data to be validated from the request.
*
* #return array
*/
public function validationData() {
return array_merge(
$this->all(),
[
'number' => preg_replace("/[^0-9]/", "", $this->number)
]
);
}
...
}
It work fine i'm using Laravel 5.4 :)

Try these steps:
1- Middleware
first of all you should create a middleware in app/Http/Middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TransformsRequest;
class DateTimeMiddleware extends TransformsRequest
{
protected $fields = [
'birth_date' => 'toGregorian',
'created_at' => 'toDateTime',
];
protected function transform($key, $value)
{
if (!array_key_exists($key, $this->fields)) {
return $value;
}
$function = $this->fields[$key];
return call_user_func($function, $value);
}
}
with this middleware you can define fields that you want to manipulate them before calling validation and call a specific function to manipulate them.
note: i defined toGregorian and toDateTime in my own helper functions. you can handle it with your own functions
2- Kernel
then modify Http/Kernel.php like bellow:
protected $middlewareGroups = [
'web' => [
...
\App\Http\Middleware\EnglishStrings::class,
],
'api' => [
...
],
];

You can do something like the following:
public function rules(Request $request)
{
if ($request->has('start_dt')){
$request->replace('start_dt', Carbon::createFromFormat('d M Y H:i:s', $request->start_dt . ' ' . $request->start_hr . ':'. $request->start_min . ':00'));
}
if ($request->has('end_dt')){
$request->replace('end_dt' ,Carbon::createFromFormat('d M Y H:i:s', $request->end_dt . ' ' . $request->end_hr . ':'. $request->end_min . ':00'));
}
return [
'start_dt' => 'required|date|after:yesterday',
'end_dt' => 'required|date|after:start_dt|before:' . Carbon::parse($request->start_dt)->addDays(30)
];
}

I took an alternative approach to this, as I want to be able to use $model->fill($validated); in my controllers. As such, I need to ensure that checkboxes have been included as false where they would otherwise be excluded from the array.
So, I created a trait, in app\Traits\ConvertBoolean.php, as follows:
<?php
namespace App\Traits;
trait ConvertBoolean
{
// protected $boolean_attributes = [];
/**
* Override the FormRequest prepareForValidation() method to
* add boolean attributes specified to the request data, setting
* their value to the presence of the data in the original request.
*
* #return void
*/
protected function prepareForValidation() {
if (isset($this->boolean_attributes) && is_array($this->boolean_attributes)) {
$attrs_to_add = [];
foreach ($this->boolean_attributes as $attribute) {
$attrs_to_add[$attribute] = $this->has($attribute);
}
$this->merge($attrs_to_add);
}
}
}
This trait looks for the existence of an array $this->boolean_attributes in the request. If it finds it, it goes through each one and adds the attribute to the request data with the value set to the presence of the attribute in the original request data.
It disregards the value of the HTML form's checkbox value. In most cases, this won't be a problem, but you could change the logic in the trait to look for specific values, if required.
Now that I have this trait, I can then use it in any request, like so:
use App\Traits\ConvertBoolean;
class MyUpdateRequest extends FormRequest
{
use ConvertBoolean;
protected $boolean_attributes = ['my_checkbox'];
// ... other class code
public function rules()
{
// Note that the data is added to the request data,
// so you still need a validation rule for it, in
// order to receive it in the validated data.
return [
'my_checkbox' => 'boolean',
// other rules
];
}
}
If you want to use $this->prepareForValidation() in your request, this is still possible.
Change MyRequest, as follows:
use App\Traits\ConvertBoolean;
class MyUpdateRequest extends FormRequest
{
use ConvertBoolean {
prepareForValidation as traitPrepareForValidation;
}
protected function prepareForValidation() {
// the stuff you want to do in MyRequest
// ...
$this->traitPrepareForValidation();
}
// ...
}

Related

Laravel Form best way to store polymorphic relationship

I have a notes model. Which has a polymorphic 'noteable' method that ideally anything can use. Probably up to 5 different models such as Customers, Staff, Users etc can use.
I'm looking for the best possible solution for creating the note against these, as dynamically as possible.
At the moment, i'm adding on a query string in the routes. I.e. when viewing a customer there's an "Add Note" button like so:
route('note.create', ['customer_id' => $customer->id])
In my form then i'm checking for any query string's and adding them to the post request (in VueJS) which works.
Then in my controller i'm checking for each possible query string i.e.:
if($request->has('individual_id'))
{
$individual = Individual::findOrFail($request->individual_id_id);
// store against individual
// return note
}elseif($request->has('customer_id'))
{
$customer = Customer::findOrFail($request->customer_id);
// store against the customer
// return note
}
I'm pretty sure this is not the best way to do this. But, i cannot think of another way at the moment.
I'm sure someone else has come across this in the past too!
Thank you
In order to optimize your code, dont add too many if else in your code, say for example if you have tons of polymorphic relationship then will you add tons of if else ? will you ?,it will rapidly increase your code base.
Try instead the follwing tip.
when making a call to backend do a maping e.g
$identifier_map = [1,2,3,4];
// 1 for Customer
// 2 for Staff
// 3 for Users
// 4 for Individual
and so on
then make call to note controller with noteable_id and noteable_identifier
route('note.create', ['noteable_id' => $id, 'noteable_identifier' => $identifier_map[0]])
then on backend in your controller you can do something like
if($request->has('noteable_id') && $request->has('noteable_identifier'))
{
$noteables = [ 'Customers', 'Staff', 'Users','Individual']; // mapper for models,add more models.
$noteable_model = app('App\\'.$noteables[$request->noteable_identifier]);
$noteable_model::findOrFail($request->noteable_id);
}
so with these lines of code your can handle tons of polymorphic relationship.
Not sure about the best way but I have a similar scenario to yours and this is the code that I use.
my form actions looks like this
action="{{ route('notes.store', ['model' => 'Customer', 'id' => $customer->id]) }}"
action="{{ route('notes.store', ['model' => 'User', 'id' => $user->id]) }}"
etc..
And my controller looks this
public function store(Request $request)
{
// Build up the model string
$model = '\App\Models\\'.$request->model;
// Get the requester id
$id = $request->id;
if ($id) {
// get the parent
$parent = $model::find($id);
// validate the data and create the note
$parent->notes()->create($this->validatedData());
// redirect back to the requester
return Redirect::back()->withErrors(['msg', 'message']);
} else {
// validate the data and create the note without parent association
Note::create($this->validatedData());
// Redirect to index view
return redirect()->route('notes.index');
}
}
protected function validatedData()
{
// validate form fields
return request()->validate([
'name' => 'required|string',
'body' => 'required|min:3',
]);
}
The scenario as I understand is:
-You submit noteable_id from the create-form
-You want to remove if statements on the store function.
You could do that by sending another key in the request FROM the create_form "noteable_type". So, your store route will be
route('note.store',['noteableClass'=>'App\User','id'=>$user->id])
And on the Notes Controller:
public function store(Request $request)
{
return Note::storeData($request->noteable_type,$request->id);
}
Your Note model will look like this:
class Note extends Model
{
public function noteable()
{
return $this->morphTo();
}
public static function storeData($noteableClass,$id){
$noteableObject = $noteableClass::find($id);
$noteableObject->notes()->create([
'note' => 'test note'
]);
return $noteableObject->notes;
}
}
This works for get method on store. For post, form submission will work.
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Requests\NoteStoreRequest $request
* #return \Illuminate\Http\Response
*/
public function store(NoteStoreRequest $request) {
// REF: NoteStoreRequest does the validation
// TODO: Customize this suffix on your own
$suffix = '_id';
/**
* Resolve model class name.
*
* #param string $name
* #return string
*/
function modelNameResolver(string $name) {
// TODO: Customize this function on your own
return 'App\\Models\\'.Str::ucfirst($name);
}
foreach ($request->all() as $key => $value) {
if (Str::endsWith($key, $suffix)) {
$class = modelNameResolver(Str::beforeLast($key, $suffix));
$noteable = $class::findOrFail($value);
return $noteable->notes()->create($request->validated());
}
}
// TODO: Customize this exception response
throw new InternalServerException;
}

How to get all possible error messages of a Laravel Controller method

So I have a Laravel Application, which has many Controllers to handle various aspects of the applications.
Now each controller has various methods. Most of the methods have validations rules defined such as:
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray);
if ($validator->fails()){
return Response::json(['response'=>implode(', ',$validator->messages()->all())],422);
}
Now the following line:
return Response::json(['response'=>implode(', ',$validator->messages()->all())],422);
actually returns whatever is wrong with the validation rules.
My question is: Is there any way to get all possible error messages programmatically?
Of course, one way to do it is going around the rule by rule and make a list manually but there are hundreds of the methods scattered over various controllers.
So, if anyone could point me in the direction of taking all the error messages in some easier way, would be much appreciated.
Thank you in advance!
UPDATE
So to clear further I need a list of all possible errors, like for above code the list will be like:
['id is required', 'id must be an integer', 'status is required', 'status must be an string']
UPDATE 2
Please keep in mind that there are hundreds of methods and also I do not want to change the final response of the method but to have some sort of external script which can help me getting the error messages without interfering with the controllers much.
In order to do that you have to extend Validator class and write a method that will iterate all rules and explicitly add error messages as if they failed.
First, create a new file app\Http\Custom\Validator.php:
<?php
namespace App\Http\Custom;
use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Validation\Validator as BaseValidator;
class Validator extends BaseValidator {
/** #var MessageBag */
protected $errorMessages;
/** #var array */
protected $hasExplicitFileErrorMessage;
protected $explicitFileRules = [
'File', 'Image', 'Mimes', 'Mimetypes', 'Dimensions',
];
function availableErrors()
{
$this->errorMessages = new MessageBag();
$this->hasExplicitFileErrorMessage = [];
foreach($this->rules as $attribute => $rules) {
$attribute = str_replace('\.', '->', $attribute);
foreach($rules as $rule) {
[$rule, $parameters] = ValidationRuleParser::parse($rule);
if($rule == '') {
continue;
}
if(($keys = $this->getExplicitKeys($attribute)) &&
$this->dependsOnOtherFields($rule)) {
$parameters = $this->replaceAsterisksInParameters($parameters, $keys);
}
// explicitly add "failed to upload" error
if($this->hasRule($attribute, $this->explicitFileRules) && !in_array($attribute, $this->hasExplicitFileErrorMessage)) {
$this->addFailureMessage($attribute, 'uploaded', []);
$this->hasExplicitFileErrorMessage[] = $attribute;
}
if($rule instanceof RuleContract) {
$messages = $rule->message() ? (array)$rule->message() : [get_class($rule)];
foreach($messages as $message) {
$this->addFailureMessage($attribute, get_class($rule), [], $message);
}
} else {
$this->addFailureMessage($attribute, $rule, $parameters);
}
}
}
return $this->errorMessages->all();
}
function addFailureMessage($attribute, $rule, $parameters = [], $rawMessage = null)
{
$this->errorMessages->add($attribute, $this->makeReplacements(
$rawMessage ?? $this->getMessage($attribute, $rule), $attribute, $rule, $parameters
));
}
// we have to override this method since file-type errors depends on data value rather than rule type
protected function getAttributeType($attribute)
{
if($this->hasRule($attribute, $this->explicitFileRules)) {
return 'file';
}
return parent::getAttributeType($attribute);
}
}
Next, let's register this class in Validation factory:
<?php
namespace App\Providers;
use App\Http\Custom\Validator; // <-- our custom validator
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
public function boot()
{
app('validator')->resolver(function ($translator, $data, $rules, $messages) {
return new Validator($translator, $data, $rules, $messages);
});
}
}
And... that's all. Let's test it:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class HomeController extends Controller {
function index(Request $request)
{
$rules = [
'id' => 'required|int|between:2,10',
'status' => 'required_with:nonexisting|string|email',
'avatar' => 'required|file|mimes:png|max:1000',
'company' => 'required_without:id|unique:companies,id'
];
$validator = Validator::make([], $rules);
dump($validator->availableErrors());
}
}
array:13 [▼
0 => "The id field is required."
1 => "The id must be an integer."
2 => "The id must be between 2 and 10."
3 => "The status field is required when nonexisting is present."
4 => "The status must be a string."
5 => "The status must be a valid email address."
6 => "The avatar failed to upload."
7 => "The avatar field is required."
8 => "The avatar must be a file."
9 => "The avatar must be a file of type: png."
10 => "The avatar may not be greater than 1000 kilobytes."
11 => "The company field is required when id is not present."
12 => "The company has already been taken."
]
It isn't pretty but here's my shot:
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(), $validationArray);
if ($validator->fails()) {
$messages = [];
$invalid_fields = array_keys($validator->messages()->toArray());
$rules = $v->getRules();
foreach($invalid_fields as $invalid_field) {
foreach($rules[$invalid_field] as $rule) {
if(str_contains($rule, ':') {
// complex rules that have parameters (min, between, size, format)
// are more difficult to work with. I haven't figured out how to do them yet
// but you should get the idea.
continue;
} else {
$messages[] = str_replace(':attribute', $invalid_field, $validator->getTranslator()->get("validation.$rule"));
}
}
}
return Response::json(['response' => implode(', ', $messages)], 422);
}
Number 1: Like I mentioned in my comment under the question, what you're trying to achieve may be done in simpler way.
Number 2: Since you do not want to change your already written code where you got ->messages() then you could do the following. I will list the steps and provide an example code.
We need to override Laravel's validator, (Validation) Factory, and ValidationService provider classes.
In App\Services folder you can create two classes Validator and ValidationFactory
in App\Providers create a class ValidationServiceProvider
Go into config/app.php file and under providers replace Illuminate\Validation\ValidationServiceProvider::class with App\Providers\ValidationServiceProvider::class
Validator class looks like so:
namespace App\Services;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\ValidationRuleParser;
use Illuminate\Contracts\Translation\Translator;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Illuminate\Contracts\Validation\Rule as RuleContract;
class Validator extends \Illuminate\Validation\Validator
{
/**
* #var MessageBag $all_messages
*/
protected $all_messages;
public function __construct(Translator $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{
parent::__construct($translator, $data, $rules, $messages, $customAttributes);
$this->all_messages = new MessageBag;
$this->getAllFormattedMessages();
}
public function makeAllRulesMessages($attribute, $rule, $parameters)
{
$this->all_messages->add($attribute, $this->makeReplacements(
$this->getMessage($attribute, $rule), $attribute, $rule, $parameters
));
}
public function messages(bool $validated_rules_messages = false)
{
return $validated_rules_messages
? $this->validatedMessages()
: $this->all_messages;
}
/**
* This is here in case the true validated messages are needed
*
* #return MessageBag
*/
public function validatedMessages()
{
return parent::messages();
}
public function getAllFormattedMessages()
{
// We'll spin through each rule and add all messages to it.
foreach ($this->rules as $attribute => $rules) {
$attribute = str_replace('\.', '->', $attribute);
foreach ($rules as $rule) {
// First we will get the correct keys for the given attribute in case the field is nested in
// an array. Then we determine if the given rule accepts other field names as parameters.
// If so, we will replace any asterisks found in the parameters with the correct keys.
[$rule, $parameters] = ValidationRuleParser::parse($rule);
if (($keys = $this->getExplicitKeys($attribute)) &&
$this->dependsOnOtherFields($rule)) {
$parameters = $this->replaceAsterisksInParameters($parameters, $keys);
}
$value = $this->getValue($attribute);
if ($value instanceof UploadedFile && $this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
) {
$this->makeAllRulesMessages($attribute, 'uploaded', []);
} elseif ($rule instanceof RuleContract) {
$this->makeCustomRuleMessage($attribute, $rule);
} else {
$this->makeAllRulesMessages($attribute, $rule, $parameters);
}
}
}
}
/**
* #param $attribute
* #param \Illuminate\Contracts\Validation\Rule $rule $rule
*/
public function makeCustomRuleMessage($attribute, $rule)
{
$this->failedRules[$attribute][get_class($rule)] = [];
$messages = (array)$rule->message();
foreach ($messages as $message) {
$this->all_messages->add($attribute, $this->makeReplacements(
$message, $attribute, get_class($rule), []
));
}
}
}
This class does one thing in summary, get all the messages of the passed rules into $all_messages property of the class. It extends and allows the base validation class run, and simply overrides messages() method to make all the collected rules available for use.
ValidationFactory overrides Illuminate\Validation\Factory and it looks like so:
namespace App\Services;
use Illuminate\Validation\Factory;
class ValidationFactory extends Factory
{
/**
* Resolve a new Validator instance.
*
* #param array $data
* #param array $rules
* #param array $messages
* #param array $customAttributes
* #return \Illuminate\Validation\Validator
*/
protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
{
if (is_null($this->resolver)) {
return new \App\Services\Validator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
This class does only one thing, overrides resolve() method in this class by making use of the instance of our custom \App\Services\Validator class instead.
ValidationServiceProvider extends Illuminate\Validation\ValidationServiceProvider and overrides registerValidationFactory() method and it looks like so:
namespace App\Providers;
use App\Services\ValidationFactory;
use Illuminate\Validation\ValidationServiceProvider as BaseValidationServiceProvider;
class ValidationServiceProvider extends BaseValidationServiceProvider
{
protected function registerValidationFactory()
{
$this->app->singleton('validator', function ($app) {
$validator = new ValidationFactory($app['translator'], $app);
// The validation presence verifier is responsible for determining the existence of
// values in a given data collection which is typically a relational database or
// other persistent data stores. It is used to check for "uniqueness" as well.
if (isset($app['db'], $app['validation.presence'])) {
$validator->setPresenceVerifier($app['validation.presence']);
}
return $validator;
});
}
}
What the above class does is also to instruct the provide to make use of our App\Services\ValidationFactory whenever the app requires one.
And we are done. All validation messages will be shown even if one of our validation rules failed.
Caveats
In order to achieve this, we needed to make a lot of changes and overriding. Except really critical this may signal that something about the app's design looks wrong.
Laravel validation implementation may change in future release and therefore may become a problem maintaining these changes.
I cannot tell if there are other side effects that might happen for overriding Laravel's default validation implementation or if all the rules return the right messages.
Normally you only want to return failed validation messages to user rather than all the possible failures.
I think that functions failed() (get the failed validation rules) or errors() (get the message container for the validator) may help you. If it does not - go to https://laravel.com/api/5.8/Illuminate/Validation/Validator.html and I hope that you find needed function.
I think you are looking for a way to have custom error messages. if this is the case then the answer is like this:
$messages = [
'id.required' => 'id is required',
'id.integer' => 'id must be an integer',
'status.required' => 'status is required',
'status.string'=> 'status must be an string'
];
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray, $messages);
more info you can find here.
I hope this is what you are looking for and my answer is helping you:)
Based on the Laravel Form Validation Procedure you can write the statement by following:
$validationArray = [
'id'=>'required|integer',
'status'=>'required|string'
];
$validator = Validator::make($request->all(),$validationArray);
if ($validator->fails()){
return Response::json(['response'=> validator->errors())],422);
}
Where errors() method return all the errors as associative array where the message will be associate with the field name accordingly and that's how you can get the errors.

Yii 2 How add a custom validation check on file upload

I am building a CSV uploader and I want to add a custom validation function that will check the header row of the CSV file to ensure the correct columns are in place.
I am trying to put a custom validation rule in the model to do this but failing at the first hurdle.
I am getting
Setting unknown property: yii\validators\FileValidator::0
exception but as far as I can tell from the documentation this should work.
Model
/**
* UploadForm is the model behind the upload form.
*/
class UploadForm extends Model
{
/**
* #var UploadedFile file attribute
*/
public $file;
/**
* #return array the validation rules.
*/
public function rules()
{
return [
[['file'], 'file', 'extensions' => 'csv', 'checkExtensionByMimeType'=>false, 'headerCheck', 'skipOnEmpty' => false]
];
}
public function attributeLabels(){
return [
'file'=>'Select csv'
];
}
function headerCheck($attribute, $params, $validato){
$this->addError($attribute, "error");
}
}
Controller function:
public function actionUpload()
{
$model = new UploadForm();
if (Yii::$app->request->isPost) {
$model->file = UploadedFile::getInstance($model, 'file');
$filename = $model->file->baseName . '.' . $model->file->extension;
if ($model->file && $model->validate()) {
$upload = $model->file->saveAs('uploads/'.$filename );
if($upload){
define('CSV_PATH','uploads/');
$csv_file = CSV_PATH . $filename;
$filecsv = file($csv_file);
foreach($filecsv as $data){
$lines = explode(',',$data);
$t=1;
}
}
}
}
return $this->render('csvUpload', ['model' => $model]);
}
View
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
<?= $form->field($model, 'file')->fileInput() ?>
<button>Submit</button>
<?php ActiveForm::end() ?>
Why is headerCheck() not getting picked up as a custom validation function?
Short Answer
Your rules should be written like so:
return [
[['file'], 'file', 'extensions' => 'csv', 'checkExtensionByMimeType'=>false, 'skipOnEmpty' => false],
[["file"], "headerCheck"],
];
Note that your validation rule, "headerCheck", is a separate item in the array.
Long Answer
A rule's structure is like so:
[["attributes_to_validate"],"vaildatorCallback","param1"=>"value","param2"=>"value]
Note the first two items are the attributes and the callback respectively, and then after that you can specify params that should be assigned to the validator, or passed to your validator callback. These params are expected in a form where the key is the name of the property, and the value is the value to assign to the property.
In the example you provided, Yii sees that you want to utilize the "file" validator, so it creates an instance of yii\validators\FileValidator. It then sees that you want the parameter "extensions" set to "csv", so it does:yii\validators\FileValidator::$extensions = "csv"; But then, because you have included your custom validator in this part of the array, it thinks that "headerCheck" is actually a value of a property you want to assign to the validator. Because you have entered this "param" without a key, the key defaults to 0 and so Yii thinks the property you want to assign is called '0'. Thus, Yii attempts this: yii\validators\FileValidator::0 = "headerCheck";
Of course, there is no property '0' on FileValidator, and so that's where the error you're getting is coming from.

Laravel Validation: grouped validation

I am attempting to add validation to my request validator for a group of fields to make sure that the group of fields does not exceed a certain amount. Once a user submits their form, they can view it in pdf form and my objective is to make sure that the collection of f_name, m_name and l_name does not run into another group of text on the pdf (thus making it illegible).
Essentially, I want something like this:
public function rules()
{
return [
'f_name' + 'm_name' + 'l_name' => 'max:50',
...
];
}
I have searched all over but the only questions I can find related are simply how to do validation. If anyone knows how to group values in validation or links to previously asked questions, please let me know. Thanks mates.
Laravel does not support multifields validation with one rules stack. Try write custom validator or reinvent the wheel:
public function rules()
{
$fields = [
"max: 50" => ["f_name", "m_name", "l_name"]
];
$rules = [];
foreach($fields as $rule => $fieldArray){
if(is_array($fieldArray)){
foreach($fieldArray as $field){
$rules[$field] = $rule;
}
}
}
return $rules;
}
You need a custom validator so you can add several fields.
In your AppServiceProvider, something like this:
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('groupedFieldsLongerThan50', function($attribute, $value, $parameters, $validator) {
$f_name = array_get($this->data, $parameters[0]);
$m_name = array_get($this->data, $parameters[1]);
$l_name = array_get($this->data, $parameters[2]);
if((strlen($f_name)+strlen($m_name)+strlen($l_name))<=50)
return true;
return false;
});
}
...
Where you are declaring your rules add:
'f_name' => 'groupedFieldsLongerThan50:f_name,m_name,l_name'

How to validate cloned form elements in laravel 5

This has been driving me crazy all day!!
My form creates users. The admin who creates these users can add several rows of inputs and then saves everything at the end. I'm using the clone() method in jQuery and I have already done the client side validation for required inputs etc... I have a very strong Domain layer but now I need server side validation so that Laravel can check if the email already exists since it must be unique. I am very proficient in Laravel, however with the new FormRequest objects I'm stuck on how to loop through each input etc since the FormRequestObject only seems to accept one entry. This all in ajax...
My formData looks like this:
counter:2
_token:KwGAUheSXbzkInh1RZ4RPenx4Fd4fF5DsPm5bjyO
firstname_1:name1
lastname_1:last1
email_1:email#email.com
password_1:keith
firstname_2:name2
lastname_2:last2
email_2:email#email.com
password_2:keith
As you can the input names have an incrementing id so the rules() in form request must loop through these. I'm really stuck, cannot find one example online.
My form reuqest class looks like this:
<?php namespace hidden\Http\Controllers\UserAccess\Requests;
use hidden\Http\Requests\Request;
use Illuminate\Auth\Guard;
use hidden\Domain\Services\UserAccess\GetUserFromEmailService;
class CreateAdministratorRequest extends Request {
/**
* 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 [
'firstname' => 'required',
'lastname' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required'
];
}
}
My controller method so far (It will eventually loop through the inputs and dispatched a command but pointless at the moment):
/**
* #param CreateAdministratorRequest $request
*/
public function createAdministrators(CreateAdministratorRequest $request)
{
// dispatch command per entry
}
If you want to keep your structure as possible you could do the following.
public function rules()
{
$counter = $this->get('counter');
$emails= [];
// validate manually if every submitted email is unique
// so, we need every email value
for ($i = 1; $i <= $counter; $i++){
$emails[] = $this->get('email_' . $i);
}
// Now, validate if every email between them are unique
// array_unique() delete repeated values, so we compare
// the size before and after filter the values
$unique = (count($emails) === count(array_unique($emails)));
if (! $unique){
return redirect()->back()
->withMessage('Sorry, all emails should be different');
}
// Now, you need to add every field into the rules
$rules = [];
for ($i = 1; $i <= $counter; $i++){
$rules['email_' . $i] = 'required|email|unique:users,email';
$rules['lastname_' . $i] = 'required';
$rules['firstname_' . $i] = 'required';
$rules['password_' . $i] = 'required';
}
return $rules;
}

Categories