Symfony 2 form + showing data from a relation - forms

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

Related

How to get choice options from another entity in Symfony

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.

QueryBuilder in the BuildForm

I have An entity Equipe that have OneToMany relation with entity Employe. It means that a team have many employees. So in EquipeType I tried to show list of employees and a chekckbox infront each of them if I want to add an employee in that team I only have to check it. That works but my problem is how to show the name, id and all other properties and put them in table. I need a for statement but what to put in it? thanks this is how I get it in my twig
My FormBuilder
->add('date')
->add('nom')
->add('employes', 'entity', array(
'class' => 'OCUserBundle:Employe',
'property' => 'username',
'multiple' => true,
'expanded' => true,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.id', 'ASC');
},
My twig
{{form_widget(form.employes)}}
Since Symfony 2.7 property in options is changed to choice_label and could be a callback like this
'choice_label' => function ($employee) {
return $employee->getName().'/'.$employee->getId();
}
This is explained in the EntityType field documentation
If you use Symfony earlier than the 2.7 version you should declare a method in Employee, something like this:
public function getDisplayName()
{
return $this->getName().'/'.$this->getId();
}
and then in options declare property as
'property' => 'display_name'
See the EntityType field documentation for Symfony 2.6 for more information.

Symfony2 form type nested too many queries

I am developing a webshop system and currently I am working at the admin tools. I've got 4 related entities
Articles
stores main article data (name, description)
ArticleSuppliers
stores variants data (articleNumber, price..)
ArticleAttributesValues
stores attributes for each variant (value e.g. red, 40cm)
ArticleAttributes
stores names of attributes (color, height...)
Since it's much easier to edit a product, I would like to merge the forms together which is working.
ArticlesType binds ArticleSuppliersType binds ArticleAttributesValuesType
My FormType: ArticleAttributesValues contains an entity choice of ArticleAttributes
This is working! But there is a huge problem. I display each variant with their attributes so there is a query for each attribute (imagine a product with 20 variants and 10 attributes).
The solution would be easy: I just need to give an array of attributeNames + id to my FormType, but I do not know how this is done.
I would be grateful for every other solution though.
Thank you in advance!
EDIT:
I will try to explain my problem with code:
// controller
$article = $em->getRepository('MyBundle:Articles')->find($id);
$form = $this->createForm(new ArticleType(), $article);
This is my article type:
// articleType
$builder->add('shortName', 'text',
array('label' => false))
->add('shortDescription', 'text',
array('label' => false))
->add('longDescription', 'textarea',
array('label' => false))
->add('variants', 'collection', array('type' => new VariantsType()))
->add('save', 'submit', array('label' => 'Save'));
This relates to VariantsType:
// variantsType
$builder->add('supplierArticleNumber', 'text',
array('label' => false))
->add('price', 'text',
array('label' => false))
->add('variantvalues', 'collection', array('type' => new VariantsvaluesType()));
This relates to VariantsvaluesType, where my choice field is.
// variantsvaluesType
$builder->add('attributeValue', 'text',
array('label' => false))
->add('attributeUnit', 'text',
array('label' => false, 'required' => false))
->add('attrName', 'entity', array(
'class' => 'MyBundle:ArticleAttributes',
'property' => 'attributeName',
));
This choice field is the same (of course there are changes sometimes), so it would be unnecessary to query it X-times...
My idea was to load all attributeNames in my controller and pass it via $options to variantsvaluesType, but this is not working...
I see, well maybe you can try the next idea. Create a service with a get function for each collection to load, then in the constructor of the service you can load all the list only one time. Then you can use that service wherever you needed. It must work like a singleton. The inconvenience is that all those list should be loaded all the time in memory, but nothing is for free. Will be something like this:
use Doctrine\ORM\EntityManager;
class CollectionsService
{
private $em;
private $collectionOne;
private $collectionTwo;
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
$this->collectionOne= $this->em->getRepository('AppBundle:CollectionOne')->findAll();
$this->collectionTwo= $this->em->getRepository('AppBundle:CollectionTwo')->findAll();
}
public function getCollectionOne(){
return $this->collectionOne;
}
public function getCollectionTwo(){
return $this->collectionTwo;
}
}
Also must work something in the functions like next one , and don't be necessary do the load in the constructor.
public function getCollectionOne(){
if($this->collectionOne == null){
$this->collectionOne= $this->em->getRepository('AppBundle:CollectionOne')->findAll();
}
return $this->collectionOne;
}
Then expose the class as a service in services.yml
parameters:
collection.controller: AppBundle\Services\CollectionsService
services:
collections.service:
class: "%collection.controller%"
arguments:
entityManager: "#doctrine.orm.entity_manager"
And finally just use the service in the controller or the form to update the data $options['data'].
$collectionOne = $this->get('collections.service')->getCollectionOne();
I hope this help you.

how to add where clause in formtype symfony2

its been days now that am looking for the 'how' of this problem. I just learned symfony2 and i cant find a way to this, i've tried different stuff found on the net but i could not get any to work.
Here is my problem, I have a form PictureType in which i include another entity Collection (which is a collection of picture or if you prefer an album of picture).
When the user uploads his picture he needs to select in which album he wants to put it.
My problem is that I cant figure out how to tell in my PictureType to select ONLY the collections of the current user.
here is my pictureType
$builder
->add('file')
->add('collection', 'entity', array(
'class' => 'AppPictureBundle:Collection',
'property' => 'name'
));
I want to insert after property something like this
Where 'user_id' = $this->getUser()
I have a manyToOne on Collection target User and a ManyToOne on picture target collection.
There the query_builder option for that:
$builder
->add('file')
->add('collection', 'entity', array(
'class' => 'AppPictureBundle:Collection',
'property' => 'name',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.user = :user')
->setParameter('user', $this->getCurrentUser());
},
));

Symfony2 Doctrine2 Many To Many Form not Saving Entities

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