I want to load two choice list, the second one load only some values based on the first choice. But my problem comes first... how to load the EntityType values in the first list from a class that is not directly related to the current class (the form type class).
->add(
'cliente',
EntityType::class,
array(
'class' => 'AppBundle:Cliente',
'choice_label' => 'nombre',
)
)
But there is no one 'cliente' field in this entity, so it throws the message you know...
Neither the property "cliente" nor one of the methods "getCliente()",
"cliente()", "isCliente()", "hasCliente()", "__get()" exist and have
public access in class "AppBundle\Entity\Envio".
Please, do you know how to solve this issue? Any help is welcome!
According to your error your form is for the entity Envio. If you want to create an EntityType choicelist based on the Cliente entity, you'll need a doctrine relation in your Envio class:
class Envio
{
/*
* #ORM\ManyToOne(targetEntity="Cliente")
*/
protected $cliente;
The error has no relation to your question about having 2 choicelists and changing the choices of your second list based on the first choice. You're probably best off using javascript for that and you'll have many choices from AJAX to limiting the choices on the fly depending on the value or innerText of the .
For that error you need to make the field as 'mapped' => false, so:
->add(
'cliente',
EntityType::class,
array(
'class' => 'AppBundle:Cliente',
'choice_label' => 'nombre',
'mapped' => false
)
)
Then for getting the property in the controller you must do:
$cliente = $form->get('cliente')->getData();
Hope this help you.
Related
SETUP:
Main entity with a related entity with ManyToOne relation.
Main entity has a formType with the related entity added.
The related entity is a big object with a lot of fields and related objects, and very slow to get.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('relatedEntity', 'entity', array(
'class' => 'ProjectName\RelatedEntityBundle\Entity\RelatedEntity',
'query_builder' => function (EntityRepository $er) {
$queryBuilder = $er->createQueryBuilder('relatedEntity');
$queryBuilder->resetDQLPart('select');
$queryBuilder->resetDQLPart('from');
$queryBuilder->select('relatedEntity')
->distinct(true)
->from('ProjectNameRelatedEntityBundle:RelatedEntity', 'relatedEntity');
return $queryBuilder;
},
....
....
}
Template:
(relateEntity has a __toString() function defined to show its name).
{{ form_label(form.relatedEntity) }}
{{ form_widget(form.relatedEntity) }}
{{ form_errors(form.relatedEntity) }}
QUESTIONS:
The Main entity as shown above, will get all objects and pass them
to the template. It works perfectly but it is very slow since the
related entity objects are big and the query may take more 10
seconds to finish hydrating all the object data.
How could I select only some fields from my related entity and show them in the template without getting all objects hydrated?
Is it possible to use the choice option or another type instead of
the default entity type to get only some fields of the related
entity and show them in the template?
How could I build a custom query hydrated as a simple array of key value, and pass that array to the formType, to the queryBuilder of my related entity field?
Finally, in case its not possible to get only some fields to be
shown in the template, should I avoid symfony 2 forms and make a
custom management of the related entity?
TESTS:
I cant seem to build the form with the choice type by passing just an array to show a selectBox with the id and name of my related entity in the template. I always get the same error, asking me to insert an array of entity objects in that choiceS option.
Lets look at some examples at the formType, buildForm function of the main entity:
WORKS, default Symfony 2 generated code with null type:
->add('relatedEntity', null, array('label'=> 'relatedEntity'))
WORKS, with 'entity' type and a simple queryBuilder:
->add('relatedEntity', 'entity', array(
'class' => 'ProjectName\RelatedEntityBundle\Entity\RelatedEntity',
'query_builder' => function (EntityRepository $er) {
$queryBuilder = $er->createQueryBuilder('relatedEntity');
$queryBuilder->resetDQLPart('select');
$queryBuilder->resetDQLPart('from');
$queryBuilder->select('relatedEntity')
->from('ProjectNameRelatedEntityBundle:RelatedEntity', 'relatedEntity');
return $queryBuilder;
},
'property' => 'descripcion'
))
DOESNT WORK with 'choice' type, with 'choices' option passing an array of values:
$arrayValues = array('1'=>'name1', '2'=>'name2', '3'=>'name3');
->add('relatedEntity', 'choice', array(
'choices' => $arrayValues,
'multiple' => false,
'label'=> 'relatedEntity'
))
DOESNT WORK with 'entity' type, with 'choices' option passing an array of values:
$arrayValues = array('1'=>'name1', '2'=>'name2', '3'=>'name3');
->add('relatedEntity', 'entity', array(
'class' => 'ProjectName\RelatedEntityBundle\Entity\RelatedEntity',
'choices' => $arrayValues ,
'multiple' => false,
'label'=> 'relatedEntity'
))
I have also tested trying to hack the choices input requeriment by building an array of objets of my related entity, but it asks me to persists those entities before being sent to the choice type.
The problem is your form element which requires its content to be an entity, which is an instance of class ProjectName\RelatedEntityBundle\Entity\RelatedEntity, but you pass an array as choices:
$arrayValues = array(
'1'=>'name1',
'2'=>'name2',
'3'=>'name3'
);
On the other hand, when you use a choice-element and add the array, your form element will return a string, whereas your entity requires relatedEntity to be an instance of the above mentioned class.
Either way, you have to ensure the data you add or retrieve from the element matches your requirements.
What you can do, is make it a choice-element and remove the class-restriction (as you have tried). Then, to ensure it will return an entity-instance rather than a string you can use Form Events. You could use FormEvents::SUBMIT or FormEvents::PRE_SUBMIT to check which entity name was selected and perform a query to fetch the corresponding entity, e.g. something like:
$objectRepository->findEntityBy(array('name' => $name));
I'd like to display a dynamic list of checkboxes in a form.
So far, I built a form embedding a static list of checkboxes, and I created a Tag entity for different values in different languages and populated the database. I'd like to replace the static checkboxes by a dynamic list based on the Tag entity.
The documentation says I should use the ChoiceListInterface. But it is really poorly documented. Would you have an example or a global logic explanation to help me ?
You can extend LazyChoiceList abstract class and implement loadChoiceList() method, create a service of it, inject it to the form and set it as choice_list option.
Finally, I used an entity field type :
->add('tags', 'entity', array(
'class' => 'bndMyBundle:Tag',
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('t')
->orderBy('t.en', 'ASC');
},
'expanded' => true,
'multiple' => true,
'property' => 'en',
))
Then, I just need to replace the 'en' value by the user's current locale to choose the right language.
I am having some trouble with a many to many relationship. I have Users and Assets. I would like to be able to assign users to an asset on the asset page.
The code below displays a list of users when creating/editing an asset, however changes made to the user checkboxes do not save, while the rest of the data is persisted.
If I add an entry to users_assets through the mysql client, these changes are shown in the asset list.
User
class User extends BaseUser
{
/**
* #ORM\ManyToMany(targetEntity="Asset", inversedBy="users")
*/
private $assets;
}
Asset
class Asset
{
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="assets")
*/
private $users;
}
AssetType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$form = $builder
->add('users', null, array(
'expanded' => true,
'multiple' => true
))
->getForm();
return $form;
}
For some reason I had to switch the doctrine mappings to get this to work:
Asset:
/**
* #ORM\ManyToMany(targetEntity="Adaptive\UserBundle\Entity\User", inversedBy="assets")
* #ORM\JoinTable(name="user_assets")
*/
private $users;
User:
/**
* #ORM\ManyToMany(targetEntity="Splash\SiteBundle\Entity\Asset", mappedBy="users")
*/
private $assets;
Now when I save the asset it saves the users associated. I did not need to define builder->add as an entity or collection. I simply pass it null and it uses the mapping info to fill in the entity info:
AssetType:
->add('users', null, array('expanded' => "true", "multiple" => "true"))
Not exactly sure why I needed to have the inversedBy and JoinTable info on the Asset vs The User but it seems to be working now!
Thanks For The Suggestions!!!
Weird enough I faced the same problem in 2016 and still had hard time finding the solution. I will share it for future googlers:
The problem is that what symfony essentially does when you save the form is this:
$asset->getUsers()->add($user)
And because you're on the inverse side of the relation it won't persist your changes.
What you really need is to make so that it calls this:
$asset->addUser($user)
Where addUser() is defined the following way on the Asset entity:
public function addUser(User $user)
{
//add to the inverse side
$this->users->add($user);
//add on the owning side (only this is persisted)
$user->addAsset($this); //$user->assets->add($asset);
}
So in order to make symfony use that $asset->addUser() method, you should set
'by_reference' => false
on your users field for AssetType form.
More about this setting here http://symfony.com/doc/current/reference/forms/types/form.html#by-reference
Remember you also need to define removeUser() method in the same way (so that it removes entity from the owning relation)
Not exactly sure why I needed to have the inversedBy and
JoinTable info on the Asset vs The User but it
seems to be working now!
The reason why your changes has been ignored is that doctrine persists only changes by the owning side of a relation (like #Florian said).
This is the link to Doctrine's documentation where this behaviour is explained: http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html
At first you should drop backslash prefix in annotations (see notice here).
And you need to use entity field type:
$builder->add('users', 'entity', array(
'class' => 'AdaptiveUserBundle:User',
'expanded' => true,
'multiple' => true,
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.username', 'ASC');
},
));
You need to use 'collection' field type in your form.
$builder->add('users', 'collection', array(
'type' => new UserType(),
'prototype' => true,
'allow_add' => true,
'allow_delete' => true
));
You need to create the UserType() form first obviously.
Here is all the info you will need, including code samples:
http://symfony.com/doc/current/cookbook/form/form_collections.html
http://symfony.com/doc/current/reference/forms/types/collection.html
I am using Symfony 2 with doctrine. I currently have an entity called Worker and in the Worker entity there is a Many To One relationship with a User entity.
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
There are more entities like Worker as well such as Manager and such. I want to create a form that creates a Job entity. In the form I am trying to create a select option that selects a Worker, but the worker's name is stored in the user database. Is there any way to print the worker's name from the user database in the form options.
$builder->add('workers','entity', [
'label' => 'Workers:',
'property' => 't.user.firstName',
'empty_value' => 'Choose a Worker',
'class' => 'Company\CompanyBundle\Entity\Worker',
'query_builder' => function (\Company\CompanyBundle\Repository\WorkerRepository $repository) {
return $repository->createQueryBuilder('t')
->add('orderBy', 't.user.firstName ASC');
}
]);
Any ideas?
I think that it would be enoough to do something like this:
$builder->add('workers', 'entity', array(
'class' => 'Company\CompanyBundle\Entity\Worker',
) );
Besides, you should implement a "__toString()" method in your Worker entity where you would return whatever you want to show (in this case, the worker name), so your __toString method in the Worker entity would be something like this:
function __toString() {
return $this->getName();
}
It's the way I usually implement this kind of relations, I hope it helps!
If you prefer, you could do this other option:
$builder->add('workers', 'entity', array(
'class' => 'Company\CompanyBundle\Entity\Worker',
'property' => 'property_name'
));
If you defined the option "property" you don't need implement the "_toString()" in the entity class
I'm using a class form in Symfony2 Beta3 as follows:
namespace Partners\FrontendBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class ConfigForm extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('no_containers', 'choice', array('choices' => array(1 => 'yes', 0 => 'no')));
...
I want to translate the 'yes' and 'no' options, but I don't know how to use the translator here.
You can use the translation resources as usual. This worked for me:
$builder->add('sex', 'choice', array(
'choices' => array(
1 => 'profile.show.sex.male',
2 => 'profile.show.sex.female',
),
'required' => false,
'label' => 'profile.show.sex.label',
'translation_domain' => 'AcmeUserBundle'
));
And then add your translations to the Resources->translations directory of your Bundle.
Update from #CptSadface:
In symfony 2.7, using the choice_label argument, you can specify the translation domain like this:
'choice_label' => 'typeName',
'choice_translation_domain' => 'messages',
Without specifying the domain, options are not translated.
I searched a while to find an answer, but finally I found out how Symfony translates form content. The easiest way in your case seems to be to just add a translation for "yes" and "no" by adding a YAML or XLIFF translation file to your application (e.g. app/Resources/translations/messages.de.yml) or your bundle. This is described here:
http://symfony.com/doc/current/book/translation.html
The problem - in my opinion - is that you don't seem to be able to use custom translation keys. The guys from FOSUserBundle solve this (or a similar) problem with "Form Themes" (http://symfony.com/doc/2.0/cookbook/form/form_customization.html). Here are two significant lines of code to achieve the usage of the form element id as translation key:
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/views/Registration/register_content.html.twig#L1 / https://github.com/FriendsOfSymfony/FOSUserBundle/blob/50ab4d8fdfd324c1e722cb982e685abdc111be0b/Resources/views/form.html.twig#L4
By adding a Form Theme you're able to modify pretty much everything of the forms in the templates - this seems to be the right way of doing this.
(Sorry, I had to split two of the links b/c I don't have enough reputation to post more than two links. Sad.)
In symfony 2.7, using the choice_label argument, you can specify the translation domain like this:
'choice_label' => 'typeName',
'choice_translation_domain' => 'messages',
Without specifying the domain, options are not translated.
CptSadface's answer was what helped me with translating my entity choices.
$builder
->add(
'authorizationRoles',
null,
[
'label' => 'app.user.fields.authorization_roles',
'multiple' => true,
'choice_label' => 'name', // entity field storing your translation key
'choice_translation_domain' => 'messages',
]
);