8000 [Form] Impossible to affect form options during buildForm in FormExtensions · Issue #8513 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[Form] Impossible to affect form options during buildForm in FormExtensions #8513
Closed
@lwc

Description

@lwc

Hi,

For background, I am attempting to write a FormTypeExtension that serialises form field constraints into data attributes on the input elements so that it is possible to write a generalised javascript library capable of validating most typical symfony forms client side.

One place that breaks down is when validation rules depend on submitted data - I've seen and have been using this to successfully validate on the server side, but without the ability to define field interdependencies declaratively, it becomes impossible to bake these rules into data attributes: there is no way to serialise the decision that happens in that closure.

With that in mind, I've been attempting to build another FormTypeExtension that introduces the notion of dependencies, see comments inline:

<?php

namespace App\Form\Extension;

use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use App\Form\EventListener\DependsListener;

class DependsExtension extends AbstractTypeExtension
{
    public function getExtendedType()
    {
        return 'form';
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setOptional(array(
            'depends'
        ));
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if (array_key_exists('depends', $options)) {

            // should builder be needed here? is this even correct?
            $builder->addEventSubscriber(new DependsListener($builder, $options['depends']));
        }
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        // bake dependencies into data attributes for client side js to act on
        if (array_key_exists('depends', $options)) {
            $view->vars['attr']['data-depends'] = json_encode($options['depends']);
        }
    }
}

And the listener:

<?php

namespace App\Form\EventListener;

use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\PropertyAccess\PropertyAccess;


class DependsListener implements EventSubscriberInterface
{
    /**
     *
     * @var FormBuilderInterface
     */
    private $builder;
    private $depends;

    public function __construct($builder, $depends)
    {
        $this->builder = $builder;
        $this->depends = $depends;
    }

    public static function getSubscribedEvents()
    {
        return array(
            FormEvents::PRE_SET_DATA => 'preSetData',
            FormEvents::PRE_SUBMIT => 'preSubmit'
        )
62BB
;
    }

    public function preSetData(FormEvent $event)
    {
        // TODO probably have to do something here when loading the form
    }

    public function preSubmit(FormEvent $event)
    {
        $root = $event->getForm()->getRoot();
        $accessor = PropertyAccess::createPropertyAccessor();

        $removeMe = true;
        foreach ($this->depends as $fieldPath => $value) {

            // TODO: use sf constraints for this? allows for more options than simple equality
            if ($accessor->getValue($root, $fieldPath)->getData() != $value) {
                $removeMe = $removeMe && true;
            }
            else {
                $removeMe = false;
            }
        }

        if ($removeMe) {

            // this is where I'd like to somehow unset / interact with the constraints defined on the field
            // 
            // note it is possible to just straight up remove the field:
            $event->getForm()->getParent()->remove($event->getForm()->getName());
            // but this is not ideal. ideally we'd disable server side validation and hide this field with js
        }
    }
}

As you can see, it seems impossible to unset / interact with the constraints associated with the field, even though it's possible to simply outright delete the field.

Could anyone shed any light on if what I'm trying to do is possible / desired / insane / stupid and help point me in the direction of a solution?

Thanks,
Luke Cawood

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0