paolo@bimodesign.com | +34 608 61 64 10

Framework

        

Doctrine - Insert many to many

Using Doctrine you could need to manage the insert/update in a relational tables. If the OneToMany isn't so difficult, in case of ManyToMany relationship you will follow this topics.
First of all we'll have this database schema's

doctrine insert many to many
I defined in that way cause for every sites could have associated to a country y every country could have more language associated. For example Canada has English/France or the administrator should assign several languages to a country, and depending of user settings, the application choose the right language.
So the issue will be to insert, from the same form/post action all the countries and languages associated into that schema.

1. Generate the entity, using these commands:

./vendor/doctrine/doctrine-module/bin/doctrine-module orm:convert-mapping --namespace="Application\\Entity\\" --force  --from-database annotation ./module/Application/src/
./vendor/doctrine/doctrine-module/bin/doctrine-module orm:generate-entities ./module/Application/src/ --regenerate-entities=true --generate-annotations=true
Those commands generate four entities: - Site.php
- SiteCountry.php
- Language.php
- Country.php

where the more interesting code is into SiteCountry.php:

    namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * SiteCountry
 *
 * @ORM\Table(name="site_country", indexes={@ORM\Index(name="fk_site_country_site_idx", columns={"site_id"}), @ORM\Index(name="fk_site_country_country_idx", columns={"coun_id"}), @ORM\Index(name="fk_site_country_lang_idx", columns={"lang_id"})})
 * @ORM\Entity
 */
class SiteCountry
{
    /**
     * @var \Application\Entity\Language
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\OneToOne(targetEntity="Application\Entity\Language")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="lang_id", referencedColumnName="lang_id", nullable=true)
     * })
     */
    private $lang;

    /**
     * @var \Application\Entity\site
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\OneToOne(targetEntity="Application\Entity\Site")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="site_id", referencedColumnName="site_id", nullable=true)
     * })
     */
    private $site;

    /**
     * @var \Application\Entity\Country
     *
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     * @ORM\OneToOne(targetEntity="Application\Entity\Country")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="coun_id", referencedColumnName="coun_id", nullable=true)
     * })
     */
    private $coun;


    /**
     * Set lang
     *
     * @param \Application\Entity\Language $lang
     * @return SiteCountry
     */
    public function setLang(\Application\Entity\Language $lang)
    {
        $this->lang = $lang;

        return $this;
    }

    /**
     * Get lang
     *
     * @return \Application\Entity\Language 
     */
    public function getLang()
    {
        return $this->lang;
    }

    /**
     * Set site
     *
     * @param \Application\Entity\Site $site
     * @return SiteCountry
     */
    public function setSite(\Application\Entity\Site $site)
    {
        $this->site = $site;

        return $this;
    }

    /**
     * Get site
     *
     * @return \Application\Entity\Site 
     */
    public function getSite()
    {
        return $this->site;
    }

    /**
     * Set coun
     *
     * @param \Application\Entity\Country $coun
     * @return siteCountry
     */
    public function setCoun(\Application\Entity\Country $coun)
    {
        $this->coun = $coun;

        return $this;
    }

    /**
     * Get coun
     *
     * @return \Application\Entity\Country 
     */
    public function getCoun()
    {
        return $this->coun;
    }
}

I started to use this code, but the error was that only the last country and the last language of it will save into the table. To solve that issue, I'm developing this code into :

namespace Application\Model\DB;

class Site extends \Application\Model\DB {

    ... code ...

    protected $entity = '\Application\Entity\Site';
    protected $entitySiteCountry = '\Application\Entity\SiteCountry';
    protected $entitySiteCurrency = '\Application\Entity\SiteCurrency';
    protected $entityCountry = '\Application\Entity\Country';
    protected $entityLanguage = '\Application\Entity\Language';


    public function create($params) {
        $stringCountry = "cou_id";
       
        $site = new $this->entity;
        $siteCountry =  new $this->entitySiteCountry;
       
        $site->setConName($params['con_name']);
        $site->setConDescription($params['con_description']);
        $site->setConTitle($params['con_name']);
       
        $this->em->persist($site);
        $this->em->flush();    <-- FLUSH SITE

        $siteCountry->setCon($site);

        foreach ($params as $keyCounty=>$valueCountry) {
            $posCounntry = strpos($keyCounty, $stringCountry);
           
            if($posCounntry === false){
                //
            }else{
                //
                // SET COUNTRY
                //
                $country = new \Application\Model\DB\Country($this->em);
                $siteCountry->setCou($country->find($params[$keyCounty]));
               
                $piecesCountry = explode("_", $keyCounty);
                $indexCountry = $piecesCountry[2];
               
                $stringLanguage = "lan_id_".$indexCountry;
               
                foreach ($params as $keyLan=>$valueLan) {
                    $posLang = strpos($keyLan, $stringLanguage);
                   
                    if($posLang === false){
                        //
                    }else{
                        //
                        // SET LANGUAGE
                        //
                        $language = new \Application\Model\DB\Language($this->em);
                        $siteCountry->setLan($language->find($params[$keyLan]));
                        $this->em->persist($siteCountry);
                        $this->em->flush(); <-- FLUSH LANGUAGE e COUNTRY
                    }
                }
               

            }
        }

        return true;
    } 
}

So the solution is simple. I need to instance the entity SiteCountry everytime that I have to persist/flush of three code. So I have to change the second foreach, in this way:


.... code ...

foreach ($params as $keyCounty=>$valueCountry) {
    $iCountry = $iCountry +1;
    $posCounntry = strpos($keyCounty, $stringCountry);

    if($posCounntry === false){
        //
    }else{
        $piecesCountry = explode("_", $keyCounty);
        $indexCountry = $piecesCountry[2];

        $stringLanguage = "lan_id_".$indexCountry;

        foreach ($params as $keyLan=>$valueLan) {
            $iLang = $iLang +1;
            $posLang = strpos($keyLan, $stringLanguage);

            if($posLang === false){
                //
            }else{   
                $siteCountry =  new $this->entityContestCountry;  <--- THIS
                $siteCountry->setCont($site);

                $country = new \Application\Model\DB\Country($this->em);
                $siteCountry->setCout($country->find($params[$keyCounty]));

                $language = new \Application\Model\DB\Language($this->em);
                $siteCountry->setLang($language->find($params[$keyLan]));

                $this->em->persist($siteCountry);
            }
        }
    }
}

$this->em->flush();