Doctrine 实体验证开始前已调度验证程序事件
问题 在Symfony 2.8+/3.x+中,是否可以在启动实体验证之前调度事件 情况: 假设我们有100个实体,它们有@LifeCycleCallbacks,它们有@postLoad事件来做一些事情,但其结果仅用于实体的有效性,在99%的情况下@postLoad的结果对系统不重要。因此,如果我们从数据库中获取了数百或数千个实体,那么对于不重要的数据,将丢失大量的机器周期 在验证开始之前,最好运行某种类型的事件,运行方法,为特定实体填充数据 而不是:Doctrine 实体验证开始前已调度验证程序事件,doctrine,symfony,symfony-validator,Doctrine,Symfony,Symfony Validator,问题 在Symfony 2.8+/3.x+中,是否可以在启动实体验证之前调度事件 情况: 假设我们有100个实体,它们有@LifeCycleCallbacks,它们有@postLoad事件来做一些事情,但其结果仅用于实体的有效性,在99%的情况下@postLoad的结果对系统不重要。因此,如果我们从数据库中获取了数百或数千个实体,那么对于不重要的数据,将丢失大量的机器周期 在验证开始之前,最好运行某种类型的事件,运行方法,为特定实体填充数据 而不是: $entity->preValidat
$entity->preValidate();
$validator = $this->get('validator');
$errors = $validator->validate($entity);
我们可以:
$validator = $this->get('validator');
$errors = $validator->validate($entity);
在validate()情况下,preValidate()将自动作为事件进行调度(当然要检查实体是否有这样的方法)
案例研究:
$entityManager->flush()
所有实体都将被刷新,即使pressave()将其更改回原来的字符串
所以如果我有任何其他实体,比如TextEntity,我想删除它
$entityManager->remove($textEntity);
$entityManager->flush();
不管文件属性的值是否与数据库中的值相同(并且更改只是暂时的),以某种方式更改的所有其他实体(更改已被条令注意到)都将被刷新
它会被冲掉的
所以我们有成百上千个毫无意义的sql更新
顺便说一句
1.->flush($textEntity)将引发异常,因为->删除($textEntity)已“删除”该实体
二,。实体属性->文件必须是Assert/file的文件类型,因为FileValidator只能接受文件或文件绝对路径的值。
但我不会存储文件的绝对路径,因为它在开发、阶段和生产环境中将完全不同
这是我试图上传文件时出现的问题,正如Symfony食谱中所描述的那样
我的解决方案是,在postLoad()中,在属性中创建一个文件实例,该属性不是doctor列,并且被指定具有断言,等等
这是可行的,但无用的postLoad()s的问题仍然存在,我考虑了一些事件。这可能是一个有弹性的、非常优雅的解决方案——而不是控制器变得“胖”
有谁有更好的解决办法吗?或者知道如何在->validate()发生时分派事件?你好,沃尔特 编辑:在symfony 3中,第一个方法作为注释中提到的线程op被弃用。检查为symfony 3制作的第二种方法
Symfony 2.3+,Symfony<3 在这种情况下,由于symfony和大多数其他bundle都使用参数来定义服务类,所以我要做的是扩展该服务。查看下面的示例,有关扩展服务的更多信息,请查看此链接 首先,您需要向需要预验证的实体添加一个标记。我通常使用接口来处理像这样的东西
namespace Your\Name\Space;
interface PreValidateInterface
{
public function preValidate();
}
在此之后,您将扩展验证程序服务
<?php
namespace Your\Name\Space;
use Symfony\Component\Validator\Validator;
class MyValidator extends Validator //feel free to rename this to your own liking
{
/**
* @inheritdoc
*/
public function validate($value, $groups = null, $traverse = false, $deep = false)
{
if (is_object($value) && $value instanceof PreValidateInterface) {
$value->preValidate();
}
return parent::validate($value, $groups, $traverse, $deep);
}
}
这是基本思想。现在你可以把这个想法和你想达到的任何目标结合起来。例如,您可以查找接口,而不是在实体上调用方法(我通常喜欢将业务逻辑保留在实体之外),如果接口在那里,您可以启动一个带有该实体的pre.validate事件,并使用侦听器来完成这项工作。之后,您可以保留来自parent::validate的结果,还可以启动post.validate事件。你知道我要说什么了。在验证方法中,您现在基本上可以做任何您喜欢的事情
PS:上面的例子是简单的方法。如果您想使用事件路由,服务扩展将更加困难,因为您需要向其中注入调度程序。查看我在开始时提供的链接,了解扩展服务的其他方法,如果您需要帮助,请告诉我
适用于Symfony 3.0->3.1 在这种情况下,他们设法使它很难和肮脏的扩展 步骤1: 创建您自己的验证程序,如下所示:
parameters:
validator.class: Your\Name\Space\MyValidator
<?php
namespace Your\Name\Space;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception;
use Symfony\Component\Validator\MetadataInterface;
use Symfony\Component\Validator\Validator\ContextualValidatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class myValidator implements ValidatorInterface
{
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* @param ValidatorInterface $validator
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* Returns the metadata for the given value.
*
* @param mixed $value Some value
*
* @return MetadataInterface The metadata for the value
*
* @throws Exception\NoSuchMetadataException If no metadata exists for the given value
*/
public function getMetadataFor($value)
{
return $this->validator->getMetadataFor($value);
}
/**
* Returns whether the class is able to return metadata for the given value.
*
* @param mixed $value Some value
*
* @return bool Whether metadata can be returned for that value
*/
public function hasMetadataFor($value)
{
return $this->validator->hasMetadataFor($value);
}
/**
* Validates a value against a constraint or a list of constraints.
*
* If no constraint is passed, the constraint
* {@link \Symfony\Component\Validator\Constraints\Valid} is assumed.
*
* @param mixed $value The value to validate
* @param Constraint|Constraint[] $constraints The constraint(s) to validate
* against
* @param array|null $groups The validation groups to
* validate. If none is given,
* "Default" is assumed
*
* @return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validate($value, $constraints = null, $groups = null)
{
//the code you are doing all of this for
if (is_object($value) && $value instanceof PreValidateInterface) {
$value->preValidate();
}
//End of code
return $this->validator->validate($value, $constraints, $groups);
}
/**
* Validates a property of an object against the constraints specified
* for this property.
*
* @param object $object The object
* @param string $propertyName The name of the validated property
* @param array|null $groups The validation groups to validate. If
* none is given, "Default" is assumed
*
* @return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validateProperty($object, $propertyName, $groups = null)
{
$this->validator->validateProperty($object, $propertyName, $groups);
}
/**
* Validates a value against the constraints specified for an object's
* property.
*
* @param object|string $objectOrClass The object or its class name
* @param string $propertyName The name of the property
* @param mixed $value The value to validate against the
* property's constraints
* @param array|null $groups The validation groups to validate. If
* none is given, "Default" is assumed
*
* @return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = null)
{
$this->validator->validatePropertyValue($objectOrClass, $propertyName, $value, $groups);
}
/**
* Starts a new validation context and returns a validator for that context.
*
* The returned validator collects all violations generated within its
* context. You can access these violations with the
* {@link ContextualValidatorInterface::getViolations()} method.
*
* @return ContextualValidatorInterface The validator for the new context
*/
public function startContext()
{
$this->validator->startContext();
}
/**
* Returns a validator in the given execution context.
*
* The returned validator adds all generated violations to the given
* context.
*
* @param ExecutionContextInterface $context The execution context
*
* @return ContextualValidatorInterface The validator for that context
*/
public function inContext(ExecutionContextInterface $context)
{
$this->validator->inContext($context);
}
}
namespace Your\Name\Space;
use Symfony\Component\Validator\ValidatorBuilder;
class myValidatorBuilder extends ValidatorBuilder
{
public function getValidator()
{
$validator = parent::getValidator();
return new MyValidator($validator);
}
}
namespace Your\Name\Space;
final class MyValidation
{
/**
* The Validator API provided by Symfony 2.4 and older.
*
* @deprecated use API_VERSION_2_5_BC instead.
*/
const API_VERSION_2_4 = 1;
/**
* The Validator API provided by Symfony 2.5 and newer.
*/
const API_VERSION_2_5 = 2;
/**
* The Validator API provided by Symfony 2.5 and newer with a backwards
* compatibility layer for 2.4 and older.
*/
const API_VERSION_2_5_BC = 3;
/**
* Creates a new validator.
*
* If you want to configure the validator, use
* {@link createValidatorBuilder()} instead.
*
* @return ValidatorInterface The new validator.
*/
public static function createValidator()
{
return self::createValidatorBuilder()->getValidator();
}
/**
* Creates a configurable builder for validator objects.
*
* @return ValidatorBuilderInterface The new builder.
*/
public static function createValidatorBuilder()
{
return new MyValidatorBuilder();
}
/**
* This class cannot be instantiated.
*/
private function __construct()
{
}
}
您需要覆盖Symfony\Component\Validator\Validation。这是一个丑陋/肮脏的部分,因为这个类是最终类,所以您无法扩展它,并且没有接口可实现,所以您必须注意symfony的未来版本中,以防向后兼容性被破坏。事情是这样的:
parameters:
validator.class: Your\Name\Space\MyValidator
<?php
namespace Your\Name\Space;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception;
use Symfony\Component\Validator\MetadataInterface;
use Symfony\Component\Validator\Validator\ContextualValidatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class myValidator implements ValidatorInterface
{
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* @param ValidatorInterface $validator
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* Returns the metadata for the given value.
*
* @param mixed $value Some value
*
* @return MetadataInterface The metadata for the value
*
* @throws Exception\NoSuchMetadataException If no metadata exists for the given value
*/
public function getMetadataFor($value)
{
return $this->validator->getMetadataFor($value);
}
/**
* Returns whether the class is able to return metadata for the given value.
*
* @param mixed $value Some value
*
* @return bool Whether metadata can be returned for that value
*/
public function hasMetadataFor($value)
{
return $this->validator->hasMetadataFor($value);
}
/**
* Validates a value against a constraint or a list of constraints.
*
* If no constraint is passed, the constraint
* {@link \Symfony\Component\Validator\Constraints\Valid} is assumed.
*
* @param mixed $value The value to validate
* @param Constraint|Constraint[] $constraints The constraint(s) to validate
* against
* @param array|null $groups The validation groups to
* validate. If none is given,
* "Default" is assumed
*
* @return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validate($value, $constraints = null, $groups = null)
{
//the code you are doing all of this for
if (is_object($value) && $value instanceof PreValidateInterface) {
$value->preValidate();
}
//End of code
return $this->validator->validate($value, $constraints, $groups);
}
/**
* Validates a property of an object against the constraints specified
* for this property.
*
* @param object $object The object
* @param string $propertyName The name of the validated property
* @param array|null $groups The validation groups to validate. If
* none is given, "Default" is assumed
*
* @return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validateProperty($object, $propertyName, $groups = null)
{
$this->validator->validateProperty($object, $propertyName, $groups);
}
/**
* Validates a value against the constraints specified for an object's
* property.
*
* @param object|string $objectOrClass The object or its class name
* @param string $propertyName The name of the property
* @param mixed $value The value to validate against the
* property's constraints
* @param array|null $groups The validation groups to validate. If
* none is given, "Default" is assumed
*
* @return ConstraintViolationListInterface A list of constraint violations.
* If the list is empty, validation
* succeeded
*/
public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = null)
{
$this->validator->validatePropertyValue($objectOrClass, $propertyName, $value, $groups);
}
/**
* Starts a new validation context and returns a validator for that context.
*
* The returned validator collects all violations generated within its
* context. You can access these violations with the
* {@link ContextualValidatorInterface::getViolations()} method.
*
* @return ContextualValidatorInterface The validator for the new context
*/
public function startContext()
{
$this->validator->startContext();
}
/**
* Returns a validator in the given execution context.
*
* The returned validator adds all generated violations to the given
* context.
*
* @param ExecutionContextInterface $context The execution context
*
* @return ContextualValidatorInterface The validator for that context
*/
public function inContext(ExecutionContextInterface $context)
{
$this->validator->inContext($context);
}
}
namespace Your\Name\Space;
use Symfony\Component\Validator\ValidatorBuilder;
class myValidatorBuilder extends ValidatorBuilder
{
public function getValidator()
{
$validator = parent::getValidator();
return new MyValidator($validator);
}
}
namespace Your\Name\Space;
final class MyValidation
{
/**
* The Validator API provided by Symfony 2.4 and older.
*
* @deprecated use API_VERSION_2_5_BC instead.
*/
const API_VERSION_2_4 = 1;
/**
* The Validator API provided by Symfony 2.5 and newer.
*/
const API_VERSION_2_5 = 2;
/**
* The Validator API provided by Symfony 2.5 and newer with a backwards
* compatibility layer for 2.4 and older.
*/
const API_VERSION_2_5_BC = 3;
/**
* Creates a new validator.
*
* If you want to configure the validator, use
* {@link createValidatorBuilder()} instead.
*
* @return ValidatorInterface The new validator.
*/
public static function createValidator()
{
return self::createValidatorBuilder()->getValidator();
}
/**
* Creates a configurable builder for validator objects.
*
* @return ValidatorBuilderInterface The new builder.
*/
public static function createValidatorBuilder()
{
return new MyValidatorBuilder();
}
/**
* This class cannot be instantiated.
*/
private function __construct()
{
}
}
最后一步覆盖config.yml中的参数validator.builder.factory.class:
参数:
validator.builder.factory.class:Your\Name\Space\MyValidation
这是我能找到的侵入性最小的方法。不是很干净,在升级symfony到将来的版本时可能需要一些维护
希望这对你有所帮助,并祝你编码愉快
Alexandru Cosoi为什么不装饰一下
验证器
服务,自己发送预验证
事件?你的意思是扩展验证器类并将其注册为服务?是的,我喜欢你的想法。但我有一个问题-我必须将MyVallidator注册为一个服务,并使用它,还是“Service.class”参数将作为新的且唯一可用的验证器类被框架自动捕获?好的,2个问题:1。Symfony\Component\Validator\Validator被标记为不推荐2。Symfony\Component\Validator\ValidatorBuilder,方法@getValidator()(如果尚未设置验证程序,则运行该方法)使用(返回)Symfony\Compo