Doctrine2 "Many-many / many-to-one"

So, the 1: M / M: 1 relationship does not work the way the M: M relationship works (obviously), but I thought that with the right configuration, you might get the same result as the M: M ratio.

Basically, I needed to add another field (position) to path_offer .

It seemed to me that I worked until I $path->getOffers() to use $path->getOffers() , which returned PersistentCollection instead of what I thought was forced ( ArrayCollection sentences). Anyway, inside the current table, I have two entries: two sentences along the same path. $path->getOffers() returns a PersistantCollection a PathOffer that has only Offer , not both.

My question is how to really use these types of relationships? Since I need this with many other aspects of this project I'm working on (many M: M collections should also be located)

My code is below!

path.php

 [..] /** * @ORM\Entity * @ORM\Table(name="path") */ class Path { /** * @var integer * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ protected $id; /** * @ORM\OneToMany(targetEntity="PathOffer", mappedBy="offer", cascade={"all"}) */ protected $offers; [..] 

PathOffer.php

 [..] /** * @ORM\Entity * @ORM\Table(name="path_offer") */ class PathOffer { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ protected $id; /** * @ORM\ManyToOne(targetEntity="Path", inversedBy="offers", cascade={"all"}) */ protected $path; /** * @ORM\ManyToOne(targetEntity="Offer", inversedBy="offers", cascade={"all"}) */ protected $offer; /** * @ORM\Column(type="integer") */ protected $pos; [..] 

Offer.php

 [..] /** * @ORM\Entity * @ORM\Table(name="offer") */ class Offer { /** * @var integer * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ protected $id; /** * @var \ZGoffers\MainBundle\Entity\PathOffer * * @ORM\OneToMany(targetEntity="PathOffer", mappedBy="path", cascade={"all"}) */ protected $paths; [..] 
+7
source share
2 answers

I get it. Hope this post can help others who were just as frustrated as I am!

path.php

 <?php namespace JStout\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="path") */ class Path { /** * @var integer * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var \JStout\MainBundle\Entity\PathOffer * * @ORM\OneToMany(targetEntity="PathOffer", mappedBy="path", cascade={"all"}) * @ORM\OrderBy({"pos" = "ASC"}) */ private $offers; [...] 

PathOffer.php

 <?php namespace JStout\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="path_offer") */ class PathOffer { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @ORM\ManyToOne(targetEntity="Path", inversedBy="offers", cascade={"all"}) */ private $path; /** * @ORM\ManyToOne(targetEntity="Offer", inversedBy="paths", cascade={"all"}) */ private $offer; /** * @ORM\Column(type="integer") */ private $pos; [...] 

Offer.php

 <?php namespace JStout\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="offer") */ class Offer { /** * @var integer * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var \JStout\MainBundle\Entity\PathOffer * * @ORM\OneToMany(targetEntity="PathOffer", mappedBy="offer", cascade={"all"}) */ private $paths; [...] 

And for my interface logic:

Pathcontroller.php

 <?php [...] /** * @Extra\Route("/path", name="admin_path") * @Extra\Route("/path/{id}/edit", name="admin_path_edit", requirements={"id" = "\d+"}) * @Extra\Template() */ public function pathAction($id = null) { $path = $this->_getObject('Path', $id); // this function either generates a new entity or grabs one from database depending on $id $form = $this->get('form.factory')->create(new Form\PathType(), $path); $formHandler = $this->get('form.handler')->create(new Form\PathHandler(), $form); // process form if ($formHandler->process()) { $this->get('session')->setFlash('notice', 'Successfully ' . ($this->_isEdit($path) ? 'edited' : 'added') . ' path!'); return $this->redirect($this->generateUrl('admin_path')); } return array( 'path' => $path, 'form' => $form->createView(), 'postUrl' => !$this->_isEdit($path) ? $this->generateUrl('admin_path') : $this->generateUrl('admin_path_edit', array('id' => $path->getId())), 'paths' => $this->_paginate('Path'), 'edit' => $this->_isEdit($path) ? true : false ); } [...] 

PathType.php (path form)

 <?php namespace JStout\MainBundle\Form; use Symfony\Component\Form\AbstractType, Symfony\Component\Form\FormBuilder; class PathType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder ->add('name') ->add('title') ->add('offers', 'collection', array( 'type' => new PathOfferType(), 'allow_add' => true, 'allow_delete' => true )) ->add('active'); } public function getDefaultOptions(array $options) { return array( 'data_class' => 'JStout\MainBundle\Entity\Path' ); } } 

PathOfferType.php (PathType offers a collection type)

 <?php namespace JStout\MainBundle\Form; use Symfony\Component\Form\AbstractType, Symfony\Component\Form\FormBuilder; class PathOfferType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder ->add('offer', 'entity', array( 'class' => 'JStout\MainBundle\Entity\Offer', 'query_builder' => function($repository) { return $repository->createQueryBuilder('o')->orderBy('o.name', 'ASC'); }, 'property' => 'name' )) ->add('pos', 'integer'); } public function getDefaultOptions(array $options) { return array( 'data_class' => 'JStout\MainBundle\Entity\PathOffer' ); } } 

PathHandler.php (how I handle the form)

 <?php namespace JStout\MainBundle\Form; use JStout\MainBundle\Component\Form\FormHandlerInterface, Symfony\Component\Form\Form, Symfony\Component\HttpFoundation\Request, Doctrine\ORM\EntityManager, JStout\MainBundle\Entity\Path; class PathHandler implements FormHandlerInterface { protected $form; protected $request; protected $entityManager; public function buildFormHandler(Form $form, Request $request, EntityManager $entityManager) { $this->form = $form; $this->request = $request; $this->entityManager = $entityManager; } public function process() { if ('POST' == $this->request->getMethod()) { // bind form data $this->form->bindRequest($this->request); // If form is valid if ($this->form->isValid() && ($path = $this->form->getData()) instanceOf Path) { // save offer to the database $this->entityManager->persist($path); foreach ($path->getOffers() as $offer) { $offer->setPath($path); $this->entityManager->persist($offer); } $this->entityManager->flush(); return true; } } return false; } } 
+4
source

You seem to be doing it right. Don't worry about PersistentCollection / ArrayCollection - all that matters is their collections.

$Path->getOffers() should really return a PathOffers collection, and every PathOffer should have a suggestion.

Therefore, it should work as follows:

 //Output a all offers associated with a path, along with the position. $pathOffers = $path->getOffers(); foreach($pathOffers as $po){ echo $po->getOffer()->id . ' [' . $po->getPosition() . "]\n"; } 

Did I miss something?

+2
source

All Articles