Symfony2 Form Validator – сравнение старых и новых значений перед запуском

Мне было интересно, есть ли способ сравнить старые и новые значения в валидаторе внутри объекта до флеша.

У меня есть объект Server который отлично отображает форму. Сущность имеет отношение к status (N-> 1), который, когда статус изменен с Unracked to Unracked , должен проверить SSH и FTP-доступ к серверу. Если доступ не достигнут, валидатор должен выйти из строя.

Я сопоставил обратный вызов валидатора с методом isServerValid() внутри объекта Server как описано здесь http://symfony.com/doc/current/reference/constraints/Callback.html . Я могу, очевидно, получить доступ к «новым» значениям через $this->status , но как я могу получить первоначальное значение?

В псевдокоде есть что-то вроде этого:

 public function isAuthorValid(ExecutionContextInterface $context) { $original = ... ; // get old values if( $this->status !== $original->status && $this->status === 'Racked' && $original->status === 'Unracked' ) { // check ftp and ssh connection // $context->addViolationAt('status', 'Unable to connect etc etc'); } } 

Заранее спасибо!

Solutions Collecting From Web of "Symfony2 Form Validator – сравнение старых и новых значений перед запуском"

Полный пример для Symfony 2.5 ( http://symfony.com/doc/current/cookbook/validation/custom_constraint.html )

В этом примере новое значение для поля «integerField» объекта «NoDecreasingInteger» должно быть выше сохраненного значения.

Создание ограничения:

 // src/Acme/AcmeBundle/Validator/Constraints/IncrementOnly.php; <?php namespace Acme\AcmeBundle\Validator\Constraints; use Symfony\Component\Validator\Constraint; /** * @Annotation */ class IncrementOnly extends Constraint { public $message = 'The new value %new% is least than the old %old%'; public function getTargets() { return self::CLASS_CONSTRAINT; } public function validatedBy() { return 'increment_only'; } } 

Создание механизма проверки ограничений:

 // src/Acme/AcmeBundle/Validator/Constraints/IncrementOnlyValidator.php <?php namespace Acme\AcmeBundle\Validator\Constraints; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Doctrine\ORM\EntityManager; class IncrementOnlyValidator extends ConstraintValidator { protected $em; public function __construct(EntityManager $em) { $this->em = $em; } public function validate($object, Constraint $constraint) { $new_value = $object->getIntegerField(); $old_data = $this->em ->getUnitOfWork() ->getOriginalEntityData($object); // $old_data is empty if we create a new NoDecreasingInteger object. if (is_array($old_data) and !empty($old_data)) { $old_value = $old_data['integerField']; if ($new_value < $old_value) { $this->context->buildViolation($constraint->message) ->setParameter("%new%", $new_value) ->setParameter('%old%', $old_value) ->addViolation(); } } } } 

Привязка валидатора к объекту:

 // src/Acme/AcmeBundle/Resources/config/validator.yml Acme\AcmeBundle\Entity\NoDecreasingInteger: constraints: - Acme\AcmeBundle\Validator\Constraints\IncrementOnly: ~ 

Внедрение EntityManager в IncrementOnlyValidator:

 // src/Acme/AcmeBundle/Resources/config/services.yml services: validator.increment_only: class: Acme\AcmeBundle\Validator\Constraints\IncrementOnlyValidator arguments: ["@doctrine.orm.entity_manager"] tags: - { name: validator.constraint_validator, alias: increment_only } 

Доступ к EntityManager внутри настраиваемого валидатора в symfony2

вы можете проверить предыдущее значение внутри действия вашего контроллера … но это не было бы действительно чистым решением!

нормальная форма-валидация будет доступ только к данным, привязанным к форме … нет «предыдущих» данных, доступных по умолчанию.

Ограничение обратного вызова, которое вы пытаетесь использовать, не имеет доступа к контейнеру или какой-либо другой услуге … поэтому вы не можете легко получить доступ к диспетчеру сущностей (или любому другому поставщику предыдущих данных) для проверки предыдущего значения.

Вам нужен настраиваемый валидатор на уровне класса . класс-уровень необходим, потому что вам нужно получить доступ ко всему объекту не только к одному значению, если вы хотите извлечь объект.

Сам валидатор может выглядеть так:

 namespace Vendor\YourBundle\Validation\Constraints; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; class StatusValidator extends ConstraintValidator { protected $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function validate($status, Constraint $constraint) { $em = $this->container->get('doctrine')->getEntityManager('default'); $previousStatus = $em->getRepository('YourBundle:Status')->findOneBy(array('id' => $status->getId())); // ... do something with the previous status here if ( $previousStatus->getValue() != $status->getValue() ) { $this->context->addViolationAt('whatever', $constraint->message, array(), null); } } public function getTargets() { return self::CLASS_CONSTRAINT; } public function validatedBy() { return 'previous_value'; } } 

… после этого зарегистрируйте валидатор как службу и пометьте его как валидатор

 services: validator.previous_value: class: Vendor\YourBundle\Validation\Constraints\StatusValidator # example! better inject only the services you need ... # ie ... @doctrine.orm.entity_manager arguments: [ @service_container ] tags: - { name: validator.constraint_validator, alias: previous_value } 

наконец, используйте ограничение для вашего объекта статуса (т. е. используя аннотации)

 use Vendor\YourBundle\Validation\Constraints as MyValidation; /** * @MyValidation\StatusValidator */ class Status { 

Предыдущие ответы совершенно верны и могут соответствовать вашему варианту использования.

Для «простого» варианта использования он может заполнить все, хотя. В случае объекта, редактируемого через (только) форму, вы можете просто добавить ограничение на FormBuilder:

 <?php namespace AppBundle\Form\Type; // ... use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; /** * Class MyFormType */ class MyFormType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('fooField', IntegerType::class, [ 'constraints' => [ new GreaterThanOrEqual(['value' => $builder->getData()->getFooField()]) ] ]) ; } } 

Это действительно для любой версии Symfony 2+.