【问题标题】:symfony2 inject service in configsymfony2 在配置中注入服务
【发布时间】:2016-07-19 15:26:33
【问题描述】:

我需要根据特定配置使用特定的验证器来验证 JSON 消息。

我已将验证器和约束定义为服务:

services:
    validator.constraint.message.country_code:
        class: RenamedBundle\Validator\Constraints\CountryCode
        arguments: ...

    validator.constraint.message.price_comma:
        class: RenamedBundle\Validator\Constraints\PriceComma
        arguments: ...

    message.validator:
        class: RenamedBundle\Service\Validator\MessageValidatorService
        arguments: ['@validator']
        calls:
            - [addConstraint, ['@validator.constraint.message.country_code']]
            - [addConstraint, ['@validator.constraint.message.price_comma']]

DtoValidatorService 中,我使用约束列表调用validate()

问题...挑战是相同的 JSON 消息仅需要列表中的少数验证器进行验证,这取决于消息属性,即。对于波兰,我想验证所有浮点值(在波兰分隔符是“,”,而不是“。”)。我已经尝试通过config.yml 做到这一点。

renamed:
    pritners:
        warehouse_wa:
            characteristic:
                country: 'pl'
                source: 'hq-pl'
            validators:
                - '@validator.constraint.message.country_code'
                - '@validator.constraint.message.price_comma'
        warehouse_ny:
            characteristic:
                country: 'us'
                source: 'hq-us'
            validators:
                - '@validator.constraint.message.country_code'

我添加了扩展:

class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     *
     * @throws \RuntimeException
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('renamed');

        $rootNode
            ->children()
                ->arrayNode('printers')
                ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->children()
                            ->arrayNode('characteristic')
                                    ->children()
                                        ->scalarNode('country')->end()
                                        ->scalarNode('source')->end()
                                    ->end()
                            ->end()
                        ->end()
                        ->children()
                            ->arrayNode('validators')
                                ->prototype('scalar')->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();

        return $treeBuilder;
    }
}

class RenamedExtension extends Extension
{
    /**
     * {@inheritDoc}
     * @throws \Exception
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);
        $container->setParameter('printers', $config['printers']);

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

此配置有效,但问题是当我将 '%printers%' 参数传递给服务时,我收到服务名称列表:

Array
(
    [0] => @validator.constraint.message.country_code
)

但是当我通过约束时,我得到了对象。

array(1) {
  [0] =>
  class RenamedBundle\Validator\Constraints\CountryCode#688 (5) {
    ...
  }
}

我现在处于死胡同。如何参数化打印机配置并避免传递内联/硬代码验证器类名。不允许在参数部分调用服务。在配置中调用它们为我提供了额外的控制和验证。 也许有人有更好的解决方案?

编辑: 根据@Artur Vesker 的建议,我更改了扩展加载方法的实现。

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

    $printers = [];
    foreach ($config['printers'] as $printerName => $printerConfig) {

        $constraints = [];
        foreach($printerConfig['valdiators'] as $constraintName) {
            $constraintName = ltrim($constraintName, '@');
            $constraints[] = new Reference($constraintName);
        }
        $printerConfig['valdiators'] = $constraints;

        $printers[$printerName] = $printerConfig;
    }

    $container->setParameter('printers', $printers);

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

尝试构建缓存我得到了:

Symfony\Component\DependencyInjection\Exception\InvalidArgumentException 
You cannot dump a container with parameters that contain references to other services 

看起来我的方法在 symfony 世界中是不允许的;]

【问题讨论】:

    标签: validation symfony dependency-injection


    【解决方案1】:

    使用Reference

    在配置中只设置 id:

            validators:
                - 'validator.constraint.message.country_code'
                - 'validator.constraint.message.price_comma'
    

    并在你的Extension

    中创建带有Reference的参数
    $validators = array_map(function($id) {
         return new Reference($id); 
    }, $config['pritners.warehouse_wa.validators']);
    
    $messageValidatorDefinition = new Definition('RenamedBundle\Service\Validator\MessageValidatorService', [new Reference('validator)]);
    
       foreach ($validators as $validator) {
          messageValidatorDefinition->addMethodCall('addConstraint', [$validator])
       } 
    

    【讨论】:

    • 感谢您的回复。现在我得到了Symfony\Component\DependencyInjection\Exception\InvalidArgumentException You cannot dump a container with parameters that contain references to other services 看起来不允许以这种方式传递服务。我可能必须创建单独的服务并在其中保留配置...
    • 哦,当然。我已经更新了我的答案。只需从 services.yml 中删除“message.validator”,它将在 Extension 中创建,并带有来自配置的约束。
    【解决方案2】:

    感谢 Artur Vesker,您的解决方案对我有用。 我试图将message.validator 保留在services.yml 中(我想要PHPStorm 提示)并通过$container->getDefinition('message.validator') 将它们扩展,但这样addMethodCall 不起作用。因此,任何尝试这种方式的人都行不通,您必须按照 Artur 的建议创建服务动态。

    我的完整代码: - 相同class Configuration implements ConfigurationInterface - 相同config.yml - 从services.yml 中删除message.validator - 在 messageValidator 中,我决定按打印机名称对约束进行分组

    /**
     * Class MessageValidatoService
     * @package RenamedBundle\Service
     */
    class MessageValidatoService
    {
        /** PrinterConfigurationDto[] */
        protected $printersConfig;
    
