我需要根据特定的配置,使用特定的验证器来验证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世界中不被允许;]
答案 0 :(得分:1)
在config中设置ids:
validators: - 'validator.constraint.message.country_code' - 'validator.constraint.message.price_comma'
在扩展程序
中使用参考创建参数$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])
}
答案 1 :(得分:0)
感谢Artur Vesker,您的解决方案适合我。
我尝试将message.validator
保留在services.yml
(我希望有PHPStorm提示)并通过$container->getDefinition('message.validator')
将其延伸,但这样addMethodCall
无效。所以任何人都会尝试这种方式,它不会起作用,你必须像Artur建议的那样创造服务动力。
我的完整代码:
- 同一class Configuration implements ConfigurationInterface
- 同一config.yml
- 从message.validator
移除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);
}
}