After several weeks of research, I turn to you to try to understand the QueryBuilder part and implement it on my application.
Concretely, here is what I would like to do:
Depending on the agent(s) chosen by the user, display targets with the same nationality.
My agent entity and my target entity each have a nationality. They are both connected thanks to Doctrine on my Mission entity.
I think I should use NOT IN in my request but don't know how to do it. I show you what I did without results.
/**
* Récupère les nationalités de l'agent
*/
public function findNationality()
{
$this
->createQueryBuilder ('m')
->select ('*')
->join('m.agents', 'a')
->join('m.cibles', 'c')
->where('a.nationality = c.nationality')
->getQuery()
->getResult();
}
My MissionType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title')
->add('description')
->add('code_name')
->add('country')
->add('speciality', EntityType::class, [
'label' => 'Choisir une spécialité: ',
'placeholder' => 'Choisir une spécialité',
'class' => Specialite::class,
'choice_label' => 'name'
])
->add('agents', EntityType::class, [
'label' => 'Choisir un ou des agent(s): ',
'class' => Agent::class,
'choice_label' => 'identification_code',
'multiple' => true,
'expanded' => true
])
->add('contacts', EntityType::class, [
'label' => 'Selectionner le(les) contact(s): ',
'class' => Contact::class,
'choice_label' => 'code_name',
'multiple' => true,
'expanded' => true
])
->add('cibles', EntityType::class, [
'label' => 'Selectionner la(les) cible(s): ',
'class' => Cible::class,
'choice_label' => 'code_name',
'multiple' => true,
'expanded' => true
])
->add('planques', EntityType::class, [
'label' => 'Selectionner la(les) planque(s): ',
'class' => Planque::class,
'choice_label' => 'code',
'multiple' => true,
'expanded' => true,
])
->add('start_date', DateType::class, [
'widget' => 'single_text'
])
->add('end_date', DateType::class, [
'widget' => 'single_text'
])
->add('status', EntityType::class, [
'label' => 'Statut de la Mission: ',
'class' => Status::class,
'choice_label' => 'name'
])
->add('type', EntityType::class, [
'label' => 'Type de Mission: ',
'class' => TypeMission::class,
'choice_label' => 'name'
]);
}
My Mission Entity
class Mission
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="string", length=255)
*/
private $code_name;
/**
* #ORM\Column(type="string", length=255)
*/
private $country;
/**
* #ORM\Column(type="date")
*/
private $start_date;
/**
* #ORM\Column(type="date")
*/
private $end_date;
/**
* #ORM\OneToMany(targetEntity=Agent::class, mappedBy="mission", cascade={"persist", "merge"})
*/
private $agents;
/**
* #ORM\OneToMany(targetEntity=Cible::class, mappedBy="mission", cascade={"persist", "merge"})
*/
private $cibles;
/**
* #ORM\OneToMany(targetEntity=Contact::class, mappedBy="mission", cascade={"persist", "merge"})
*/
private $contacts;
/**
* #ORM\OneToMany(targetEntity=Planque::class, mappedBy="mission", cascade={"persist", "merge"})
*/
private $planques;
/**
* #ORM\ManyToOne(targetEntity=Specialite::class, inversedBy="missions", cascade={"persist", "merge"})
*/
private $speciality;
/**
* #ORM\ManyToOne(targetEntity=Status::class, inversedBy="missions", cascade={"persist", "merge"})
*/
private $status;
/**
* #ORM\ManyToOne(targetEntity=TypeMission::class, inversedBy="missions", cascade={"persist", "merge"})
*/
private $type;
public function __construct()
{
$this->agents = new ArrayCollection();
$this->cibles = new ArrayCollection();
$this->contacts = new ArrayCollection();
$this->planques = new ArrayCollection();
}
I would like the list of targets to update dynamically according to the choice of agents.
Any help will be welcome and I thank you in advance because I admit going around in circles and no longer know where I am.
Related
I try to validate FormType inside CollectionType with some simple groups rules but It doesn't work, but if i try to make the same without validations groups, it's work fine.
Any idea?
This is a complete and simple exemple that reproduct the error https://github.com/ychakroun/symfony-collection-type-issue
/**
* Sticker
*
* #ORM\Table(name="sticker")
* #ORM\Entity(repositoryClass="App\Repository\StickerRepository")
*/
class Sticker
{
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="App\Entity\Link", mappedBy="sticker", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\OrderBy({"position"="ASC"})
* #Assert\Valid()
*/
private $links;
}
/**
* Link
*
* #ORM\Table(name="link")
* #ORM\Entity(repositoryClass="App\Repository\LinkRepository")
*/
class Link
{
/**
* #var mixed
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string|null
* #Assert\NotBlank()
*
* #ORM\Column(name="title", type="string")
*/
private $title;
/**
* #var bool
*
* #ORM\Column(name="external", type="boolean")
*/
private $external;
/**
*
* #var string|null
*
* #Assert\NotBlank(groups={"isExternal"})
* #Assert\Url(groups={"isExternal"})
* #ORM\Column(name="url", type="text", nullable=true)
*/
private $url;
/**
* #var \App\Entity\PageVersion|null
*
* #Assert\NotBlank(groups={"isInternal"})
* #ORM\ManyToOne(targetEntity="App\Entity\PageVersion")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="page_version_id", referencedColumnName="id", nullable=true)
* })
*/
private $pageVersion;
/**
* #var \App\Entity\Category|null
*
* #Assert\NotBlank(groups={"isInternal"})
* #ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="links")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=true)
* })
*/
private $category;
/**
* #var \App\Entity\Sticker|null
*
* #ORM\ManyToOne(targetEntity="App\Entity\Sticker", inversedBy="links")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="sticker_id", referencedColumnName="id", nullable=true)
* })
*/
private $sticker;
}
And this is the forms i use:
class StickerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('links', CollectionType::class, [
'entry_type' => LinkType::class,
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'attr' => [
'class' => 'collection',
],
'by_reference' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Sticker::class,
]);
}
}
class LinkType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'label' => 'Titre du menu:',
'attr' => [
'input-group' => 'true',
],
])
->add('external', ChoiceType::class, [
'label' => false,
'expanded' => true,
'choices' => [
'Lien interne' => false,
'Lien externe' => true,
],
'choice_attr' => [
'class' => 'link-type',
],
'label_attr' => [
'class' => 'btn-group btn-group-toggle',
'data-toggle' => 'buttons',
]
])
->add('url', UrlType::class, [
'label' => 'SAISISSEZ L\'URL EXTERNE',
'attr' => ['placeholder' => 'https://'],
])
->add('pageVersion', EntityType::class, [
'required' => false,
'class' => Page::class,
'choice_label' => 'name',
])
->add('category', EntityType::class, [
'required' => false,
'class' => Category::class,
'choice_label' => 'title',
'query_builder' => function (CategoryRepository $er) {
return $er->createQueryBuilder('c')->where('c.enabled = 1');
},
])
->add('position', HiddenType::class, [
'attr' => [
'class' => 'my-position',
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Link::class,
'cascade_validation' => true,
'validation_groups' => function (FormInterface $form) {
/** #var Link $link */
$link = $form->getData();
$groups = ['Default'];
if ($link->getExternal()) {
$groups[] = 'isExternal';
} else {
$groups[] = 'isInternal';
}
return $groups;
},
]);
}
}
We can see that the url field is validated and it's blank
If i try to remove groups={"isExternal"} from link entity, the validation will work, like in this image:
I think you need to add the validation groups on the Sticker entity too :
/**
* Sticker
*
* #ORM\Table(name="sticker")
* #ORM\Entity(repositoryClass="App\Repository\StickerRepository")
*/
class Sticker
{
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="App\Entity\Link", mappedBy="sticker", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\OrderBy({"position"="ASC"})
* #Assert\Valid(groups={"isInternal", "isExternal"})
*/
private $links;
}
This option is only valid on the root form and is used to specify which groups will be used by the validator.
This is the response https://github.com/symfony/symfony/issues/31441
Hello we must add an addEventListener
class StickerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('links', CollectionType::class, [
'entry_type' => LinkType::class,
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'attr' => [
'class' => 'collection',
],
'by_reference' => false,
])
->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
;
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Sticker::class,
]);
}
public function onPreSubmit(FormEvent $event)
{
if ($event->getData()) {
$data = $event->getData();
$data['links'] = array_values($data['links']);
$event->setData($data);
}
}
}
I have a problem with one of my forms. The form has to create a new Colle entity and link some other Colle entities to it.
When I submit it, a new entity is created for each item in collesEnfants collection field. The new entity created is correctly linked to the parent and has the right 'ordre' field but it's a newly created entity and not the entity I've selected.
My form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom', TextType::class,['label' => 'Nom de la colle'])
->add('collesEnfants', CollectionType::class,
['label' => false,
'entry_type' => SousColleFormType::class,
'required' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'PACES\ColleBundle\Entity\Colle'
]);
}
SousColleFormType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('matiere', EntityType::class, [
'class' => 'PACESColleBundle:Matiere',
'attr' => ['class'=> 'matiere'],
'choice_label' => 'name',
'label' => false,
'required' => false,
'placeholder' => 'Choisissez une matière',
'mapped' => false])
->add('nom', EntityType::class, [
'class' => 'PACESColleBundle:Colle',
'attr' => ['class' => 'colles'],
'choice_label' => 'nom',
'label' => false,
'group_by' => 'matiere',
'required' => true,
'placeholder' => 'choose.colle'])
->add('ordre', IntegerType::class,[
'attr'=>['class'=>'ordre'],
'required' => true,
'label' => false]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'PACES\ColleBundle\Entity\Colle'
]);
}
Controller :
$formColleMere = $this->createForm(AjoutSuperColleFormType::class, $colle);
$formColleMere->add('submit', SubmitType::class, ['label' => 'Créer']);
$formColleMere->handleRequest($request);
if ($formColleMere->isSubmitted() && $formColleMere->isValid()) {
$collesEnfants = $formColleMere->get('collesEnfants')->getData();
foreach ($collesEnfants as $enfant) {
$colle->addColleEnfant($enfant);
}
if (!$colle->getCollesEnfants()->isEmpty()) {
$em->persist($colle);
$em->flush();
}
Colle entity :
class Colle
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Colle", mappedBy="colleMere", cascade={"persist"})
* #ORM\OrderBy({"ordre" = "asc"})
*/
private $collesEnfants;
/**
* #ORM\ManyToOne(targetEntity="Colle", inversedBy="collesEnfants", cascade={"persist"})
* #ORM\JoinColumn(name="colleMere_id", referencedColumnName="id")
*/
private $colleMere;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*/
protected $nom;
{........}
/**
* #ORM\ManyToOne(targetEntity="PACES\ColleBundle\Entity\Matiere", inversedBy="colles", cascade={"persist"})
* #ORM\JoinColumn(name="matiere_id", referencedColumnName="id")
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $matiere;
/**
* Cet attribut sert aux 'super colles' qui sont le résultat d'une fusion de colles d'une même UE
* #var integer
* #ORM\Column(name="ordre", type="integer", nullable=true)
*/
protected $ordre;
I succeeded in doing what I want by adding 'mapped' => false to collesEnfants field.
I also changed these lines in the Controller :
$collesEnfants = $formColleMere->get('collesEnfants')->getData();
foreach ($collesEnfants as $enfant) {
$colle->addColleEnfant($enfant);
}
To :
$collesEnfants = $formColleMere->get('collesEnfants')->getData();
foreach ($collesEnfants as $enfant) {
$colle->addColleEnfant($enfant['nom']);
$enfant['nom']->setOrdre($enfant['ordre']);
}
I have a problem with one of my forms. When I submit it, I have this error : Object of class ... could not be converted to string.
I already looked at some other cases like mine but I really don't know what is wrong. Method toString doesn't exist in my entity but I never needed it for all my other forms which look like this one.
My form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom', TextType::class,['label' => 'Nom de la colle'])
->add('collesEnfants', CollectionType::class,
['label' => false,
'entry_type' => SousColleFormType::class,
'required' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'PACES\ColleBundle\Entity\Colle'
]);
}
SousColleFormType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('matiere', EntityType::class, [
'class' => 'PACESColleBundle:Matiere',
'attr' => ['class'=> 'matiere'],
'choice_label' => 'name',
'label' => false,
'required' => false,
'placeholder' => 'Choisissez une matière',
'mapped' => false])
->add('nom', EntityType::class, [
'class' => 'PACESColleBundle:Colle',
'attr' => ['class' => 'colles'],
'choice_label' => 'nom',
'label' => false,
'group_by' => 'matiere',
'required' => true,
'placeholder' => 'choose.colle'])
->add('ordre', IntegerType::class,[
'attr'=>['class'=>'ordre'],
'required' => true,
'label' => false]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'PACES\ColleBundle\Entity\Colle'
]);
}
Colle entity :
class Colle
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Colle", mappedBy="colleMere", cascade={"persist"})
* #ORM\OrderBy({"ordre" = "asc"})
*/
private $collesEnfants;
/**
* #ORM\ManyToOne(targetEntity="Colle", inversedBy="collesEnfants", cascade={"persist"})
* #ORM\JoinColumn(name="colleMere_id", referencedColumnName="id")
*/
private $colleMere;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*/
protected $nom;
{........}
/**
* #ORM\ManyToOne(targetEntity="PACES\ColleBundle\Entity\Matiere", inversedBy="colles", cascade={"persist"})
* #ORM\JoinColumn(name="matiere_id", referencedColumnName="id")
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $matiere;
/**
* Cet attribut sert aux 'super colles' qui sont le résultat d'une fusion de colles d'une même UE
* #var integer
* #ORM\Column(name="ordre", type="integer", nullable=true)
*/
protected $ordre;
I am devolping an application in Symfony 2.7, I generated the CRUD from a PRODUCT TABLE (InProducto) which is related OneToMany to another table (InUnidadMedida).
When I open Edit Form, the value in ENTITY FIELD (which is a select field from UNIDAD DE MEDIDA table) always appears the first option of related table (UNIDAD DE MEDIDA). And It suppose to get the value in the field of the table INPRODUCTO
InProductoType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nombre')
->add('descripcion')
->add('unidadMedida', 'entity', array(
'class' => 'NivalInventarioBundle:InUnidadMedida',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.nombre', 'ASC');
},
'choice_label' => 'nombre',
'by_reference' => false,
'property' => 'type',
'expanded' => false,
'multiple' => false
))
}
Controller:
private function createEditForm(InProducto $entity)
{
$form = $this->createForm(new InProductoType(), $entity, array(
'action' => $this->generateUrl('inproducto_update', array('id' => $entity->getIdProducto())),
'method' => 'PUT',
));
$form->add('submit', 'submit', array('label' => 'Guardar'));
return $form;
}
Producto table (Entity)
/**
* InProducto
*
* #ORM\Table(name="in_producto")
* #ORM\Entity
*/
class InProducto
{
/**
* #ORM\ManyToOne(targetEntity="InSubLinea", inversedBy="InProducto")
* #ORM\JoinColumn(name="id_sub_linea", referencedColumnName="id_sub_linea")
*/
protected $subLinea;
/**
* #ORM\ManyToOne(targetEntity="InUnidadMedida", inversedBy="InProducto")
* #ORM\JoinColumn(name="id_unidad_medida", referencedColumnName="id_unidad_medida")
*/
protected $unidadMedida;
/**
* #var integer
*
* #ORM\Column(name="id_producto", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
Unidad de medida TABLE (entity)
/**
* InUnidadMedida
*
* #ORM\Table(name="in_unidad_medida")
* #ORM\Entity
*/
class InUnidadMedida
{
/**
* #ORM\OneToMany(targetEntity="InProducto", mappedBy="InUnidadMedida")
*/
protected $InProducto;
public function __construct()
{
$this->InProducto = new ArrayCollection();
}
The form type will be guessed automatically by Symfony, if you've mapped these entities correctly.
So make it just
->add('unidadMedida', null, array(
'choice_label' => 'nombre',
'expanded' => false,
'multiple' => false
))
And there is no such option as property. Did you mean property_path?
After hours of scratching head, it was very simple, I add the following properties to my fields:
'by_reference' => true,
'mapped' => true,
Thanks Dmitry Malyshenko for your time.
I have an entity Client and this entity needs to have an association to another Client with information about relationship. This entity is ClientRelationship.
I need to have this asociation in one property, because I want to use symfony form with collection type field.
Problem is that I do not know how to write it to have that association in one Client property.
Now I have in Client two asociation properties, but that is my problem. In form on one side is relationship visible, but on other Client its not.
Client entity:
/**
* #ORM\OneToMany(targetEntity="ClientRelationship", mappedBy="leftClient", cascade={"persist", "remove"})
*/
private $leftRelationships;
/**
* #ORM\OneToMany(targetEntity="ClientRelationship", mappedBy="rightClient")
*/
private $rightRelationships;
ClientRelationship entity:
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length = 255)
* #Assert\Length(max = 255)
* #Assert\NotBlank()
*/
private $relationship;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="leftRelationships")
* #ORM\JoinColumn(name="leftClient_id", referencedColumnName="id")
*/
private $leftClient;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="rightRelationships")
* #ORM\JoinColumn(name="rightClient_id", referencedColumnName="id")
*/
private $rightClient;
EDIT: added forms
ClientType form:
$builder->add('leftRelationships', 'bootstrap_collection', array(
'type' => new ClientRelationshipType(),
'label' => 'relationships',
'allow_delete' => true,
'allow_add' => true,
'by_reference' => false,
'add_button_text' => 'add',
'delete_button_text' => 'remove',
'options' => array(
'businessCase' => $options['businessCase'],
'client' => $options['client'],
),
));
ClientRelationshipType form:
$builder->add('rightClient', 'entity', array(
'label' => 'client',
'class' => 'MyProject\CoreBundle\Entity\Client',
'property' => 'name',
'query_builder' => function (ClientRepository $er) use ($options) {
$qb = $er->createQueryBuilder('c');
if ($options['businessCase'] instanceof BusinessCase) {
$qb->andWhere($qb->expr()->in('c.id', ':clients'));
$qb->setParameter(':clients', $options['businessCase']->getClients()->toArray());
}
if ($options['client'] instanceof Client) {
$qb->andWhere($qb->expr()->neq('c.id', ':client'));
$qb->setParameter(':client', $options['client']);
}
return $qb;
},
));
$builder->add('relationship', 'text', array(
'label' => 'relationship',
));