Cómo simplificar una relación Many-To-Many consigo misma

En esta breve entrada expongo cómo el programador puede facilitarse un poco su actividad cuando en Doctrine establezca una relación de una entidad consigo misma. El código que se expone es de una entidad del framework Symfony, pero fácilmente se puede adaptar a cualquier entorno PHP donde, eso sí, se esté empleando este conocido ORM.

Según la documentación de Doctrine, el código para la relación Many-To-Many (de muchos a muchos) de una entidad que se referencia a si misma, por ejemplo un producto que puede tener otros productos relacionados, sería:

class Product
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ManyToMany(targetEntity="Product", mappedBy="myProducts")
     */
    private $relatedWithMe;

    /**
     * @ManyToMany(targetEntity="Product", inversedBy="relatedWithMe", fetch="EAGER")
     * @JoinTable(name="related_products",
     *      joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="related_product_id", referencedColumnName="id")}
     *      )
     */
    private $myProducts;

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

    public function addRelatedWithMe(Product $relatedWithMe): self
    {
        if (!$this->relatedWithMe->contains($relatedWithMe)) {
            $this->relatedWithMe[] = $relatedWithMe;
            $relatedWithMe->addMyProduct($this);
        }

        return $this;
    }

    public function removeRelatedWithMe(Product $relatedWithMe): self
    {
        if ($this->relatedWithMe->contains($relatedWithMe)) {
            $this->relatedWithMe->removeElement($relatedWithMe);
            $relatedWithMe->removeMyProduct($this);
        }

        return $this;
    }

    /**
     * @return Collection|Product[]
     */
    public function getMyProducts(): ?Collection
    {
        return $this->myProducts;
    }

    public function addMyProduct(Product $myProduct): self
    {
        if (!$this->myProducts->contains($myProduct)) {
            $this->myProducts[] = $myProduct;
        }

        return $this;
    }

    public function removeMyProduct(Product $myProduct): self
    {
        if ($this->myProducts->contains($myProduct)) {
            $this->myProducts->removeElement($myProduct);
        }

        return $this;
    }

Sigue leyendo