sfcod/email-engine

View on GitHub
DependencyInjection/EmailEngineExtension.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace SfCod\EmailEngineBundle\DependencyInjection;

use Psr\Log\LoggerInterface;
use SfCod\EmailEngineBundle\Mailer\Mailer;
use SfCod\EmailEngineBundle\Mailer\TemplateManager;
use SfCod\EmailEngineBundle\Template\ParametersAwareInterface;
use SfCod\EmailEngineBundle\Template\Params\ParameterResolver;
use SfCod\EmailEngineBundle\Template\Params\ParameterResolverInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Class EmailEngineExtension
 *
 * @author Virchenko Maksim <muslim1992@gmail.com>
 *
 * @package SfCod\EmailEngineBundle\DependencyInjection
 */
class EmailEngineExtension extends Extension
{
    /**
     * Loads a specific configuration.
     *
     * @param array $configs
     * @param ContainerBuilder $container
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new EmailEngineConfiguration();

        $config = $this->processConfiguration($configuration, $configs);

        $this->createSenders($config, $container);
        $this->createTemplates($config, $container);
        $this->createResolver($config, $container);
    }

    /**
     * Get extension alias
     *
     * @return string
     */
    public function getAlias(): string
    {
        return 'sfcod_email_engine';
    }

    /**
     * Create parameter resolver
     *
     * @param array $config
     * @param ContainerBuilder $container
     */
    private function createResolver(array $config, ContainerBuilder $container)
    {
        $resolver = new Definition(ParameterResolverInterface::class);
        $resolver->setPublic(true);
        $resolver->setClass(ParameterResolver::class);
        $resolver->setArguments([
            new Reference(ContainerInterface::class),
        ]);

        $container->setDefinition(ParameterResolverInterface::class, $resolver);
    }

    /**
     * Create senders
     *
     * @param array $config
     * @param ContainerBuilder $container
     */
    private function createSenders(array $config, ContainerBuilder $container)
    {
        $configuration = [];
        $mainSender = $this->getSender($config['main_sender'], $config);

        if (isset($mainSender['chain'])) {
            foreach ($mainSender['chain']['senders'] as $sender) {
                $configuration[$sender] = $this->getSender($sender, $config);
            }
        } else {
            $configuration[$config['main_sender']] = $mainSender;
        }

        $senders = [];
        $repositories = [];
        foreach ($configuration as $senderName => $senderConfig) {
            if (false === isset($senderConfig['sender'], $senderConfig['repository']) ||
                false === isset($senderConfig['sender']['class'], $senderConfig['repository']['class'])) {
                throw new InvalidConfigurationException(sprintf('"sender" and "repository" must be defined in "%s" sender.', $senderName));
            }

            $sender = new Definition($senderConfig['sender']['class']);
            $sender
                ->setPublic(true)
                ->setAutoconfigured(true)
                ->setAutowired(true);

            $repository = new Definition($senderConfig['repository']['class']);
            $repository
                ->setPublic(true)
                ->setAutoconfigured(true)
                ->setAutowired(true);

            $container->addDefinitions([
                $senderConfig['repository']['class'] => $repository,
                $senderConfig['sender']['class'] => $sender,
            ]);

            // Prepare senders and repositories for injecting into mailer
            $senders[$senderConfig['sender']['class']] = $sender;
            $repositories[$senderConfig['repository']['class']] = $repository;
        }

        $mailer = new Definition(Mailer::class);
        $mailer
            ->setPublic(true)
            ->setArguments([
                new Reference(ParameterResolverInterface::class),
                new Reference(LoggerInterface::class),
            ])
            ->addMethodCall('setConfiguration', [$configuration])
            ->addMethodCall('setSenders', [$senders])
            ->addMethodCall('setRepositories', [$repositories]);

        $container->setDefinition(Mailer::class, $mailer);
    }

    /**
     * Get sender from senders config
     *
     * @param string $sender
     * @param array $config
     *
     * @return array
     */
    private function getSender(string $sender, array $config): array
    {
        if (false === isset($config['senders'][$sender])) {
            throw new InvalidConfigurationException(sprintf('Main sender "%s" does not exist in senders "%s".', $sender, json_encode(array_keys($config['senders']))));
        }

        return $config['senders'][$sender];
    }

    /**
     * Create templates
     *
     * @param array $config
     * @param ContainerBuilder $container
     */
    private function createTemplates(array $config, ContainerBuilder $container)
    {
        foreach ($config['templates'] as $template) {
            if (in_array(ParametersAwareInterface::class, class_implements($template))) {
                /** @var ParametersAwareInterface $template */
                foreach ($template::listParameters() as $parameter) {
                    $definition = new Definition($parameter);
                    $definition
                        ->setPublic(true)
                        ->setAutowired(true)
                        ->setAutoconfigured(true)
                        ->addTag(sprintf('%s.parameter', $template));

                    $container->setDefinition($parameter, $definition);
                }
            }
        }

        $templateManager = new Definition(TemplateManager::class);
        $templateManager
            ->setPublic(true)
            ->addArgument($config['templates']);

        $container->setDefinition(TemplateManager::class, $templateManager);
    }
}