إنشاء كيان أثناء تحديث كيان آخر

1

لدي سؤال واحد لا أجد إجابة عليه.

لدي كيان المستخدم الخاص بي مع حقل "الحالة".

ما أريد القيام به هو تخزين سطر جديد في جدول آخر "StatusEvent" في كل مرة يتم فيها تغيير حالة المستخدم لتتبع سجل حالات المستخدمين.

لقد حاولت العمل باستخدام طريقة PreUpdate ولكنها لا تسمح بإنشاء كيانات جديدة في هذه الخطوة.

ربما كنت أفكر أنه قد يكون من الممكن مع أحداث أخرى (onFlush ربما؟) ولكن هذه ليست لديها طرق LifecycleEventArgs من PreUpdate (والتي تسمح بمعرفة ما إذا تم تغيير حقل).

أي شخص قد صادف بالفعل نفس النمط أو لديه فكرة عن كيفية تنفيذه؟

شكرا مقدما ،

3 الاجابة

3
افضل جواب

هذه حالة جميلة لاستخدام حدث مخصص ومستمع.

قم بإنشاء فئة UserEvents لإبقاء ثابت مع اسم الحدث مثل

class UserEvents
{
    const STATUS_CHANGED = 'user.status.changed';
}

إنشاء UserStatusChangedEvent الذي يمتد الأحداث ويأخذ المستخدم كمعلمة.

class UserChangedEvent extends Event
{
    private $user;
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function getUser(): User
    {
        return $this->user;
    }
}

ثم قم بإنشاء مستمع وتسجيله لالتقاط / معالجة هذا الحدث وإنشاء الإدخال الذي تحتاجه باستخدام البيانات من كائن المستخدم الذي تم تمريره في حالة إرساله.

class UserListener
{
    public function onStatusChanged(UserChangedEvent $event)
    {
        $user = $event->getUser();
        //TODO: Create your new status change entry. If you need the entity manager, just inject it in the constructor, like with any other service
    }
}

ثم تحتاج إلى تسجيل مستمعك كخدمة ووضع علامة عليه

AppBundle\Event\Listener\UserListener:
    tags:
        - { name: kernel.event_listener, event: user.status.changed, method: onStatusChanged }

والآن كل ما عليك فعله هو إرسال نسخة جديدة من الحدث في كل مرة تتغير فيها الحالة ، لتمريرها إلى المستخدم الذي استمرتم به للتو.

$eventDispatcher->dispatch(
    UserEvents::STATUS_CHANGED,
    $user
);

تحرير: للدفاع عن الإرسال اليدوي للحدث المخصص مقابل الإرسال التلقائي لـ onFlush ، فإن رمز الحدث المخصص أسهل في القراءة حتى من مبتدئ ليس لديه معرفة بكيفية / متى يتم تشغيل أحداث دورة حياة المذهب أو كيف يعمل مدير الكيان داخليا. الكرز في الأعلى هو أن الإرسال يعمل كتذكير جميل بأن لديك مستمعًا هناك ، وهو ما سيكون مفيدًا عند إعادة زيارة الكود الخاص بك في غضون بضعة أشهر.

:مؤلف
2
افضل جواب

يعمل الحل بواسطةDimitris ، ولكنه يتطلب منك إرسال الحدث يدويًا.

أود أن استخدام onFlush طريقة مثلك. (إذا كنت تكتب مكتبة ، فأنت أفضل حالًا مع الحدث المخصص)

يمكنك استخدام UnitOfWork للحصول على مجموعات التغيير.

public function onFlush(OnFlushEventArgs $event)
{
    $em = $event->getEntityManager();
    $uow = $em->getUnitOfWork();

    foreach ($uow->getScheduledEntityInsertions() as $entity) {
        $this->newEntities[] = $entity;

        if ($entity instanceof User) {
            $changeSet = $uow->getEntityChangeSet($entity);

            // if the $changeSet contains the status, log the change
            $log = new Log();

            $em->persist($log);
            $uow->computeChangeSet($em->getClassMetadata(Log::class), $log);
        }
    }

    foreach ($uow->getScheduledEntityUpdates() as $entity) {
        // same here, create a private method to avoid duplication
    }
}

المقايضة لهذا المستمع هو أنه سيتم تسجيل الأشياء فقط على تدفق. إذا تغير كيانك الحالة عدة مرات قبل المسح ، فسيتم تسجيل الحالة الأخيرة فقط. على سبيل المثال ، الحالة 1 -> 2 -> 3 سيتم تسجيلها فقط على أنها الحالة 1 -> 3

إذا كنت تخطط لإنشاء حقل حالة معقدة مع العديد من الحالات والانتقالات ، فألق نظرة على مكون سير العمل واستخدم المستمعين من هناك. إنه عمل أكثر قليلاً ، ولكنه يستحق ذلك.

:مؤلف
0

لذا ما فعلته باتباع نصيحة كل من Dimitris و Padam67.

  • تحديد DoctrineListener الذي يستمع إلى حدث onFlush وتسجيله
  • إرسال حدث مخصص في DoctrineListener
  • تحديد الاستماع إلى EventSubscriber في حدثي المخصص
  • تحديد معالج لإدارة المنطق
  • استدعاء المعالج من EventSubscriber

