I
I
Igor2021-09-03 10:21:40
symfony
Igor, 2021-09-03 10:21:40

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

I use doctrine iterable
Bulk update.

The task runs asynchronously.

There are doubts that I clear already filled entities that I try to use after them.

How to optimally update entities in an iterator so as not to run out of memory and at the same time clear the memory.

This is my choice.

Beard

<?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]));
    }
}

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question