Symfony4 validate form with mapped false form fields - symfony

I have an entity with age.
I created a search form to show records where ageMin < age < ageMax.
So I added non mapped form fields ageMin and ageMax:
$builder
->add('ageMin', IntegerType::class, [
'mapped' => false,
'required' => false
])
->add('ageMax', IntegerType::class, [
'mapped' => false,
'required' => false
])
;
I would like to check if the user has entered min greater than max (very simple requirement!).
I tried Symfony validate form with mapped false form fields but it is for symfony 2 and Components required is confusing for me.
I found these help pages too:
https://symfony.com/doc/current/form/without_class.html#form-option-constraints and tried:
->add('ageMin', IntegerType::class, [
'mapped' => false,
'required' => false,
'constraints' => new LessThan('ageMax')
])
There are no errors but it doesn't work.
Other page:
https://symfony.com/doc/current/validation/raw_values.html

If there is an entity with the property age you could use LessThan for ageMin and GreaterThan for ageMax to compare these values to age.
$builder
->add('ageMin', IntegerType::class, [
'mapped' => false,
'required' => false,
'constraints' => [new LessThan(['propertyPath' => 'age'])]
])
->add('ageMax', IntegerType::class, [
'mapped' => false,
'required' => false,
'constraints' => [new GreaterThan(['propertyPath' => 'age'])]
])
;
Take care for proper import of the constraints.
EDIT:
The example above assumes that the form makes use of the entity having the property age. If the name of the entity class is Foo, then the method configureOptions would look like:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Foo::class,
]);
}

Related

Symfony 5 - display an required input field after a specify dropdown select

I would like to build a form with Symfony 5 that should display a required input field for a certain dropdown selection.
After selecting "sonstiges" I need a required input field.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('salutation', ChoiceType::class, [
'label' => 'salutation',
'required' => true,
'constraints' => [
new NotBlank()
],
'placeholder' => 'Bitte wählen',
'label_attr' => [
'class' => 'visually-hidden'
],
'choices' => [
'Herr' => 'herr',
'Frau' => 'frau',
'Diverse' => 'diverse',
],
])
->add('firstname', TextType::class, [
'label' => false,
'required' => true,
'constraints' => [
new NotBlank()
],
'attr' => [
'placeholder' => "firstname",
],
])
->add('afterWork', ChoiceType::class, [
'label' => 'afterWork',
'required' => true,
'constraints' => [
new NotBlank()
],
'placeholder' => 'Bitte wählen',
'label_attr' => [
'class' => 'visually-hidden'
],
'choices' => [
'Studium' => 'studium',
'weitere Schule' => 'weiterSchule',
'Ausbildung' => 'ausbildung',
'Sonstiges' => 'sonstiges',
],
])
}
I added a $builder->addEventListener, unfortunately it didn't give me the result I need, I also added the input field as not required, hidden it and displayed it with a javascript. Unfortunately, it will not be validated because it is not required in the $builder.
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
if (!is_null($data) && $data['afterWork'] == "sonstiges") {
$form->add('afterWorkText', TextType::class, [
'label' => "afterWorkText",
'required' => false,
'constraints' => array(
new NotBlank(),
),
'attr' => [
'placeholder' => "afterWorkText"
],
'label_attr' => [
'class' => 'visually-hidden'
],
]);
}
}
Is there a way to insert this field with "display:none" attribute and activate it with a javascript? In addition, the field should then be set to required.
Or can someone help me to find the right solution here?

Symfony - Error with the data parameter on a form

Context of the problem :
I created a symfony form.
Each tool has a collection of modules.
The user has a collection of modules of any tool.
What I want :
I want for each tool there are checkboxes corresponding to the tool's modules. The module checkboxes that the user owns are checked.
([] = checkbox)
Tool1 : []Module1 [x]Module2 [x]Module3
Tool2 : []Module4 [x]Module5
Tool3 : [x]Module6 []Module7
What I currently have:
For each tool, there are checkboxes corresponding to the tool's modules. But I have a problem to tick the checkboxes of user's modules. I get an error on the data parameter.
The form field :
$user = $options['user'];
$tools = $options['tools'];
foreach ($tools as $tool) {
$name = 'profile_'.str_replace(array('-', ' ', '.'), '', $tool->getLibelle());
$builder
->add($name, ChoiceType::class, [
'label' => $tool->getLibelle(),
'choices' => $tool->getModules(),
'choice_value' => 'id',
'choice_label' => function (?Module $module) {
return $module ? $module->getName() : '';
},
'data'=> $user->getModules(), // ERROR HERE
'expanded' => true,
'multiple' => true,
'mapped'=>false
])
;
}
[...]
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
'user'=> null,
'category'=> null,
'tools'=> null,
]);
}
The error :
My question :
Why do I have this error? How can I use the data parameter correctly to achieve the expected result?
You are on the good way, try to dump what is $user->getModules() returning, it has to be an array. May be is not returning an array, check te relation.
I did a little test and it works perfectly.
$name = 'name_field';
$builder->add($name,ChoiceType::class, array(
'choices' => array('Yes', 'No'),
'data' => array('Yes', false),
'mapped' => false,
'expanded' => true,
'multiple' => true
));
Here the solution :
Seems to me that $user->getModules() returns a collection. I managed to find another solution and that works (I changed the type of the field to EntityType)
foreach ($tools as $tool) {
$name = 'acces_'.str_replace(array('-', ' ', '.'), '', $tool->getLibelle());
$builder
->add($name, EntityType::class, [
'class'=> Module::class,
'label' => $tool->getLibelle(),
'data' => $user->getModules(),
'choices'=> $tool->getModules(),
'choice_value' => 'id',
'choice_label' => 'name',
'expanded' => true,
'multiple' => true,
'required' => true,
'mapped'=>false,
])
;
}
ChoiceType: data parameter need array
EntityType: data parameter need collection
Thanks for the help !

