Override / remove server-side validation in Symfony2.5 - validation

Say I have the following form builder buildForm method:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'travelTime',
'datetime',
array(
'label' => 'Time',
'date_widget' => 'single_text',
'time_widget' => 'single_text',
'invalid_message' => 'Please enter a valid date and time',
)
)
->add(
'acmeEntity',
'entity',
array(
'label' => 'Acme Entity:',
'class' => 'AcmeBundle:MyEntity',
'expanded' => false,
'multiple' => false,
)
)
}
How can I override (or remove) validation for the 'acmeEntity' form field (and only that field) so that if I call:
$form->handleRequest($request);
$form->isValid();
in a Controller, then acmeEntity will not be included in the validation that determines whether $form->isValid() returns true? I've tried adding constraints => false, to the field options, but I'm receiving this error message:
Notice: Trying to get property of non-object in /var/www/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php line 67
Does anyone know the proper way to disable server-side validation for a Symfony form field?
EDIT:
Note that I am not looking for how to disable validation completely. This can be done by adding:
// Disable form validation
$builder->addEventListener(FormEvents::POST_SUBMIT, function ($event) {
$event->stopPropagation();
}, 900); // Always set a higher priority than ValidationListener
to the bottom of a form builder.
Rather, I want to know how to completely disable validation for a single form field. Thanks everyone and good hunting!

You can define a custom form type for your entity and use the 'validation_groups' => false. This should disable the validation only for this field.
The custom form type may look like that:
// .../Your/OwnBundle/Form/Type/AcmeEntityType.php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class AcmeEntityType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => false
));
}
public function getParent()
{
return 'entity';
}
public function getName()
{
return 'acme_entity';
}
}
You can then use:
$builder
->add(
'travelTime',
'datetime',
array(
'label' => 'Time',
'date_widget' => 'single_text',
'time_widget' => 'single_text',
'invalid_message' => 'Please enter a valid date and time',
)
)
->add(
'acmeEntity',
'acme_entity',
array(
'label' => 'Acme Entity:',
'class' => 'AcmeBundle:MyEntity',
'expanded' => false,
'multiple' => false,
)
)
}

I assume that you call buildForm() from MyEntityType extending AbstractType so just use the options resolver by adding the function setDefaultOptions() in your type as said in the symphony doc here
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => false,
));
}

You can try this, I have used it in the past :
Create a validation group for your MainEntity
Add this validation group to your form
Do not add this validation group to AcmeEntity

Related

Validate when passing individual data using JSON.stringify

Before passing the data to Controller, the data is being added :
formData.push({"name":"channels","value":JSON.stringify(channels)});
Cause of this even when no data is present, its passed like
'channels' => '[]'
Now the issue is when I try to validate this in validator, I cannot use
'channels' =>'required',
'channels.*' =>'required|exists:channels,id',
How do validate the above data? Don't want to convert the format as its a working system. Any suggestions are appreciated. Thanks.
Updated for Request All Params:
'_token' => 'DjqgmNab0o3ifrVrSvHh6dM5vxLP7tZDc47pq05r',
'startdate' => '05 Sep 2018',
'years' => NULL,
'months' => NULL,
'enddate' => NULL,
'addChannel' => NULL,
'offerRuns' => 'UL',
'numberOfRuns' => NULL,
'limitPeriod' => 'FP',
'licenseAudioTrack' => '1',
'amount' => NULL,
'include_materials_costs' => '1',
'include_withholding_taxes' => '1',
'paymentTermsType' => 'US',
'termsAndConditionDescription' => NULL,
'document_s3_url' => NULL,
'file' => NULL,
'fileSize' => NULL,
'materialSpecificationDescription' => NULL,
'note' => NULL,
'countries' => '[]',
'platforms' => '["1","2","3","4","5","6","7","8","9"]',
'platforms-exclusive' => '[]',
'platforms-non-exclusive' => '[]',
'platforms-holdback' => '[]',
'channels' => '[]',
'languages' => '[["56","AL",1,"seller"]]',
'currencySelectedTerm' => 'EP',
'currencyId' => '1',
'paymentTerms' => '[]'
Check the present validation rule. It states:
present
The field under validation must be present in the input data but can
be empty.
Also look into sometimes rule:
In some situations, you may wish to run validation checks against a
field only if that field is present in the input array. To quickly
accomplish this, add the sometimes rule to your rule list
https://laravel.com/docs/5.7/validation#conditionally-adding-rules
As I understood channels is passed as JSON string & required validator is not working because it is not an empty string.
You can create a custom validator to validate empty JSON string & use it.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class JsonRequired implements Rule
{
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return ! empty(json_decode($value, true));
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The :attribute is required.';
}
}
And use it as 'channels' =>'new JsonRequired'.
If you only need once throughout your application, you may use a Closure instead of a rule object.
Laravel custom validation
You can use json_decode for data first and then apply validations
public function store(Request $request)
{
$request_data = $request->all();
foreach($request_data as $key=>$value)
{
$request_data[$key] = json_decode($value);
}
// And then pass data in validator rules
$rules = [
// All rules here
];
$validator = Validator::make($request_data, $rules);
// other code
}

