I have 2 entities and manytomany association:
/**
* #ORM\Entity
* #ORM\Table(name="m2m_table1")
*/
class Table1
{
/**
* #ORM\Id
* #ORM\Column(type="integer", nullable=false, options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="Table2", inversedBy="table1", fetch="LAZY", cascade="all")
* #ORM\JoinTable(name="m2m_links",
* joinColumns={#ORM\JoinColumn(name="table1_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="table2_id", referencedColumnName="id")}
* )
*/
private $table2;
...
}
/**
* #ORM\Entity
* #ORM\Table(name="m2m_table2")
*/
class Table2
{
/**
* #ORM\Id
* #ORM\Column(type="integer", nullable=false, options={"unsigned":true})
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="Table1", mappedBy="table2", fetch="LAZY", cascade="all")
*/
private $table1;
...
}
And I want to have opportunity to add inverse entities to owner entities and vice versa. I can add inverse entities to owner entities, but I can't add owner entities to inverse entities.
$table1 = $em->find('XxxM2mBundle:Table1', 1);
$table2 = $em->find('XxxM2mBundle:Table2', 1);
$table2->addTable1($table1);
$em->flush($table2);
Link is not added. Example is simplified, in fact there are 2 forms, 1-st to adjust links for Table1 and 2-nd to adjust links for Table2. I use Symfony\Bridge\Doctrine\Form\Type\EntityType for it. 2-nd form doesn't work with this configuration.
Form class:
namespace Xxx\M2mBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type as FormType;
use Symfony\Bridge\Doctrine\Form\Type as DoctrineFormType;
class Table2 extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('post')
->add('name', FormType\TextType::class, [
'required' => true,
'label' => 'Name',
])
->add('table1', DoctrineFormType\EntityType::class, [
'required' => false,
'expanded' => true,
'multiple' => true,
'class' => 'Xxx\\M2mBundle\\Entity\\Table1',
'choice_label' => 'name',
'label' => 'Table1',
])
->add('save', FormType\SubmitType::class, [
'label' => 'Save',
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'Xxx\\M2mBundle\\Entity\\Table2',
]);
}
}
I have changed association for Table2 to:
/**
* #ORM\ManyToMany(targetEntity="Table1", inversedBy="table2", fetch="LAZY", cascade="all")
* #ORM\JoinTable(name="m2m_links",
* joinColumns={#ORM\JoinColumn(name="table2_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="table1_id", referencedColumnName="id")}
* )
*/
private $table1;
It helped, but I think that it is not good decision, and now I get error The table with name 'm2m_links' already exists. when I try to update schema ./bin/console doctrine:schema:update --dump-sql --force.
You need to understand about Owning and Inverse side of the association.
This is how you are able to work smoothly with ManyToMany associations:
generate the two enities you need with the CLI e.g.
bin/console doctrine:generate:entity
go to doctrine association mapping for ManyToMany bidirectional
add the annotations and the constructors to your Entities. Do not forget to write ORM\ before your annotations. Change Entity and table names to your own situation.
extend the ManyToMany annotation at the Owner side or both sides of your relationship with the cascade={"persist"} option. e.g.
#ORM\ManyToMany(targetEntity="Tag", inversedBy="images", cascade={"persist"})
automatically generate needed methods and update the schema with the CLI.
bin/console doctrine:generate:entities AppBundle
bin/console doctrine:schema:update --force
add a __toString() method to both entities.
If you want to be able to add owner-entities on inversed entity you could make a small change in the add and remove method of the inversed entity as the example below shows.
example:
# Tag entity
public function addImage(\AppBundle\Entity\Image $image)
{
$image->addTag($this); // new rule!
$this->images[] = $image;
return $this;
}
public function removeImage(\AppBundle\Entity\Image $image)
{
$image->removeTag($this); // new rule!
$this->images->removeElement($image);
}
add 'by_reference' => false to the entity form field options
example:
class TagType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('images', EntityType::class, array(
'multiple' => true,
'expanded' => true,
'class' => 'AppBundle:Image',
'by_reference' => false # HERE!
))
;
}
// ..
Related
In a controller, I create a form:
$em = $this->getDoctrine()->getManager();
$formProjetSearch = $this->createForm(EgwProjetSearchType::class, $em, [
'em' => $this->getDoctrine()->getManager(),
]);
In my EgwProjetSearchType, I have:
$builder->add('dispositif', 'entity', array(
'class' => 'LeaPrestaBundle:EgwDispositif',
'property' => 'nomDispositif',
'label' => 'nomDispositif',
'required' => true,
'empty_value' => '',
'query_builder' => function(EntityRepository $er)
{
return $er->createQueryBuilder('d')
->where('d.isActive = :isActive')
->setParameter('isActive', 1)
->orderBy('d.nomDispositif','ASC');
},
));
And I've got this error:
Neither the property "dispositif" nor one of the methods "getDispositif()", "dispositif()", "isDispositif()", "hasDispositif()", "__get()" exist and have public access in class "Doctrine\ORM\EntityManager".
Nonetheless, the entity exists:
<?php
namespace Lea\PrestaBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Lea\PrestaBundle\Entity\EgwDispositif
*
* #ORM\Table(name="egw_dispositif")
* #ORM\Entity
*/
class EgwDispositif
{
/**
* #var integer $idDispositif
*
* #ORM\Column(name="id_dispositif", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idDispositif;
/**
* #ORM\ManyToOne(targetEntity="EgwTypePrestation", inversedBy="dispositifs")
* #ORM\JoinColumn(name="id_type_prestation", referencedColumnName="id")
*/
private $idTypePrestation;
ETC ...
Thanks for your help !
Thakns for your messages, but, I just want to display in a form a entity in a listbox : i use the type EgwProjetSearchType, i add a field which is "dispositif" coming from entity EgwDispositif (which exists) and the return message is :
Neither the property "dispositif" nor one of the methods
"getDispositif()", "dispositif()", "isDispositif()", "hasDispositif()",
"__get()" exis``t and have public access in class
"Doctrine\ORM\EntityManager"
So it's not a problem of argument EM passed in the form EgwProjetSearchType : Symfony says "the entity doesnt exists"....
I dont have to pass EwgDispositif ?? It was not the cas in Symfony 2 : i had :
$formProjetSearch = $this->createForm(new EgwProjetSearchType($this-
getDoctrine()->getManager()));
And this doesnt work anymore in 3.4.
So i changed the code :
$formProjetSearch = $this->createForm(EgwProjetSearchType::class, $em, [
'em' => $this->getDoctrine()->getManager(),
]);
In your entity you need to define your getters and setters. Your entity class is private so you must define public getters and setters to access the private objects.
https://symfony.com/doc/current/doctrine.html
I am trying to create a Form with a Category selector for a Page entity - Pages can have many categories in multiple Registries.
The relationship from Page --> Category is a OneToMany/ManyToOne with an intermediate PagesCategoryEntity that has a discriminator property (categoryRegistryId).
I have successfully figured out how to use the 'entity' Type in the FormBuilder to create ONE multiple select box. But in the end, I will need to have multiple select-boxes (for each registry) with a discriminator value in the html somewhere.
So, I need to know how to get the additional properties of PagesCategoryEntity into the Form and then how I can access them in the getters/setters of the PageEntity.
Surely, I cannot be the only person to have values in the intermediate entity that need to be accessible in the form and persistence layer?
I appreciate you taking the time to look at this!
craig
truncated Entity and Form classes for Brevity.
class CategoryEntity
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
// other properties, getters, setters, etc...
}
class PageEntity
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="PagesCategoryEntity",
* mappedBy="page", cascade={"all"},
* orphanRemoval=true, indexBy="categoryRegistryId")
*/
private $categories;
// other properties, getters, setters, etc...
}
class PagesCategoryEntity
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $categoryRegistryId;
/**
* #ORM\ManyToOne(targetEntity="CategoryEntity")
* #ORM\JoinColumn(name="categoryId", referencedColumnName="id")
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="PageEntity", inversedBy="categories")
* #ORM\JoinColumn(name="entityId", referencedColumnName="pageid")
*/
private $page;
}
class PageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('categories', 'entity', array(
'class' => 'MyCoolBundle:CategoryEntity',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.parent = :parent')
->setParameter('parent', 19)
->orderBy('c.name', 'ASC');
},
'property' => "name",
'multiple' => true,
'required' => false,
'empty_data' => null,
));
}
}
Try this then:
->add('categories',
'entity',
array(
'class'=>'Acme\MycoolBundle\Entity\Category',
'property'=>'name',
'query_builder' => function (\Acme\MycoolBundle\Entity\CategoryRepository $repository)
{
return $repository->createQueryBuilder('c')
->where('c.parent = :parent')
->setParameter('parent', 19)
->add('c.name', 'ASC');
}
)
);
try to create a category Repository if u don't have and adapt the scripts for ur needs it works for me!
Try to use Collection instead of using Entity!
Collection is used in one to many/ many to one
you can try the tutorial of symfony cookbook for collection forms
http://symfony.com/doc/current/cookbook/form/form_collections.html
i hope it helps ;)
I have two OneToMany relationship between two entities : Serie ---> Episode and Episode ---> Musique which follow :
class Serie{
........
/**
* #ORM\OneToMany(targetEntity="Episode", mappedBy="serieId")
* #ORM\OrderBy({"numeroE" = "ASC"})
*/
private $episodes;
........
}
class Episode{
........
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="Serie", inversedBy="episodes")
* #ORM\JoinColumn(name="serie_id", referencedColumnName="id")
*/
private $serieId;
/**
* #ORM\OneToMany(targetEntity="Musique", mappedBy="episodeId")
* #ORM\OrderBy({"min" = "ASC", "sec" = "ASC"})
*/
private $musiques;
}
class Musique{
........
/**
* #ORM\ManyToOne(targetEntity="Episode", inversedBy="musiques")
* #ORM\JoinColumn(name="episode_id", referencedColumnName="id")
*/
private $episodeId;
........
}
In my My\ContentBundle\Form\Type\MusiqueType, which represents a Musique Entity Form, I have :
class MusiqueType extends AbstractType{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('episodeId', 'entity', array(
'class' => 'MyContentBundle:Episode' ,
)
->add('serieId', 'entity', array(
'class' => 'MyContentBundle:Serie' ,
'mapped' => false
)
;
}
}
In my view, i'd need to display the 'episodeId' field. However, as there are around one thousand Episodes in my database, i'd like to display in this field only the episodes that belong to the Serie selected.
I was going to use some ajax actions to do this, but i was wondering : is there was a simpler way to do it?
Thank you !
You can be more specific in the generation of your form by adding a custom query to it :
use Doctrine\ORM\EntityRepository;
// ...
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:User',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')->orderBy('u.username', 'ASC');
},
));
Refer to the entity Field Type page of SF2 doc for more details.
I'm beginner in Symfony 2.
I'm trying to display a form with a "select" where is the "options" from a query.
I put the following code in my form :
use Doctrine\ORM\EntityRepository;
use Bloc\MainBundle\Entity\Table;
use Bloc\MainBundle\Entity\Table2;
public function addAction(Request $request)
{
$table = new Table();
$form = $this->createFormBuilder($table , array('attr' => array('role' => 'form')))
->add('num', 'integer', array('label' => 'Numéro', 'attr' => array('class' => 'form-control')))
->add('nom_emetteur', 'text', array('label' => 'Emetteur', 'attr' => array('class' => 'form-control')))
->add('numero', 'entity', array('class' => 'BlocMainBundle:Table2', 'property' => 'numero'))
...
}
And I have the following error:
Neither the property "numero" nor one of the methods "getNumero()", "isNumero()", "hasNumero()", "__get()" or "__call()" exist and have public access in class "Bloc\MainBundle\Entity\Table".
I understand that the error tells me that "numero" is not in the entity Table but I question the entity Table2.
I must miss something, but I do not know where ...
My entity definition looks like this :
Table 1:
<?php...
class Table
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="num", type="integer")
*/
private $num;
//Getter and setter...
}
Table 2
<?php
namespace Bloc\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Fournisseur
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Bloc\MainBundle\Entity\Table2Repository")
*/
class Table2
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="numero", type="integer")
*/
private $numero;
/**
* Set numero
*
* #param integer $numero
* #return Fournisseur
*/
public function setNumero($numero)
{
$this->numero = $numero;
return $this;
}
/**
* Get numero
*
* #return integer
*/
public function getNumero()
{
return $this->numero;
}
...
}
Can you help me please ?
If you do not have the relationship set then you need to tell the FormBuilder to not map it to a field.
->add('numero', 'entity', array(
'mapped' => false,
'class' => 'BlocMainBundle:Table2',
'property' => 'numero',
));
To accomplish the options the way that you want (using multiple fields for the option text) you need to use a choice type and build your options list like this:
->add('numero', 'choice', array(
'mapped' => false,
'choices' => $this->buildChoices()
));
protected function buildChoices() {
$choices = [];
$table2Repository = $this->getDoctrine()->getRepository('BlocMainBundle:Table2');
$table2Objects = $table2Repository->findAll();
foreach ($table2Objects as $table2Obj) {
$choices[$table2Obj->getId()] = $table2Obj->getNumero() . ' - ' . $table2Obj->getName();
}
return $choices;
}
you can use this solution. Its a simply and from documentation of symfony2. I m use this
->add('numero', 'entity', array(
'class' => 'BlocMainBundle:Table2',
'choice_label' => 'numero' // MAGIC read next paragraph, it is a private variable
))
Like is in documentation writed:
If the entity object does not have a __toString() method the choice_label option is needed.
Use this solution because its native symfony solution for this situations :)
I hope i will help you or the other people
If you need information's table, you could create a constructor in your Form class
in your controller :
$emForm = $this->getDoctrine()->getRepository('RelacionesDoctrineBundle:Adolecente');
$asignatura = new AsignaturasType($emForm);// your form class
in your form class
class AsignaturasType extends AbstractType {
protected $repository;
function __construct($repository)
{
$this->repository = $repository;
}
}
and done! you use it:
$findAdolecente = $this->repository->findAll();
I'm trying to get a multi-relation (multiple, dependent one-to-many relations) form working, but no success. I'm using Symfony 2.3 with FOSUserbundle.
Entity User
use FOS\UserBundle\Entity\User as BaseUser;
[...]
/**
* #ORM\Entity
* #Gedmo\Loggable
* #ORM\Table(name="ta_user", indexes={#ORM\Index(name="IDX_LOGIN_TOKEN", columns={"login_token"})})
*/
class User extends BaseUser
{
[...]
/**
* #ORM\OneToMany(targetEntity="UserLifestyle", mappedBy="user", fetch="LAZY", cascade={"persist", "remove"})
*/
protected $lifestyle;
UserManager
use Doctrine\ORM\EntityManager;
use FOS\UserBundle\Entity\UserManager as BaseUserManager;
use Acme\UserBundle\Entity\LifestyleQuestion;
use Acme\UserBundle\Entity\UserLifestyle;
[...]
class UserManager extends BaseUserManager {
public function createUser() {
$user = parent::createUser();
$lifestyle = new UserLifestyle();
$lifestyle->setQuestion($this->objectManager->getReference('Acme\UserBundle\Entity\LifestyleQuestion', 1));
$user->addLifeStyle($lifestyle);
$lifestyle = new UserLifestyle();
$lifestyle->setQuestion($this->objectManager->getReference('Acme\UserBundle\Entity\LifestyleQuestion', 2));
$user->addLifeStyle($lifestyle);
$lifestyle = new UserLifestyle();
$lifestyle->setQuestion($this->objectManager->getReference('Acme\UserBundle\Entity\LifestyleQuestion', 3));
$user->addLifeStyle($lifestyle);
return $user;
}
Entity UserLifestyle
/**
* #ORM\Entity
* #Gedmo\Loggable
* #ORM\Table(name="ta_user_lifestyle")
*/
class UserLifestyle
{
/**
* #ORM\Id
* #ORM\Column(type="smallint")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="lifestyle")
* #ORM\JoinColumn(name="user_id")
*/
protected $user;
/**
* #ORM\ManyToOne(targetEntity="LifestyleQuestion", inversedBy="answeredByUser")
* #ORM\JoinColumn(name="question_id")
*/
protected $question;
/**
* #ORM\ManyToOne(targetEntity="LifestyleAnswer", inversedBy="userAnswers")
* #ORM\JoinColumn(name="answer_id")
* #Gedmo\Versioned
*/
protected $answer;
Then, there's a form type
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', NULL, array('label' => 'E-Mail'))
[...]
->add('lifestyle', 'collection', array(
'type' => new RegistrationLifestyleType(),
'allow_add' => false,
'allow_delete' => false,
'label' => false,
))
and now there should be a related RegistrationLifestyleType. But I've no idea how it should look like. I expect, that there are three choice fields in my registration form, showing a question (as label) and bunch of answers (as choice field) related to these questions. The UserManager assigns three questions to a newly created user, so one can get a question with:
$lifestyles = $user->getLifestyles();
foreach ($lifestyles as $lifestyle) {
$question = $lifestyle->getQuestion(); // echo $question->getQuestion();
$answers = $lifestyle->getQuestion()->getAnswers(); // loop through $answers and echo $answer->getAnswer();
}
But how I can modify the form type, to get this working. Important: my intention is to use built-in functionality as most as possible and trying to avoid inflating form types and others by injecting service containers and entity managers.
Found a solution, perhaps someone can use it. The problem seems, that LifestyleQuestion and LifestyleAnswer are 1:n relations at the same object (UserLifestyle), so Symfony does not know how to deal with it, even if I set the LifestyleQuestion to a specific question in UserManager already. Regarding https://stackoverflow.com/a/9729888/672452 one has to use form listeners, so the parent object is available in sub form. So here is my "simple" RegistrationLifestyleType (without using any injected container or manager):
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Security\Core\SecurityContext;
class RegistrationLifestyleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($builder) {
$form = $event->getForm();
$lifestyle = $event->getData();
if (!($lifestyle instanceof \Acme\UserBundle\Entity\UserLifestyle) || !$lifestyle->getQuestion()) return;
$label = $lifestyle->getQuestion()->getQuestion();
$questionId = $lifestyle->getQuestion()->getId();
$form->add('answer', 'entity', array(
'class' => 'AcmeUserBundle:LifestyleAnswer',
'empty_value' => '',
'property' => 'answer',
'query_builder' => function(EntityRepository $er) use ($questionId) {
return $er
->createQueryBuilder('t1')
->andWhere('t1.question = :question')
->setParameter('question', $questionId)
->orderBy('t1.answer', 'ASC')
;
},
'label' => $label,
));
});
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\UserBundle\Entity\UserLifestyle',
'error_bubbling' => false,
));
}
}