Render a Collection of Text Fields which is passed to Doctrine array field - symfony

In my entity i have an array field:
/**
* #var array
*
* #ORM\Column(name="work_experience", type="array")
*/
private $workExperience;
now i want to render a collection of text fields which will be passed to this array field.
->add('workExperience', 'collection', array(
'type' => 'text',
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
#'by_reference' => false,
'options' => array(
'required' => false,
'attr' => array('class' => 'line-box')
),
))
but now when i render this field, no input is shown? What is my mistake?
{{ form_row(form.workExperience) }}
Thank you

When prototyping, the collection field(s) is only render if your entity has a value assigned to workExperience inside your controller, Otherwise you would need to use javascript to take the prototype info and create the input field(s), this is also true if you want to add new field(s) with or without your entity having any value.
To get the following to render with values
{{ form_row(form.workExperience) }}
You can do something like the following:
public function controllerAction(Request $request)
{
//By populating your entity with values from your database
//workExperience should receive a value and be rendered in your form.
$em = $this->getDoctrine()->getManager();
$entity = $em
->getRepository('yourBundle:entity')
->findBy(...yourParameters...);
$form = $this->createForm('your_form_type', $entity);
...
Or
...
//If you do not have any data in your database for `workExperience`
//then you would need to set it in your controller.
$arr = array('email' => 'name#company.com', 'phone' => '888-888-8888');
$entity->setWorkExperience($arr);
$form = $this->createForm('your_form_type', $entity);
...
Keep in mind that collection are usually used for one-to-many or many-to-many relationships.
Using it for array can be done but there is not much documented on it. While this link is not a perfect fit, the general ideas presented many be helpful: form_collections

Related

How to prefill field of type EntityType from PHP

In my form, I have a field of type EntityClass:
$builder
->add(
'user',
EntityType::class,
[
'required' => false,
'label' => 'User',
'placeholder' => 'Please choose',
'choice_label' => 'email',
'choice_value' => 'id',
'class' => 'AppBundle:User',
]
)
;
This field works fine - until I try to pre-fill it from my PHP code. Then it stays empty, and only shows "Please choose".
Pre-filling looks like this:
$user = $this->userRepository->find(...);
$form->get('user')->setData($user);
But it also does not work if I call ->setData($user->getId()), or even ->setData($user->getEmail()).
So how do I prefill a field of type EntityType?
You should not prefill Form, you should prefill Model, if you need it.
$user = $this->userRepository->find(...);
$entity = new YourEntity();
$entity->setUser($user);
$form = $this->createForm(YourEntity::class, $entity);
And it's not about EntityType. It's about any Type in Symfony - there is no way to bind a default value for them. Data is binded on Model.
UPD from comment: It's not true, that Form could be used without Model. It could be used without Doctrine Entity or any other ORM (or not ORM) Entity. But they still operate with data, i.o. with model.
\Symfony\Component\Form\FormFactoryInterface has definition
public function create($type = 'form', $data = null, array $options = array());
So some kind of $data is always present when you're using Form Component.

How to set a collection size in a symfony2 form

What i want to do is creating 4 form ResponseType inside the form QuestionType() , is it possible to fix the size of the colllection of responses ?
my code for QuestionType :
$builder->add('responses', 'collection', array(
'type' => new ResponseType(),
'allow_add' => true,
'allow_delete' => true,
))
;
I know that question is old. But I just got on it.
You have to use the Count constraint on your collection field.
So in your QuestionType
/**
* #Assert\Count(max=4, maxMessage="You can have max {{ limit }} reponses")
*/
private $responses;

How to create two related radiobuttons?

The code below creates 2 radiobuttons, however they are not related to each other. One is rendered with a name description_form[friend] and the other one with the name - description_form[guide]. How can they be rendered with the same name? The documentation is not clear about this subject.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('friend', RadioType::class, array(
'label' => 'Friend',
'required' => false
))
->add('guide', RadioType::class, array(
'label' => 'Guide',
'required' => false
));
}
Using a list of RadioType is not quite easy, that's why everybody recommends you to use a ChoiceType which dynamically creates a radio list depending on an array of choice data.
When you create a FormTypeInterface, it has to represent (commonly) one field or one sub form in a global form, so each field name has to be unique to be mapped to the corresponding data.
The buildForm method allows to add some sub fields in you FormType, in your case the field holds two sub fields as radio button and each has a specific name, this is intended by default, but you should always keep in mind the global array data you want to deal with.
Here's your example :
class MyCustomFormType extends \Symfony\Component\Form\AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('friend', RadioType::class, array(
'label' => 'Friend',
'required' => false
))
->add('guide', RadioType::class, array(
'label' => 'Guide',
'required' => false
));
}
public function getBlockPrefix
{
return 'my_custom';
}
// ...
}
So this form type data should look like :
$myCustomFormData = array(
'friend' => $friendData,
'guide' => $guideData,
);
And nested in a global form it would be :
$formData = array(
'my_custom' => $myCustomFormData,
);
But you can name the field as you want :
// In a controller extending \Symfony\Bundle\FrameworkBundle\Controller\Controller
$form = $this->createFormBuilder()
->add('custom_field', MyCustomFormType::class)
->getForm();
// Then
$form->setData(array('custom_field' => $myCustomFormData));
Note that currently, since you map "friend" and "guide" data to RadioType they should hold a boolean value as :
$myCustomFormData = array(
'friend' => true, // checked
'guide' => false, // unchecked
);
But how would you unselect a value then ?
You should had a placeholder to do that, and handle it while submission...
Also, changing the name can be done using the finishView method of your type class, it takes the FormView (built view of your type), the form itself and the options as arguments :
public function finishView(FormView $view, FormInterface $form, array $options)
{
$childName = $view->vars['full_name']; // "my_custom" by default
foreach ($view as $childView) {
$childView->vars['full_name'] = $childName;
}
}
But you would also need to add a DataMapperInterface to get back the submitted value to the form type itself instead.
To do all that, you need to know how the Form Component works and it's not easy.
Easy way
So I agree with the other answers, you should use a ChoiceType to get it out-of-the-box.
I assume your custom form type is about choosing either a "friend" or a "guide", so it could look like this :
$builder
->add('fellow', ChoiceType::class, array(
'choices' => array(
'I have a friend' => 'friend',
'I\'d like a guide' => 'guide',
),
'expanded' => true, // use a radio list instead of a select input
// ...
Have a look at the official docs
Then your data will look like :
$form->add('custom_field', MyCustomFormType::class);
$form->setData(array(
'custom_field' => 'friend',
));
When rendered the "friend" choice will be selected and you will be able to change it to "guide".
An array of choices for the choices options takes labels as keys and choice values as values :
<div id="form_custom_field">
<input type="radio" name="form[custom_field]" value="friend" checked="checked">
<label>I have a friend</label>
<input type="radio" name="form[custom_field]" value="guide">
<label>I'd like a guide</label>
...
This is how I do radio buttons in Symfony 2.7 , hope it helps you.
$yes_no = array('1'=>'Yes','0'=>'No');
->add('myfieldname', 'choice',array(
'choices' => $yes_no,
'label'=>'YourLabelGoeshere',
'required'=>true,
'expanded'=>true,
'multiple'=>false,
'placeholder'=>false
))
Perhaps consider using the ChoiceType field.
See here: Documentation
This allows you to output the options as radio buttons if you choose.
RadioType is used internally by ChoiceType. In most use cases you want to use ChoiceType.

Symfony2, Sonata, FormMapper, add hidden field to be handled in PrePersist/PreUpdate

I actually did some tricks so i could be able to persist a user if its ID is passed by an url parameter. (Custom action from user list).
/admin/se/api/bundle/create?user=7
I actually could not find how to send the user entity returned by a findByOne(array('id' => $user_id)) so i guess i'll need to pass the $user_id through a hidden field and handle its value in a PrePersist
Otherwise passing the id that way
->add('user', 'hidden', array('data' => $user_id))
will return an error :
This value is not valid.
Symfony\Component\Validator\ConstraintViolation
Object(Symfony\Component\Form\Form).children[user] = 7
Caused by:
Symfony\Component\Form\Exception\TransformationFailedException
Compound forms expect an array or NULL on submission.
This is my first attempt that is not working :
$container = $this->getConfigurationPool()->getContainer();
$request = $container->get('request');
$user_id = $request->get('user');
if(!empty($user_id)){
$em = $this->getModelManager()->getEntityManager($this->getClass());
$user = $em->getRepository('ApiBundle:User')->findOneBy(array('id' => $user_id));
if($user){
$formMapper
->with('User', array('description' => '<strong>User : </strong>'.$user->getDisplayName()))
->add('user', 'hidden', array('data' => $user_id))
// this of course doesn't work as explained above. How can i have my own hidden input not related to any property
->end();
}
So how would i do that? Any better solution is welcomed.
Well this is the best trick i found. I wish 'sonata_type_model_hidden' has more options. I guess i could do my own custom field to be able to do that. But i'm not sure how and anyway this solution is fast to implement.
$formMapper
->with('Guide', array('description' => '<strong>Guide : </strong>'.$guide->getDisplayName()))
->add('guide', 'sonata_type_model_autocomplete', array(
'property' => array('firstname', 'lastname', 'username', 'email'),
'data_class' => null, // IMPORTANT
'data' => $guide,
'attr' => array('class' => 'sonata-autocomplete-hidden'), // custom class
'label_attr' => array('class' => 'sonata-autocomplete-hidden'), // custom class
)
)
->end();
To hide the field :
.sonata-autocomplete-hidden{
display:none;
}
If you have any better solutions, you're welcome.

formMapper: a field twice in a form

I have a form where I ask the user to select a movie from a list that already exists on the database , and if the film does not exist in he must add another label from input.
When I did this I have an error of course :
$formMapper
->add('movie', 'sonata_type_model', array('label'=>'Select a movie', 'query' => $myData))
->add('movie', 'text', array('label'=>'or grasp one', 'required'=>false));
How do I correct this error?
Add property in your entity and check on add form by your own query like:
/* #var $DM \Doctrine\ORM\EntityManager */
$DM = $this->getDoctrine();
$Result = $DM->getRepository('Traffic\ControlBundle\Entity\Movies')->findBy(array('yourfilters' => $yourfilters));
if(count($Result) == 0){
$formbuilder->add('entityPropertyName','text');
}else{
$formbuilder->add('field','entity', array('class' => 'TrafficControlBundle:Movies'));
}
if movie not exists add text field by that property.
And on submit check if form is valid then set that property value in relational entity.
See this:
$em = $this->getDoctrine()->getManager();
$item->setMovieTitle($this->getRequest()->request->get('movie_name_field'));
$em->persist($item);
$em->flush();
You can map only one field to the form (managed by sonata) and manage the other by your own:
$formMapper
->add('movieList', 'sonata_type_model', array('label'=>'Select a movie', 'query' => $myData, 'mapped' => false))
->add('movie', 'text', array('label'=>'or grasp one', 'required'=>false));
Then In your Controller You can get the user choice:
$movieList = $form->get('movieList');
Then you can do whatever you want (create or update your object as exemple )

Resources