Passing Arguments into Symfony 3 Form configureOptions - php

I have the following form:
class TestFormType extends AbstractType
{
protected $testArgument;
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (isset($options['testArgument'])) {
$this->testArgument = $options['testArgument'];
}
$builder->add('textField', 'Symfony\Component\Form\Extension\Core\Type\TextType');
}
public function configureOptions(OptionsResolver $optionsResolver)
{
$optionsResolver->setRequired('testArgument');
$optionsResolver->setDefaults(
array(
'data_class' => get_class($this->testArgument)
)
);
}
}
I am passing the value for the testArgument attribute via form options (Symfony 3 modifications), but when is comes to get the class name of the attribute to set the 'data_class' inside configureOptions method, it is always null. Basically I need to depend on the form type class attribute inside the configureOptions method.Can someone please help me out here to the right direction ?

I had to pass the dependency in configureOptions method from the form factory create method itself:
$form = $this->factory->create(
'app\TestBundle\Form\Type\TestFormType',
$this->testArgument,
array(
'data_class' => get_class($this->testArgument)
)
);
as it would not be set by the default settings in the form type and had to refactor the form type class as follows:
class TestFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('textField', 'Symfony\Component\Form\Extension\Core\Type\TextType');
}
}

You should pass the *Type __constructor for use to
use App\Entity\Blog;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class BlogType extends AbstractType {
private $someDependency;
public function __construct($someDependency)
{
$this->someDependency = $someDependency;
}
// ...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'empty_data' => new Blog($this->someDependency),
]);
} }

Related

Symfony 3: Testing form type submission with an entity with default values

In Symfony's How to Unit Test your Forms document is quite straight forward.
But I got a problem.
I have an doctrine entity with some default values. And a form type which is bound to the model. I mean 'data_class' is the entity. In this case, when I call $form->submit(); and $form->isSynchronized(), then the default values are gone. And the $this->assertEquals($object, $objectToCompare); fails.
How can I test a form type which is bound to a model class with some default values?
I suspect this is a bug. When the form type instance is created in $this->factory->create(TestedType::class, $objectToCompare);, the default values of $objectToCompare should be applied.
Or, should I create an extension to handle that?
Here're the example codes. To simplify the code, I changed the doctrine entity into the simple model.
The model
For comparing, I added 2 properties: one with the default value and the other without.
// AppBundle/Model/TestObject.php
namespace AppBundle\Model;
class TestObject
{
private $test;
private $test2 = 'default value';
public function getTest()
{
return $this->test;
}
public function setTest($test)
{
$this->test = $test;
}
public function getTest2()
{
return $this->test2;
}
public function setTest2($test2)
{
$this->test2 = $test2;
}
}
The form type
It is straight forward.
adding 2 text type fields and 1 submit type field
set default option 'data_class' to the model
// AppBundle/Form/Type/TestedType.php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Model\TestObject;
class TestedType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('test', TextType::class);
$builder->add('test2', TextType::class);
$builder->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults(array(
'data_class' => TestObject::class,
));
}
}
The testing
It's little modified version of How to Unit Test your Forms.
$object is populated with property access
checking fields existence is omitted.
// tests/AppBundle/Form/Type/TestedTypeTest.php
namespace Tests\AppBundle\Form\Type;
use AppBundle\Form\Type\TestedType;
use AppBundle\Model\TestObject;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\PropertyAccess\PropertyAccess;
class TestedTypeTest extends TypeTestCase
{
public function testSubmitValidData()
{
$formData = [
'test' => 'test',
];
$objectToCompare = new TestObject();
$form = $this->factory->create(TestedType::class, $objectToCompare);
$object = new TestObject();
$propertyAccessor = PropertyAccess::createPropertyAccessor();
foreach ($formData as $key => $value) {
$propertyAccessor->setValue($object, $key, $value);
}
$form->submit($formData);
$this->assertTrue($form->isSynchronized());
$this->assertEquals($object, $objectToCompare);
}
}

Symfony 2.8/3.0 - Pass array to another formType from a collectionType

I could get this to work prior to v2.8 but as symfony now uses fully qualified class names name i'm unsure sure how to proceed.
I can pass an array (to populate a choice field) to a form without issue but if there is an another formType added via a collectionType how can a pass the array?
BTW - the array is gathered from data from a custom annotations - NOT an entity
Heres my code:
PageType.php
<?php
namespace Prototype\PageBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
const ActiveComponentsType = 'Prototype\PageBundle\Form\ActiveComponentsType';
const collectionType = 'Symfony\Component\Form\Extension\Core\Type\CollectionType';
class PageType extends AbstractType
{
private $cmsComponentArray;
public function __construct($cmsComponentArray = null)
{
$this->cmsComponentArray = $cmsComponentArray;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$cmsComponentArray = $options['cmsComponentArray'];
$componentChoices = array();
foreach($cmsComponentArray as $cmsComponent){
$componentChoices[$cmsComponent['name']] = $cmsComponent['route'];
}
//correct values are shown here
//print_r($componentChoices);
$builder
->add('title')
->add('parent')
->add('template')
->add('active')
->add('content')
->add('components', collectionType, array(
'entry_type' => ActiveComponentsType, // i want to pass $cmsComponentArray to ActiveComponentsType
'allow_add' => true,
'allow_delete' => true
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Prototype\PageBundle\Entity\Page',
'cmsComponentArray' => null
));
}
}
The ActiveComponentsType embeded form does work - except I'm unsure how to pass the $componentChoices array to it.
Any ideas?
The collection type defines the entry_options option which is used to configure the options that are passed to the embedded form type.

The form's view data is expected to be an instance of another class error creating form instance of another entity

