简体   繁体   中英

OneToMany relation operations in doctrine-symfony

It is not clear to me how to handle the operations from the OneToMany side.

Let us say object A is on the ManyToOne side, and object B on the OneToMany side.

  1. I want when creating an object B, to assign many objects A to it - my solution in this was to fetch the A objects and assign to them the B object

  2. I want when deleting an object B, to set to null all references from objects A - from researching I see that I probably have to add an ondelete="setNull" functionality on the table

Is there a better way (or alternatives in general) to handle this situation?

use Doctrine\ORM\Mapping as ORM;

class AObject
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\BObject", inversedBy="a", onDelete="SET NULL")
     */
    private $b;
}

class BObject
{
    // ...

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\AObject", mappedBy="b", cascade={"persist"})
     */
    private $a;

    public function __construct()
    {
        $this->a = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * @return \Doctrine\Common\Collections\ArrayCollection
     */
    public getA()
    {
        return $this->a;
    }

    /**
     * @param \Doctrine\Common\Collections\ArrayCollection $a
     */
    public setA($a)
    {
        $this->a = $a;
    }

    /**
     * @param AObject $a
     * @return BObject
     */
    public addA($a)
    {
        $this->a[] = $a;
        $a->setB($this); // assign B object to A

        return $this; // For method chaining
    }

    /**
     * @param AObject $a
     */
    public removeA($a)
    {
        $this->a->removeElement($a);
    }
}

For 1) you'll still have to fetch your objects, your code can't magically know which A objects assign to your new B object.

With the class B defined above, you can write

$b = new BObject();
$b
    ->addA($a1)
    ->addA($a2)
    ->addA(new AObject());
$entityManager->persist($b);
$entityManager->flush();

and A objects will have the reference to B.

For 2) it's yet the better solution, handle it to the database level.

For your purposes you can use doctrine lifecycle events .

For sample, first you need to define event listener:

# /src/FooBundle/Resources/config/services.yml
services:

# Event subscribers
foo.bar.subscriber:
    class: FooBundle\EventListener\BarSubscriber
    tags:
       - { name: doctrine.event_subscriber }

Then, create one:

// FooBundle\EventListener\BarSubscriber.php
namespace FooBundle\EventListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;

class BarSubscriber implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return array(
            'preRemove',
            'postPersist',
        );
    }

    // This method will be executed on each entity, before it has been deleted
    // You can do here what u want in your second paragraph
    public function preRemove(LifecycleEventArgs $args) 
    {
        $entity = $args->getEntity();

        // Do not forget to check your entity type
        if (!$entity instanceof SomeInterface) {
            return;
        }

        // Some code that handle some actions on related entites
    }

    // This code will be executed after each entity creation
    // Here you can add more relations for your main entity
    public function postPersist(LifecycleEventArgs $args) 
    {
        $entity = $args->getEntity();
        if (!$entity instanceof TimestampableInterface) {
            return;
        }

        // Some actions on relations, after the establishment of the main object
    }
}

Also, as an alternative, you can use Doctrine cascade operations , for removing all child entites

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM