我的实体:
<?php
namespace Acme\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* PriceQuoteRequest
*
* @ORM\Table(name="pricequote")
* @ORM\Entity
*
* @Assert\Callback(methods={"isGutterValid"}, groups={"flow_dormerRequest_step2"})
*/
class PriceQuoteRequest
{
/**
* @var integer $dormerInnerHeight
*
* @ORM\Column(name="h", type="float")
* @Assert\NotBlank(groups={"flow_dormerRequest_step2"})
* @Assert\Range(min="80", max="1000", groups={"flow_dormerRequest_step2"})
*/
public $dormerInnerHeight;
/**
* @var boolean $dormerGutter
*
* @ORM\Column(name="gutter", type="boolean")
*
* @Assert\Type(type="boolean", groups={"flow_dormerRequest_step2"})
* Assert\Callback(methods={"isGutterValid"}, groups={"flow_dormerRequest_step2"})
*/
public $dormerGutter;
/**
* @ORM\OneToOne(targetEntity="Acme\AppBundle\Entity\GutterMaterial")
*
* Assert\Type(type="Acme\AppBundle\Entity\GutterMaterial", groups={"flow_dormerRequest_step2"})
*/
public $dormerGutterMaterial;
/**
*
* @param ExecutionContextInterface $context
*
* Assert\Callback(groups={"flow_dormerRequest_step2"})
*/
public function isGutterValid(ExecutionContextInterface $context)
{
if ($this->getDormerGutter() === true && $this->getDormerGutterMaterial() === null) {
$context->buildViolation('Gutter checked but no Material selected.')
->atPath('dormerGutter')
->addViolation();
}
}
}
我的测试:
<?php
namespace Acme\AppBundle\Tests\Entity;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Validator\Validation;
class RequestFunctionalTest extends KernelTestCase
{
/**
* @var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* {@inheritDoc}
*/
public function setUp()
{
self::bootKernel();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager()
;
}
public function testDormerGutterValidator()
{
$priceQuoteRequest = new PriceQuoteRequest();
$priceQuoteRequest->setDormerGutter(true);
// Validating on single property will not call "isGutterValid" Callback, exptected only 1 error otherwise
// $violationList = $this->getValidator()->validateProperty($priceQuoteRequest, 'dormerGutter', array('flow_dormerRequest_step2'));
// Validating full object will call "isGutterValid" but return all errors resp. > 1
$violationList = $this->getValidator()->validate($priceQuoteRequest, array('flow_dormerRequest_step2'));
dump($violationList);
$this->assertEquals(1, count($violationList));
}
}
如果 DormerGutter 设置为true(在表单中检查,则必须选择 DormerGutterMaterial 。
isGutterValid 方法应检查此方法,否则将违规指向 DormerGutter 字段:
if ($this->getDormerGutter() === true && $this->getDormerGutterMaterial() === null) {
$context->buildViolation('Gutter checked but no Material selected.')
->atPath('dormerGutter')
->addViolation();
}
测试将 DormerGutter 设置为true,但不选择 DormerGutterMaterial 。 在属性 DormerGutter 上进行验证我希望 isGutterValid 回调返回1错误。
相反,它将返回0错误,因为在单个属性上调用 validateProperty 不会调用整个对象的“全局”回调。类。
相反,我可以验证完整的对象。这确实会调用 isGutterValid 方法。 它将返回至少2个错误,因为这也验证了所有其他属性,例如的 dormerInnerHeight 不幸的是 - 在我的用例中 - 我只想要返回1个错误。
有没有办法将Callback指向单个属性,只允许我调用validateProperty方法并仍然触发回调?
替代/解决方法当然是使用表达式约束:
/**
* @var boolean $dormerGutter
*
* @ORM\Column(name="gutter", type="boolean")
*
* @Assert\Type(type="boolean", groups={"flow_dormerRequest_step2"})
* @Assert\Expression(
* "this.getDormerGutterMaterial() !== null or this.getDormerGutter() == 0",
* message="Gutter checked but no Material selected.",
* groups={"flow_dormerRequest_step2"}
* )
*/
public $dormerGutter;
Ps。:所有验证组都成功链接到flow_dormerRequest_step2。
GitHub Gist上的完整代码here
答案 0 :(得分:0)
我建议您使用实体验证器。
例如,在一个项目中,我有一个连接2个用户的投票实体。 (一个在选民中,一个在投票中 - &gt; getTargetUser)。
这是投票:
/**
* Vote est l'entitĂŠ de jointure entre un juge, un jugĂŠ
* et la votable choisie par le juge.
*
* @ORM\Table(name="vote")
* @ORM\Entity(repositoryClass="Face\Bundle\VoteBundle\Entity\Repository\VoteRepository")
* @Serializer\ExclusionPolicy("all")
* @Assert\GroupSequence({"properties", "Vote"})
* @AssertVote\UniqueCharacteristic
* @AssertVote\VoterNotTargetUser
*/
class Vote
{
/**
* Vote's id.
*
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @Serializer\Expose
*/
private $id;
/**
* User who gave the vote.
*
* @var User
* @ORM\ManyToOne(targetEntity="Face\Bundle\UserBundle\Entity\User", cascade={"persist"})
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull(message = "vote.voter_user.not_null", groups={"properties"})
* @Serializer\Expose
*/
private $voterUser;
/**
* The votable (ie: profile) which the voterUser voted on.
*
* @var Votable
* @ORM\ManyToOne(
* targetEntity="Face\Bundle\VoteBundle\Entity\AbstractVotable",
* inversedBy="votes",
* cascade={"persist"}
* )
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull(message = "vote.votable.not_null", groups={"properties"})
*/
private $votable;
我不得不说我的选民与目标用户不同。
然后我创建了一个新的约束:
/**
* Block votes of one User on its profile
*
* @package Face\Bundle\UserBundle\Validator\Validator
*/
class VoterNotTargetUserValidator extends ConstraintValidator
{
/**
* The entity manager.
*
* @var EntityManager
*/
private $em;
/**
* Default constructor.
*
* @param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Block votes of one User on its profile
*
* @param Vote $value The value that should be validated
* @param Constraint $constraint The constraint for the validation
*
*/
public function validate($value, Constraint $constraint)
{
if ($value->getVoterUser() === $value->getVotable()->getTargetUser()) {
$this->context->addViolation($constraint->message);
}
}
}
投票中使用的验证码:
/**
* Block votes of one User on its profile
*
* @package Face\Bundle\UserBundle\Validator\Validator
*/
class VoterNotTargetUserValidator extends ConstraintValidator
{
/**
* The entity manager.
*
* @var EntityManager
*/
private $em;
/**
* Default constructor.
*
* @param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Block votes of one User on its profile
*
* @param Vote $value The value that should be validated
* @param Constraint $constraint The constraint for the validation
*
* @api
*/
public function validate($value, Constraint $constraint)
{
if ($value->getVoterUser() === $value->getVotable()->getTargetUser()) {
$this->context->addViolation($constraint->message);
}
}
}
您可以查看doc以获取更多详细信息,例如如何将其添加到您要验证的字段中。
http://symfony.com/fr/doc/current/cookbook/validation/custom_constraint.html