أعلم أنها تصنع الكثير من الملفات ، لكني أحب فصل كل شيء قدر الإمكان حتى تتمكن من قراءة رمز أنظف وأبسط :)

حدد DoctrineListener التي تستمع في حدث onFlush:

config/services.yaml

App\EventListener\Doctrine\DoctrineListener:
    tags:
        - { name: doctrine.event_listener, event: onFlush }

App\EventListener\Doctrine\DoctrineListener.php

<?php

namespace App\EventListener\Doctrine;

use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\UnitOfWork;
use App\Event\TalentStatusChangedEvent;
use App\Entity\Talent;
use App\Event\Constants\TalentEvents;

class DoctrineListener
{
    private $logger;
    private $dispatcher;

    public function __construct(
        LoggerInterface $logger,
        EventDispatcherInterface $dispatcher
    ) {
        $this->logger = $logger;
        $this->dispatcher = $dispatcher;
    }

    public function onFlush(OnFlushEventArgs $event)
    {
        $entityManager = $event->getEntityManager();
        $uow = $entityManager->getUnitOfWork();

        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            if ($entity instanceof Talent) {
                $this->createTalentStatusChangedEvent($entity, $uow);
            }
        }

        foreach ($uow->getScheduledEntityUpdates() as $entity) {
            if ($entity instanceof Talent) {
                $this->createTalentStatusChangedEvent($entity, $uow);
            }
        }
    }

    private function createTalentStatusChangedEvent(Talent $entity, UnitOfWork $uow)
    {
        $this->logger->info(self::class . ' - talentStatusChanged: ' . $entity . ' - start');
        $changeSet = $uow->getEntityChangeSet($entity);
        if (array_key_exists('status', $changeSet)) {
            $talentStatusChangedEvent = new TalentStatusChangedEvent($entity, new \DateTime());
            $this->dispatcher->dispatch(TalentEvents::STATUS_CHANGED, $talentStatusChangedEvent);
            $this->logger->info(self::class . ' - talentStatusChanged: ' . $entity . ' - success');
        } else {
            $this->logger->info(self::class . ' - talentStatusChanged: ' . $entity . ' - fail');
        }

    }
}

تحديد TalentStatusChangedEvent

App\Event\TalentStatusChangedEvent.php

<?php

namespace App\Event;

use App\Entity\Talent;
use Symfony\Component\EventDispatcher\Event;

class TalentStatusChangedEvent extends Event
{
    private $talent;
    private $statusChangedDate;

    public function __construct(Talent $talent, \DateTime $date)
    {
        $this->talent = $talent;
        $this->statusChangedDate = $date;
    }

    public function getTalent()
    {
        return $this->talent;
    }

    public function getStatus()
    {
        return $this->talent->getStatus();
    }

    public function getStatusChangedDate()
    {
        return $this->statusChangedDate;
    }
}

تحديد EventSubscriber للحدث الخاص بي (تعريف ملف منفصل يحتوي على جميع الأحداث الخاصة بي لكل نوع)

App\EventListener\Admin\User\TalentSubscriber.php

<?php

namespace App\EventListener\Admin\User;

use App\Domain\User\StatusChanged\StatusChangedHandler;
use App\Entity\Talent;
use App\Event\Constants\TalentEvents;
use App\Event\TalentStatusChangedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class TalentSubscriber implements EventSubscriberInterface
{
    private $statusChangedHandler;

    public function __construct(
        StatusChangedHandler $statusChangedHandler
    ) {
        $this->statusChangedHandler = $statusChangedHandler;
    }

    public static function getSubscribedEvents()
    {
        return array(
            TalentEvents::STATUS_CHANGED => 'statusChanged',
        );
    }

    public function statusChanged(TalentStatusChangedEvent $event) {
        $this->statusChangedHandler->handle($event);
    }
}

تعريف معالج لإدارة إنشاء الكيان المرتبط بالفعل

App\Domain\User\StatusChanged.php

<?php

namespace App\Domain\User\StatusChanged;

use App\Entity\Talent;
use App\Entity\TalentStatusEvent;
use App\Event\TalentStatusChangedEvent;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;

class StatusChangedHandler
{
    private $entityManager;
    private $logger;

    public function __construct(
        EntityManagerInterface $entityManager,
        LoggerInterface $logger
    ) {
        $this->entityManager = $entityManager;
        $this->logger = $logger;
    }

    public function handle(TalentStatusChangedEvent $event)
    {
        $this->logger->info(self::class . ' - Talent ' . $event->getTalent() . ' - start');
        $talentStatusEvent = new TalentStatusEvent();
        $talentStatusEvent->setTalent($event->getTalent());
        $talentStatusEvent->setStatus($event->getStatus());
        $this->entityManager->persist($talentStatusEvent);
        // Calling ComputeChangeSet and not flush because we are during the onFlush cycle
        $this->entityManager->getUnitOfWork()->computeChangeSet(
            $this->entityManager->getClassMetadata(TalentStatusEvent::class),
            $talentStatusEvent
        );
        $this->logger->info(self::class . ' - Talent ' . $event->getTalent() . ' - success');
    }
}
:مؤلف

أسئلة ذات صلة

فوق
قائمة طعام