I have got a simple smyfony2 form with one choices element. When I choose "kerosin" or "diesel" the form won't validate, what is correct. When I won't choose any of the three options and submit the form empty, $form->validate() will return true, but it shouldn't. Any ideas? Using the HTML5 required is not a solution for me.
This is my Form AbstractType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Form erzeugen
$builder->add('treibstoff', 'choice', array(
'choices' => array(
'kerosin' => 'form.quiz1.kerosin',
'benzin' => 'form.quiz1.benzin',
'diesel' => 'form.quiz1.diesel',
),
'multiple' => false,
'expanded' => true,
'label' => ' '
))
->getForm();
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// Validierung erzeugen
$collectionConstraint = new Collection(array(
'treibstoff' => array(
new Choice(array(
'choices' => array('benzin'),
'message' => 'form.quiz.falscheantwort',
'strict' => true
)
)
)
));
$resolver->setDefaults(array(
'validation_constraint' => $collectionConstraint
));
}
public function getName()
{
...
Validation works like this:
if($Request->getMethod() == "POST") {
$form->bind($Request);
if($form->isValid()) {
echo "valid";
Thanks in advance.
Edit:
I changed the setDefaultOptions like suggested and added NotBlank. That worked out for me:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// Validierung erzeugen
$collectionConstraint = new Collection(array(
'treibstoff' => array(
new Choice(array(
'choices' => array('benzin'),
'message' => 'form.quiz.falscheantwort',
'strict' => true,
)
),
new NotBlank()
)
));
$resolver->setDefaults(array(
'validation_constraint' => $collectionConstraint
));
}
You set only valid choice to benzin in setDefaultOptions, but you didn't specify the field as required. Note that required in form field only sets HTML5 validation on:
Also note that setting the required option to true will not result in
server-side validation to be applied. In other words, if a user
submits a blank value for the field (either with an old browser or web
service, for example), it will be accepted as a valid value unless you
use Symfony's NotBlank or NotNull validation constraint.
So, you'll have to add also NotBlank constraint to treibstoff field.
Related
when i submit my form, I have two values to choose from:
WorkPlanningDay and workPlanningHour.
if workplanninghour is specified, we insert a row in the workplanninghour table and vice versa
When I submit a workPlanningHour, I want to prevent doctrine from inserting a workPlanningDay object with null data. I tried to use a $form->remove but without success.
here is my request datas :
{"statuses":[6],"work_planning":1,"work_planning_hour":{"count_jrtt":1,"end_at":"31-12","jrtt":true,"lower_hour_jrtt":35,"nbr_jrtt":24,"start_at":"01-01","upper_hour_jrtt":39},"work_planning_period":0,"working_time_aspect":{"days":["MON","TUE","WED","THU","FRI"],"time":0,"weekly_hours":35}}
here is my form with my EventListener :
class WorkingTimeParametersRESTType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('work_planning', Field\ChoiceType::class, array(
'property_path' => 'workPlanning',
'required' => true,
'choices' => WorkingTimeParameters::getAllowedWorkPlannings(),
))
->add('work_planning_period', Field\ChoiceType::class, array(
'property_path' => 'workPlanningPeriod',
'required' => false,
'choices' => WorkingTimeParameters::getAllowedWorkPlanningPeriods(),
))
->add('working_time_aspect', WorkingTimeAspectType::class, array(
'property_path' => 'workingTimeAspect',
'required' => true,
'constraints' => array(new Valid())
))
->add('work_planning_hour', WorkPlanningHourType::class, array(
'property_path' => 'workPlanningHour',
'required' => true,
'constraints' => array(new Valid())
))
->add('work_planning_day', WorkPlanningDayType::class, array(
'property_path' => 'workPlanningDay',
'required' => true,
'constraints' => array(new Valid())
))
->add('statuses', Field\CollectionType::class, array(
'entry_type' => CategoryIntegerType::class,
'entry_options' => array(
'category_type' => Category::TYPE_STATUS,
),
'allow_add' => true,
'allow_delete' => true,
'required' => true,
'error_bubbling' => false,
))
->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'))
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
/** #var WorkingTimeParameters $contract */
$parameters = $event->getData();
$form = $event->getForm();
// dd($form->has('work_planning_day'));
// Remove useless work planning
if ($parameters->getWorkPlanning() == Contract::WORK_PLANNING_HOUR) {
$parameters->setWorkPlanningDay(null);
$form->remove('work_planning_day');
}
else if ($parameters->getWorkPlanning() == Contract::WORK_PLANNING_DAY) {
$parameters->setWorkPlanningHour(null);
$form->remove('work_planning_hour');
}
else {
dd('rerer');
$form->remove('work_planning_day');
$form->remove('work_planning_hour');
$parameters->setWorkPlanningDay(null);
$parameters->setWorkPlanningHour(null);
$parameters->setWorkPlanningPeriod(null);
}
})
;
}
/**
* #param FormEvent $event
*/
public function onPreSetData(FormEvent $event)
{
$rttParameters = $event->getData();
$form = $event->getForm();
// The company can be set only on creation
if (!$rttParameters || !$rttParameters->getId()) {
$form->add('company', CompanyIntegerType::class, array(
'required' => true,
));
}
}
and here are the requests that doctrine is trying to launch :
INSERT INTO w2d_tm_work_planning_hour (startAt, endAt, jrtt, countJrtt, lowerHourJrtt, upperHourJrtt, nbrJrtt) VALUES ('1970-01-01 00:00:00', '1970-12-31 00:00:00', 1, 1, 35, 39, 24);
UPDATE w2d_tm_work_planning_day SET startAt = NULL, endAt = NULL, nbrDays = NULL, nbrJrtt = NULL WHERE id = 1;
how to do so that doctrine does not launch the second request with the null values ?
Symfony form type are not made to handle different entities depending on a option you selected inside.
This not how Symfony FormType are supposed to be used. This is why the data_class attribute is made for one entity.
You best option is to create two distinct FormType and submit only the one needed depending on what the user selected on frontend. (easy with a little of javascript)
The second advantage of this is you will have much more understandable and maintable code.
I've been looking for a solution for hours. I'am working with Symfony 3.2.
I'am using Symfony Forms in order to display a data-table (with different filter chosen in the form) with Ajax => No submit button.
I can access to different result of data-table to view more information.
What i want is that when i leave the detail page by clicking on a button and come back on the research page, that i could keep in history all filters that i have chosen.
i wanted use session but actually, it seems has 0 impact. Below is some codes.
Controller:
public function indexAction(Request $request)
{
$form = $this->createSearchForm();
$request->getSession()->set('form_data', $form->getData());
$form->handleRequest($request);
$form->setData($request->getSession()->get('form_data'));
$this->datatable($form->getData());
return $this->render('backend/jobOffer/index.html.twig', [
'form' => $form->createView()
]);
}
FormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('publicationState', ChoiceType::class, [
'label' => 'backend.job_offer.publication_state',
'required' => false,
'choices' => JobOffer::getPublicationStateChoices(),
'choice_translation_domain' => 'choices',
])
->add('job', Select2EntityType::class, [
'label' => 'backend.job',
'required' => false,
'class' => 'AppBundle:Job',
'text_property' => 'label',
'remote_route' => 'job_autocomplete',
])
->add('ids', HiddenType::class, [
'required' => false,
])
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
'method' => 'GET',
]);
}
I have a problem, there is a EntityType (or Select2EntityType) field named "job" in my form builder and i can't get the content of this field in my session. This field shows a autocomplete data-list after typing 2 letters, and we can choose one of job.
and also, when i refresh the page, i lose all filters, but i am supposed to store them in session ?
Thanks in advance for your help,
Yes you can use $form->getData() but to do that you need to do this according to the doc here with using if ($form->isSubmitted() && $form->isValid()) { among others.
I'm using Symfony <4 and I have an issue with a form using an other form in a Many-To-Many relation with parameter.
You will find below my FeatureForm:
->add('tags',CollectionType::class,
array(
'entry_type' =>TagFeatureType::class,
'allow_add' => true,
'allow_delete' => true,
'data' => $datas,
'entry_options' => array(
'label' => false,
)
)
)
Now my TagFeatureType:
->add('tag', EntityType::class,
array(
'choice_label' => 'name',
'class' => 'AppBundle:Tag',
'query_builder' => function(TagRepository $tr){
return $tr->findObjectNotMine();
}
)
)
I would like to inject a parameter into findOBjectNotMine but I cannot pass parameter from controller because the TagFeatureType is created by FeatureForm. Inside the buildForm function I cannot pass any extra parameter.
I see 2 possibilites, 1st I modify default options to allow an extra option, but it's a bit disgusting. 2nd, I could use session parameter and inject session service inside the constructor... But it looks like more a workaround than a proper way...
Do you know an elegant way to inject parameter to a form from buildForm function inside FormType?
Best regards
In case you need to pass container-known parameters to custom form type, you can go the way you attempted above (obviously via parameter injection). However, if you want to pass data from controller down to form type, you can pass it via $options (last) argument (in buildForm):
FeatureForm
public function buildForm(FormBuilderInterface $builder, array $options)
// ....
$builder->add('tags', CollectionType::class, array (
'entry_type' => TagFeatureType::class,
'allow_add' => true,
'allow_delete' => true,
'data' => $datas,
'entry_options' => array(
'label' => false,
'some_custom_param' => $options['some_custom_param']
)
)
);
// ....
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Task::class
));
$resolver->setRequired(array(
'some_custom_param'
));
}
And then, in TagFeatureType should have configured options:
TagFeatureType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(array(
'some_custom_param'
));
}
And then finally, include it inside of buildForm:
public function buildForm(FormBuilderInterface $builder, array $options)
// ....
$someCustomParam = $options['some_custom_param'];
// ....
$builder->add('tag', EntityType::class, array(
'choice_label' => 'name',
'class' => 'AppBundle:Tag',
'query_builder' => function(TagRepository $tr) use ($someCustomParam)
{
return $tr->findObjectNotMine($someCustomParam);
}
);
// ....
}
Obvious downside of this would be a need for all the forms in path to have setRequired or setDefault.
Hope this helps...
In your TagFeatureType you can just pass in the argument via constructor injection:
class TagFeatureType extends AbstractType
{
private $tagRepository;
public function __construct(TagRepository $tagRepository)
{
$this->tagRepository = $tagRepository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$tr = $this->tagRepository;
// ...
->add('tag', EntityType::class,
array(
'choice_label' => 'name',
'class' => 'AppBundle:Tag',
'query_builder' => function(TagRepository $tr){
return $tr->findObjectNotMine();
}
)
)
}
}
There is a DependencyInjectionExtension form extension that will check if the form type is registered in the service container and then inject it, instead of creating a new instance. This should make sure that the tag repository exists.
Can I specify how the form field shold be mapped on data class?
Let's say I have form with check box and on my data entity the field is stored as string.
class FormType extends AbstractType {
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'data_class' => DataEntity::class,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('issueType', CheckboxType::class, [
'label' => 'issueType',
]);
}
}
class DataEntity {
/** #var string Either PLASTIC or PAPER */
private $issueType;
public function getIssueType() {
return $this->issueType;
}
public function setIssueType($issueType) {
$this->issueType = $issueType;
}
}
Can I made the checkbox to be mapped as 'PLASTIC' if ture and 'PAPER' if false?
You can use data transformer to cast bolean to the string. See this tutorial: https://symfony.com/doc/current/form/data_transformers.html.
$builder->get('issueType')
->addModelTransformer(new CallbackTransformer(
function ($type) {
// your logic here
return $type;
},
function ($type) {
// your logic here
return $type;
}
));
try :
$builder->add('newsletter', 'choice', array(
'label' => 'Newsletter erhalten',
'attr' => array(
'class' => 'form-control',
),
'choices' => array(array('yes' => 'plastic'), array('no' => 'paper')),
'expanded' => true,
'multiple' => true,
'required' => false,
));
also check the answer here Symfony2 Change checkbox values from 0/1 to 'no'/'yes'
I've created a form which has some fields, whose required status is dependend on the value entered into another field (to be specific, whether the value is > 0).
It is about ordering a textbook and if the quantity is > 0, then the address-related fields must also be entered.
I've added a custom validation callback, which can check whether the count is > 0 and then adds a Violation. But it gets only executed if the field is not empty (since it is not "required").
Is there a way to always validate, but without having to use the required flag on the field?
Below a snippet:
[...]
$builder->add('firstname', 'text', array(
'label' => 'label.firstname',
'required' => false,
'property_path' => 'Order.firstname',
'constraints' => array(
new Callback(array('callback' => array($this, 'validateOrder')))
)
));
[...]
public function validateOrder($data, ExecutionContextInterface $context)
{
/** #var \Symfony\Component\Form\Form $form */
$form = $context->getRoot();
if(
(
(int)$form->get('count_d')->getData() > 0 ||
(int)$form->get('count_f')->getData() > 0 ||
(int)$form->get('count_i')->getData() > 0
) && $data == '') {
$context->addViolation('profile.order.error');
}
}
Wouldn't it be more appropriate to put callback at form directly (instead of field)?
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'constraints' => array(
new Callback(array('callback' => array($this, 'validateOrder')))
)
));
}
This way, the callback will be invoked in any case...