Преобразователь данных против ограничений - PullRequest
2 голосов
/ 21 марта 2020

Я наткнулся на вопрос о Symfony DataTransformer с и о том, как правильно их использовать. Хотя я знаю, как реализовать и добавить их в свое поле формы, мне было интересно, как DataTransformer s должны сочетаться с Constraint s .

код показывает мой вариант использования.

Форма

<?php

namespace AppBundle\Form;

use AppBundle\Form\DataTransformer\Consent\ConsentTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\IsTrue;

class ConsentTestForm extends AbstractType
{
    /** @var ConsentTransformer $consentTransformer */
    private $consentTransformer;

    /**
     * ConsentTestForm constructor.
     * @param ConsentTransformer $consentTransformer
     */
    public function __construct(ConsentTransformer $consentTransformer)
    {
        $this->consentTransformer = $consentTransformer;
    }

    /**
     * @inheritDoc
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('accountConsent', CheckboxType::class, [
            'constraints' => [
                new IsTrue()
            ]
        ]);
        $builder->get('accountConsent')->addModelTransformer($this->consentTransformer);

        $builder->add('submit', SubmitType::class);
    }
}

Модель

<?php

class User extends Concrete implements \Pimcore\Model\DataObject\DirtyIndicatorInterface
{
    protected $accountConsent;

    /**
     * ...
     */
    public function getAccountConsent () {
        // ...
    }

    /**
     * ...
     */
    public function setAccountConsent ($accountConsent) {
        // ...
    }
}

Много кода было опущено ради краткость. Модель представляет собой класс Pimcore .

DataTransformer

<?php

namespace Passioneight\Bundle\FormBuilderBundle\Form\DataTransformer\Consent;

use Pimcore\Model\DataObject\Data\Consent;
use Symfony\Component\Form\DataTransformerInterface;

class ConsentTransformer implements DataTransformerInterface
{
    /**
     * @inheritDoc
     * @param Consent|null $consent
     */
    public function transform($consent)
    {
        return $consent instanceof Consent && $consent->getConsent();
    }

    /**
     * @inheritDoc
     * @param bool|null $consented
     */
    public function reverseTransform($consented)
    {
        $consent = new Consent();
        $consent->setConsent($consented ?: false);
        return $consent;
    }
}

. Как вы можете видеть любое представленное значение (т. Е. null, true, false) будет преобразован в Consent и наоборот.

Контроллер

<?php

namespace AppBundle\Controller;

use AppBundle\Form\ConsentTestForm;
use AppBundle\Model\DataObject\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class TestController
 * @package AppBundle\Controller
 *
 * @Route("/test")
 */
class TestController extends AbstractFrontendController
{
    /**
     * @Route("/form")
     * @param Request $request
     * @return Response
     */
    public function formAction(Request $request)
    {
        $user = new User();

        $form = $this->createForm(ConsentTestForm::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted()) {
            if ($form->isValid()) {
                p_r("VALID");
                p_r($user);
            } else {
                p_r("NOT VALID");
            }
        };

        return $this->renderTemplate(':Test:form.html.twig', [
            "form" => $form->createView()
        ]);
    }
}

Обратите внимание, как new User() передается как сущность, чтобы автоматически заполнить его отправленными значениями.

Представление

{{ form(form) }}

Проблема

Проблема Форма может быть построена очень хорошо, в конечном счете, отображая флажок с моей указанной меткой. Благодаря преобразователю состояние checked отображается даже правильно, так как метод transform преобразует User s Consent в boolean.

Однако при отправке формы отображается ошибка, говорящая о том, что требуется согласие учетной записи. Хотя это нормально, когда дело доходит до отправки формы без согласия, это не совсем желаемый результат, когда вы соглашаетесь с остальным.

При согласии представленное значение преобразуется в Consent, который затем будет содержать значение true. Но поскольку преобразование выполняется до проверки отправленного значения , отображается вышеупомянутая ошибка. Это происходит потому, что поле accountConsent, добавленное в форму, имеет набор Constraint, а именно IsTrue. Из-за этого IsTrueValidator проверяет Consent (вместо фактически переданного значения).

Очевидно, IsTrueValidator не может знать о классе Pimcore Consent.

Вопрос

Все это оставляет меня с вопросом: как мне правильно объединить ограничение IsTrue с моим ConsentDataTransformer?

1 Ответ

3 голосов
/ 08 апреля 2020

Проблема с проверкой заключается в том, что вы пытаетесь проверить объект как логический тип. Ограничения всегда выполняются при попытке проверки и преобразователи при отправке формы. Итак, вы уже преобразовали данные, и поэтому проверка IsBool завершается неудачно, потому что значение относится к типу Consent object; не булево.

Чтобы решить эту проблему, вы должны создать новое ограничение проверки, перезаписывающее IsTrue.

<?php

namespace App\Form\Validator;

use Symfony\Component\Validator\Constraints\IsTrue;

class IsConsented extends IsTrue
{
    public $message = 'You need to consent!';
}

И валидатор в том же пространстве имен;

<?php

namespace App\Form\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\IsTrueValidator;

class IsConsentedValidator extends IsTrueValidator
{
    public function validate($value, Constraint $constraint)
    {
        return parent::validate($value->getConsent(), $constraint);
    }
}

Тогда вы необходимо изменить ограничение IsTrue с помощью IsConsented следующим образом;

<?php

namespace App\Form;

use App\Entity\User;
use App\Form\DataTransformer\ConsentTransformer;
use App\Form\Validator\IsConsented;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ConsentTestFormType extends AbstractType
{
    /** @var ConsentTransformer $consentTransformer */
    private $consentTransformer;

    /**
     * ConsentTestForm constructor.
     * @param ConsentTransformer $consentTransformer
     */
    public function __construct(ConsentTransformer $consentTransformer)
    {
        $this->consentTransformer = $consentTransformer;
    }

    /**
     * @inheritDoc
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('accountConsent', CheckboxType::class, [
            'constraints' => [
                new IsConsented()
            ]
        ]);
        $builder->get('accountConsent')->addModelTransformer($this->consentTransformer);

        $builder->add('submit', SubmitType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

Вот и все. Ваша форма действительна сейчас. Вывод должен выглядеть так:

FormController.php on line 30:
"VALID"
...