Jen*_*ens 12 validation symfony
我有一个与另一个实体具有OneToMany关系的实体,当我坚持父实体时,我想确保子项不包含重复项.
这是我一直在使用的类,折扣集合不应包含给定客户端具有相同名称的两个产品.
我有一个客户实体,有一系列折扣:
/**
* @ORM\Entity
*/
class Client {
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=128, nullable="true")
*/
protected $name;
/**
* @ORM\OneToMany(targetEntity="Discount", mappedBy="client", cascade={"persist"}, orphanRemoval="true")
*/
protected $discounts;
}
/**
* @ORM\Entity
* @UniqueEntity(fields={"product", "client"}, message="You can't create two discounts for the same product")
*/
class Discount {
/**
* @ORM\Id
* @ORM\Column(type="string", length=128, nullable="true")
*/
protected $product;
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Client", inversedBy="discounts")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
protected $client;
/**
* @ORM\Column(type="decimal", scale=2)
*/
protected $percent;
}
Run Code Online (Sandbox Code Playgroud)
我尝试使用UniqueEntity作为Discount类,你可以看到,问题是似乎验证器只检查数据库上加载了什么(它是空的),所以当实体被持久化时我得到一个"SQLSTATE [23000]:完整性约束违规".
我已经检查了Collection约束购买它似乎只处理字段集合,而不是实体.
还有All validator,它允许您定义要应用于每个实体的约束,但不能定义整个集合.
我为此创建了一个自定义约束/验证器.
它使用" All "断言验证表单集合,并采用可选参数:属性的属性路径以检查实体相等性.
(这是针对Symfony 2.1,使其适应Symfony 2.0检查答案的结尾):
有关创建自定义验证约束的更多信息,请查看Cookbook
约束:
#src/Acme/DemoBundle/Validator/constraint/UniqueInCollection.php
<?php
namespace Acme\DemoBundle\Validator\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class UniqueInCollection extends Constraint
{
public $message = 'The error message (with %parameters%)';
// The property path used to check wether objects are equal
// If none is specified, it will check that objects are equal
public $propertyPath = null;
}
Run Code Online (Sandbox Code Playgroud)
验证者:
#src/Acme/DemoBundle/Validator/constraint/UniqueInCollectionValidator.php
<?php
namespace Acme\DemoBundle\Validator\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Form\Util\PropertyPath;
class UniqueInCollectionValidator extends ConstraintValidator
{
// We keep an array with the previously checked values of the collection
private $collectionValues = array();
// validate is new in Symfony 2.1, in Symfony 2.0 use "isValid" (see below)
public function validate($value, Constraint $constraint)
{
// Apply the property path if specified
if($constraint->propertyPath){
$propertyPath = new PropertyPath($constraint->propertyPath);
$value = $propertyPath->getValue($value);
}
// Check that the value is not in the array
if(in_array($value, $this->collectionValues))
$this->context->addViolation($constraint->message, array());
// Add the value in the array for next items validation
$this->collectionValues[] = $value;
}
}
Run Code Online (Sandbox Code Playgroud)
在你的情况下,你会像这样使用它:
use Acme\DemoBundle\Validator\Constraints as AcmeAssert;
// ...
/**
* @ORM\OneToMany(targetEntity="Discount", mappedBy="client", cascade={"persist"}, orphanRemoval="true")
* @Assert\All(constraints={
* @AcmeAssert\UniqueInCollection(propertyPath ="product")
* })
*/
Run Code Online (Sandbox Code Playgroud)
对于Symfony 2.0,请通过以下方式更改验证功能:
public function isValid($value, Constraint $constraint)
{
$valid = true;
if($constraint->propertyPath){
$propertyPath = new PropertyPath($constraint->propertyPath);
$value = $propertyPath->getValue($value);
}
if(in_array($value, $this->collectionValues)){
$valid = false;
$this->setMessage($constraint->message, array('%string%' => $value));
}
$this->collectionValues[] = $value;
return $valid
}
Run Code Online (Sandbox Code Playgroud)
小智 6
这是一个使用多个字段的版本,就像 UniqueEntity 一样。如果多个对象具有相同的值,验证将失败。
用法:
/**
* ....
* @App\UniqueInCollection(fields={"name", "email"})
*/
private $contacts;
//Validation fails if multiple contacts have same name AND email
Run Code Online (Sandbox Code Playgroud)
约束类...
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class UniqueInCollection extends Constraint
{
public $message = 'Entry is duplicated.';
public $fields;
public function validatedBy()
{
return UniqueInCollectionValidator::class;
}
}
Run Code Online (Sandbox Code Playgroud)
验证器本身......
<?php
namespace App\Validator\Constraints;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
class UniqueInCollectionValidator extends ConstraintValidator
{
/**
* @var \Symfony\Component\PropertyAccess\PropertyAccessor
*/
private $propertyAccessor;
public function __construct()
{
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}
/**
* @param mixed $collection
* @param Constraint $constraint
* @throws \Exception
*/
public function validate($collection, Constraint $constraint)
{
if (!$constraint instanceof UniqueInCollection) {
throw new UnexpectedTypeException($constraint, UniqueInCollection::class);
}
if (null === $collection) {
return;
}
if (!\is_array($collection) && !$collection instanceof \IteratorAggregate) {
throw new UnexpectedValueException($collection, 'array|IteratorAggregate');
}
if ($constraint->fields === null) {
throw new \Exception('Option propertyPath can not be null');
}
if(is_array($constraint->fields)) $fields = $constraint->fields;
else $fields = [$constraint->fields];
$propertyValues = [];
foreach ($collection as $key => $element) {
$propertyValue = [];
foreach ($fields as $field) {
$propertyValue[] = $this->propertyAccessor->getValue($element, $field);
}
if (in_array($propertyValue, $propertyValues, true)) {
$this->context->buildViolation($constraint->message)
->atPath(sprintf('[%s]', $key))
->addViolation();
}
$propertyValues[] = $propertyValue;
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11780 次 |
| 最近记录: |