ongr-io/ElasticsearchBundle

View on GitHub
DependencyInjection/Compiler/MappingPass.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

/*
 * This file is part of the ONGR package.
 *
 * (c) NFQ Technologies UAB <info@nfq.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ONGR\ElasticsearchBundle\DependencyInjection\Compiler;

use ONGR\ElasticsearchBundle\Annotation\Index;
use ONGR\ElasticsearchBundle\DependencyInjection\Configuration;
use ONGR\ElasticsearchBundle\Mapping\Converter;
use ONGR\ElasticsearchBundle\Mapping\DocumentParser;
use ONGR\ElasticsearchBundle\Mapping\IndexSettings;
use ONGR\ElasticsearchBundle\Service\IndexService;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

class MappingPass implements CompilerPassInterface
{
    /**
     * @var array
     */
    private $indexes = [];

    /**
     * @var string
     */
    private $defaultIndex = null;

    public function process(ContainerBuilder $container)
    {
        $kernelDir = $container->getParameter('kernel.project_dir');

        foreach ($container->getParameter(Configuration::ONGR_SOURCE_DIR) as $dir) {
            $this->handleDirectoryMapping($container, $kernelDir . $dir);
        }

        $container->setParameter(Configuration::ONGR_INDEXES, $this->indexes);
        $container->setParameter(
            Configuration::ONGR_DEFAULT_INDEX,
            $this->defaultIndex ?? current(array_keys($this->indexes))
        );
    }

    /**
     * @param ContainerBuilder $container
     * @param string $dir
     *
     * @throws \ReflectionException
     */
    private function handleDirectoryMapping(ContainerBuilder $container, string $dir): void
    {
        /** @var DocumentParser $parser */
        $parser = $container->get(DocumentParser::class);
        $indexesOverride = $container->getParameter(Configuration::ONGR_INDEXES_OVERRIDE);
        $converterDefinition = $container->getDefinition(Converter::class);

        foreach ($this->getNamespaces($dir) as $namespace) {
            $class = new \ReflectionClass($namespace);

            if (isset($indexesOverride[$namespace]['alias']) && $indexesOverride[$namespace]['alias']) {
                $indexAlias = $indexesOverride[$namespace]['alias'];
            } else {
                $indexAlias = $parser->getIndexAliasName($class);
            }

            /** @var Index $document */
            $document = $parser->getIndexAnnotation($class);
            $indexMetadata = $parser->getIndexMetadata($class);

            if (!empty($indexMetadata)) {
                $indexMetadata['settings'] = array_filter(
                    array_replace_recursive(
                        $indexMetadata['settings'] ?? [],
                        [
                            'number_of_replicas' => $document->numberOfReplicas,
                            'number_of_shards' => $document->numberOfShards,
                        ],
                        $indexesOverride[$namespace]['settings'] ?? []
                    ),
                    function ($value) {
                        if (0 === $value) {
                            return true;
                        }

                        return (bool)$value;
                    }
                );

                $indexSettings = new Definition(
                    IndexSettings::class,
                    [
                        $namespace,
                        $indexAlias,
                        $indexAlias,
                        $indexMetadata,
                        $indexesOverride[$namespace]['hosts'] ?? $document->hosts,
                        $indexesOverride[$namespace]['default'] ?? $document->default,
                    ]
                );

                $indexServiceDefinition = new Definition(IndexService::class, [
                    $namespace,
                    $converterDefinition,
                    $container->getDefinition('event_dispatcher'),
                    $indexSettings,
                    $container->getParameter(Configuration::ONGR_PROFILER_CONFIG)
                        ? $container->getDefinition('ongr.esb.tracer') : null
                ]);
                $indexServiceDefinition->setPublic(true);
                $converterDefinition->addMethodCall(
                    'addClassMetadata',
                    [
                        $namespace,
                        $parser->getPropertyMetadata($class)
                    ]
                );

                $container->setDefinition($namespace, $indexServiceDefinition);
                $this->indexes[$indexAlias] = $namespace;
                $isCurrentIndexDefault = $parser->isDefaultIndex($class);
                if ($this->defaultIndex && $isCurrentIndexDefault) {
                    throw new \RuntimeException(
                        sprintf(
                            'Only one index can be set as default. We found 2 indexes as default ones `%s` and `%s`',
                            $this->defaultIndex,
                            $indexAlias
                        )
                    );
                }

                if ($isCurrentIndexDefault) {
                    $this->defaultIndex = $indexAlias;
                }
            }
        }
    }

    private function getNamespaces($directory): array
    {
        if (!is_dir($directory)) {
            return [];
        }

        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory));
        $files = new \RegexIterator($iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);

        $documents = [];

        foreach ($files as $file => $v) {
            $documents[] = $this->getFullNamespace($file) . '\\' . $this->getClassname($file);
        }

        return $documents;
    }

    private function getFullNamespace($filename)
    {
        $lines = preg_grep('/^namespace /', file($filename));
        $namespaceLine = array_shift($lines);
        $match = array();
        preg_match('/^namespace (.*);$/', $namespaceLine, $match);
        $fullNamespace = array_pop($match);

        return $fullNamespace;
    }

    private function getClassname($filename)
    {
        $directoriesAndFilename = explode('/', $filename);
        $filename = array_pop($directoriesAndFilename);
        $nameAndExtension = explode('.', $filename);
        $className = array_shift($nameAndExtension);

        return $className;
    }
}