I get the following error when trying to create a form from another entity to pass through to my view.
I have two entities in this context CourseGuide and CourseGuideRow and I would like to pass through a form view of CourseGuideRowType to my view - how can I do this?
The form's view data is expected to be an instance of class
CRMPicco\CourseBundle\Entity\CourseGuide, but is an instance of class
CRMPicco\CourseBundle\Entity\CourseGuideRow. 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
CRMPicco\CourseBundle\Entity\CourseGuideRow to an instance of
CRMPicco\CourseBundle\Entity\CourseGuide.
This is my controller:
// CourseGuideController.php
public function viewAction(Request $request)
{
if (!$courseId = $request->get('id')) {
throw new NotFoundHttpException('No Course ID provided in ' . __METHOD__);
}
$resource = $this->get('crmpicco.repository.course_guide_row')->createNew();
$form = $this->getForm($resource);
// ...
}
My Symfony FormBuilder class:
// CourseGuideRowType.php
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\FormBuilderInterface;
class CourseGuideRowType extends AbstractResourceType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('channel', 'crmpicco_channel_choice', array('data_class' => null))
->add('name', 'text')
->add('courses', 'text')
;
}
/**
* #return string name
*/
public function getName()
{
return 'crmpicco_course_guide_row';
}
}
I have tried the data_class => null suggestion mentioned elsewhere, but this has no effect.
If I pass through the data_class like this:
$form = $this->getForm($resource, array('data_class' => 'CRMPicco\CourseBundle\Entity\CourseGuideRow'));
I then get this:
Neither the property "translations" nor one of the methods
"getTranslations()", "translations()", "isTranslations()",
"hasTranslations()", "__get()" exist and have public access in class
"CRMPicco\CourseBundle\Entity\CourseGuideRow".
Why is this? There are translations attached to the CourseGuide entity but not the CourseGuideRow.
try to add this function in your FormType:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'YourBundle\Entity\YourEntity',
));
}
And don't forget the specific use:
use Symfony\Component\OptionsResolver\OptionsResolver;
EDIT
In native Symfony (with the Form component):
public function showAction()
{
/.../
$entity = new YourEntity();
$form = $this->createForm('name_of_your_form_type', $entity);
# And the response:
return $this->render('your_template.html.twig', ['form' => $form->createView()]);
}

Propel form returns object instead of id

I get this when the form is saved.
Object of class Business\CompaniesBundle\Model\Province could not be converted to int in /Library/WebServer/Documents/business/src/Business/CompaniesBundle/Model/om/BaseCompany.php
<?php
namespace Business\CompaniesBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Business\CompaniesBundle\Model\IndustriesQuery;
use Business\CompaniesBundle\Model\Province;
use Business\CompaniesBundle\Model\ProvinceQuery;
class CompanyType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('company_name');
$builder->add('company_description','textarea');
$builder->add('company_address');
$builder->add('province_id', 'model', array(
'class'=>'Business\CompaniesBundle\Model\Province'
//'query'=> ProvinceQuery::create()->orderByProvinceName()
));
$builder->add('industries', 'model', array(
'class'=>'Business\CompaniesBundle\Model\Industries',
'query'=> IndustriesQuery::create()->orderByIndustryName(),
'multiple'=>true,
'expanded'=>true,
'required'=>true
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array('data_class'=>'Business\CompaniesBundle\Model\Company'));
}
public function getName() {
return 'company';
}
Any idea of what is the issue here?
Fixed it by changing
$builder->add('province_id', 'model', array(
'class'=>'Business\CompaniesBundle\Model\Province'
));
to
$builder->add('province', 'model', array(
'class'=>'Business\CompaniesBundle\Model\Province'
));
setProvince method of the Company object expected an object of Province type

Embedding a dynamically generated form into another

I use Symfony2.1's FormBuilder to create a dynamic form in the controller:
$form = $this->createFormBuilder($defaultData);
$form->add('field','text');
I would like to embed another form in the same way and embed it with the main form.
$subForm = $this->createFormBuilder();
$subForm->add('subfield','text');
// Does not work
$form->add('subform', $subForm);
Unfortunately this setup does not work properly. I could not find any way how to add a dynamically generated subform into a dynamically generated form, like the example above.
What's the function call I am missing?
The exception thrown by the example code above is
UnexpectedTypeException: Expected argument of type "string or
Symfony\Component\Form\FormTypeInterface",
"Symfony\Component\Form\Form" given.
I would create two Form Clases in two separate fields:
<?php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class FirstFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('field', 'text')
->add('subform', new SecondFormType());
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\DemoBundle\Entity\FirstEntity'
));
}
public function getName()
{
return 'first';
}
}
<?php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SecondFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('subfield', 'text');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\DemoBundle\Entity\SecondEntity',
));
}
public function getName()
{
return 'second';
}
}
After this creation you can create your form from any Controller with this line for creation forms:
$form = $this->createForm(new FirstFormType());
or for edition forms:
$form = $this->createForm(new FirstFormType(), $first);
In your example code you are trying to assig to a field an entire form instead of a Form Type.
Hope it helps
Recommended symfony-way is to create separate FormType classes, as mentioned above. But if you really want to..
You can add dynamic subform into form builder, by calling
$formBuilder->add($subform /* FormBuilder */);
This subform will have name "form" if created with $this->createFormBuilder(); , that means that you can not add two or more subforms created this way - newer one will overwrite previous with same name.
If you need to add multiple sub-forms, you have to create their builders with
$this->get('form.factory')->createNamedBuilder($uniqName)
Without making classes... inside a Controller Action:
$subFormBuilder = $this->createFormBuilder(
null /* default data */,
['label' => 'Sub Form'] /* options */
)
->add('name');
$form = $this->createFormBuilder()
->add($subFormBuilder)
->add('number')
->getForm();

Categories