cascading manytomany relationship symfony insertion and edit

亡梦爱人 提交于 2019-12-13 04:02:00

问题


i'm here searching for your help, and i hope i can find some help i have a cascading manytomany relationship, and i want to make insertion of objectif i have manytomany between partenaire indicateur, manytomany between indicateur and annee and onetomany between annee objectif

which mean that a partenaire can have many objectif, each of those objectif bellong to an annee and an annee bellong to indicateur

here is my view on twig

<ul class="spancabine">
                                                    <div>
                                                        <label class="checkbox-inline">
                                                            {% for indicateur in indicateures %}
                                                            <li class="check-ser">
                                                                <label class="check">
                                                                    <span><input name="indicateur[]"
                                                                               onchange="change_checkbox(this)"
                                                                               type="checkbox"
                                                                               class="indicateur{{ indicateur.id }}"
                                                                               value="{{ indicateur.id }}">{{ indicateur.titre }} </span>
                                                                </label>
                                                            </li>
                                                        </label>
                                                    </div>
                                                    <div>
                                                        <label class="checkbox-inline">
                                                            <div class="show{{ indicateur.id }}" style="  display: none; margin-left: 100px">
                                                                {% for annee in indicateur.annee %}
                                                                    <li class="check-ser">
                                                                        <label class="check">
                                                                    <span><input type="checkbox" name="annee[]"
                                                                               class="annee{{ annee.id }}"
                                                                               alt="{{ annee.id }}{{ indicateur.id }}"
                                                                               onchange="change_checkboxx(this)"
                                                                               value="{{ annee.id }}">{{ annee.annee }}  </span>
                                                                        </label>
                                                                    </li>
                                                                    <li class="check-ser">
                                                                        <label class="check">
                                                                    <span>
                                                                        <div class="object{{ annee.id }}{{ indicateur.id }}" style="display: none !important;">
                                                                            <input class="object{{ annee.id }}"
                                                                                   type="text" name="objectif[]"
                                                                                   value="" style="">
                                                                        </div> </span>
                                                                        </label>
                                                                    </li>
                                                                    <br>
                                                                {% endfor %}
                                                            </div>
                                                            <br>
                                                            {% endfor %}
                                                            {#<input type="checkbox" value="">Indicateur 1#}
                                                        </label>
                                                    </div>

                                                </ul>

and here is my action of insertion in my controller

public function AjouterPartenaireAction(Request $request) {
    $partenaire = new Partenaire();
    $form = $this->createForm('ApfeBundle\Form\PartenaireType', $partenaire);
    $form->handleRequest($request);
    $indicateures=$request->get('indicateur');
    $annees=$request->get('annee');
    $objectifs=$request->get('objectif');
    $em = $this->getDoctrine()->getManager();
    foreach ($indicateures as $indicateur) {
        $indicateure     = $em->getRepository('ApfeBundle:Indicateure')->findOneById($indicateur);
        foreach ($annees as $annee) {
            $annee = $em->getRepository('ApfeBundle:Annee')->findOneById($annee);
            $annee->addIndicateure($indicateure);
            $em->persist($annee);
            $em->flush();
        }
        $partenaire->addIndicateure($indicateure);
    }
    $em->persist($partenaire);
    $em->flush();
    $mi = new \MultipleIterator();
    $mi->attachIterator(new \ArrayIterator($annees));
    $mi->attachIterator(new \ArrayIterator($objectifs));
    $mi->attachIterator(new \ArrayIterator($indicateures));
    foreach ($mi as $value) {
        $annees =  $value[0];
        $objectif = $value[1];
        $idindicateur = $value[2];
        $em1 = $this->getDoctrine()->getManager();
        $indicateure     = $em->getRepository('ApfeBundle:Indicateure')->findOneById($idindicateur);
        $annee     = $em->getRepository('ApfeBundle:Annee')->findOneById($annees);

        $obejctif = new Objectif();
        $obejctif->setAnneeId($annee);
        $obejctif->setObjectif($objectif);
        $obejctif->setPartenaireId($partenaire);
        $obejctif->setIndicateureId($indicateure);
        $em1->persist($obejctif);
        $em1->flush();
    }

    $form = $this->createForm(new PartenaireType(), $partenaire);
    $form->handleRequest($request);
    $em = $this->getDoctrine()->getManager();
    $idpartenaire = $partenaire->getId();
    $partenaires = $em->getRepository('ApfeBundle:Partenaire')->findAll();
    return $this->container->get('templating')->renderResponse('partenaire/new.html.twig', array(
        'idpartenaire' => $idpartenaire,
        'partenaires' => $partenaires,
    ));
}

the probleme is when i submit it doesnt insert correctly all my entreies as it appear in the pictures bellow

somebody can help please thank you


回答1:


Ok. This will be a reeeealy long post. Sorry for that.

Here's how I replicated your problem.

First create the entities. I'm using yml for that, so if you use annotations, then it will be your job to convert yml style to annotation style.

//AppBundle/Resource/config/doctrine/Annee.orm.yml
AppBundle\Entity\Annee:
    type: entity
    table: null
    repositoryClass: AppBundle\Repository\AnneeRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    manyToMany:
        indicateurs:
            targetEntity: AppBundle\Entity\Indicateur
            mappedBy: annees
            cascade: ['persist']
            fetch: EAGER
    oneToMany:
        objectifes:
            targetEntity: AppBundle\Entity\Objectif
            mappedBy: annee
            cascade: ['persist', 'remove']
            orphanRemoval: true
            nullable: true
    fields:
        name:
            type: string
            length: 255
    lifecycleCallbacks: {  }


//AppBundle/Resource/config/doctrine/Indicateur.orm.yml
    AppBundle\Entity\Indicateur:
    type: entity
    table: null
    repositoryClass: AppBundle\Repository\IndicateurRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    manyToMany:
        annees:
            targetEntity: AppBundle\Entity\Annee
            inversedBy: indicateurs
            joinTable:
                name: annees_indicateurs
                joinColumns:
                    indicateur_id:
                        referencedColumnName: id
                inverseJoinColumns:
                    annee_id:
                        referencedColumnName: id
            cascade: ['persist', 'remove']
            fetch: EAGER
        partenaires:
            targetEntity: AppBundle\Entity\Partenaire
            mappedBy: indicateurs
            cascade: ['persist']
            fetch: EAGER
    fields:
        name:
            type: string
            length: 255
    lifecycleCallbacks: {  }


//AppBundle/Resources/config/doctrine/Objectif.orm.yml
AppBundle\Entity\Objectif:
    type: entity
    table: null
    repositoryClass: AppBundle\Repository\ObjectifRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    manyToOne:
        annee:
            targetEntity: AppBundle\Entity\Annee
            inversedBy: objectifes
            joinColumn:
                name: annee_id
                referencedColumnName: id
            cascade: ['persist']
    fields:
        name:
            type: string
            length: 255
    lifecycleCallbacks: {  }


//AppBundle/Resources/config/doctrine/Partenaire.orm.yml
AppBundle\Entity\Partenaire:
    type: entity
    table: null
    repositoryClass: AppBundle\Repository\PartenaireRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    manyToMany:
        indicateurs:
            targetEntity: AppBundle\Entity\Indicateur
            inversedBy: partenaires
            joinTable:
                name: indicateurs_partenaires
                joinColumns:
                    partenaire_id:
                        referencedColumnName: id
                inverseJoinColumns:
                    indicateur_id:
                        referencedColumnName: id
            cascade: ['persist', 'remove']
            fetch: EAGER
    fields:
        name:
            type: string
            length: 255
    lifecycleCallbacks: {  }

Next step is to set up the doctrine's methods. Those will be used, internally, to take actions.

//AppBundle/Entity/Annee.php
//pay attention to the way addIndicateur(), removeIndicateur() methods are wrote. They are essential so that the many-to-many between Annee and Indicateur entities to work. The same for Indicateur.php and Partenaire.php
<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Annee
 */
class Annee
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $objectifes;

    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $indicateurs;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->objectifes = new \Doctrine\Common\Collections\ArrayCollection();
        $this->indicateurs = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Annee
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * Add objectife
     *
     * @param \AppBundle\Entity\Objectif $objectife
     * @return Annee
     */
    public function addObjectife(\AppBundle\Entity\Objectif $objectife)
    {
        $this->objectifes[] = $objectife;

        return $this;
    }

    /**
     * Remove objectife
     *
     * @param \AppBundle\Entity\Objectif $objectife
     */
    public function removeObjectife(\AppBundle\Entity\Objectif $objectife)
    {
        $this->objectifes->removeElement($objectife);
    }

    /**
     * Get objectifes
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getObjectifes()
    {
        return $this->objectifes;
    }

    /**
     * Add indicateur
     *
     * @param \AppBundle\Entity\Indicateur $indicateur
     * @return Annee
     */
    public function addIndicateur(\AppBundle\Entity\Indicateur $indicateur)
    {
        if ($this->indicateurs->contains($indicateur)) {
            return;
        }

        $this->indicateurs[] = $indicateur;
        $indicateur->addAnnee($this);

        return $this;
    }

    /**
     * Remove indicateur
     *
     * @param \AppBundle\Entity\Indicateur $indicateur
     */
    public function removeIndicateur(\AppBundle\Entity\Indicateur $indicateur)
    {
        if (!$this->indicateurs->contains($indicateur)) {
            return;
        }

        $this->indicateurs->removeElement($indicateur);
        $indicateur->removeAnnee($this);
    }

    /**
     * Get indicateurs
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getIndicateurs()
    {
        return $this->indicateurs;
    }
}



//AppBundle/Entity/Indicateur.php
<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Indicateur
 */
class Indicateur
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Indicateur
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $annees;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->annees = new \Doctrine\Common\Collections\ArrayCollection();
        $this->partenaires = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Add annee
     *
     * @param \AppBundle\Entity\Annee $annee
     * @return Indicateur
     */
    public function addAnnee(\AppBundle\Entity\Annee $annee)
    {
        if ($this->annees->contains($annee)) {
            return;
        }

        $this->annees[] = $annee;
        $annee->addIndicateur($this);

        return $this;
    }

    /**
     * Remove annee
     *
     * @param \AppBundle\Entity\Annee $annee
     */
    public function removeAnnee(\AppBundle\Entity\Annee $annee)
    {
        if (!$this->annees->contains($annee)) {
            return;
        }

        $this->annees->removeElement($annee);
        $annee->removeIndicateur($this);
    }

    /**
     * Get annees
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getAnnees()
    {
        return $this->annees;
    }

    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $partenaires;

    /**
     * Add partenaire
     *
     * @param \AppBundle\Entity\Partenaire $partenaire
     * @return Indicateur
     */
    public function addPartenaire(\AppBundle\Entity\Partenaire $partenaire)
    {
        if ($this->partenaires->contains($partenaire)) {
            return;
        }

        $this->partenaires[] = $partenaire;
        $partenaire->addIndicateur($this);

        return $this;
    }

    /**
     * Remove partenaire
     *
     * @param \AppBundle\Entity\Partenaire $partenaire
     */
    public function removePartenaire(\AppBundle\Entity\Partenaire $partenaire)
    {
        if (!$this->partenaires->contains($partenaire)) {
            return;
        }

        $this->partenaires->removeElement($partenaire);
        $partenaire->removeIndicateur($this);
    }

    /**
     * Get partenaires
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getPartenaires()
    {
        return $this->partenaires;
    }
}



//AppBundle/Entity/Objectif.php
<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Objectif
 */
class Objectif
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Objectif
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * @var \AppBundle\Entity\Annee
     */
    private $annee;

    /**
     * Set annee
     *
     * @param \AppBundle\Entity\Annee $annee
     * @return Objectif
     */
    public function setAnnee(\AppBundle\Entity\Annee $annee = null)
    {
        $this->annee = $annee;

        return $this;
    }

    /**
     * Get annee
     *
     * @return \AppBundle\Entity\Annee 
     */
    public function getAnnee()
    {
        return $this->annee;
    }
}



