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