Symfony Form Callback is not returning the object as the first argument

I am trying to add a callback constraint to a checkbox in Symfony 2.4. The idea is to check for other values on the object and decided weather or not to allow the validation to pass.
I've got the callback working but the first argument that is returned is not the entity but the value of the checkbox. The Symfony documentation states that the first argument will be the object. http://symfony.com/doc/current/reference/constraints/Callback.html#the-callback-method. I'm not sure what I'm missing
Heres the form code:
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;
//...
public function buildForm(FormBuilderInterface $builder, array $options) {
parent::buildForm($builder, $options);
$builder->add('enabled', 'checkbox', array(
'required' => false,
'constraints' => array(
new Assert\Callback(array(
'callback' => array(
$this,
'validateisReady'),
'groups' => $this->validationGroups))
),
))
;
}
public static function validateisReady($object, ExecutionContextInterface $context) {
//..
if($object->getItems()->count() < 1){
$context->addViolationAt('enabled', 'items.missing');
}
//..
}
The $object holds the boolean value of the checkbox. I wanted it to be the entity. Any ideas?
You need to add this constraint to your entity object, not a form.

ZF2 + Duplicate Form Validation on composite key

I have a Form having primary key on two fields (gid, bid). I need to add validation to block duplicate entries into database.
I have checked with ZF2 Solution for this . http://framework.zend.com/manual/2.2/en/modules/zend.validator.db.html#excluding-records . While this approach of handling composite keys is not look the ideal way, But still I am trying it because it look like only buil-in way. Now it require me to provide second field's value (value option in exclude), which is again a problem. As I am trying it
$inputFilter->add(array(
'name' => 'gid',
'required' => true,
'validators' => array(
array(
'name' => 'NotEmpty',
'options' => array(
'messages' => array(
'isEmpty' => 'required'
),
),
),
array (
'name' => 'Zend\Validator\Db\NoRecordExists',
'options' => array (
'table' => 'gtable',
'field' => 'gid',
'adapter' => $this->dbAdapter,
'messages' => array(
\Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database'
),
'exclude' => array(
'field' => 'bid',
'value' => [?],
),
)
),
)
));
How do I get this value, As Form is absolute separate Class/File than controller where I have the submitted form values. Is some better architecture solution of this problem exists Or Some hack to pass submitted field value to Form Class is only solution ?
Note : I am not in favor of Build My Validation Plugin for this task as short time is constraint for functionality.
You can do all the job in your form. To achieve that, you could define your forms as factories in your module Module.php.
Module.php
use MyNamespace\MyForm;
//NOTE THAT THE SERVICE MANAGER IS INJECTED. YOUR FORM COULD RECEIVE IT THROUGH THE CONSTRUCTOR
public function getServiceConfig()
{
return array(
'factories' => array(
'my_form' => function( $sm ) {
$form = new MyForm( $sm );
return $form;
},
),
);
}
When you want to use the form is as easy as use this code in your controller:
class MyController extends AbstractActionController
{
public function createAction() {
$form = $this->getServiceLocator()->get( 'my_form' ) );
(...)
}
}
And your MyForm.php
use Zend\Form\Form;
class MyForm extends Form
{
public $serviceManager, $request, $postData;
public function __construct( $serviceManager ) {
parent::__construct( null );
$this->serviceManager = $serviceManager;
$this->request = $serviceManager->get( 'Application')->getMvcEvent()->getRequest();
$this->postData = get_object_vars( $this->request->getPost() );
}
}
This way you can get advantage of the Service Manager within your form. And the public postData, where you'll find the bid value you're looking for to build your NoRecordExists filter.
You could add the parameters to the getInputFilter, like this :
getInputFilter($gid, $bid)
And then on the controller, when you set the filter you pass the 2 parameters, and then just check as $form->isValid(); ...
Alternative try this:
array(
'name' => 'Db\NoRecordExists',
'options' => array(
'table' => 'gtable',
'field' => 'gid',
'adapter' => $this->dbAdapter,
),
),
I'm unsure on your use case. If you were to add a database entry the primary keys for that table would not be known until you insert anyway - If you have foreign key constraints you could handle the exception from the database.
I am not in favor of Build My Validation Plugin for this task
The validator is also not designed to validate multiple fields as they are attached to a form element on a 1-1 basis. You will therefore need to create your own.
The below example has NOT been tested, so take it as an example of the approach rather than working code.
The key bit is the isValid method.
namespace MyModule\Validator\Db;
use Zend\Validator\Db\NoRecordExists;
class CompositeNoRecordExists extends NoRecordExists
{
protected $field2;
protected $field2Value;
public function __construct($options = null)
{
parent::__construct($options);
if (array_key_exists('field2', $options)) {
$this->setField2($options['field2']);
} else {
throw new \BadMethodCallException('Missing field2 option!');
}
}
protected function setField2Value(array $context)
{
if (! isset($context[$this->field2])) {
throw new \BadMethodCallException('Unable to find value for field 2');
}
$this->field2Value = $context[$this->field2];
}
public function isValid($value)
{
// The isValid() method is actually given a 2nd argument called $context
// Which is injected by the inputFilter, via the input and into the validator chain
// $context contains all of RAW form element values, keyed by thier element name.
// Unfortunately due to the ValidatorInterface you are unable to add this to the method
// signature. So you will need to be 'creative':
$args = func_get_args();
if (isset($args[1]) && is_array($args[1])) {
$this->setField2Value($args[1]);
} else {
throw new \BadMethodCallException('Missing validator context');
}
return parent::isValid($value);
}
public function getSelect()
{
$select = parent::getSelect();
$select->where->equalTo($this->field2, $this->field2Value);
return $select;
}
}
Then all you would need to do is update the validator config, adding the field2 field name.
array (
'name' => 'MyModule\Validator\Db\CompositeNoRecordExists',
'options' => array (
'table' => 'gtable',
'field' => 'gid',
'field2' => 'bid',
'adapter' => $this->dbAdapter,
'messages' => array(
\Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database'
),
)
),

