nordsoftware/lumen-doctrine

View on GitHub
src/DoctrineServiceProvider.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php namespace Nord\Lumen\Doctrine\ORM;

use Doctrine\Common\Cache\Cache;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Tools\Setup;
use Exception;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\ServiceProvider;
use Nord\Lumen\Doctrine\ORM\Configuration\ConnectionConfiguration;
use Nord\Lumen\Doctrine\ORM\Configuration\SqlAdapter;
use Nord\Lumen\Doctrine\ORM\Configuration\SqliteAdapter;
use Nord\Lumen\Doctrine\ORM\Contracts\ConfigurationAdapter;

class DoctrineServiceProvider extends ServiceProvider
{
    const CONFIG_KEY = 'doctrine';

    const METADATA_ANNOTATIONS = 'annotations';
    const METADATA_XML         = 'xml';
    const METADATA_YAML        = 'yaml';

    const DRIVER_MYSQL  = 'mysql';
    const DRIVER_PGSQL  = 'pgsql';
    const DRIVER_SQLSRV = 'sqlsrv';
    const DRIVER_SQLITE = 'sqlite';


    /**
     * @inheritdoc
     */
    public function register()
    {
        $this->app->configure(self::CONFIG_KEY);

        $this->registerBindings($this->app, $this->app['config']);
        $this->registerFacades();
        $this->registerCommands();
    }


    /**
     * Registers container bindings.
     *
     * @param Container        $container
     * @param ConfigRepository $config
     */
    protected function registerBindings(Container $container, ConfigRepository $config)
    {
        $container->singleton('Doctrine\ORM\EntityManager', function () use ($config) {
            return $this->createEntityManager($config);
        });

        $container->alias('Doctrine\ORM\EntityManager', 'Doctrine\ORM\EntityManagerInterface');
    }


    /**
     * Registers facades.
     */
    protected function registerFacades()
    {
        if (!class_exists('EntityManager')) {
            class_alias('Nord\Lumen\Doctrine\ORM\Facades\EntityManager', 'EntityManager');
        }
    }


    /**
     * Registers console commands.
     */
    protected function registerCommands()
    {
        $this->commands([
            'Nord\Lumen\Doctrine\ORM\Console\GenerateProxiesCommand',
            'Nord\Lumen\Doctrine\ORM\Console\SchemaCreateCommand',
            'Nord\Lumen\Doctrine\ORM\Console\SchemaDropCommand',
            'Nord\Lumen\Doctrine\ORM\Console\SchemaUpdateCommand',
            'Nord\Lumen\Doctrine\ORM\Console\FixturesLoadCommand',
        ]);
    }


    /**
     * Creates the Doctrine entity manager instance.
     *
     * @param ConfigRepository $config
     *
     * @return EntityManager
     * @throws Exception
     * @throws \Doctrine\ORM\ORMException
     */
    protected function createEntityManager(ConfigRepository $config)
    {
        if (!isset($config['doctrine'])) {
            throw new Exception('Doctrine configuration not registered.');
        }

        if (!isset($config['database'])) {
            throw new Exception('Database configuration not registered.');
        }

        $doctrineConfig = $config['doctrine'];
        $databaseConfig = $config['database'];

        $connectionConfig = $this->createConnectionConfig($doctrineConfig, $databaseConfig);

        $type              = array_get($doctrineConfig, 'mapping', self::METADATA_ANNOTATIONS);
        $paths             = array_get($doctrineConfig, 'paths', [base_path('app/Entities')]);
        $debug             = $config['app.debug'];
        $proxyDir          = array_get($doctrineConfig, 'proxy.directory');
        $simpleAnnotations = array_get($doctrineConfig, 'simple_annotations', false);

        $metadataConfiguration = $this->createMetadataConfiguration($type, $paths, $debug, $proxyDir, null,
            $simpleAnnotations);

        $this->configureMetadataConfiguration($metadataConfiguration, $doctrineConfig);

        $eventManager = new EventManager;

        $this->configureEventManager($doctrineConfig, $eventManager);

        $entityManager = EntityManager::create($connectionConfig, $metadataConfiguration, $eventManager);

        $this->configureEntityManager($doctrineConfig, $entityManager);

        return $entityManager;
    }


    /**
     * Creates the Doctrine connection configuration.
     *
     * @param array $doctrineConfig
     * @param array $databaseConfig
     *
     * @return array
     * @throws Exception
     */
    protected function createConnectionConfig(array $doctrineConfig, array $databaseConfig)
    {
        $connectionName   = array_get($doctrineConfig, 'connection', $databaseConfig['default']);
        $connectionConfig = array_get($databaseConfig['connections'], $connectionName);

        if ($connectionConfig === null) {
            throw new Exception("Configuration for connection '$connectionName' not found.");
        }

        return $this->normalizeConnectionConfig($connectionConfig);
    }


