问题
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