        /**
         * array of constraints for preValidator grouped by printer name ex:
         * [
         *    'printer_name' => [Constraint1, Constraint2]
         * ]
         * @var array
         */
        protected $constraints;
    
    
        /**
         * MessageValidatorService constructor.
         *
         * @param array      $printersConfiguration
         * @param Serializer $serializer
         *
         * @throws \RuntimeException
         */
        public function __construct(array $printersConfiguration, Serializer $serializer)
        {
            $this->serializer = $serializer;
            $this->constraints = [];
            $this->printersConfig = [];
    
            foreach ($printersConfiguration as $printerConfig) {
                /** @var PrinterConfigurationDto $configDto */
                $configDto = $this->serializer->fromArray(
                    $printerConfig,
                    'RenamedBundle\Dto\PrinterConfiguration\PrinterConfigurationDto'
                );
                $this->printersConfig[] = $configDto;
            }
        }
    
        /**
         * @param string     $printerName
         * @param Constraint $constraint
         *
         * @return $this
         */
        public function addConstraint($printerName, Constraint $constraint)
        {
            $this->constraints[$printerName][] = $constraint;
    
            return $this;
        }
    
        /**
         * Return PrinterConfigurationDto, or throw RuntimeException if none configuration fir to ReceiptDto parameters.
         *
         * @param ReceiptDto $receiptDto
         *
         * @return PrinterConfigurationDto
         *
         * @throws \RuntimeException
         * @throws \OutOfRangeException
         */
        protected function getPrinterConfig($receiptDto)
        {
            $countryCodes = $receiptDto->getCountryCodes();
            if (array_key_exists($receiptDto->getCountryId(), $countryCodes) === false) {
                throw new \OutOfRangeException('Missing country code for country id:' . $receiptDto->getCountryId());
            }
    
            /** @var PrinterConfigurationDto $printerConfig */
            foreach ($this->printersConfig as $printerConfig) {
                if ($printerConfig->getSourceApp() === $receiptDto->getSourceApp()
                    && $printerConfig->getCountry() === $countryCodes[$receiptDto->getCountryId()]
                ) {
                    return $printerConfig;
                }
            }
            throw new \RuntimeException(
                'No printer configuration found for app:' . $receiptDto->getSourceApp()
                . ', country id: ' . $receiptDto->getCountryId()
            );
        }
    
        /**
         * @param ReceiptDto $receiptDto
         *
         * @return Constraint[]
         *
         * @throws \RuntimeException
         * @throws \OutOfRangeException
         */
        public function getValidatorConstraints(ReceiptDto $receiptDto)
        {
            $printerConfig = $this->getPrinterConfig($receiptDto);
    
            if (array_key_exists($printerConfig->getName(), $this->constraints) === false) {
                return [];
            }
    
            return $this->constraints[$printerConfig->getName()];
        }
    }
    
    /**
     * Class RenamedExtension
     * @package RenamedBundle\DependencyInjection
     */
    class RenamedExtension extends Extension
    {
        /**
         * {@inheritDoc}
         * @throws \Exception
         * @throws ServiceNotFoundException
         * @throws InvalidArgumentException
         * @throws BadMethodCallException
         */
        public function load(array $configs, ContainerBuilder $container)
        {
            $configuration = new Configuration();
            $config = $this->processConfiguration($configuration, $configs);
    
            //set parameters before load services
            //rewrite printers configuration from RenamedBundle\Resources\printers.yml
            //structure changes must be implemented also in RenamedBundle\Dto\PrinterConfiguration\PrinterConfigurationDto
            $printers = [];
            foreach ($config['printers'] as $printerName => $printerConfig) {
                $printers[] = [
                    'name' => $printerName,
                    'country' => $printerConfig['characteristic']['country'],
                    'source_app' => $printerConfig['characteristic']['source_app'],
                ];
            }
    
            $container->setParameter('printers', $printers);
    
            //load services, add constraints
            $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
            $loader->load('services.yml');
    
            $definition = new Definition(PrinterManagerService::class);
    
            $definition->addArgument('%printers%');
            $definition->addArgument(new Reference('serializer'));
    
            foreach ($config['validators'] as $printerName => $printerConfig) {
                if (array_key_exists('validators', $printerConfig)
                    && is_array($printerConfig['validators'])
                ) {
                    foreach ($printerConfig['validators'] as $constraintName) {
                        $constraint = new Reference(ltrim($constraintName, '@'));
                        $definition->addMethodCall('addConstraint', [$printerName, $constraint]);
                    }
                }
            }
    
            $container->setDefinition('message.validator', $definition);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多