Validation symfony2在配置中注入服务
我需要根据特定的配置使用特定的验证器来验证JSON消息 我已经将验证器和约束定义为服务: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:
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);
}
}