Passed
Pull Request — master (#5)
by Alex
03:09
created

AbstractCascadeService::getClassMetadata()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 13
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php

declare(strict_types=1);

namespace Arp\DoctrineEntityRepository\Persistence;

use Arp\DoctrineEntityRepository\EntityRepositoryInterface;
use Arp\DoctrineEntityRepository\Persistence\Exception\PersistenceException;
use Arp\Entity\EntityInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Psr\Log\LoggerInterface;

/**
 * @author  Alex Patterson <[email protected]>
 * @package Arp\DoctrineEntityRepository\Persistence
 */
abstract class AbstractCascadeService
{
    /**
     * @var LoggerInterface
     */
    protected LoggerInterface $logger;

    /**
     * @var array
     */
    protected array $options;

    /**
     * @var array
     */
    protected array $collectionOptions;

    /**
     * @param LoggerInterface $logger
     * @param array           $options
     * @param array           $collectionOptions
     */
    public function __construct(LoggerInterface $logger, array $options = [], array $collectionOptions = [])
    {
        $this->logger = $logger;
        $this->options = $options;
        $this->collectionOptions = $collectionOptions;
    }

    /**
     * @param EntityManagerInterface $entityManager
     * @param string                 $entityName
     *
     * @return EntityRepositoryInterface
     * @throws PersistenceException
     * @todo We should implement a way to decorate the call the getRepository() with a concrete implementation
     *       of the EntityRepositoryProviderInterface
     *
     */
    protected function getTargetRepository(
        EntityManagerInterface $entityManager,
        string $entityName
    ): EntityRepositoryInterface {
        try {
            /** @var EntityRepositoryInterface $targetRepository */
            $targetRepository = $entityManager->getRepository($entityName);
        } catch (\Throwable $e) {
            $errorMessage = sprintf(
                'An error occurred while attempting to load the repository for entity class \'%s\' : %s',
                $entityName,
                $e->getMessage()
            );
            $this->logger->error($errorMessage, ['exception' => $e]);

            throw new PersistenceException($errorMessage, $e->getCode(), $e);
        }

        if (null === $targetRepository || !($targetRepository instanceof EntityRepositoryInterface)) {
            $errorMessage = sprintf(
                'The entity repository must be an object of type \'%s\'; \'%s\' returned in \'%s::%s\'',
                EntityRepositoryInterface::class,
                (is_object($targetRepository) ? get_class($targetRepository) : gettype($targetRepository)),
                static::class,
                __FUNCTION__
            );
            $this->logger->error($errorMessage);

            throw new PersistenceException($errorMessage);
        }

        return $targetRepository;
    }

    /**
     * @param EntityInterface $sourceEntity
     * @param string          $fieldName
     * @param ClassMetadata   $sourceMetadata
     * @param ClassMetadata   $targetMetadata
     *
     * @return EntityInterface|EntityInterface[]|iterable
     *
     * @throws PersistenceException
     */
    protected function resolveTargetEntityOrCollection(
        EntityInterface $sourceEntity,
        string $fieldName,
        ClassMetadata $sourceMetadata,
        ClassMetadata $targetMetadata
    ) {
        $methodName = 'get' . ucfirst($fieldName);

        if (!method_exists($sourceEntity, $methodName)) {
            $errorMessage = sprintf(
                'Failed to find required entity method \'%s::%s\'. The method is required for cascade operations '
                . 'of field \'%s\' of target entity \'%s\'',
                $sourceMetadata->getName(),
                $methodName,
                $fieldName,
                $targetMetadata->getName()
            );

            $this->logger->error($errorMessage);

            throw new PersistenceException($errorMessage);
        }

        try {
            $targetEntityOrCollection = $sourceEntity->{$methodName}();
        } catch (\Throwable $e) {
            $errorMessage = sprintf(
                'The call to resolve entity of type \'%s\' from method call \'%s::%s\' failed: %s',
                $targetMetadata->getName(),
                $sourceMetadata->getName(),
                $methodName,
                $e->getMessage()
            );
            $this->logger->error($errorMessage);

            throw new PersistenceException($errorMessage, $e->getCode(), $e);
        }

        return $targetEntityOrCollection;
    }

    /**
     * @param EntityInterface|EntityInterface[]|iterable $entityOrCollection
     * @param array                                      $mapping
     *
     * @return bool
     */
    protected function isValidAssociation($entityOrCollection, array $mapping): bool
    {
        if (null === $entityOrCollection) {
            /**
             * @todo mapping class has a methods to fetch the id field mapping directly
             *
             * Note that we are hard coding the '0' key as the single field the we use as the id/primary key.
             * If we implement EntityInterface correctly we will never have a composite key.
             */
            return isset($mapping['joinColumns'][0]['nullable'])
                ? (bool)$mapping['joinColumns'][0]['nullable']
                : false;
        }

        return (is_callable($entityOrCollection) || $entityOrCollection instanceof EntityInterface);
    }

    /**
     * @param EntityManagerInterface $entityManager
     * @param string                 $entityName
     *
     * @return ClassMetadata
     *
     * @throws PersistenceException
     */
    protected function getClassMetadata(EntityManagerInterface $entityManager, string $entityName): ClassMetadata
    {
        try {
            return $entityManager->getClassMetadata($entityName);
        } catch (\Throwable $e) {
            $errorMessage = sprintf(
                'The entity metadata mapping for class \'%s\' could not be loaded: %s',
                $entityName,
                $e->getMessage()
            );
            $this->logger->error($errorMessage);

            throw new PersistenceException($errorMessage, $e->getCode(), $e);
        }
    }
}
0 ignored issues
show
Coding Style introduced by
End of line character is invalid; expected "\n" but found "\r"
Loading history...