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
Related
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!
))
;
}
// ..
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,
));
}
}
I followed the documentation to override the register form of the FosUser and I display the roles I want like this. Here is my register form.
<?php
namespace My\BlogBundle\Form;
use My\BlogBundle\Entity\User;
use Symfony\Component\Form\FormBuilder;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
class MyRegisterType extends BaseType
{
public function buildForm(FormBuilder $builder, array $options)
{
parent::buildForm($builder ,$options);
$user = new User();
$builder
->add('roles' ,'choice' ,array('choices'=>$user->getRoles() ) ;
}
public function getName()
{
return 'my_register_type';
}
}
And here is my User entity.
<?php
namespace My\BlogBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* My\BlogBundle\Entity\User
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="My\BlogBundle\Entity\UserRepository")
*/
class User extends BaseUser
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
protected $roles=array();
/**
*#ORM\OneToMany(targetEntity="Article" ,mappedBy="user")
*/
protected $article;
/**
*#ORM\OneToMany(targetEntity="Comment" ,mappedBy="user")
*/
protected $comment;
public function __construct()
{
parent::__construct();
$this->roles=array('searcher' ,'annoucer');
}
}
My issue now is I don't know how to display on that field only the roles I added because I get ROLE_USER with the choices also and when I submit the form I get this error
Catchable Fatal Error: Argument 1 passed to FOS\UserBundle\Model\User::setRoles() must be an array, string given, called in /var/www/blog/vendor/symfony/src/Symfony/Component/Form/Util/PropertyPath.php on line 346 and defined in /var/www/blog/vendor/bundles/FOS/UserBundle/Model/User.php line 709
Any help would be more than appreciated, thanks. BTW I'm sorry I couldn't add other tags :P
I think you problem is because you are using a ChoiceField. A ChoiceField will return only one role (a string type, this id of the role) but the method setRoles expect an array. This means you need to either add the option multiple => true or change to another type of field like a Collection field. Using multiple will return an array which will be accepted by setRoles and using a Collection field will also return an array.
Bottom line, you need to choose a form field that returns an array instead of a single result, a string. You can see all form types here
Hope this helps.
I also have the same issue, then i use this line of code in controller to resolve it.
in your registration form
->add('roles', 'choice', array(
'mapped' => false,
'required' => true,
'label' => 'User Type',
'choices' => array(
'ROLE_USER' => 'User',
'ROLE_STAFF' => 'Staff',
'ROLE_INSTITUTE' => 'Institute',
),
'expanded' => true,
))
and in controller
$role = $form->get('roles')->getData();
$user->setRoles(array($role));
$em->persist($user);
$em->flush();