//AppBundle/Entity/Partenaire.php
<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Partenaire
 */
class Partenaire
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Partenaire
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * @var \Doctrine\Common\Collections\Collection
     */
    private $indicateurs;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->indicateurs = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Add indicateur
     *
     * @param \AppBundle\Entity\Indicateur $indicateur
     * @return Partenaire
     */
    public function addIndicateur(\AppBundle\Entity\Indicateur $indicateur)
    {
        if ($this->indicateurs->contains($indicateur)) {
            return;
        }

        $this->indicateurs[] = $indicateur;
        $indicateur->addPartenaire($this);

        return $this;
    }

    /**
     * Remove indicateur
     *
     * @param \AppBundle\Entity\Indicateur $indicateur
     */
    public function removeIndicateur(\AppBundle\Entity\Indicateur $indicateur)
    {
        if (!$this->indicateurs->contains($indicateur)) {
            return;
        }

        $this->indicateurs->removeElement($indicateur);
        $indicateur->removePartenaire($this);
    }

    /**
     * Get indicateurs
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getIndicateurs()
    {
        return $this->indicateurs;
    }
}

Next, the forms (Type). I used php app/console doctrine:generate:crud for each entity, and I altered the resulted forms as:

//AppBundle/Form/AnneeType.php
<?php

namespace AppBundle\Form;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AnneeType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('indicateurs', EntityType::class, [
                'class' => 'AppBundle:Indicateur',
                'placeholder' => 'Choose an Indicateur',
                'choice_label' => function($indicateurs) {
                    return $indicateurs->getName();
                },
                'multiple' => true,
                'expanded' => false,
                'by_reference' => false,
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Annee'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_annee';
    }
}




//AppBundle/Form/IndicateurType.php
<?php

namespace AppBundle\Form;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class IndicateurType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('partenaires', EntityType::class, [
                'class' => 'AppBundle:Partenaire',
                'placeholder' => 'Choose a Partenaire',
                'choice_label' => function($partenaire) {
                    return $partenaire->getName();
                },
                'multiple' => true,
                'expanded' => false,
                'by_reference' => false,
            ])
            ->add('annees', EntityType::class, [
                'class' => 'AppBundle:Annee',
                'placeholder' => 'Choose an Annee',
                'choice_label' => function($annee) {
                    return $annee->getName();
                },
                'multiple' => true,
                'expanded' => false,
                'by_reference' => false,
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Indicateur'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_indicateur';
    }
}




//AppBundle/Form/ObjectifType.php
<?php

namespace AppBundle\Form;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ObjectifType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('annee', EntityType::class, [
                'class' => 'AppBundle:Annee',
                'placeholder' => 'Select Annee',
                'choice_label' => function($annee) {
                    return $annee->getName();
                },
                'multiple' => false,
                'expanded' => false
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Objectif'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_objectif';
    }
}





//AppBundle/Form/PartenaireType.php
<?php

namespace AppBundle\Form;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PartenaireType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('indicateurs', EntityType::class, [
                'class' => 'AppBundle:Indicateur',
                'placeholder' => 'Choose an Indicateur',
                'choice_label' => function($indicateur) {
                    return $indicateur->getName();
                },
                'multiple' => true,
                'expanded' => false,
                'by_reference' => false,
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Partenaire'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_partenaire';
    }
}

For rendering, I used the default crud forms, with the addition of novalidate attribute (for all of the forms):

{{ form_start(form,{attr:{'novalidate':'novalidate'}}) }}

I used the default CRUD controllers, and routes. If you are using, like me, yaml when creating the CRUD for each entity, then you'll need to import the main routing.yml file into app/config/routing.yml:

crud:
    resource: '@AppBundle/Resources/config/routing.yml'

otherwise, if you're using annotations, you're all set.



来源:https://stackoverflow.com/questions/46779882/cascading-manytomany-relationship-symfony-insertion-and-edit

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!