components-web-app/api-components-bundle

View on GitHub
src/EventListener/Form/EntityPersistFormListener.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

/*
 * This file is part of the Silverback API Components Bundle Project
 *
 * (c) Daniel West <daniel@silverback.is>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace Silverback\ApiComponentsBundle\EventListener\Form;

use Doctrine\Persistence\ManagerRegistry;
use Silverback\ApiComponentsBundle\AttributeReader\TimestampedAttributeReader;
use Silverback\ApiComponentsBundle\Entity\User\AbstractUser;
use Silverback\ApiComponentsBundle\Event\FormSuccessEvent;
use Silverback\ApiComponentsBundle\EventListener\Api\UserEventListener;
use Silverback\ApiComponentsBundle\Exception\InvalidArgumentException;
use Silverback\ApiComponentsBundle\Helper\Timestamped\TimestampedDataPersister;
use Silverback\ApiComponentsBundle\Helper\User\UserDataProcessor;
use Silverback\ApiComponentsBundle\Serializer\Normalizer\UserNormalizer;
use Silverback\ApiComponentsBundle\Utility\ClassMetadataTrait;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
 * @author Daniel West <daniel@silverback.is>
 */
abstract class EntityPersistFormListener implements FormSuccessEventListenerInterface
{
    use ClassMetadataTrait;

    private ?TimestampedAttributeReader $timestampedAnnotationReader;
    private ?TimestampedDataPersister $timestampedDataPersister;
    private ?UserEventListener $userEventListener;
    /** @var NormalizerInterface|DenormalizerInterface|null */
    private ?NormalizerInterface $normalizer;
    private ?UserDataProcessor $userDataProcessor;
    private string $formType;
    private string $dataClass;
    private bool $returnFormDataOnSuccess;

    public function __construct(string $formType, string $dataClass, bool $returnFormDataOnSuccess = true)
    {
        $this->formType = $formType;
        $this->dataClass = $dataClass;
        $this->returnFormDataOnSuccess = $returnFormDataOnSuccess;
    }

    public function init(
        ManagerRegistry $registry,
        TimestampedAttributeReader $timestampedAnnotationReader,
        TimestampedDataPersister $timestampedDataPersister,
        UserEventListener $userEventListener,
        NormalizerInterface $normalizer,
        UserDataProcessor $userDataProcessor
    ): void {
        if (!$normalizer instanceof DenormalizerInterface) {
            throw new InvalidArgumentException(sprintf('$normalizer must also implement %s', DenormalizerInterface::class));
        }
        $this->initRegistry($registry);
        $this->timestampedAnnotationReader = $timestampedAnnotationReader;
        $this->timestampedDataPersister = $timestampedDataPersister;
        $this->userEventListener = $userEventListener;
        $this->normalizer = $normalizer;
        $this->userDataProcessor = $userDataProcessor;
    }

    public function __invoke(FormSuccessEvent $event): void
    {
        // This is not a sub-request because forms have greater permissions to create entitites with whatever properties wanted.

        if (
            $this->formType !== $event->getForm()->formType
            || !is_a($data = $event->getFormData(), $this->dataClass, true)
        ) {
            return;
        }

        if ($this->timestampedAnnotationReader->isConfigured($data)) {
            $this->timestampedDataPersister->persistTimestampedFields($data, true);
        }

        $entityManager = $this->registry->getManagerForClass($this->dataClass);
        if (!$entityManager) {
            throw new InvalidArgumentException(sprintf('Could not find entity manager for %s', $this->dataClass));
        }

        if ($data instanceof AbstractUser) {
            $uow = $entityManager->getUnitOfWork();
            $oldData = $uow->getOriginalEntityData($data);
            $oldUser = null;
            if (\count($oldData)) {
                $normalized = $this->normalizer->normalize($oldData);
                /** @var AbstractUser $oldUser */
                $oldUser = $this->normalizer->denormalize($normalized, $data::class, null, [
                    UserNormalizer::ALREADY_CALLED => true,
                ]);
            }

            $this->userDataProcessor->processChanges($data, $oldUser);
            $this->userEventListener->postWrite($data, $oldUser);
        }

        $entityManager->persist($data);
        $entityManager->flush();

        if ($this->returnFormDataOnSuccess) {
            $event->result = $data;
        }
    }
}