I got a form in Symfony but it won't validate and I don't know why.
From UserBundle/Entity/Task.php
/**
*
* #ORM\ManyToMany(targetEntity="Project\TaskBundle\Entity\Task", inversedBy="users")
* #ORM\JoinTable(name="project_users_tasks")
*
*/
protected $tasks;
From UserBundle/Form/CreateUserFormType.php which is my formbuilder:
$builder->add('tasks', 'entity', array(
'class' => 'TaskBundle:Task',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('b')
->andWhere('b.owner = :owner')
->setParameter('owner', $this->securityContext->getToken()->getUser())
->addOrderBy('b.updated', 'ASC');
},
'expanded' => false,
'multiple' => false
));
The post request in my browser:
------WebKitFormBoundary6IBT2Ycy78N9AI7u
Content-Disposition: form-data; name="createUser[tasks][]"
14
The result I get for the form is an error concerning the tasks:
"This value is not valid"
I got no other validation what so ever. So why can't the task 14(which is clean the dishes for example) be assigned to my user? I mean the id of the task should work or not?
Edit:
Symfony doesn't seem to recognize the data, that's why. A print_r of $form->getData();
[tasks:protected] => Doctrine\Common\Collections\ArrayCollection Object
(
[_elements:Doctrine\Common\Collections\ArrayCollection:private] => Array
(
)
)
But how can that be? I can see that my browser is posting the data.
At first glance, there are two brackets too much in your submitted field name: createUser[tasks][] should be createUser[tasks], since the entity field is a collapsed, single-valued entity field.
Also try to debug Symfony's request object. var_dump($request->request->get('createUser')) should return something like
array(
'tasks' => 14,
)
If, on the other hand, you really want to submit one or more tasks, set the "multiple" option on the entity field to true. Then the request data should be something like
array(
'tasks' => array(
0 => 14,
)
)
Related
I've a Symfony 4 project.
My users can submit holidays.
There are several types of leave (illness, overtime, etc.), and for each of these types, they have a balance.
When they submit a leave, they must choose from the list of leave types the type they wish. But I want to display in this list only those whose balance is greater than 0.
So, in my form I've:
->add('typeConge', EntityType::class, [
'class' => TypeConge::class,
'label' => false,
'placeholder' => "Type d'absence",
'choice_label' => 'nom',
])
And in my User entity, I've the function to take only typeConges with positive balance:
public function getSoldesActif()
{
$soldesTypesConge = $this->soldeConges;
foreach ($soldesTypesConge as $solde) {
if ($solde->getSolde() == 0) {
$soldesTypesConge->removeElement($solde);
}
}
return $soldesTypesConge;
}
But even with the documentation, I don't understand how can I affect this list ?
Can someone help me please ?
You can use the query_builder-option on the entity type to specify which entities are displayed.
It should probably look roughly, something like this:
->add('typeConge', EntityType::class, [
'class' => TypeConge::class,
'label' => false,
'placeholder' => "Type d'absence",
'choice_label' => 'nom',
'query_builder' => function(EntityRepository $repo) {
$builder = $repo->createQueryBuilder('typeConge');
return $builder->where('typeConge.solde > 0');
},
])
The query builder is responsible for which entities are fetched and then displayed in the list. By default it will fetch all entities and with the query_builder-option you can restrict this to your liking using the ORM-query builder from Doctrine.
There is also an example in the docs, you can use as reference: https://symfony.com/doc/current/reference/forms/types/entity.html#ref-form-entity-query-builder
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.
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.
The user has the ability to filter a list of data. For this use case i build a form type "UserFilterType", which look like this.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName', 'text', array('attr' => array('maxlength' => 255)))
->add('roles', 'entity', array(
'class' => 'ApplicationUserBundle:Role',
'property' => 'name',
'label' => false,
'multiple' => false,
'expanded' => false,
'empty_value' => 'msg.role.all',
'translation_domain' => 'role', 'required' => false)
));
}
So far everything works very well. The user can filter the output. But if the user wants to sort the output, eg by first name. A get request executed and filter options lost. I will save the filter options temporary, maybe in a session.
I try the following option:
$session = $this->getRequest()->getSession();
$session->set('userFilter', serialize($form->getData());
.....
$form->setData(unserialize($session->set('userFilter')));
The form fields are filled out correctly, but if i execute the filter again with a post request i get the following error message
"Entities passed to the choice field must be managed. Maybe persist them in the entity manager?"
Does anyone have any idea how I should proceed? I hope I could describe my problem understandable.
Instead of field type 'entity', you might want to use 'choice' and set the querybuilder for the choicelist in the form.
This is caused by the fact, that 'entity' type field expects an Entity as a value. When you unserialize the data, even if it was an Entity from the database, the EntityManager doesn't know this(the unserialized object is just an instance of ApplicationUserBundle:Role, it might not be in the database yet) so this is not a "managed" entity.
You can also add the EntityManager that you found a "lost lamb" and add it back to the manager by:
$filters = unserialize($session->set('userFilter'));
$roles = $filters['roles'];
$entityManager->merge($roles);
This way you tell the EntityManager that this object is already persisted(through ->persist) and EntityManager should treat it as an object fetched from the database
but you need to test it
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