覆盖来自父级应用程序供应商包的服务定义参数(附实例)

84 阅读2分钟

在这个例子中,我们将创建一个供应商捆绑包。它将有一个带有默认参数的服务定义。然后我们将用来自父应用程序的参数来覆盖默认参数。

捆绑

结构

.
├── composer.json
└── src
    ├── BuddyBundle.php
    ├── DependencyInjection
    │   ├── BuddyExtension.php
    │   └── Configuration.php
    ├── Resources
    │   └── config
    │       └── services.yaml
    ├── Service
    │   ├── PrinterInterface.php
    │   ├── ScreenService.php
    │   └── TerminalService.php
    └── Utility
        └── HelloUtility.php

composer.json

{
  "name": "Inanzzz/buddy",
  "type": "symfony-bundle",
  "description": "Buddy up",
  "license": "MIT",
  "require": {
    "php": "^7.2",
    "symfony/config": "^4.2",
    "symfony/dependency-injection": "^4.2"
  },
  "autoload": {
    "psr-4": {
      "Inanzzz\\Buddy\\": "src/"
    }
  },
  "config": {
    "sort-packages": true
  },
  "prefer-stable": true,
  "minimum-stability": "stable"
}

BuddyBundle.php

declare(strict_types=1);

namespace Inanzzz\Buddy;

use Inanzzz\Buddy\DependencyInjection\BuddyExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class BuddyBundle extends Bundle
{
    public function getContainerExtension()
    {
        if (null === $this->extension) {
            $this->extension = new BuddyExtension();
        }

        return $this->extension;
    }
}

BuddyExtension.php

declare(strict_types=1);

namespace Inanzzz\Buddy\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

class BuddyExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yaml');

        $this->setServices($container, $config);
    }

    public function getAlias()
    {
        return Configuration::CONFIG_ROOT;
    }

    private function setServices(ContainerBuilder $container, array $config): void
    {
        // Fetch existing service definition and override its class with either:
        // - the default value coming from 'Configuration.php' file
        // or
        // - the specific value coming from the parent application.
        $classService = $container->getDefinition(sprintf('%s.service.%s',
            Configuration::CONFIG_CHILD,
            Configuration::CONFIG_PRINTER
        ));
        $classService->setClass($config[Configuration::CONFIG_CHILD][Configuration::CONFIG_PRINTER]);

        // Fetch existing service definition and override its arguments one by one with either:
        // - the default values coming from 'Configuration.php' file
        // or
        // - the specific values coming from the parent application.
        $argumentService = $container->getDefinition('Inanzzz\Buddy\Utility\HelloUtility');
        $argumentService->setArgument(
            sprintf('$%s', Configuration::CONFIG_MESSAGE),
            $config[Configuration::CONFIG_CHILD][Configuration::CONFIG_MESSAGE]
        );
        $argumentService->setArgument(
            sprintf('$%s', Configuration::CONFIG_PRINTER),
            $classService
        );
    }
}

Configuration.php

declare(strict_types=1);

namespace Inanzzz\Buddy\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    public const CONFIG_ROOT = 'inanzzz';
    public const CONFIG_CHILD = 'buddy';

    public const CONFIG_MESSAGE = 'message';
    public const CONFIG_PRINTER = 'printer';

    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder(self::CONFIG_ROOT);

        $treeBuilder->getRootNode()
            ->children()
                ->arrayNode(self::CONFIG_CHILD)
                ->addDefaultsIfNotSet()
                    ->children()
                        ->scalarNode(self::CONFIG_MESSAGE)
                            ->cannotBeEmpty()
                            ->defaultValue('inanzzz')
                        ->end()
                        ->scalarNode(self::CONFIG_PRINTER)
                            ->cannotBeEmpty()
                            ->defaultValue('Inanzzz\Buddy\Service\TerminalService')
                        ->end()
                    ->end()
                ->end()
            ->end();

        return $treeBuilder;
    }
}

services.yaml

services:

    # SERVICE
    buddy.service.printer:
        class: ~

    # UTILITY
    Inanzzz\Buddy\Utility\HelloUtility:
        arguments:
            $printer: ~
            $message: ~

PrinterInterface.php

declare(strict_types=1);

namespace Inanzzz\Buddy\Service;

interface PrinterInterface
{
    public function print(string $message): void;
}

ScreenService.php

declare(strict_types=1);

namespace Inanzzz\Buddy\Service;

class ScreenService implements PrinterInterface
{
    public function print(string $message): void
    {
        echo sprintf('Printing %s to screen!', $message);
    }
}

终端服务.php

declare(strict_types=1);

namespace Inanzzz\Buddy\Service;

class TerminalService implements PrinterInterface
{
    public function print(string $message): void
    {
        echo sprintf('Printing %s to terminal!', $message);
    }
}

HelloUtility.php

declare(strict_types=1);

namespace Inanzzz\Buddy\Utility;

use Inanzzz\Buddy\Service\PrinterInterface;

class HelloUtility
{
    private $printer;
    private $message;

    public function __construct(
        PrinterInterface $printer,
        string $message
    ) {
        $this->printer = $printer;
        $this->message = $message;
    }

    public function handle(): void
    {
        $this->printer->print($this->message);
    }
}

父应用程序

假设你将HelloUtility 类注入到父应用程序中的一个服务中。根据使用情况,你将会得到给定的结果。

config/packages/inanzzz.yaml

inanzzz:
    buddy:
        printer: 'Inanzzz\Buddy\Service\ScreenService'

// Printing inanzzz to screen!
inanzzz:
    buddy:
        message: 'moon'

// Printing moon to terminal!
inanzzz:
    buddy:
        printer: 'Inanzzz\Buddy\Service\ScreenService'
        message: 'mama'

// Printing mama to screen!
inanzzz:
    buddy:

inanzzz:

// Printing inanzzz to terminal!
inanzzz:
    buddy:
        printer: 'Some\Parent\Servie\That\Implements\PrinterInterface'
        message: 'something'

// Printing something to parent printer!