Doctrine2 One-To-Many / Many-To-One relations

后端 未结 2 618
傲寒
傲寒 2021-02-03 13:51

So, 1:M / M:1 relations don\'t work the way M:M relations work (obviously), but I thought that with proper configuration, you can get the same output as a M:M relation.

相关标签:
2条回答
  • 2021-02-03 14:14

    I figured it out. Hopefully this post can help others who got as frustrated as I did!

    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 frontend 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 (the 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's offers 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 process 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;
        }
    }
    
    0 讨论(0)
  • 2021-02-03 14:22

    Looks like you're doing it right. Don't worry about the PersistentCollection/ArrayCollection stuff -- all that matters is that they're collections.

    $Path->getOffers() should indeed return a collection of PathOffers, and each PathOffer should have an offer.

    So it ought to work like this:

    //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";
    } 
    

    Am I missing something?

    0 讨论(0)
提交回复
热议问题