Symfony 2.0 validator, Blank() doesn't work

I am new on Symfony and I meet the following problem.
I'd like to generate a form without class.
I want to add a Blank() validator on one field.
See below.
class searchPropertyType extends AbstractType
{
public function getDefaultOptions(array $options)
{
$collectionConstraint = new Collection(array(
'keywords' => new blank()
));
return array('validation_constraint' => $collectionConstraint);
}
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('keywords')
->add('neighborhood')
->add('price_min')
->add('price_max')
->add('type')
->add('date_from' , 'date')
->add('date_to' , 'date')
;
}
public function getName()
{
return 'searchProperty';
}
}
The form is properly displayed but still, I can't send the form, I got a HTML5 alert saying that I must fill out this field.
ANy idea? I have been working on that the full day and it drives me crazy.
Thank you so much if you have time to help ;-)
To disable HTML5 client side validation add 'required' => false to getDefaultOptions:
public function getDefaultOptions(array $options)
{
$collectionConstraint = new Collection(array(
'keywords' => new blank()
));
return array(
'validation_constraint' => $collectionConstraint,
'required' => false
);
}
public function buildForm(FormBuilder $builder, array $options) {
$builder
->add('neighborhood','text',array('required' => false,))
->add('price_min','text',array('required' => false,))
->add('date_from', 'date', array('widget' => 'single_text', 'format' => 'dd MMM yyyy', 'required' => false))
);
}
Add required=>false
Hope this helps.

Setting an Error Message located in language file

In a form I have a callback function that checks if a number is_available.
If it returns TRUE it shows an error message.
The callback is working but it displays : lang:shortcodes.not_unique instead of the content given in a separate file.
I can't figure out what is wrong and didn't find about that in the user guide.
Thank you for your help.
public function __construct()
{
parent::__construct();
// Load all the required classes
$this->load->model('shortcodes_m');
$this->load->library('form_validation');
$this->lang->load('shortcodes');
// Set the validation rules
$this->item_validation_rules = array(
array(
'field' => 'number',
'label' => 'lang:shortcodes.number',
'rules' => 'trim|max_length[100]|required|numeric'
),
array(
'field' => 'name',
'label' => 'lang:shortcodes.name',
'rules' => 'trim|max_length[100]|required|callback_shortcodes_check'
)
);
}
public function shortcodes_check($str)
{
if($this->shortcodes_m->is_available($str) == TRUE)
{
$this->form_validation->set_message('shortcodes_check','lang:shortcodes.not_unique');
return FALSE;
}
else
{
return TRUE;
}
}
You need to fetch the line from the language file. The docs don't make any mention of being able to use field name translation with the set_message() method. Use:
$this->lang->line('not_unique');

Resources