Create Symfony Form where fields appear and disappear based on choice - forms

I'm trying to create a form where some of the fields appear or disappear based on the choice the user make inside the first field.
Form Type
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('type', ChoiceType::class, [
'placeholder' => 'Please select the type of difference',
'choices' => array(
'Patch' => 'Patch',
'Regional' => 'Regional',
'Version' => 'Version',
'Platform' => 'Platform',
),
])
->add('maingame', EntityType::class, [
'class' => MainGame::class,
'placeholder' => 'Please select a game',
'choice_label' => 'title',
])
->add('gameversion', EntityType::class, [
'class' => GameVersion::class,
'placeholder' => 'Please select a Main Game first',
'choice_label' => 'title',
])
->add('platform', EntityType::class, [
'class' => Platform::class,
'choice_label' => 'title',
])
->add('text')
->add('nation', EntityType::class, [
'class' => Nation::class,
'choice_label' => 'name',
])
->add('confirm')
;
}
I can't manage to find the right way to do it. Can someone help?

You can create one form for every choice. This is clear and fast solution. You can show links instead of choice list. User choose type by clicking on link and you can render your form based on this choice. One choice one method with route and form in controller.
Ajax - Handle javascript onChange event after choice was selected and retrieve and render updated form. You can pass this choice while building your form as option.
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('maingame', EntityType::class, [
'class' => MainGame::class,
'placeholder' => 'Please select a game',
'choice_label' => 'title',
])
->add('platform', EntityType::class, [
'class' => Platform::class,
'choice_label' => 'title',
])
->add('text')
->add('confirm');
switch ($options['type']) {
case 'Patch':
$builder->add('gameversion', EntityType::class, [
'class' => GameVersion::class,
'placeholder' => 'Please select a Main Game first',
'choice_label' => 'title',
])
break;
case 'Regional':
$builder->add('nation', EntityType::class, [
'class' => Nation::class,
'choice_label' => 'name',
])
break;
// etc...
}
}

Related

Symfony: how to cache a FormInterface object

I have a form type in Symfony that shows in every page list and I'd like to catch it to avoid unnecessary database queries as it uses two EntityTypes fields:
->add('brand', EntityType::class, [
'required' => false,
'class' => Brand::class,
'choice_label' => 'name',
'query_builder' => function (BrandRepository $er) {
return $er->createQueryBuilder('b')
->orderBy('b.name', 'ASC');
},
])
->add('car_manufacturer', EntityType::class, [
'class' => CarManufacturer::class,
'choice_label' => 'name',
'required' => false,
'query_builder' => function (CarManufacturerRepository $er) {
return $er->createQueryBuilder('m')
->orderBy('m.name', 'ASC');
},
])
->add('Search', SubmitType::class);
So in the controller I tried to do this:
private function getFiltersForm(): FormInterface {
$cachedForm = $this->cache
->getItem('form_list_filters')
->expiresAfter(3600*24);
if (!$cachedForm->isHit()) {
$form = $this->formFactory->createNamedBuilder('', SearchItemType::class)->getForm();
$cachedForm->set($form);
$this->cache->save($cachedForm);
}
return $cachedForm->get();
}
But it doesn't work, the isHit()is always false and the form is always regenerated which makes me thing there must be some problem on the save process.
Any idea? thanks in advance

Can't get a way to read the property "title" in class "App\Entity\Travel" triggered by $form->createView()

I am getting the following error on my symfony page. Using a CRUD system when I try to edit one item, I fall onto the following error:
Can't get a way to read the property "title" in class
"App\Entity\Travel".
My "Travel" entity has no such "title" property as it is not expected. The only place a "title" property is defined is in the TravelTranslation entity, which is in a ManyToOne relationship with travel.
After I have commented all references to the form in my twig templates all I find is that the error is triggered by $form->createView() in my controller.
/**
* #Route("/{id}/edit", name="travel_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Travel $travel): Response
{
$entityManager = $this->getDoctrine()->getManager();
$langs = $entityManager->getRepository(Lang::class)->findAll();
$media = $entityManager->getRepository(Media::class)->findAll();
$form = $this->createForm(TravelType::class, $travel);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
return $this->redirectToRoute('travel_index');
}
return $this->render('crud/travel/edit.html.twig', [
'langs' => $langs,
'travel' => $travel,
'media' => $media,
'form' => $form->createView()
]);
}
But then my TravelType form contains the following code:
class TravelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('main_title')
->add('category', EntityType::class,[
'class' => Category::class,
'choice_label' => 'name',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('c')
->andWhere('c.type = :type')
->setParameter('type', 'country')
->orderBy('c.name', 'ASC');
},
])
->add('price_driver', MoneyType::class,[
'divisor' => 100,
])
->add('price_codriver', MoneyType::class,[
'divisor' => 100,
])
/* ->add('country') */
->add('km')
->add('media', EntityType::class, [
'class' => Media::class,
'choice_label' => 'name',
'multiple' => true
])
->add('status')
->add('duration')
->add('level')
->add('travelTranslations', CollectionType::class, [
'entry_type' => TravelTranslationType::class,
'entry_options' => [
'label' => false
],
'by_reference' => false,
// this allows the creation of new forms and the prototype too
'allow_add' => true,
// self explanatory, this one allows the form to be removed
'allow_delete' => true
])
->add('dates', CollectionType::class, [
'entry_type' => DatesType::class,
'entry_options' => [
'label' => false
],
'by_reference' => false,
// this allows the creation of new forms and the prototype too
'allow_add' => true,
// self explanatory, this one allows the form to be removed
'allow_delete' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Travel::class,
'allow_extra_fields' => true
]);
}
}
I managed to fix the error by getting into the DatesType::class (a custom entity type form not DateType) and fixing it as it was the one getting with the trouble of referring to the wrong property "title" in a collection type
By changing
->add('travel', EntityType::class, [
'class' => Travel::class,
'choice_label' => 'title'
] )
By:
->add('travel', EntityType::class, [
'class' => Travel::class,
'choice_label' => 'main_title'
] )
Where main_title is the real property.

