skqr/hateoas

View on GitHub
Config/ResourceEntityMapper.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * @copyright 2014 Integ S.A.
 * @license http://opensource.org/licenses/MIT The MIT License (MIT)
 * @author Javier Lorenzana <javier.lorenzana@gointegro.com>
 */

namespace GoIntegro\Hateoas\Config;

// ORM.
use Doctrine\ORM\EntityManagerInterface;
// Metadata.
use GoIntegro\Hateoas\Metadata\Entity\MetadataCache;
// RAML.
use GoIntegro\Raml\DocNavigator;
// Utils.
use GoIntegro\Hateoas\Util;

class ResourceEntityMapper
{
    const RESOURCE_ENTITY_INTERFACE = 'GoIntegro\\Hateoas\\JsonApi\\ResourceEntityInterface';

    const ERROR_MISSING_ENTITY = "No entity matches the resource \"%s\".",
        ERROR_ENTITIES_PER_RESOURCE = "The resource \"%s\" listed in the RAML doc matches the following entity class names: \"%s\". If you want to keep the short-names of these resource entities you need to map all but one of them to other resource types in the bundle configuration.";

    /**
     * @var EntityManagerInterface
     */
    private $em;
    /**
     * @var MetadataCache
     */
    private $metadataCache;
    /**
     * @var DocNavigator
     */
    private $docNavigator;
    /**
     * @var ResourceEntityMapCache
     */
    private $mapCache;
    /**
     * @var array
     */
    private $indexedClassNames;

    /**
     * @param EntityManagerInterface $em
     * @param MetadataCache $metadataCache
     * @param DocNavigator $docNavigator
     * @param ResourceEntityMapCache $mapCache
     */
    public function __construct(
        EntityManagerInterface $em,
        MetadataCache $metadataCache,
        DocNavigator $docNavigator,
        ResourceEntityMapCache $mapCache
    )
    {
        $this->em = $em;
        $this->metadataCache = $metadataCache;
        $this->docNavigator = $docNavigator;
        $this->mapCache = $mapCache;
        $this->indexedClassNames = $this->indexEntityClassNames();
    }

    /**
     * @return array
     */
    private function indexEntityClassNames()
    {
        $indexedClassNames = [];
        $entityClassNames = $this->em->getConfiguration()
            ->getMetadataDriverImpl()
            ->getAllClassNames();

        foreach ($entityClassNames as $name) {
            // @todo Support subtypes.
            $resourceType = Util\Inflector::typify($name);
            $indexedClassNames[$resourceType][] = $name;
        }

        return $indexedClassNames;
    }

    /**
     * @return array
     * @throws ResourceEntityMappingException
     * @todo The configuration doesn't actually allow overridding resource type to entity class mappings as the error message suggests. Oops.
     */
    public function map()
    {
        if ($this->mapCache->isFresh()) {
            return $this->mapCache->read();
        }

        $map = [];

        foreach ($this->docNavigator->getDoc()->getResources() as $type) {
            $resourceClasses = $this->getResourceClasses($type);

            if (1 < count($resourceClasses)) {
                $message = sprintf(
                    self::ERROR_ENTITIES_PER_RESOURCE,
                    $type,
                    implode(', ', $resourceClasses)
                );
                throw new ResourceEntityMappingException($message);
            }

            $map[$type] = reset($resourceClasses);
        }

        $this->mapCache->keep($map);

        return $map;
    }

    /**
     * @param string $type
     * @return array
     * @throws ResourceEntityMappingException
     */
    private function getResourceClasses($type)
    {
        if (empty($this->indexedClassNames[$type])) {
            $message = sprintf(self::ERROR_MISSING_ENTITY, $type);
            throw new ResourceEntityMappingException($message);
        }

        $resourceClasses = [];

        foreach ($this->indexedClassNames[$type] as $className) {
            $class = $this->metadataCache->getReflection($className);

            if ($class->implementsInterface(self::RESOURCE_ENTITY_INTERFACE)) {
                $resourceClasses[] = $className;
            }
        }

        return $resourceClasses;
    }
}