Validation symfony2在配置中注入服务

Validation symfony2在配置中注入服务,validation,symfony,dependency-injection,Validation,Symfony,Dependency Injection,我需要根据特定的配置使用特定的验证器来验证JSON消息 我已经将验证器和约束定义为服务: services: validator.constraint.message.country_code: class: RenamedBundle\Validator\Constraints\CountryCode arguments: ... validator.constraint.message.price_comma: class:

我需要根据特定的配置使用特定的验证器来验证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 world中,我的做法似乎是不允许的;]

使用

仅在配置中设置ID:

并在您的扩展中使用引用创建参数

$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])
   } 

谢谢Artur Vesker,你的解决方案适合我。 我试图将
message.validator
保存在
services.yml
中(我希望有PHPStorm提示),并通过
$container->getDefinition('message.validator')
在扩展中获取它们,但这样
addMethodCall
就不起作用了。因此,任何尝试这种方式的人都不会成功,您必须按照Artur的建议创建服务动态性

我的完整代码: -相同的
类配置实现了ConfigurationInterface
-相同的
config.yml
-从
services.yml
-在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);
    }
}

谢谢你的回复。现在我得到了
Symfony\Component\DependencyInjection\Exception\InvalidArgumentException您不能转储包含对其他服务引用的参数的容器
看起来不允许以这种方式传递服务。实际上,我必须创建单独的服务并在其中保留配置…哦,当然。我已经更新了我的答案。只需从services.yml中删除“message.validator”,它将在扩展中创建,并带有来自配置的约束。
/**
 * 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);
    }
}