    /**
     * Normalizes the connection config to a format Doctrine can use.
     *
     * @param array $config
     *
     * @return array
     * @throws \Exception
     */
    protected function normalizeConnectionConfig(array $config)
    {
        $adapter = $this->createConfigurationAdapter($config['driver']);

        $configuration = new ConnectionConfiguration($adapter);

        return $configuration->map($config);
    }


    /**
     * @param string $driver
     *
     * @return ConfigurationAdapter
     * @throws Exception
     */
    protected function createConfigurationAdapter($driver)
    {
        switch ($driver) {
            case self::DRIVER_MYSQL:
            case self::DRIVER_PGSQL:
            case self::DRIVER_SQLSRV:
                return new SqlAdapter();
            case self::DRIVER_SQLITE:
                return new SqliteAdapter();
            default:
                throw new Exception("Driver '{$driver}' is not supported.");
        }
    }


    /**
     * Creates the metadata configuration instance.
     *
     * @param string $type
     * @param array  $paths
     * @param bool   $isDevMode
     * @param string $proxyDir
     * @param Cache  $cache
     * @param bool   $useSimpleAnnotationReader
     *
     * @return Configuration
     * @throws \Exception
     */
    protected function createMetadataConfiguration(
        $type,
        $paths,
        $isDevMode,
        $proxyDir,
        $cache,
        $useSimpleAnnotationReader = true
    ) {
        switch ($type) {
            case self::METADATA_ANNOTATIONS:
                return Setup::createAnnotationMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache,
                    $useSimpleAnnotationReader);
            case self::METADATA_XML:
                return Setup::createXMLMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
            case self::METADATA_YAML:
                return Setup::createYAMLMetadataConfiguration($paths, $isDevMode, $proxyDir, $cache);
            default:
                throw new Exception("Metadata type '$type' is not supported.");
        }
    }


    /**
     * Configures the metadata configuration instance.
     *
     * @param Configuration $configuration
     * @param array         $doctrineConfig
     *
     * @throws ORMException
     */
    protected function configureMetadataConfiguration(
        Configuration $configuration,
        array $doctrineConfig
    ) {
        if (isset($doctrineConfig['filters'])) {
            foreach ($doctrineConfig['filters'] as $name => $filter) {
                $configuration->addFilter($name, $filter['class']);
            }
        }
        if (isset($doctrineConfig['logger'])) {
            $configuration->setSQLLogger($doctrineConfig['logger']);
        }
        if (isset($doctrineConfig['proxy']) && isset($doctrineConfig['proxy']['auto_generate'])) {
            $configuration->setAutoGenerateProxyClasses($doctrineConfig['proxy']['auto_generate']);
        }
        if (isset($doctrineConfig['proxy']) && isset($doctrineConfig['proxy']['namespace'])) {
            $configuration->setProxyNamespace($doctrineConfig['proxy']['namespace']);
        }
        if (isset($doctrineConfig['repository'])) {
            $configuration->setDefaultRepositoryClassName($doctrineConfig['repository']);
        }

        $namingStrategy = array_get($doctrineConfig, 'naming_strategy', 'Nord\Lumen\Doctrine\ORM\NamingStrategy');
        $configuration->setNamingStrategy(new $namingStrategy);
    }


    /**
     * Configures the Doctrine event manager instance.
     *
     * @param array        $doctrineConfig
     * @param EventManager $eventManager
     */
    protected function configureEventManager(array $doctrineConfig, EventManager $eventManager)
    {
        if (isset($doctrineConfig['event_listeners'])) {
            foreach ($doctrineConfig['event_listeners'] as $name => $listener) {
                $eventManager->addEventListener($listener['events'], new $listener['class']);
            }
        }
    }


    /**
     * Configures the Doctrine entity manager instance.
     *
     * @param array         $doctrineConfig
     * @param EntityManager $entityManager
     */
    protected function configureEntityManager(array $doctrineConfig, EntityManager $entityManager)
    {
        if (isset($doctrineConfig['filters'])) {
            foreach ($doctrineConfig['filters'] as $name => $filter) {
                if (!array_get($filter, 'enabled', false)) {
                    continue;
                }

                $entityManager->getFilters()->enable($name);
            }
        }

        if (isset($doctrineConfig['types'])) {
            $databasePlatform = $entityManager->getConnection()->getDatabasePlatform();

            foreach ($doctrineConfig['types'] as $name => $className) {
                Type::addType($name, $className);
                $databasePlatform->registerDoctrineTypeMapping($name, $name);
            }
        }
    }
}