我不明白为什么某些约束在验证后不会在错误消息中插入属性名称。我有这个实体类:
<?php
namespace AC\OperaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AC\UserBundle\Entity\Utente;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Episodio
* @package AC\OperaBundle\Entity
* @ORM\Entity(repositoryClass="AC\OperaBundle\Repository\EpisodioRepository")
* * @ORM\Table(
* name="ac_Episodio",
* uniqueConstraints={@ORM\UniqueConstraint(name="unique_idx", columns={"opera", "numero_episodio", "extra"})},
* indexes={ @ORM\Index(name="opera_idx", columns={"opera"}),
* @ORM\Index(name="numero_episodio_idx", columns={"numero_episodio"}),
* @ORM\Index(name="extra_idx", columns={"extra"}),
* @ORM\Index(name="attivo_idx", columns={"attivo"}) })
*/
class Episodio {
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var Opera
*
* @ORM\ManyToOne(targetEntity="AC\OperaBundle\Entity\Opera", inversedBy="episodi")
* @ORM\JoinColumn(name="opera", referencedColumnName="id", nullable=false)
*/
protected $opera;
/**
* @var string
*
* @ORM\Column(name="numero_episodio", type="string", length=5, nullable=false)
*/
protected $numero_episodio;
/**
* @var string
*
* @ORM\Column(name="extra", type="string", length=30, nullable=false)
*/
protected $extra;
/**
* @var string
*
* @ORM\Column(name="titolo_italiano", type="string", length=150, nullable=false)
* @Assert\NotBlank()
*/
protected $titolo_italiano;
/**
* @var string
*
* @ORM\Column(name="titolo_originale", type="string", length=150, nullable=false)
* @Assert\NotBlank()
*/
protected $titolo_originale;
/**
* @var \DateTime
*
* @ORM\Column(name="data_ita", type="date", nullable=true)
* @Assert\Date()
*/
protected $data_ita;
/**
* @var \DateTime
*
* @ORM\Column(name="data_jap", type="date", nullable=true)
* @Assert\Date()
*/
protected $data_jap;
/**
* @var int
*
* @ORM\Column(name="durata", type="smallint", nullable=false, options={"default" = 0})
*/
protected $durata;
/**
* @var string
*
* @ORM\Column(name="trama", type="text", nullable=false)
*/
protected $trama;
/**
* @var int
*
* @ORM\Column(name="ordine", type="smallint", nullable=false, options={"default" = 0})
*/
protected $ordine;
/**
* @var int
*
* @ORM\Column(name="promosso", type="integer", nullable=false, options={"default" = 0})
*/
protected $promosso;
/**
* @var int
*
* @ORM\Column(name="rimandato", type="integer", nullable=false, options={"default" = 0})
*/
protected $rimandato;
/**
* @var int
*
* @ORM\Column(name="bocciato", type="integer", nullable=false, options={"default" = 0})
*/
protected $bocciato;
/**
* @var boolean
*
* @ORM\Column(name="fansub", type="boolean", nullable=false)
*/
protected $fansub;
/**
* @var boolean
*
* @ORM\Column(name="attivo", type="boolean", nullable=false)
*/
protected $attivo;
/**
* @var \DateTime
*
* @ORM\Column(name="data_aggiornamento", type="date")
*/
protected $data_aggiornamento;
/**
* @var Utente
*
* @ORM\ManyToOne(targetEntity="AC\UserBundle\Entity\Utente")
* @ORM\JoinColumn(name="utente_aggiornamento", referencedColumnName="id", nullable=true)
*/
protected $utente_aggiornamento;
/**
* @param boolean $attivo
*/
public function setAttivo($attivo)
{
$this->attivo = $attivo;
}
/**
* @return boolean
*/
public function getAttivo()
{
return $this->attivo;
}
/**
* @param int $bocciato
*/
public function setBocciato($bocciato)
{
$this->bocciato = $bocciato;
}
/**
* @return int
*/
public function getBocciato()
{
return $this->bocciato;
}
/**
* @param \DateTime $data_aggiornamento
*/
public function setDataAggiornamento($data_aggiornamento)
{
$this->data_aggiornamento = $data_aggiornamento;
}
/**
* @return \DateTime
*/
public function getDataAggiornamento()
{
return $this->data_aggiornamento;
}
/**
* @param \DateTime $data_ita
*/
public function setDataIta($data_ita)
{
$this->data_ita = $data_ita;
}
/**
* @return \DateTime
*/
public function getDataIta()
{
return $this->data_ita;
}
/**
* @param \DateTime $data_jap
*/
public function setDataJap($data_jap)
{
$this->data_jap = $data_jap;
}
/**
* @return \DateTime
*/
public function getDataJap()
{
return $this->data_jap;
}
/**
* @param int $durata
*/
public function setDurata($durata)
{
$this->durata = $durata;
}
/**
* @return int
*/
public function getDurata()
{
return $this->durata;
}
/**
* @param string $extra
*/
public function setExtra($extra)
{
$this->extra = $extra;
}
/**
* @return string
*/
public function getExtra()
{
return $this->extra;
}
/**
* @param boolean $fansub
*/
public function setFansub($fansub)
{
$this->fansub = $fansub;
}
/**
* @return boolean
*/
public function getFansub()
{
return $this->fansub;
}
/**
* @param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param string $numero_episodio
*/
public function setNumeroEpisodio($numero_episodio)
{
$this->numero_episodio = $numero_episodio;
}
/**
* @return string
*/
public function getNumeroEpisodio()
{
return $this->numero_episodio;
}
/**
* @param \AC\OperaBundle\Entity\Opera $opera
*/
public function setOpera($opera)
{
$this->opera = $opera;
}
/**
* @return \AC\OperaBundle\Entity\Opera
*/
public function getOpera()
{
return $this->opera;
}
/**
* @param int $ordine
*/
public function setOrdine($ordine)
{
$this->ordine = $ordine;
}
/**
* @return int
*/
public function getOrdine()
{
return $this->ordine;
}
/**
* @param int $promosso
*/
public function setPromosso($promosso)
{
$this->promosso = $promosso;
}
/**
* @return int
*/
public function getPromosso()
{
return $this->promosso;
}
/**
* @param int $rimandato
*/
public function setRimandato($rimandato)
{
$this->rimandato = $rimandato;
}
/**
* @return int
*/
public function getRimandato()
{
return $this->rimandato;
}
/**
* @param string $titolo_italiano
*/
public function setTitoloItaliano($titolo_italiano)
{
$this->titolo_italiano = $titolo_italiano;
}
/**
* @return string
*/
public function getTitoloItaliano()
{
return $this->titolo_italiano;
}
/**
* @param string $titolo_originale
*/
public function setTitoloOriginale($titolo_originale)
{
$this->titolo_originale = $titolo_originale;
}
/**
* @return string
*/
public function getTitoloOriginale()
{
return $this->titolo_originale;
}
/**
* @param string $trama
*/
public function setTrama($trama)
{
$this->trama = $trama;
}
/**
* @return string
*/
public function getTrama()
{
return $this->trama;
}
/**
* @param \AC\UserBundle\Entity\Utente $utente_aggiornamento
*/
public function setUtenteAggiornamento($utente_aggiornamento)
{
$this->utente_aggiornamento = $utente_aggiornamento;
}
/**
* @return \AC\UserBundle\Entity\Utente
*/
public function getUtenteAggiornamento()
{
return $this->utente_aggiornamento;
}
}
在控制器中,使用$form->isValid()
方法执行classi调用以检查验证。如果有错误,我会致电$form->getErrorsAsString()
,这就是结果:
ERROR: This value should not be blank.
ERROR: This value should not be blank.
numeroEpisodio:
No errors
titoloItaliano:
No errors
titoloOriginale:
No errors
dataIta:
ERROR: This value is not valid.
dataJap:
ERROR: This value is not valid.
durata:
No errors
trama:
No errors
extra:
No errors
fansub:
No errors
使用@Assert\NotBlank()
的属性不会在错误消息中输入属性名称!由于这个原因,我有前两个错误行与e generic:
ERROR: This value should not be blank.
ERROR: This value should not be blank.
我不知道谁是未通过验证的财产。我查看了Symfony Component的源代码,我发现它不是空白约束:
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class NotBlankValidator extends ConstraintValidator
{
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof NotBlank) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\NotBlank');
}
if (false === $value || (empty($value) && '0' != $value)) {
$this->context->addViolation($constraint->message);
}
}
}
对于数据约束:
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class DateValidator extends ConstraintValidator
{
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof Date) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Date');
}
if (null === $value || '' === $value || $value instanceof \DateTime) {
return;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string) $value;
if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
}
}
}
重要的区别是$this->context->addViolation($constraint->message);
VS $this->context->addViolation($constraint->message, array('{{ value }}' => $value));
为什么?
这是控制器的核心部分
if ($request->getMethod() == 'POST') {
try {
$form->handleRequest($request);
if ($form->isValid()) {
/* @var $item Episodio */
$item = $form->getData();
$em->persist($item);
$em->flush();
$msg = new Message(true, Message::OK_MESSAGE);
} else {
$msg = new Message(false, Message::KO_MESSAGE);
$errors = Utility::getErrorMessages($form);
$msg->setData($errors);
}
} catch (\Exception $ex) {
$msg = new Message(false, $ex->getMessage());
}
return new Response($this->get('jms_serializer')->serialize($msg, 'json'));
}
这是从表单中获取错误的实用程序类
class Utility {
static function getErrorMessages(\Symfony\Component\Form\Form $form) {
$errors = array();
foreach ($form->getErrors() as $key => $error) {
$template = $error->getMessageTemplate();
$parameters = $error->getMessageParameters();
foreach($parameters as $var => $value){
$template = str_replace($var, $value, $template);
}
$errors[$key] = $template;
}
//if ($form->hasChildren()) {
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$errors[$child->getName()] = Utility::getErrorMessages($child);
}
}
//}
return $errors;
}
}
表单类
class EpisodioForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('numeroEpisodio', 'text');
$builder->add('titoloItaliano', 'text',array());
$builder->add('titoloOriginale', 'text');
$builder->add('dataIta', 'date', array('widget' => 'single_text', 'format' => 'dd/MM/yyyy'));
$builder->add('dataJap', 'date', array('widget' => 'single_text', 'format' => 'dd/MM/yyyy'));
$builder->add('durata', 'text');
$builder->add('trama', 'textarea');
$builder->add('extra', 'text');
$builder->add('fansub', 'checkbox');
}
/**
* Returns the name of this type.
*
* @return string The name of this type
*/
public function getName()
{
return "episodio_form";
}
}
框架的版本是 Symfony 2.4
答案 0 :(得分:2)
为什么$this->context->addViolation($constraint->message);
VS $this->context->addViolation($constraint->message, array('{{ value }}' => $value));
?
这是因为日期约束验证器显示错误消息中未能验证的值,例如
45.04.2014 is not a valid date.
而NotBlank
约束验证器不需要,因为导致错误的值总是为空。
将属性名称放在错误消息中
这可以实现:
通过将实体中约束的注释更改为:
class Episodio {
/**
* @Assert\NotBlank(message="'titolo_italiano' should not be blank.")
*/
protected $titolo_italiano;
/**
* @Assert\NotBlank(message="'titolo_originale' should not be blank.")
*/
protected $titolo_originale;
}
为了便于阅读,我省略了列定义和实体中的其他字段。
或者通过定义将属性名称传递给错误消息的自定义验证器:
约束:
class MyCustomNotBlank extends NotBlank
{
public $message = "'{{ propertyName }}' should not be blank.";
}
验证员:
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class MyCustomNotBlankValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!$value) {
$this->context->addViolation(
$constraint->message,
array('{{ propertyName}}' => $this->context->getPropertyPath())
);
}
}
}
这将向您展示如何定义自定义验证约束: http://symfony.com/doc/current/cookbook/validation/custom_constraint.html
获取包含属性名称和错误消息的键值数组
Afaik,Symfony2的表单组件中没有这样做的功能,所以必须实现它。
见https://stackoverflow.com/a/13763053/277106。您可以将该功能添加到服务以获得可重用性。在您的(AJAX)控制器中,您可以执行以下操作:
public function someAction(Request $request)
{
$errors = array();
$form = $this->createForm(new MyType);
if (!$form->bindRequest($request)->isValid()) {
$errors = $this->myFormService->getErrorMessages($form);
}
else {
// save the entity
}
return JsonResponse($errors);
}