Symfony CollectionType with different fields - php

Question:
Consider following Order form with so many requirements:
Title: [_________________]
REQUIREMENTS:
What sizes? [X] Small [X] Medium [_] Large
What shapes? [_] Circle [X] Square [_] Triangle
What colors? [X] Red [_] Green [X] Blue
.
.
.
How can I generate and handle the form in Symfony 3.2?
What do I think:
[ Order ] ------OneToMany------ [ Requirement ] ------OneToMany------ [ Selection ]
OrderType
class OrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$form = $builder
->add('title', TextType::class, array());
->add('requirements', CollectionType::class,
array(
'entry_type' => RequirementType::class
)
)
->add('submit', SubmitType::class, array(();
return $form;
}
}
Problem
I don't know how to write the RequirementType, as they are not exactly the same (size, shape, color, ...).
This is what I think:
RequirementType
class RequirementType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$form = $builder
->add(??????, EntityType::class,
array(
'label' => ??????,
'expanded' => true,
'multiple' => true,
'class' => Selection::class,
'query_builder' => call_user_func(function (EntityRepository $er, $requirement) {
return $er->createQueryBuilder('s')
->where('s.requirement = :requirement')
->setParameter('requirement', $requirement)
},$em->getRepository($args['class']), $requirement);
)
);
return $form;
}
}

If I correctly understand, the requirement’s attributes ("Small", "Medium", "Large"…) are stored in the Collection’ table, and are related to the requirement (“sizes”, “shapes”, “colors” …) with a oneToMany relation (a requirement can have multiple selection) ….
If so,
the following code works:
OrderType.php
$builder
->add('requirements', CollectionType::class,
array(
'entry_type' => RequirementType::class
)
);
RequirementType.php :
$builder
->add('name', HiddenType::class, array('disabled'=>true))
->add('collections', EntityType::class, array(
'class' => ‘AppBundle:Collection’,
'choice_label' => 'name', 'multiple' =>true))
In your Twig view :
{{ form_start(orderForm) }}
{% for requirement in orderForm.requirements %}
<label>{{ requirement.name.vars.value }}</label>
{{ form_widget(requirement.collections) }}
{{ form_widget(requirement.name) }}
<br>
{% endfor %}
{{ form_end(orderForm) }}

Related

Symfony Embed Forms PRE_SET_DATA access parent form field data

i have a InspectionPlan Form Type which has a product EntityType field, (Products can have attributes i assign some by fixtures) however, the formtype also has a CollectionType Field for another entity called InspectionPlanSections, this uses a InspectionPlanSectionFormType which has a CollectionTypeField for an entity called InspectionPlanQuestions with a InspectionPlanQuestionFormType with allow_add' => true and a prototype, these questions have a relation to the earlier mentioned ProductAttributes, , only the product attributes the earlier submittet Product has should be assignable
class InspectionPlanQuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('question', TextareaType::class, [
'attr' => ['class' => 'tinymce'] ])
->add('productAttribute', EntityType::class, [
'class' => ProductAttribute::class,
'placeholder' => 'Attribut auswählen',
'choice_label' => 'name'
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => InspectionPlanQuestion::class,
]);
}
}
this loads all product attributes which is not exactly what i want
class InspectionPlanQuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('question', TextareaType::class, [
'attr' => ['class' => 'tinymce'] ]);
$builder->addEventListener(FormEvents::PRE_SET_DATA,
function (FormEvent $event)
{
$form = $event->getForm();
$child = $event->getData();
if ($child) {
$form->add('productAttribute', ChoiceType::class, [
'placeholder' => 'forms.product.attribute_placeholder',
'choices' => $child->getInspectionPlanSection()->getInspectionPlan()->getProduct()->getProductAttributes(),
'choice_label' => function ($choice, $key, $value) {
return $choice->getName();
},
]);
}
}
);
}
this gives me the desired attributes in the select, but only after i atleast submitted one section with a question, because if none were submitted, the $event->getData() returns always null
so when i create a fresh InspectionPlan and add a product, submit the form, and then add a Section and questions via javascript, i get no product attributes select field rendered in my question field
Can someone explain me why this works, after i submitted at least one question?

Symfony form multiple select entities with many relationships

Sorry in advance for the quality of my English
I would create a form to create a Employee. Employee and Team are connected with the ManytoOne bidirectional Relation, Team and Division are connected with the ManytoOne bidirectional Relation, and Division and Center are connected with the manytoOne bidirectional Relation. In my form I want to be able to filter the list of Teams with Javascript, according to the choice made on a first select on Center then Division to reduce the list of Teams.
Here is my code :
EmployeeType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('birthDate', DateType::class)
->add('firstName', TextType::class)
->add('name', TextType::class)
->add('isActive', CheckboxType::class, array('required' => false))
->add('employeePicture', EmployeePictureType::class)
->add('team', TeamType::class)
->add('save', SubmitType::class);
}
TeamType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('shortName',EntityType::class, array(
'class' => 'RTELiveWorkingBundle:Team',
'choice_label' => 'shortName',
'placeholder' => 'Choisir une équipe'
))
->add('divsion', DivisionType::class);
}
DivisionType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('shortName',EntityType::class, array(
'class' => 'RTELiveWorkingBundle:Division',
'choice_label' => 'shortName',
'placeholder' => 'Choisir un GMR'
))
->add('center', CenterType::class);
}
And CenterType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('shortName',EntityType::class, array(
'class' => 'RTELiveWorkingBundle:Center',
'choice_label' => 'shortName',
'placeholder' => 'Choisir un centre'
));
}
My controller :
public function addAction(Request $request) {
$employee = new Employee();
$form = $this->createForm(EmployeeType::class, $employee);
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($employee);
$em->flush();
return $this->redirectToRoute('rte_live_working_employee_view', array('id' => $employee->getId()));
}
return $this->render('RTELiveWorkingBundle:Employee:add.html.twig', array('form' => $form->createView()));
}
My forms :
I have the error :
Catchable Fatal Error: Method
RTE\LiveWorkingBundle\Entity\Team::__toString() must return a string
value
But I have to implement __toString() in my entity :
public function __toString()
{
return $this->getShortName();
}
have you got an idea ?