Symfony manually $form->submit(); with multidimensional array

Im struggling since 10 hours with Symfony 5.1.7 and $form->submit();
My target is a JSON API that converts data to a similiar array. I already debugged and found following part.
Can someone please help me what I am doing wrong here?
To test it, i have created a manually PHP array to submit it.
My Code in Controller
$form = $this->createForm(AddCommentFormType::class);
$test = [
'content' => 'Test',
'media' => [
[
'path' => '1.png',
],
[
'path' => '2.png',
],
],
'_token' => '3bF4qkiUPjKNuGnbY-ySdO6B2sCLzKcS4ar7auX3Dek',
];
$form->submit($test);
AddCommentFormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content', TextareaType::class, [
'constraints' => [
new NotBlank(),
new Length([
'max' => 10000,
]),
],
])
->add('media', CollectionType::class, [
'entry_type' => MediaFormType::class,
'constraints' => [
new Count([
'min' => 1,
'max' => 5,
]),
],
])
->add('_token', HiddenType::class, [
'mapped' => false,
'constraints' => [
new NotBlank(),
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
MediaFormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('path', TextType::class, [
'constraints' => [
new NotBlank(),
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Media::class,
]);
}
Validator Result
children[media].data
This collection should contain 1 element or more.
[]
children[media]
This form should not contain extra fields.
[▼
[▼
"path" => "1.png"
]
[▼
"path" => "2.png"
]
]
your form has no default data, since you create it with
$form = $this->createForm(AddCommentFormType::class);
createForm can take an additional parameter for default data. This alone is not necessarily a problem, the default is an array of the form (or something very similar, maybe empty strings instead of null)
[
'content' => null,
'media' => [],
'_token' => null,
]
However, the CollectionType will not allow adding or removing elements by default. Setting it's options allow_add (and optionally allow_remove, if you ever set default values) will change that.
So the minimal change would be:
->add('media', CollectionType::class, [
'allow_add' => true, // <-- this is new
'entry_type' => MediaFormType::class,
'constraints' => [
new Count([
'min' => 1,
'max' => 5,
]),
],
])
If your type is AddCommentFormType the form expects by default the data to be in add_comment_form keys like:
$test = [
‘add_comment_form’ => [
'content' => 'Test',
'media' => [
[
'path' => '1.png',
],
[
'path' => '2.png',
],
],
'_token' => '3bF4qkiUPjKNuGnbY-ySdO6B2sCLzKcS4ar7auX3Dek',
]
];

Symfony 2.8 - FormBuilder : why field becomes required when I define its type?

I'm building a form like with two non mandatory fileds :
$form = $this->createFormBuilder($contact);
$form->add('name');
$form->add('subject', TextType::class);
$form->getForm();
After rendering the first field is not required (it's normal) but why the second is ?! What's wrong with this code ?
Thanks :)
The problem must be the related entity to this form. Are name and subject nullable?. If no ORM configured then you need to manually set the required attribute to each form field. Look the example of a contact form without ORM.
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('fullName', null, array(
'required' => true,
'attr' => array(
'placeholder' => 'Name',
'class' => 'text gradient'
)))
->add('email','email', array(
'required' => true,
'attr' => array(
'placeholder' => 'Email',
'class' => 'text gradient'
)))
->add('subject', null, array(
'required' => true,
'attr' => array(
'placeholder' => 'Subject',
'class' => 'text gradient'
)))
->add('body', 'textarea', array(
'required' => true,
'attr' => array(
'placeholder' => 'Message',
'class' => 'text gradient'
)));
}
required default value is defined in type class method configureOptions()
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'required' => true,
));
}
and in all parents for this type (parent is defined by getParent() method)
first parent is Symfony\Component\Form\Extension\Core\Type\FormType and there required default value is defined as true, to it is strange that first input is not required.
you can define required while adding new element $form->add('subject', TextType::class, array('required' => false));

Handle array of string in edit form in Sonata Admin Bundle

In one of my entities, I got an array attribute. I thought Sonata Admin Bundle could handle it but it seems that it requires some attention.
I'm pretty sure SONATA_TYPE_COLLECTION field type could handle that but I didn't find any clue on how to configure the field in configureFormFields()
Do anyone know how to configure it ?
Thanks
I give you an example that I used:
Entity:
/**
* #ORM\Column(type="array", nullable=true)
*/
private $tablaXY = [];
use Sonata\AdminBundle\Form\Type\CollectionType;
->add('tablaXY',CollectionType::class, [
'required' => false,
'by_reference' => false, // Use this because of reasons
'allow_add' => true, // True if you want allow adding new entries to the collection
'allow_delete' => true, // True if you want to allow deleting entries
'prototype' => true, // True if you want to use a custom form type
'entry_type' => TablaType::class, // Form type for the Entity that is being attached to the object
],
[
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position',
]
)
form:
class TablaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('ejeX', TextType::class,['label' => 'Eje X (texto)',
'required' => true,'attr' => array('class' => 'form-control'),])
->add('ejeY', NumberType::class,['label' => 'Eje Y (Número)',
'required' => true])
;
}
}
You can use the Sonata CollectionType class, which is capable of adding and removing elements from an array:
use Sonata\AdminBundle\Form\Type\CollectionType;
...
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('group')
->add('roles', CollectionType::class, array(
'allow_add' => true,
'allow_delete' => true,
))
->end()
;
}

Resources