So I want to create a form with two dates. I want those dates to have constraints, that check if one of each is bigger than the other. This way I can get a valid time span.
I build a custom FormType like this:
class MainFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add( 'date_from', DateType::class, [
'constraints' => [
new Assert\NotBlank()
]
])
->add( 'date_to', DateType::class, [
'constraints' => [
new Assert\NotBlank(),
new Assert\GreaterThanOrEqual([
'value' => $builder->get('date_from'),
'message' => 'datepickers.to_less_than_from'
])
]
]);
}
}
At this point symfony does not throw an error, but the validation wont work either.
Is there any way to compare two dates, that are located in the same form, via Symfonys validator?
You can do this with this code
->add('dateToPublish', DateTimeType::class, array(
'label' => 'notifications.date_to_publish',
'required' => true,
'widget' => 'single_text',
'constraints' => [
new NotBlank(),
new DateTime(),
]))
->add('dateToEnd', DateTimeType::class, array(
'label' => 'notifications.date_to_end',
'required' => true,
'widget' => 'single_text',
'constraints' => [
new NotBlank(),
new DateTime(),
new GreaterThan([
'propertyPath' => 'parent.all[dateToPublish].data'
]),
]));
As you can see I'm making a constraint on dateToEnd to be greater than dateToPublish
Hopes this helps you
Related
I have all my rules in the Validation config file, like the documentation suggest:
https://codeigniter4.github.io/userguide/libraries/validation.html#saving-sets-of-validation-rules-to-the-config-file
For example:
public $userCreate = [
'first_name' => [
'label' => 'First Name',
'rules' => 'required|string|max_length[60]'
],
'last_name' => [
'label' => 'Last Name',
'rules' => 'required|string|max_length[60]',
],
'email' => [
'label' => 'Auth.email',
'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]',
],
];
In my controllers I can access my validation groups like this:
$validation = \Config\Services::validation();
$rules = $validation->getRuleGroup('userCreate');
As my app gets bigger, I need more and more validation rules, so the question is, is there a way to organize them in separate files and not to have all of them in a single config file? Something like the custom rules, which are loaded in the config file and stored separately.
Steps
Create a custom directory for storing your validation rules. I.e app/Validation.
Create a class under that directory for your 'User' rules. I.e: app/Validation/UserRules.php
<?php
namespace App\Validation;
class UserRules
{
public function create()
{
return [
'first_name' => [
'label' => 'First Name',
'rules' => 'required|string|max_length[60]'
],
'last_name' => [
'label' => 'Last Name',
'rules' => 'required|string|max_length[60]',
],
'email' => [
'label' => 'Auth.email',
'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]',
],
];
}
public function update()
{
return [
// Add 'User' update rules here.
];
}
}
In the \Config\Validation config file, set the relevant 'User' validation rules in the constructor. I.e:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
// ...
class Validation extends BaseConfig
{
// --------------------------------------------------------------------
// Setup
// --------------------------------------------------------------------
public $userCreate = [];
public $userUpdate = [];
public function __construct()
{
$this->userCreate = ($userRules = new \App\Validation\UserRules())->create();
$this->userUpdate = $userRules->update();
}
// ...
}
In your Controllers, you may access validation groups as usual.
Thanks to #steven7mwesigwa I came up with a solution that suits me the most.
First I created separate classes inside the App/Validation folder. For example these 2 classes:
App\Validation\Auth.php
<?php
namespace App\Validation;
class Auth {
public $login = [
'email' => [
'label' => 'E-mail',
'rules' => 'required|max_length[254]|valid_email',
],
'password' => [
'label' => 'Password',
'rules' => 'required',
],
'remember' => [
'label' => 'Remember me',
'rules' => 'if_exist|permit_empty|integer',
]
];
}
App\Validation\User.php
<?php
namespace App\Validation;
class User {
public $userCreate = [
'first_name' => [
'label' => 'First Name',
'rules' => 'required|string|max_length[60]',
],
'last_name' => [
'label' => 'Last Name',
'rules' => 'required|string|max_length[60]',
],
'email' => [
'label' => 'E-mail',
'rules' => 'required|max_length[254]|valid_email|is_unique[users.email]',
],
];
}
The next step is to add a construct method to the existing validation config file:
App\Config\Validation.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Validation extends BaseConfig {
...
// --------------------------------------------------------------------
// Rules
// --------------------------------------------------------------------
public function __construct() {
$ruleGroups = [
new \App\Validation\Auth(),
new \App\Validation\User(),
];
foreach ($ruleGroups as $ruleGroupClass) {
foreach ((array) $ruleGroupClass as $groupName => $rules) {
$this->{$groupName} = $rules;
}
}
}
}
I am not able to add field which is not available in my database
I have tried adding
$this->crud->addFields([
[
'name' => 'coupon_type',
'label' => 'Coupon For',
'type' => 'select_from_array',
'options' => [
// Options
],
'allows_null' => true,
'default' => 1,
'attributes' => [
'id' => 'coupon_type'
]
]
]);
I want to add fields in my create page.
You can do this by defining accessors for your "virtual" attributes
public function getIsAdminAttribute()
{
return $this->attributes['admin'] == 'yes';
}
and defining the appends on your model
protected $appends = ['is_admin'];
Find everything in the docs here:
https://laravel.com/docs/5.8/eloquent-serialization#appending-values-to-json
I made this ActiveDataProvider:
$dataProvider = new ActiveDataProvider([
'query' => $query->asArray(), // It is a simple SQL query.
'key' => 'item',
'sort' => [
'attributes' => [
'item',
'quantity',
],
]
]);
I need to add the filters for item and quantity because it doesn't work (it doesn't search):
You must have not set your attributes item and quantity as safe in your SearchModel rules(), method.
You have to set them as safe in SearchModel.
public function rules()
{
return [
[['item', 'quantity'], 'safe'],
];
}
In your case, your Data Provider should be simply like this:
// It is not necessary to use "$query->asArray()", because your "$query" itself is object of ActiveQuery
$dataProvider = new ActiveDataProvider([
'query' => $query,
'key' => 'item',
]);
I tried to validate a simple form in zend framework 2 for days now.
I checked the documentation and a lot of posts considering this topic but I found no solution!
I have a very simple form:
class AlimentForm extends Form
{
public function __construct($name = null)
{
parent::__construct('aliment');
$this->add(array(
'required'=>true,
'name' => 'year',
'type' => 'Text',
'options' => array(
'label' => 'Jahr',
),
));
$this->add(array(
'required'=>true,
'name' => 'number',
'type' => 'Text',
'options' => array(
'label' => 'Number',
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Go',
'id' => 'submitbutton',
),
));
}
}
I created a custom InputFilter:
namespace Application\Form;
use Zend\InputFilter\InputFilter;
class AlimentInputFilter extends InputFilter {
public function init()
{
$this->add([
'name' => AlimentForm::year,
'required' => true,
'validators' => array(
array(
'name' => 'Between',
'options' => array(
'min' => 1900,
'max' => 3000,
),
),
),
]);
}
}
and finally in my controller I try to validate the form
public function alimentAction(){
$form = new AlimentForm();
$form->setInputFilter(new AlimentInputFilter());
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$year = $form->get('year')->getValue();
$number = $form->get('number')->getValue();
return array('result' => array(
"msg" => "In the Year ".$year." you get ".$number." Points"
));
}
}
return array('form' => $form);
}
It can't be that difficult, but from all those different ways to validate a form I found in the net, I'm a bit confused...
What am I missing?
Greetings and thanks in advance
U.H.
Ok, I solved the problem.
I created a Model for the aliment Form that has a year and a number attribute
and I defined an input filter within that model:
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class Aliment implements InputFilterAwareInterface
{
public $year;
public $number;
public function exchangeArray($data){
$this->year = (!empty($data['year']))?$data['year']:null;
$this->number = (!empty($data['number']))?$data['number']:null;
}
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$inputFilter->add(array(
'name' => 'year',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Between',
'options' => array(
'min' => 1900,
'max' => 3000
)
)
),
));
$inputFilter->add(array(
'name' => 'number',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Between',
'options' => array(
'min' => 1,
'max' => 10
)
)
),
));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
In the action method of the controller I was able to set the InputFilter of the model to the form and voila! It worked!
public function alimentAction(){
$form = new AlimentForm();
$request = $this->getRequest();
if ($request->isPost()) {
$aliment = new \Application\Model\Aliment;
$form->setInputFilter($aliment->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$aliment->exchangeArray($form->getData());
$year = $form->get('year')->getValue();
return array(
'result' => array(
//your result
)
);
}
}
return array('form' => $form);
}
The form is correctly validated now and returns the corresponding error messages.
I hope, it help somebody whos got similar problems with validating!
I have form, and need to create inline validation:
$builder
->add('Count1', 'integer', [
'data' => 1,
'constraints' => [
new NotBlank(),
new NotNull(),
],
])
->add('Count2', 'integer', [
'constraints' => [
new NotBlank(),
new NotNull(),
],
])
->add('Count3', 'integer', [
'data' => 0,
'constraints' => [
new NotBlank(),
new NotNull(),
],
])
How white inline validation Expression for rules
Count2 >=Count1
Count3 <=Count2
Count2 >= $someVariable
Other solution by using Expression Constraint for cases 1 and 2.
use Symfony\Component\Validator\Constraints as Assert;
// ...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'constraints' => [
new Assert\Expression([
'expression' => 'value["Count2"] >= value["Count1"]',
'message' => 'count2 must be greater than or equal to count1'
]),
new Assert\Expression([
'expression' => 'value["Count3"] <= value["Count2"]',
'message' => 'count3 must be less than or equal to count2'
]),
],
]);
}
For case 3 you can use Assert\GreaterThanOrEqual constraint directly on Count2 field.
I guess your form doesn't have a binding object model, otherwise to read the documentation referred is enough and better because you could use these expression on your properties directly.
You can utilize CallbackValidator (docs):
In your case, in order to validate one field againt another, you need to add constraint to a form type, not the field:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'constraints' => array(
new Assert\Callback(function($data){
// $data is instance of object (or array) with all properties
// you can compare Count1, Count2 and Count 3
// and raise validation errors
});
)
));
}
You can also pass constraints option while creating a form if you don't want to set it in setDefaultOptions.
Starting from easiest
3) Count2 >= $someVariable
->add('Count3', 'integer', [
'data' => 0,
'constraints' => [
new NotBlank(),
new NotNull(),
new GreaterThanOrEqual($someVariable),
],
])
1) As for two first, you must implement constraint for a class scope, rather than property scope. And assign these constraints for a whole form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Count1', 'integer', [
'data' => 1,
'constraints' => [
new NotBlank(),
new NotNull(),
],
])
->add('Count2', 'integer', [
'constraints' => [
new NotBlank(),
new NotNull(),
],
])
->add('Count3', 'integer', [
'data' => 0,
'constraints' => [
new NotBlank(),
new NotNull(),
],
])
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(['constraints' => [
new YourCustomConstraint(),
]]);
}
How to implement validator, see in the documentation.
But in your YourCustomConstraintValidator you will have something like
public function validate($value, Constraint $constraint)
{
if ($value->getCount1() > $value->getCount2() {
$this->context->addViolation(...);
}
}
I had some problem comparing two dates using Symfony's Expressions.
This is the code that works:
$builder->add(
'end',
DateTimeType::class,
[
'label' => 'Campaign Ends At',
'data' => $entity->getEnd(),
'required' => true,
'disabled' => $disabled,
'widget' => 'single_text',
'constraints' => [
new Assert\GreaterThan(['value' => 'today']),
new Assert\Expression(
[
//here end and start are the name of two fields
'expression' => 'value > this.getParent()["start"].getData()',
'message' => 'my.form.error.end.date.bigger.than.start'
]
),
]
]
);