Embedded forms relations doctrine

363 Views Asked by At

in my symfony app, i'm using embedded forms. In my case, an object "CompetenceGroupe" can have multiple objects "CompetenceItem", but an object "CompetenceItem" belongs to only one object "CompetenceGroupe", so the relation is manyToOne.

The form work perfectly, and I have two tables (one for each entity), and it's well saved in the database.

But when I select an CompetenceGroupe object with doctrine in my controller, I have all informations of the object, and he's got an empty "competenceItems" property, so I can't retrieve the childs object (CompetenceItem).

My "CompetenceGroupe" entity :

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="competences_groupes")
 */
class CompetenceGroupe
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id_competence_groupe;

/**
 * @var User $user
 *
 * @ORM\ManyToOne(targetEntity="User", cascade={"persist", "merge"})
 * @ORM\JoinColumn(name="id_user", referencedColumnName="id_user", nullable=false)
 */
private $user;

/**
 * @ORM\Column(type="string", length=60, nullable=true)
 */
protected $titre;

protected $competence_items;

public function __construct()
{
    $this->competence_items = new ArrayCollection();
}

public function getCompetenceItems()
{
    return $this->competence_items;
}

/**
 * Get idCompetenceGroupe
 *
 * @return integer
 */
public function getIdCompetenceGroupe()
{
    return $this->id_competence_groupe;
}

/**
 * Set titre
 *
 * @param string $titre
 *
 * @return CompetenceGroupe
 */
public function setTitre($titre)
{
    $this->titre = $titre;

    return $this;
}

/**
 * Get titre
 *
 * @return string
 */
public function getTitre()
{
    return $this->titre;
}

/**
 * Set user
 *
 * @param \AppBundle\Entity\User $user
 *
 * @return CompetenceGroupe
 */
public function setUser(\AppBundle\Entity\User $user)
{
    $this->user = $user;

    return $this;
}

/**
 * Get user
 *
 * @return \AppBundle\Entity\User
 */
public function getUser()
{
    return $this->user;
}

public function addItem(CompetenceItem $item)
{
    $this->competence_items->add($item);
}

public function removeItem(CompetenceItem $item)
{
    // ...
}

/**
 * Set competenceItems
 *
 * @param \AppBundle\Entity\CompetenceItem $competenceItems
 *
 * @return CompetenceGroupe
 */
public function setCompetenceItems(\AppBundle\Entity\CompetenceItem $competenceItems = null)
{
    $this->competence_items = $competenceItems;

    return $this;
}
}

And my "CompetenceItem" entity :

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="competences_items")
 */
class CompetenceItem
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id_competence_item;

/**
 * @ORM\Column(type="string", length=60, nullable=false)
 */
protected $libelle;

/**
 * @var CompetenceNiveau $niveau
 *
 * @ORM\ManyToOne(targetEntity="CompetenceNiveau", cascade={"persist", "merge"})
 * @ORM\JoinColumn(name="id_competence_niveau", referencedColumnName="id_competence_niveau", nullable=true)
 */
private $niveau;

/**
 * @var CompetenceGroupe $competence_groupe
 *
 * @ORM\ManyToOne(targetEntity="CompetenceGroupe", cascade={"persist", "merge"})
 * @ORM\JoinColumn(name="id_competence_groupe", referencedColumnName="id_competence_groupe", nullable=false)
 */
private $competence_groupe;

/**
 * Get idCompetenceItem
 *
 * @return integer
 */
public function getIdCompetenceItem()
{
    return $this->id_competence_item;
}

/**
 * Set libelle
 *
 * @param string $libelle
 *
 * @return CompetenceItem
 */
public function setLibelle($libelle)
{
    $this->libelle = $libelle;

    return $this;
}

/**
 * Get libelle
 *
 * @return string
 */
public function getLibelle()
{
    return $this->libelle;
}

/**
 * Set niveau
 *
 * @param \AppBundle\Entity\CompetenceNiveau $niveau
 *
 * @return CompetenceItem
 */
public function setNiveau(\AppBundle\Entity\CompetenceNiveau $niveau = null)
{
    $this->niveau = $niveau;

    return $this;
}

/**
 * Get niveau
 *
 * @return \AppBundle\Entity\CompetenceNiveau
 */
public function getNiveau()
{
    return $this->niveau;
}

/**
 * Set competenceGroupe
 *
 * @param \AppBundle\Entity\CompetenceGroupe $competenceGroupe
 *
 * @return CompetenceItem
 */
public function setCompetenceGroupe(\AppBundle\Entity\CompetenceGroupe $competenceGroupe)
{
    $this->competence_groupe = $competenceGroupe;

    return $this;
}

/**
 * Get competenceGroupe
 *
 * @return \AppBundle\Entity\CompetenceGroupe
 */
public function getCompetenceGroupe()
{
    return $this->competence_groupe;
}
}

I think I have a missing annotation of the "competence_items" property in the CompetenceGroupe entity, but i'm really not sure ...

Thanks for your help !

3

There are 3 best solutions below

0
On

A good practice may be to have a competence form, which would be call inside your competence group form

You may add a CollectionType as parrent and include query to search which competence already exist

There are some good example with post form type in symfony demo blog

Or you can use form events (onSubmit, preSubmit, etc...) to charge your entity with your required competence. This example show a message form which allow to choose friend from preset data, this is a good example.

2
On

EDIT: If one competenceGroupe can have many competenceItems, then that is a OneToMany relationship; this is the inverse side of the relationship as defined by doctrine, but that is ok. Your question asked how to pull a competenceGroupe and retrieve all related competenceItems. You can do this by making the competenceItems an ArrayCollection in your CompetenceGroupe entity, just as you have done. You do have to define that further in the annotation, see (updated) code below.

For an ArrayCollection, you can remove your method setCompetenceItems and instead define a method addCompetenceItem in your CompetenceGroupe entity.

class CompetenceGroupe
{
    /**
     * @ORM\OneToMany(targetEntity="CompetenceItem", mappedBy="competence_groupe")
     */
     protected $competenceItems;

    public function __construct()
    {
        $this->competenceItems= new ArrayCollection();
    }

    /**
     * Add competenceItem
     *
     * @param CompetenceItem $competenceItem
     * @return CompetenceGroupe
     */
    public function addCompetenceItem(CompetenceItem $competenceItem)
    {
        $this->competence_items->add($competenceItem);

        return $this;
    }
}

You'll also need to define the owning side to make all this work.

0
On

You have tow choice , even to create a Many-To-One, Unidirectional , in this case , you need clean some code , take a look:
In CompetenceGroupe class :

class CompetenceGroupe
{

/**
 * Many competence have One Group.
 * @ManyToOne(targetEntity="CompetenceItem")
 * @JoinColumn(name="id_competence_item", referencedColumnName="id_competence_item")
 */
protected $competence_items;

public function __construct()
{
   // $this->competence_items = new ArrayCollection();
//delete that line
}

In CompetenceItem class :

class CompetenceItem
{

You need to delete private $competence_groupe; attribute with his annotation :

By this way, when you dump a CompetenceGroupe object you gonna find the competence items.

Also, you can do it with One-To-Many, Bidirectional ,if you want to get the data from the inverse side and from the owning side .