Ingin meminta pendapat tentang solusi untuk pembaruan saat ini. Beberapa kondisi dan kasus penggunaan:

  1. Teman diperluas banyak properti dari Entitas Pengguna inti dari UserBundle
  2. Saat kami menambahkan teman, entitas pemilik juga akan muncul di entitas anak
  3. Saat kita menghapus teman, teman juga harus dicabut dari asosiasi dengan pihak pemilik

Solusi saya adalah menggunakan pelanggan Acara di onFlush, mungkin ada yang punya pendekatan yang lebih baik?

<?php

namespace App\Bundle\UserBundle\Form\EventListener;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Events;
use Doctrine\ORM\PersistentCollection;
use Oro\Bundle\UserBundle\Entity\User;
use Doctrine\ORM\Event\OnFlushEventArgs;

class UserEventSubscriber implements EventSubscriber
{
    /**
     * @return array
     */
    public function getSubscribedEvents(): array
    {
        return [
            Events::onFlush,
        ];
    }

    /**
     * @param OnFlushEventArgs $args
     */
    public function onFlush(OnFlushEventArgs $args): void
    {
        $uow = $args->getEntityManager()->getUnitOfWork();

        $updates = array_merge(
            $uow->getScheduledCollectionUpdates(),
            $uow->getScheduledCollectionDeletions()
        );

        array_walk(
            $updates,
            fn (PersistentCollection $collection) => $this->processFriendsOnFlush($collection, $args)
        );
    }

    /**
     * @param PersistentCollection $collection
     * @param OnFlushEventArgs     $args
     */
    private function processFriendsOnFlush(PersistentCollection $collection, OnFlushEventArgs $args): void
    {
        $mapping = $collection->getMapping();

        if ($collection->getOwner() instanceof User && $mapping['fieldName'] === 'friends')
        {
            $entityManager = $args->getEntityManager();
            /** @var User $ownerUser */
            $ownerUser = $collection->getOwner();
            $snapshotFriends = $collection->getSnapshot();
            $updatedFriends = $collection->getValues();

            if (count($updatedFriends) > 0 && $snapshotFriends !== $updatedFriends) {
                array_walk($updatedFriends, fn(User $friend) => $this->bindFriendAssociation($entityManager, $ownerUser, $friend));
            }

            if (count($snapshotFriends) > 0) {
                array_walk($snapshotFriends, fn(User $friend) => $this->removeFriendAssociation($entityManager, $ownerUser, $friend));
            }
        }
    }

    /**
     * Bind friend association to keep bidirectional relation on adding new friend user
     *
     * @param EntityManager $entityManager
     * @param User          $ownerUser
     * @param User          $friend
     *
     * @throws \Doctrine\ORM\ORMException
     */
    private function bindFriendAssociation(EntityManager $entityManager, User $ownerUser, User $friend): void
    {
        if (!$friend->getFriends()->contains($ownerUser)) {
            $friend->addFriend($ownerUser);
            $entityManager->persist($friend);
            $entityManager->getUnitOfWork()->computeChangeSet($entityManager->getClassMetadata(get_class($friend)), $friend);
        }
    }

    /**
     * Remove friend user association to keep bidirectional relation on remove friend user from child
     *
     * @param EntityManager $entityManager
     * @param User          $ownerUser
     * @param User          $friend
     *
     * @throws \Doctrine\ORM\ORMException
     */
    private function removeFriendAssociation(EntityManager $entityManager, User $ownerUser, User $friend): void
    {
        if ($friend->getFriends()->contains($ownerUser)) {
            $friend->removeFriend($ownerUser);
            $entityManager->persist($friend);
            $entityManager->getUnitOfWork()->computeChangeSet($entityManager->getClassMetadata(get_class($friend)), $friend);
        }
    }
}


Sama bagaimana saya menambahkan asosiasi ManyToMany:

$this->extendExtension->addManyToManyRelation(
            $schema,
            $schema->getTable('oro_user'),
            'friends',
            $schema->getTable('oro_user'),
            ['last_name', 'first_name'],
            ['last_name', 'first_name'],
            ['last_name', 'first_name'],
            [
                'extend' => [
                    'owner' => ExtendScope::OWNER_CUSTOM,
                    'target_title' => ['id'],
                    'target_detailed' => ['id'],
                    'target_grid' => ['id'],
                    'cascade' => ['persist'],
                ],
                'dataaudit' => ['auditable' => true],
            ]
        );
1
Pavel Budo 11 Mei 2021, 20:47

1 menjawab

Jawaban Terbaik

Pastikan relasinya dua arah, dan Anda telah menginisialisasi ArrayCollection untuk kedua properti dalam konstruktor entitas, seperti yang dinyatakan dalam dokumentasi: https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/association-mapping.html#many-to-many-bidirectional.

Dan Anda memiliki add*, remove* metode dengan implementasi yang tepat, seperti:

    public function addFriend(User $user)
    {
        if (!$this->friends->contains($user)) {
            $this->friends->add($user);
            $user->addFriend($this);
        }

        return $this;
    }

    public function removeFriend(User $user)
    {
        if ($this->friends->contains($user)) {
            $this->friends->removeElement($user);
            $user->removeFriend($this);
        }

        return $this;
    }

Jika itu adalah kode yang dibuat oleh OroPlatform, Anda dapat memengaruhinya dengan menulis Oro\Bundle\EntityExtendBundle\Tools\GeneratorExtensions\AbstractEntityGeneratorExtension khusus. Contoh yang baik adalah Oro\Bundle\EntitySerializedFieldsBundle\Tools SerializedDataGeneratorExtension.

Bagi Anda, itu akan menjadi sesuatu seperti:

# ...
public function generate(array $schema, ClassGenerator $class): void
{
    $method = $class->getMethod('addFriend');
    $method->setBody('if (!$this->friends->contains($user)) {
        $this->friends->add($user);
        $user->addFriend($this);
    }');

    $method = $class->getMethod('removeFriend');
    $method->setBody('if ($this->friends->contains($user)) {
        $this->friends->removeElement($user);
        $user->removeFriend($this);
    }');
}
# ...

Dan jangan lupa untuk wadah layanan dengan tag oro_entity_extend.entity_generator_extension.

1
Andrey Yatsenko 12 Mei 2021, 16:26