Embed form into another form linked to entity in Symfony2 - php

I need to embed one form into another form and I'm doing as follow:
use Symfony\Component\Form\AbstractType,
Symfony\Component\Form\FormBuilderInterface,
Symfony\Component\OptionsResolver\OptionsResolverInterface,
Common\CommonBundle\Form\AddressExtraInfoType;
class StandardAddressType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('country', 'entity', array( ... ))
->add('state', 'entity', array( ... ))
->add('city', 'entity', array( ... ))
->add('extra_info', new AddressExtraInfoType());
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Common\CommonBundle\Entity\StandardAddress'
));
}
public function getName() {
return 'common_commonbundle_standard_address';
}
}
Since the main form need to be attached to the 'data_class' => 'Common\CommonBundle\Entity\StandardAddress' then when I try to get the form this error come up:
Neither the property "extra_info" nor one of the methods "getExtraInfo()", "isExtraInfo()", "hasExtraInfo()", "__get()" exist and have public access in class "Common\CommonBundle\Entity\StandardAddress"
How I can fix this? How I can embed the second form into the first one without get this eror?

try this:
$builder->add('extra_info', new AddressExtraInfoType(), array('mapped' => false));
You don't have field extra_info in class Common\CommonBundle\Entity\StandardAddress so you must use non mapped field in form type

Finally and after read my code once again I found where the error was. In my AddressExtraInfoentity I had address_extra_info and of course changing that to extra_info fix the issue, anyway thanks for your reply

Related

Symfony Forms, formtype label based on a property value of the data object

Is it possible to set the label value of a form type to be a property value that is available in the data object?
My FormType class:
class ShapeFractionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('value', NumberType::class, [
'label' => 'name'
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => ShapeFraction::class,
]);
}
}
The ShapeFraction data class:
class ShapeFraction
{
public string $name;
public string $type;
public float $value;
// ...
}
Rendering the form:
$shapeFraction = new ShapeFraction('A', 'length', 10);
$form = $formFactory->create(ShapeFractionType::class, $shapeFraction);
// Render using Twig...
The result is a simple form containing a single input with 'name' as label. Is it possible to use the property value of ShapeFraction::$name as the label of the field? Result will become like this: A: <input..>.
I would like to achieve this without using the object data directly in Twig or something.
Thanks in advance!
You can access the data that you pass to your form using $options['data'].
That means you can do this:
/** #var ShapeFraction $shapeFraction */
$shapeFraction= $options['data'];
$builder->add('value', NumberType::class, [
'label' => $shapeFraction->name
]);

Symfony - Multiple Fields for one Entity Attribute

