I am trying to use Symfony Form Component with Silex framework at the moment. I added some fields in buildForm method of my form type class. Also user can click on a button and add unlimited textarea element using javascript on the frontend. Now on PRE_SUBMIT event, I do the following to add these fields to the form
$data = $event->getData();
$form = $event->getForm();
foreach ($data as $key => $value) {
if (stristr($key, '_tb_') !== false) {
$id = str_ireplace('_tb_', '', $key);
$form->add('_tb_' . $id, 'hidden');
$form->add('_title_' . $id, 'text', [
'required' => false,
'label' => 'Title',
'constraints' => [
new Length(['min' => 6]),
]
]);
$form->add('_img_url_' . $id, 'text', [
'required' => false,
'label' => 'Image Url',
'constraints' => [
new Url(),
]
]);
$form->add('_img_alt_' . $id, 'text', [
'required' => false,
'label' => 'Image Alt',
'constraints' => []
]);
$form->add('_content_' . $id, 'textarea', [
'required' => true,
'attr' => [
'data-role' => '_richeditor'
],
'constraints' => [
new Length(['min' => 100]),
]
]);
}
}
I can see these fields are added to the form and populated once the form is submitted for the first time but for some reason all the constraints are ignored only for these new added fields. Is there a way to force Form to honour the constraints for the newly added elements?
The form component and validation can be tricky. A easy misconception is that the form type option "required" will imply a NotBlank validation constraint. This is not the case, the docs explain that option to be "superficial and independent from validation" only concerned with form element rendering (HTML5 required attr, label, etc).
To make things trickier, that you are specifying a minimum length constraint, one might assume that no (or zero) length would be considered invalid. This is also not the case. The length validator is only concerned with non-null / non-empty values. :-/
So! Modifying the text area field to include NotBlank() should do the trick:
$form->add('_content_' . $id, 'textarea', [
'required' => true,
'attr' => [
'data-role' => '_richeditor'
],
'constraints' => [
new NotBlank(),
new Length(['min' => 100]),
]
]);
Related
I'm building an Address form in Symfony 5.1.
It is created in an AddressType Class that has some required fields.
$builder
->add('name', TextType::class, [
'required' => false,
'label' => 'address.name',
'help' => 'address.name_help',
'attr' => [
'placeholder' => 'address.name_ph',
]
])
->add('company', TextType::class, [
'required' => false,
'label' => 'address.company',
'attr' => [
'placeholder' => 'address.company_ph',
]
])
->add('first_line', TextType::class, [
'label' => 'address.first_line',
'attr' => [
'placeholder' => 'address.first_line_ph',
]
])
->add('second_line', TextType::class, [
'required' => false,
'label' => 'address.second_line',
'attr' => [
'placeholder' => 'address.second_line_ph',
]
])
->add('add_info', TextType::class, [
'required' => false,
'label' => 'address.add_info',
'help' => 'address.add_info_help',
'attr' => [
'placeholder' => 'address.add_info_ph',
]
])
->add('postcode', TextType::class, [
'label' => 'address.postcode',
'attr' => [
'placeholder' => 'address.postcode_ph',
]
])
->add('city', TextType::class, [
'label' => 'address.city',
'attr' => [
'placeholder' => 'address.city_ph',
]
])
->add('state', TextType::class, [
'required' => false,
'label' => 'address.state',
'attr' => [
'placeholder' => 'address.state_ph',
]
])
->add('country', CountryType::class, [
'label' => 'address.country',
'preferred_choices' => ['FR'],
'attr' => [
'data-toggle' => 'select',
'placeholder' => 'address.country_ph',
]
])
->add('save',
SubmitType::class,
[
'label' => $options['submit_btn_label'],
]
);
If I submit this form with the submit button, everything works as expected, my form is being processed for validation and if It detects some errors, they are shown on each field.
Here is the function that handle the form :
public function new(Request $request)
{
$user = $this->getUser();
$address = new Address();
$address->setCreatedBy($user);
$form = $this->createForm(AddressType::class, $address);
//handle form
$form->handleRequest($request);
if ($form->isSubmitted()){
//if submit, add hidden fields
$address = $form->getData();
//if valid, process
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($address);
$em->flush();
$this->addFlash(
'success',
'Your address was created.'
);
return $this->redirectToRoute('address_index');
}
}
return $this->render('address/new.html.twig', [
'form' => $form->createView(),
'mode' => 'new',
]);
}
Now, If I submit this form through an AJAX request :
$(document).on('click', '.create-address', function() {
console.log('submitting new address form...');
var $form = $(this).closest('form')
var data = $form.serializeArray();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data
});
});
In this case, my form is processed for validation and It passes ($form->isValid() returns true) even if I don't provide some of the required fields.
This causes the process of persisting the object to occur and so I get a PDOException.
My question is:
Why is my form not handled the same way (especially at the validation step) according how I post the data ?
And how different are those two methods from the point of view of the function that handle the request ?
The required option only adds the required attribute to the markup. This forces the browser to provide a value, but doesn't add any validation server side. You can trigger validation before your ajax call and submit only if the form status is valid.
But it is recommended to add server side validation explicitly by using the constraints option in the form or annotating the target entity. Refer to the documentation for more details.
I have a very simple array of strings being stored in a database and provided via an API.
Using Symfony's form types I'm adding validation for various bits of data.
I've hit a wall with a CollectionType that is essentially an array of strings, for example:
['key', 'words', 'are', 'the', 'best']
With the form code:
->add('keywords', CollectionType::class, [
'allow_add' => true,
'constraints' => [
new Count(['min' => 1]),
new NotBlank(['allowNull' => false])
]
])
This is allowing the following to pass the constraints:
[null] and ['']
If I can figure out what I'm doing wrong I'd like to add Regex validation to each element as well.
If you just want to remove empty elements, delete_empty should do the trick and you could remove NotBlank.
To apply additional validation to the elements, you'll have to pass the constraint to the collection item, not to the collection itself, by using entry_options:
->add('keywords', CollectionType::class, [
'allow_add' => true,
'delete_empty' => true,
'constraints' => [
new Count(['min' => 1]),
],
'entry_options' => [
'constraints' => [
new Regex(['pattern' => '/whateverpattern/']),
],
],
])
I'm not sure what's going on. I'm using Zend Form 2 with a multiselect field. When I submit the code, the values exist in post. When I run the values through zend form 2, I get no validation errors but the multiselect field is suddenly empty.
class Form extends \Zend\Form\Form
{
// input filter to set up filters and validators
protected $myInputFilter;
public function __construct()
{
// create the zend form
parent::__construct();
// make it a bootstrap form
$this->setAttribute('class', 'form-horizontal');
$this->setAttribute('role', 'form');
// set the default objects we'll use to build the form validator
$this->myInputFilter = new \Zend\InputFilter\InputFilter();
}
}
class AddPublicationForm extends Form
{
public function __construct()
{
// create the zend form
parent::__construct();
$this->setAttribute('class', 'form-horizontal');
$this->setAttribute('id', 'add-publication-form');
$this->add([
'name' => 'author[]',
'attributes' => [
'class' => 'form-control',
'data-placeholder' => 'Author',
'multiple' => 'multiple',
'placeholder' => 'Author',
],
'required' => false,
'type' => \Zend\Form\Element\Select::class,
'options' => [
'value_options' => [
'check1' => 'check1',
'check2' => 'check2',
],
],
]);
$this->myInputFilter->add([
'filters' => [],
'name' => 'author[]',
'required' => false,
'validators' => [],
]);
// attach validators and filters
$this->setInputFilter($this->myInputFilter);
// prepare the form
$this->prepare();
}
}
These are the zend form objects that I am using. I am using Slim Framework 2 as my backend. Here is the controller object:
public function addAction()
{
$request = $this->app->request;
$form = new Form\AddPublicationForm();
if ($request->isPost()) {
$params = $request->params();
// DUMP 1: exit('<pre>'.print_r($params, true).'</pre>');
$form->setData($params);
if ($form->isValid()) {
$data = $form->getData();
// DUMP 2: exit('<pre>'.print_r($data, true).'</pre>');
}
}
}
DUMP 1:
Array
(
[author] => Array
(
[0] => check1
[1] => check2
)
}
DUMP 2:
Array
(
[author[]] =>
)
I realize that I could very easily just bypass the validation here because I'm not using any validators on that field. I'm more concerned with the underlying cause though.
Why is the validated author data empty?
When you specify multiple in attributes, Zend\Form and Zend\InputFilter add []after the name. You should not do it yourself otherwise, in the html code, the element appears under the name author[][] and the setData method don't match.
To see it, replace required by true and look at the html code of the form.
$this->add([
'name' => 'author',
'attributes' => [
'class' => 'form-control',
'data-placeholder' => 'Author',
'multiple' => 'multiple',
'placeholder' => 'Author',
],
'required' => false,
'type' => \Zend\Form\Element\Select::class,
'options' => [
'value_options' => [
'check1' => 'check1',
'check2' => 'check2',
],
],
]);
$this->myInputFilter->add([
'filters' => [],
'name' => 'author',
'required' => false,
'validators' => [],
]);
I have a form class in Symfony2 and I am adding validation criteria to each element of the form (NOT to the connected entity object). I would like to add validation groups to some of the elements but I cannot seem to figure it out how.
I specify validation groups of the form like this:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\CommonBundle\Entity\Campaign',
'validation_groups' => array('Default', 'new_title')
));
}
And on a specific form element I do this:
->add('title', 'text', array(
'required' => true,
'help_text' => 'This is the title.',
'constraints' => array(new NotBlank(), new Length(array('min' => 3, 'max' => 150))),
'validation_groups' => array('new_title')
However, this doesn't seem to be working. Am I doing something wrong?
You can assign a validation group to a specific form element (as described in the docs for current symfony2 version) like this:
->add('title', 'text', array(
'required' => true,
'help_text' => 'This is the title.',
'constraints' => array(new NotBlank(array('groups' => array('new_title')), new Length(array('min' => 3, 'max' => 150, 'groups' => array('new_title')))),
I'm trying to create a form with translated labels. I'm not using an AbstractType class, I just want to declare my form in a controller :
$form = $this->createFormBuilder($user)
->add('website', 'url', array(
'required' => false,
'label' => 'profession.website.label'
));
How can I add a translation domain to my form ?
I believe you pass it as an array to the 2rd argument for createFormBuilder
$form = $this->createFormBuilder($user, [
'translation_domain' => 'comment'
])->add('website', 'url', [
'required' => false,
'label' => 'profession.website.label'
]);
First argument is the data for the form, the second is the options.
http://api.symfony.com/2.0/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#method_createFormBuilder