Symfony : Entity of type "App\Entity\Classement" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?

I have this form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', EntityType::class, [
'class' => Country::class,
'choice_label' => 'name',
'label' => false,
'placeholder' => '-',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.name', 'ASC');
}
])
->add('season', EntityType::class, [
'class' => Season::class,
'choice_label' => 'year',
'label' => false,
'placeholder' => '-',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->orderBy('s.year', 'ASC');
}
])
->add('clubHistos', CollectionType::class, [
'entry_type' => ClubHistoType::class,
'entry_options' => [
'label' => false
],
'by_reference' => false,
'allow_add' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Classement::class,
]);
}
Here is my ClubHistoType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('position', null, [
'label' => false
])
->add('matches', null, [
'label' => false
])
->add('victories', null, [
'label' => false
])
->add('draws', null, [
'label' => false
])
->add('losses', null, [
'label' => false
])
->add('goals', null, [
'label' => false
])
->add('points', null, [
'label' => false
])
->add('season', EntityType::class, [
'class' => Season::class,
'choice_label' => 'year',
'label' => false,
'placeholder' => '-',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->orderBy('s.year', 'ASC');
}
])
->add('club', EntityType::class, [
'class' => Club::class,
'choice_label' => 'name',
'label' => false,
'required' => false,
'placeholder' => '-',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.name', 'ASC');
}
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ClubHisto::class,
]);
}
And my function to create a new country table in my controller :
/**
* #Route("back/table/new", name="new_table")
*/
public function createCountryTable(Request $request, EntityManagerInterface $manager) {
$countryTable = new Classement();
$clubHistoriques = new ClubHisto();
$countryTable->addClubHisto($clubHistoriques);
$form = $this->createForm(ClassementType::class, $countryTable);
dd($form);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
// foreach($clubHistoriques as $histo) {
// $histo->setSeason($countryTable->getSeason());
// }
$manager->persist($countryTable);
$manager->flush();
return $this->redirectToRoute('edit_table', ['id' => $countryTable->getId()]);
}
return $this->render('back/createTables.html.twig', [
'form' => $form->createView()
]);
}
But as soon as I call the form, it gives me this error :
Entity of type "App\Entity\Classement" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?
I don't really understand because I have other forms that work exactly like this one and everything works fine...
I know this subject has been asked many times, but the solutions never worked that were given never really worked.
Your help is highly appreciated !
Try just instantiate your form without $countryTable
$form = $this->createForm(ClassementType::class);
You already bind the form to the data so you shouldn't need to pre-instantiate it.
Then handle the data inside your if (form->isSubmitted()) logic
/* #var $countryTable \App\Entity\Classement */
$countryTable = $form->getData();
$clubHistos = $countryTable->getClubHistos();

Hide a label from an Embedded Form Type in a Collection type

I have a form collection type called ContactType which has a CollectionType field called contact2Companies with entry_type AddCompanyFromContactType. AddCompanyFromContactType has a field company which uses another form type CompanyNameType.
In the frontend, we see the label Unternehmen from ContactType. In the grey box, first line, is the label Name des Unternehmens which comes from AddCompanyFromContactType and under this the label Name which comes from CompanyNameType.
How can I hide/remove the label Name?
Code Excerpts
ContactType
$builder
->add('contact2Companies', CollectionType::class, [
'required' => false,
'label' => 'contact.companies',
'entry_type' => AddCompanyFromContactType::class,
'allow_add' => true,
'allow_delete' => true,
])
AddCompanyFromContactType
$builder
->add('company', CompanyNameType::class, [
'required' => true,
'label' => 'company.name',
'attr' => [
'autofocus' => true,
],
])
CompanyNameType
$builder
->add('name', TextType::class)
Simply add this to the field name of CompanyNameType:
$builder
->add('name', TextType::class, [
'label' => false,
])
CollectionType has 2 levels of labels. One for all inputs, and the other for each input in Collection.
$builder->add('name', CollectionType::class, [
'entry_type' => MoneyType::class,
'label' => 'Top Name Label',
'entry_options' => [
'label' => 'Name Label',
],
])
label on the first level is rendered in <legend> tag and it is one for all inputs.
label under entry_options is for each row.
According to your needs, you can set them false.

Symfony 3 Form using RepeatedType with FOSRest

How can you submit a RepeatedType using FOSRest?
I have added the following formtype:
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', TextType::class);
$builder->add('firstName', TextType::class);
$builder->add('lastName', TextType::class);
$builder->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'The password fields must match.',
'required' => true
]);
}
And this is the data I submit:
array(1) {
["user"]=>
array(4) {
["email"]=>
string(21) "Testuser#test.be"
["firstName"]=>
string(4) "Test"
["lastName"]=>
string(9) "User"
["password"]=>
array(2) {
["first"]=>
string(8) "password"
["second"]=>
string(8) "password"
}
}
}
Everything works, except the repeatedType throws: This form should not contain extra fields.
What's the correct format to submit to a repeatedType? The docs aren't really clear on this ...
Thank you
try to change this:
$builder->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'The password fields must match.',
'required' => true
]);
to this:
$builder->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password')
]);