Symfony transform ChoiceType in CheckboxType

I've problem with my form type. I have an entity activity and an other entity class. It's in ManyToMany. When I display the form, it's in ChoiceType, but I want it to be in CheckboxType. So I've :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('libelle')
->add('horraire')
->add('horraireDebut')
->add('horraireFin')
->add('description')
->add('classes');
}
It display a ChoiceType but I want a CheckboxType, so I changed this to :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('libelle')
->add('horraire')
->add('horraireDebut')
->add('horraireFin')
->add('description')
->add('classes', CheckboxType::class);
}
But this only displays one checkbox while I have several recordings (which appears well with the first code).
My form.html.twig :
<div class="form-group{% if form.classes.vars.errors|length %} has-error{% endif %}">
<label for="{{ form.classes.vars.id }}" class="col-sm-3 control-label no-padding-right required">Classes <span class="red">*</span></label>
<div class="col-sm-9">
{{ form_widget(form.classes,{'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.classes) }}
</div>
How can I get a checkbox line or a checkbox dropdown ?
Thanks!
Just use multiple and expanded options together to achieve this (Ref):
$builder->add('classes', null, array(
'multiple' => true,
'expanded' => true,
));
Then checkboxes will be rendered.
Note that null value mean EntityType::class in case you use Doctrine ORM. Otherwise, use EntityType::class and 'class' => Entity::class option.
In order to achieve what you're looking for, you could also try the EntityType with a query builder:
->add('classes', EntityType::class, array(
'by_reference' => true,
'multiple' => true,
'expanded' => false,
'class' => 'AppBundle\Entity\Class',
'property' => 'name',
'query_builder' => function(\Doctrine\ORM\EntityRepository $er) {
$qb = $er->createQueryBuilder('c');
return $qb->orderBy('c.name', 'ASC');
}
))

symfony ajax form dynamically modify

I have the following form that contains data from the database it still WIP ( i'm missing a few fields that i didn't add yet).
The form loads data in the first select and based on that select i use ajax to populate a second select with options based on the first select ( basically the associations to the selected value). And from there again another select with certain options and so on and at the end when i submit the form i want to generate a report from database based on the data.
For the moment i'm stuck with the second field because i always get an error:
This value is not valid.
The form class:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('survey', EntityType::class, [
'class' => SurveyManager::class,
'placeholder' => 'Choose option',
'attr' => [
'class' => 'field-change',
],
])
->add('headquarter', ChoiceType::class, [
'choices' => [],
])
->add('submit', SubmitType::class, [
'label' => 'Save',
])
;
}
I'm not reall sure how to fix the error or how should i handle this type of form. Can you help me out guys ?
Based on the answer i did this
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$form->add('headquarter', EntityType::class, [
'class' => HeadQuarterManager::class,
'query_builder' => function(HeadQuarterManagerRepository $er) {
return $er->getHeadquarter($data['survey']);
},
]);
}
);
But i'm getting this error:
Notice: Undefined variable: data
Not really sure how to pass the data to the getHeadquarter method so i can return an array of id => name for the select.
When you run the function $form->isValid(), it checks against the form it built in the buildForm function. Any extra fields/value that aren't there will cause this error.
You can change this behaviour by using form events.
In the end this is how i did it:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('survey', EntityType::class, [
'class' => SurveyManager::class,
'attr' => [
'class' => 'field-change',
],
])
->add('submit', SubmitType::class, [
])
->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$modifier = $data['survey'];
$form->add('headquarter', EntityType::class, [
'class' => HeadQuarterManager::class,
'query_builder' => function (HeadQuarterManagerRepository $er) use ($modifier) {
return $er->getHeadquarter($modifier);
},
]);
}
);
}

How to set choice field values in Symfony2?

Whats the best way to set a collection of key/values pairs (obtained from MySQL) as a choice field 'choices' inside a controller?
I think about something similar to :
$form = $this->createForm(new AddNews(), $news);
$newsList = $this->getDoctrine()
->getRepository('BakaMainBundle:News')->getAllNews();
$titlesList = ...($newsList); // some fuction that extract title=>id
// array from news object collection
$form->get('newsList')->setData($titlesList);
where the AddNews() form looks like :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(...)
->add(...)
->add('accept' , 'submit')
->add('newsList', 'choice', array
(
'mapped' => false,
'required' => true
));
}
Perhaps something like below (assuming Symfony >= 2.7). See docs for field options:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(...)
->add(...)
->add('accept' , 'submit')
->add('newsList', 'entity', array
(
'class' => ''BakaMainBundle:News'',
'choice_label' => 'title',
'mapped' => false,
'required' => true
));
}
You could get your "news" directly from your formType file, using your repository, like that:
private function getNews(){
$newsList = $this->getDoctrine()
->getRepository('BakaMainBundle:News')->getAllNews();
$titlesList = ...($newsList); // some fuction that extract title=>id
// array from news object collection
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(...)
->add(...)
->add('accept' , 'submit')
->add('newsList', 'choice', array
(
'mapped' => false,
'required' => true,
'choices' => $this->getNews()
));
}

Categories