I'm validating question and answers (for test creation). I'd like to ensure that the answers array contains at least one 'correct' item. So where answers.*.correct => true.
I currently have the following:
public function rules()
{
return [
'title' => 'required|string|max:255',
'test_id' => 'required|integer|exists:tests,id',
'content' => 'required',
'answers' => 'required|array',
'answers.*.text' => 'required|string|max:255',
'answers.*.correct' => 'required|boolean'
];
}
At the moment i can miss out adding a correct answer causing an impossible question.
I've checked the documentation and can't see anything that stands out.
Any help would be appreciated.
EDIT ANSWER
I used this (as the answer mentions): Laravel validate at least one item in a form array
I managed to create a custom rule like so:
public function passes($attribute, $value)
{
foreach ($value as $arrayElement) {
if ($arrayElement['correct'] == true) {
return true;
}
}
return false;
}
Then in my existing rules() section for the request I added in the new rule i created:
'answers' => ['required', 'array', new ArrayAtLeastOneBoolTrue()],
You are here validating that the array answers has:
A string with max. 255 chars (text)
A boolean (correct)
To check the completeness of this array, the Laravel request is the wrong place to check. Technically it would be possible to create your own validation rule, but makes no sense here. Instead you should iterate this array in your controller and verify the completeness of each answers.
Two more ideas, to do it better:
Don't send all answers and if they were answered correctly in one single array. Instead, send the selected answer to your api with a single request per answer at the moment the user clicks it. This will 1. prevent that somebody can send you the information that he answered 100% correct (not good if this is a school software :) ) and 2. depending on how much questions there are it will reduce the data sent to the server to a minumum, mainly because...
... it seems you send the whole text of the question to the server to identify the answer. Use a unique ID per question and send it as question ID + the selected or entered answer.
EDIT: Thanks for the comments, sorry for my misunderstanding here. As mentioned above then of course custom validation makes totally sense here to check per question if at least one answer is correct. Check this out: https://stackoverflow.com/a/53791208/2264877
Related
I have a multiple fields binded to "x-editable" plugin that sends the changed data and like all of the data, I want to validate them.
But the problem here is that it always sends just 2 fields: name=is_on_probation&value=1. Here, the name key is the field that must be validated (it is the same as DB column) and a value is pretty self-explanatory.
I've written a validation rule like this:
'is_on_probation' => 'sometimes|...'
But then I realized that it won't be validated, because the actual subject of validation is in the value field.
So, the question here is: how can I validate the value field and apply the corresponding rule, accordingly to the name value?
I created this logic in order not to pollute the controller with additional arrays and logic, so it is going to save only the passed parameter.
Kinds regards!
P.S. I've searched for the StackOverflow, but did not find the question that describes my problem the best. May be I am wrong and did not see it.
Update
After thinking a bit, I cam up with the solution to use switch and pass the request field there, like this:
switch($this->request->get('name')) {
case "email":
$rules = [
'value' => 'sometimes|required|unique:users,email|email:rfc',
];
break;
case "phone":
$rules = [
'value' => 'sometimes|required|min:8|max:13|regex:/^(\+371)?\s?[0-9]{8}$/'
];
break;
}
$rules[] = [
'pk' => 'required|exists:users,id'
];
The question is still in place, however - is it the best solution and a good practice to do so?
Try to merge your data into the request before starting validation.
$request->merge([$request->input('name') => $request->merge([$request->input('value')]);
Then your validation
'email' => 'sometimes|...','phone' => 'sometimes|...',
I am using form requests in Laravel for validation. I have noticed a pattern that emerges all the time and I couldn't find a solution for it on SE (or at least googling it didn't help me).
Let's say we are creating an API and we are using Laravel's apiResource to create the usual CRUD methods: store, update and delete
Obviously, when we are storing a new record, the field id is not required, but the rest of the fields might be required (and in most cases are). But when we are updating a record, we face the opposite situation. id is required while other fields are no longer required.
Is it possible to handle this situation with one form request in Laravel? Can we use Laravel's required_if in an intelligent way to avoid code duplication?
Edit: it doesn't have to be necessarily a Laravel solution. A solution that uses PHP would be fine too (as long as it is clean and follows SOLID principles).
I faced this problem lots of times and I understand your frustration...
From my point of view and professional experience, the best solution was all the time to have specific FormRequests for each case:
One for store with its own rules
Other for update with similar rules but not same as store
And last one for delete (for sure way less rules than the others and no duplication)
I know you said "no code duplication", but as it is right now, that is not possible (but you should not have code duplication as I stated before).
You said "as long as it is clean and follows SOLID principles", remember SOLID, S = Single Responsability, so if you want to solve this issue with a single FormRequest you are already breaking S. I cannot image a FormRequest with 10 or 15 inputs and those depends on if it is store, update, or delete. That is going to not be clean and for sure will not follow SOLID principles.
What about checking the method and then returning a set of rules based on that method? For instance, I have an InvoiceFormRequest with the following rules. So I'm able to use one form request for two different methods.
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
if ($this->isMethod('post')) {
return [
'template' => 'required',
'due_by_date' => 'required',
'description' => 'required',
'charge_items' => 'required',
];
}
if ($this->isMethod('put')) {
return [
'due_by_date' => 'required',
'description' => 'required',
'charge_items' => 'required',
];
}
}
Here are two possible solutions that I came up with
Use the controller method for returning the proper validation rules:
public function rules()
{
$method = $this->route()->getActionMethod();
switch($method){
case 'store':
return [
\\ validation rules
]
...
}
}
Use $this->getMethod() instead of $this->route()->getActionMethod() and validate by HTTP methods instead.
You could also store your validation rules in an array and manipulate it to reduce code duplication.
This resolves the issue of code duplication to a good extent, I think.
I got stuck with this stupid error messages in Zend Framework 2. Spent two hours and nothing, I still got two different error messages with NotEmpty validator.
$nameNotEmptyMessage = 'Pole wymagane';
$inputFilter->add(array(
'name' => 'name',
'required' => true,
'filters' => array(
new Filter\StringTrim()
),
'validators' => array(
new Validator\NotEmpty(array(
'messages' => array(
Validator\NotEmpty::INVALID => $nameNotEmptyMessage,
Validator\NotEmpty::IS_EMPTY => $nameNotEmptyMessage
)
))
)
));
And what? It works pretty cool when I send form with "name" element inside it. When it's empty, I've got my message. But, when I send form without the "name" element inside it, the message is still "Value is required and can't be empty"! Why?
I need to have in both cases the same message because sometimes the form won't have "name" element.
Thank you guys very much!
Thanks for replying to the comment to clarify the problem. First I must say, I feel your pain on this one. After having not used ZF2 for over a year and spending some time figuring this out, it highlights yet another painful thing in ZF2.
That said, here is my simple solution. Not necessary correct, or the best way to go about it but it solves your problem with the least amount of code. In your controller where you instantiate and validate the form, add this code before you call new Form:
if (!isset($_POST['name'])) {
$this->getRequest()
->setPost(
new \Zend\Stdlib\Parameters(
array('name' => '')
)
);
}
This seemed the simplest route rather than setting up a default validation translator and adding a duplicate message. It is just checking to see if the name field was not present, and if so sets it with any empty value.
Now the reason why we have to do this:
This happens because of how \Zend\InputFilter\Input works. When the InputFilter runs, it sees the element is missing. It then checks the required property, sees it's true but the input has no value. No value being different than empty.
When the filter is required but has no input, \Zend\InputFilter\Input::prepareRequiredValidationFailureMessage is called. If you look at the source of that function, you'll see:
protected function prepareRequiredValidationFailureMessage()
{
$notEmpty = new NotEmpty();
$templates = $notEmpty->getOption('messageTemplates');
return [
NotEmpty::IS_EMPTY => $templates[NotEmpty::IS_EMPTY],
];
}
Notice how it creates a new NotEmpty validator and fetches its default message, thus bypassing the translation you set within the InputFilter.
The alternative:
One alternative, which may be more correct, but also requires more code and could arguably be more confusing looking back later would be to create a new class implementing \Zend\Validator\Translator\TranslatorInterface and set an array with the translation for that message and then calling \Zend\Validator\AbstractValidator::setDefaultTranslator passing that so the default translator will be used on that message.
This is just difficult because there is no short way of setting up that object because of how the classes are inherited and I wasn't able to find a quick solution to set up the default translator with just that message. Also, if you have another translator in place, it might interfere with it.
So it seems the simplest thing is to just check for the absence of that form field and populate it with any empty value if it's missing.
Hope that helps!
Other more simple way to fix this is to overwrite the Form setData($data) and to check if $data['name'] is set. Something like this:
public function setData($data)
{
if(!isset($data['name'])){
$data['name'] = '';
}
return parent::setData($data);
}
I don't understand why this doesn't work, i have searched the forums and read the official documentation, obviously I'm making a silly mistake:
public function rules()
{
$customer = Customer::find($this->customers);
return [
'data.name' => 'required|unique:customers,name,1'
];
}
That is a CustomerUpdateFormRequest that i created, i manually typed the customer id which is 1 in the rule, also with: 'data.name' => 'required|unique:customers,name,'.$customer->id,
The problem is that it still doesnt pass the unique validation, i giving me the Customer already exists error even after manually or dynamically type the customer id.
I read this http://laravel.com/docs/5.0/validation and https://laracasts.com/discuss/channels/requests/laravel-5-validation-request-how-to-handle-validation-on-update and I 'Believe' that my code is correct, why is not working?
The code above is working just fine, no syntax errors or logic, the problem was in the database, there was another customer with the same data inserted i guess before the validation form was created.
I dont know if i should delete this question or leave it here, anyway the code its fine if u have any problems like mine PLEASE CHECK YOUR DB.
Thanks!
public function rules()
{
$customer= $this->route('customer'); //$this->('route-name')
return [
'data.name' => 'required|unique:customers,name,' . $customer->id
];
}
I've done quite a few Lithium tutorials (links below in case they help someone else, and also to show I've done my homework:) and I understand the most basic parts of creating models, views, controllers and using MVC to create a DB record based on form input.
However, I'm new to MVC for webapps and Lithium, and I'm not sure how I should write my code in more complicated situations. This is a general question, but two specific validation questions that I have are:
How should I validate date data submitted from the form?
How should I check that the two user email fields have the same value?
I would be very grateful for any help with these questions, and concrete examples like this will also really help me understand how to do good MVC coding in other situations as well!
Date entry - validating data split across multiple form inputs
For UI reasons, the sign up form asks users to enter their DOB in three fields:
<?=$this->form->field('birthday', array('type' => 'select', 'list' => array(/*...*/))); ?>
<?=$this->form->field('birthmonth', array('type' => 'select', 'list' => array(/*...*/))); ?>
<?=$this->form->field('birthyear', array('type' => 'select', 'list' => array(/*...*/))); ?>
What is the best way to validate this server-side? I think I should take advantage of the automagic validation, but I'm not sure of the best way do that for a set of variables that aren't really part of the Model. E.g.:
Should I post-process the $this->request->data in UsersController? E.g. modify $this->request->data inside UsersController before passing it to Users::create.
Should I pull the form fields out of $this->request->data and use a static call to Validator::isDate inside UsersController?
Is there a way to write a validation rule in the model for combinations of form variables that aren't part of the model?
should I override Users::create and do all the extra validation and post-processing there?
All of these seem like they could work, although some seem a little bit ugly and I don't know which ones could cause major problems for me in the future.
[EDIT: Closely related to this is the problem of combining the three form fields into a single field to be saved in the model]
Email entry - checking two form fields are identical, but only storing one
For common sense/common practice, the sign up form asks users to specify their email address twice:
<?=$this->form->field('email_address'); ?>
<?=$this->form->field('verify_email_address'); ?>
How can I write an automagic validation rule that checks these two form fields have the same value, but only saves email_address to the database?
This feels like it's pretty much the same question as the above one because the list of possible answers that I can think of is the same - so I'm submitting this as one question, but I'd really appreciate your help with both parts, as I think the solution to this one is going to be subtle and different and equally enlightening!
[EDIT: Closely related to this is the problem of not storing verify_email_address into my model and DB]
Some background reading on Lithium
I've read others, but these three tutorials got me to where I am with users and sign up forms now...
Blog tutorial
Extended blog tutorial
MySQL blog tutorial
Some other StackOverflow questions on closely related topics (but not answering it and also not Lithium-specific)
One answer to this question suggests creating a separate controller (and model and...?) - it doesn't feel very "Lithium" to me, and I'm worried it could be fragile/easily buggy as well
This wonderful story convinced me I was right to be worried about putting it in the controller, but I'm not sure what a good solution would be
This one on views makes me think I should put it in the model somehow, but I don't know the best way to do this in Lithium (see my bulleted list under Date Entry above)
And this Scribd presentation asked the question I'm hoping to answer on the last page... whereupon it stopped without answering it!
NB: CakePHP-style answers are fine too. I don't know it, but it's similar and I'm sure I can translate from it if I need to!
I'd recommend doing this in the Model rather than the Controller - that way it happens no matter where you do the save from.
For the date field issue, in your model, override the save() method and handle converting the multiple fields in the data to one date field before calling parent::save to do the actual saving. Any advanced manipulation can happen there.
The technique described in your comment of using a hidden form field to get error messages to display sounds pretty good.
For comparing that two email fields are equal, I'd recommend defining a custom validator. You can do this in your bootstrap using Validator::add.
use lithium\util\Validator;
use InvalidArgumentException;
Validator::add('match', function($value, $format = null, array $options = array()) {
$options += array(
'against' => '',
'values' => array()
);
extract($options);
if (array_key_exists($against, $values)) {
return $values[$against] == $value;
}
return false;
});
Then in your model:
public $validates = array(
"email" => array(
"match",
"message" => "Please re-type your email address.",
"against" => "email2"
)
);
Edit: Per the comments, here's a way to do custom rule validation in a controller:
public function save() {
$entity = MyModel::create($this->request->data);
$rules = array(
"email" => array(
"match",
"message" => "Please re-type your email address.",
"against" => "email2"
)
);
if (!$entity->validates($rules)) {
return compact('entity');
}
// if your model defines a `$_schema` and sets `$_meta = array('locked' => true)`
// then any fields not in the schema will not be saved to the db
// here's another way using the `'whitelist'` param
$blacklist = array('email2', 'some', 'other', 'fields');
$whitelist = array_keys($entity->data());
$whitelist = array_diff($whitelist, $blacklist);
if ($entity->save(null, compact('whitelist'))) {
$this->redirect(
array("Controller::view", "args" => array($entity->_id)),
array('exit' => true)
);
}
return compact('entity');
}
An advantage of setting the data to the entity is that it will be automatically prefilled in your form if there's a validation error.