I have three three select fields for one entity attribute. As the picture below shows.
Is there a way to detect which of the select fields is used; then get its value and map it with the corresponding attribute?
And is it possible to send parameters to a form type (in this example TestType , please see below). I am trying to make it generic and re-usable for other attributes.
Here is what I have up to now.
MyForm.php
<?php
namespace MyBundle\Form;
use MyBundle\Form\Type\TestType;
use ..etc
class MyForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class)
->add('D1', TestType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\Project'
));
}
public function getBlockPrefix()
{
return 'mybundle_project';
}
}
TestType.php
<?php
namespace MyBundle\Form\Type;
use Sonata\AdminBundle\Form\Type\Filter\ChoiceType;
use ..etc
class TestType extends AbstractType
{
/*
* private $myArray1;
* private $myArray2;
* private $myArray3; numberOfSeletcs
* private $numberOfSeletcs;
Secondary Question: Is it possible to send these values as parameters?
public function __construct($array1, $array2, $array3, $n)
{
$this->myArray1= $array1;
$this->myArray2= $array2;
$this->myArray3= $array3;
$this->numberOfSeletcs= $n;
}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$myArray1 = array('label1'=>'','Value1'=>'Value1', 'Value2'=>'Value2','Value3'=>'Value3');
$myArray2 = array('label2'=>'', 'Value4'=>'Value4','Value5'=>'Value5');
$myArray3 = array('label3'=>'', 'Value6'=>'Value6','Value6'=>'Value6');
$builder
// ...
->add('H1', 'choice', array(
'choices' => $myArray1,
'choice_attr' => ['label1' => ['disabled selected hidden'=>'']]))
->add('H2', 'choice', array(
'choices' => $myArray2,
'choice_attr' => ['label2' => ['disabled selected hidden'=>'']]))
->add('H3', 'choice', array(
'choices' => $myArray3,
'choice_attr' => ['label3' => ['disabled selected hidden'=>'']]));
}
}
Thanks.
To detect which of the select fields is used you have to use Javascript. As you know Symfony is a PHP framework working on the server-side and to detect event on client-side javascript is needed. And for pass parameter to your form type you have the answer in this topic

How to POST nested entities with FOSRest and Symfony Form

Using Symfony 3.2 and FOS REST Bundle I have created some REST endpoints for a resource and use a Symfony Form to define the fields for API DOC. Everything is working fine to this point.
Now I'm trying to improve my schema and added a sub entity (one-to-one) to my resource. I want the main resource to save the sub entity - there is no dedicated endpoint for the sub entity.
I followed the instruction on the Symfony documentation and removed all other fields to isolate any issues.
This is how my form type looks now:
<?php
namespace VendorName\MyBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CountryType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('mySubEntity', EntityType::class, array(
'multiple' => false,
'expanded' => false,
'property' => 'name',
'class' => 'MyBundle\Entity\mySubEntity'));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'VendorName\MyBundle\Entity\Country',
'csrf_protection' => false
));
}
}
Now when I load my api-docs, I receive the error message The option "property" does not exist. Defined options are: "action", "allow_extra_fields", [...].
To be honest, I don't even know if adding the Entity to the form is the right approach to make it show up in the API Docs. Any help in resolving the above issue and / or best practices to achieve this would be appreciated.
EDIT: Thanks to #miikes this error is now resolved and I can see the api doc showing up correctly with the fields of the nested form. However, now my issue is that the form does not populate the sub entity on the parent entity. This seems to be related to the way I modelled the parent-child relationship and I have posted a new question for this issue.
To resolve your error try to use choice_label, instead of property option.
'choice_label' => 'name'
But referring to the documentation, EntityType is a kind of ChoiceType, so using this type, you can only select existing entity, not persist new one.
The easiest and most clear way for creating new entity instance is creating another type class, designed for your entity, and adding the type as the field to your CountryType.
class MyEntityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('some-field', TextType::class)
->add('another-field', TextType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => MyEntity::class
]);
}
}
class CountryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('mySubEntity', MyEntityType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Country::class,
'csrf_protection' => false
));
}
}
So you should pass form data as
['mySubEntity']['some-field'] = 'foo'
['mySubEntity']['another-field'] = 'bar'
Another tip is to use Country::class instead of string 'VendorName\MyBundle\Entity\Country', because in case of renaming class, IDE refactoring should affect on your type.

Overriding form title in Symfony2

I have a Symfony form built with
class BracketCommandForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
//...
}
public function getName() {
return 'bracket_command_form';
}
}
When this form is rendered, it comes with this heading-like <legend>New bracket command</legend> with the actual form below it.
Goal
I want to override the legend, without changing the name of the form or anything else. Can this be done? If so, where?
Any tips appreciated!
That text didn't came from your FormType class, it is passed from the Controller (by default in newAction function)
Try this in your controller
$form = $this->createForm(new BracketCommandForm (), $command, array(
'show_legend' => true, //false if you don't want the legend at all
'label' => 'My Text',
));
Alternatively, I believe you could have this as part of the defaults for that form
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'label' => 'My Text',
));
}

creating types on the fly for collection type

Is it possible to create FormType on the fly and avoid manually creating new form type class.
Currently I have to do this:
MyController.php
$builder = $this->createBuilder(new ParentEntity());
$builder
->add('parentfield1')
->add('parentfield2');
->add('children', 'collection', array('type' => new ChildType());
ChildType.php
class ChildType extends \Symfony\Component\Form\AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('childname1')
->add('childname2');
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'ChildEntity',
);
}
public function getName() {
return 'childentity';
}
}
I would like to avoid creating ChildType class manually and do something like this instead:
MyController.php
$childBuilder = $this->createBuilder(new ChildEntity());
$childBuilder
->add('childfield1')
->add('childfield2');
// how to create FormType from builder???
$childType = $childBuilder->??????;
$builder = $this->createBuilder(new ParentEntity());
$builder
->add('parentfield1')
->add('parentfield2');
->add('children', 'collection', array('type' => $childType);
I tried to get the type with:
$childBuilder->->getFormConfig()->getType();
but that did not work. I get error:
The form's view data is expected to be an instance of class Namespace\Bundle\Entity\ChildEntity, but is an instance of class Doctrine\Common\Collections\ArrayCollection. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms an instance of class Doctrine\Common\Collections\ArrayCollection to an instance of Namespace\Bundle\Entity\ChildEntity.
Currently the best I came up with is to create custom FormType that accepts array of fields as constructor but is there a better way?

Categories