IgorPI
@IgorPI

Entity of type 'Src\Entity\User' for IDs id(52) was not found?

Использую doctrine iterable
Массовое обновление.

Задача выполняется асинхронно.

Есть сомнения, что я очищаю уже заполненые сущности, которые пытаюсь после их использовать.

Как оптимально обновлять сущности в итераторе, что бы не просесть по памяти и при этом очищать память.

Это мой вариант.
Борода

<?php

namespace Src\MessageHandler;

use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use Src\Entity as Entity;
use Src\Message as Message;
use Src\Service as Service;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;

/**
 * Передача контактов другому пользователю.
 */
final class TransferContactsHandler implements MessageHandlerInterface
{
    private EntityManagerInterface $entityManager;
    private MessageBusInterface $messageBus;
    private Service\SSENotification $SSENotification;

    public function __construct(
        EntityManagerInterface  $entityManager,
        MessageBusInterface     $messageBus,
        Service\SSENotification $SSENotification
    )
    {
        $this->entityManager = $entityManager;
        $this->messageBus = $messageBus;
        $this->SSENotification = $SSENotification;
    }

    /**
     * @param Message\TransferContacts $message
     *
     * @throws \Doctrine\DBAL\ConnectionException
     */
    public function __invoke(Message\TransferContacts $message)
    {
        $this->entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
        $projectRepository = $this->entityManager->getRepository(Entity\Project::class);
        $userRepository = $this->entityManager->getRepository(Entity\User::class);

        $usersCollection = $this->getUsers($message->getUserIds());

        if ($usersCollection->count() === 0) {
            return;
        }

        $usersIterator = $usersCollection->getIterator();
        $contactsIterator = $this->getContactsIterator($message->getContactIds());

        $project = null;
        if ($message->getProjectId() > 0) {
            $project = $projectRepository->find($message->getProjectId());
        }

        $contactTransferCounter = 0;

        // originator - Пользователь / Инициатор
        $originator = $userRepository->find($message->getOriginator());

        $transferCounter = [];

        $batchSize = 10; // Количество итераций для фиксации.
        $i = 0;
        $entities = []; // Для освобождения памяти.

        // Процесс назначения ответственных.
        while ($contactsIterator->valid()) {
            $contact = $contactsIterator->current();
            $entities[] = $contact;

            if (!$contact instanceof Entity\Contact) {
                break;
            }

            if (!$usersIterator->valid()) {
                // Перемотка в начало
                $usersIterator->rewind();
            }

            /**@var Entity\User $targetUser */
            $targetUser = $usersIterator->current();

            $contact->setOwner($targetUser);

            // Подсчёт количества операций для каждого пользователя.
            $transferCounter[$targetUser->getId()] = ($transferCounter[$targetUser->getId()] ?? 0) + 1;

            // Опционально меняем проект
            if ($project instanceof Entity\Project) {
                // Обязательно проверь, есть ли проект у контакта
                if ($contact->getProject() instanceof Entity\Project) {
                    if ($project->getId() != $contact->getProject()->getId()) {
                        $contact->setProject($project);
                        $contact->setLastCallAt(null);
                        $contact->setLastStatus(null);
                    }
                } else {
                    $contact->setProject($project);
                    $contact->setLastCallAt(null);
                    $contact->setLastStatus(null);
                }
            }

            // Передаю задачи
            /** @var Entity\Task $task */
            foreach ($contact->getTasks()->getIterator() as $task) {
                $task->setOwner($targetUser);
                $task->setProject($contact->getProject()); // Так же меняем проект задачи.
            }

            // Опционально меняю дату создания контакта.
            if ($message->getNewDateTime() instanceof DateTime) {
                $contact->setCreatedAt($message->getNewDateTime());
            }

            $usersIterator->next();
            $contactsIterator->next();

            ++$contactTransferCounter;

            if ((++$i % $batchSize) === 0) {
                $this->entityManager->flush(); // Фиксирую изменения.

                // Уничтожаю обработанные объекты.
                foreach ($entities as $key => $item) {
                    unset($entities[$key]);
                }
            }
        }

        $this->entityManager->flush();
        $this->entityManager->clear();

        if ($contactTransferCounter) {

            // Информируем пользователя, который совершил передачу контактов.
            $this->messageBus->dispatch(
                (new Envelope(
                    new Message\SystemNotification(
                        sprintf("%d контакт(ов) успешно передан(ы)!", $contactTransferCounter),
                        $message->getOriginator()
                    )
                ))->with(new DispatchAfterCurrentBusStamp())
            );


            // Создание системных уведомлений для каждого пользователя.
            foreach ($transferCounter as $user_id => $count) {
                $this->messageBus->dispatch(
                    (new Envelope(
                        new Message\SystemNotification(
                            sprintf("%s Вам передал %d контакт(ов)", $originator->getFullName(), $count),
                            $user_id
                        )
                    ))->with(new DispatchAfterCurrentBusStamp())
                );
            }

            $this->SSENotification->emit("contacts-transferred", [], $message->getOriginator());
        }
    }

    private function getContactsIterator (array $ids): iterable
    {
        return $this->entityManager
            ->createQuery(sprintf("select c from %s c where c.id in (:ids)", Entity\Contact::class))
            ->toIterable([
                "ids" => $ids
            ]);
    }

    /**
     * @param array $ids
     *
     * @return ArrayCollection
     */
    private function getUsers (array $ids): ArrayCollection
    {
       // TODO: В будущем будут другие условия выборки
        return new ArrayCollection($this->entityManager->getRepository(Entity\User::class)->findBy(["id" => $ids]));
    }
}

  • Вопрос задан
  • 449 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы