8000 [RFC][OptionsResolver] Add a new "addNormalizer()" method and normalization hierarchy · Issue #30310 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[RFC][OptionsResolver] Add a new "addNormalizer()" method and normalization hierarchy #30310
Closed
@yceruto

Description

@yceruto

The normalization procedure is the last step of the option resolution algorithm, it allow us to change the default or passed value after validating it.

Problem

Currently, we have the possibility to configure only one normalizer per option. For example, this is a form type definition that belongs to a third-party package:

class Type1 extends AbstractType
{
    private $dummy;

    public function __construct(Dummy $dummy)
    {
        $this->dummy = $dummy;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefault('foo', null);

        $resolver->setAllowedTypes('foo', ['null', 'callable']);

        $resolver->setNormalizer('foo', function (Options $options, $foo) {
            if (\is_callable($foo)) {
                $foo = $foo($this->dummy);
            }

            return $foo;
        });
    }
}

Note that the dummy instance is used in the normalizer closure.

Next, we want to create a custom form type (in app context) named Type2, that extend from Type1 using getParent() method and it need to add an extra allowed type + normalization to the foo option:

class Type2 extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->addAllowedTypes('foo', 'string');

        $resolver->setNormalizer('foo', function (Options $options, $foo) {
            if (\is_string($foo)) {
                $foo = FooFactory::fromString($foo);
            }

            return $foo;
        });
    }

    public function getParent()
    {
        return Type1::class;
    }
}

At this point, the code is incorrect, because the parent normalizer of the foo option has been overridden and the ability to normalize a callable is lost.

As workaround, we can copy the parent code and all internal deps (e.g. dummy service), but clearly this is a case that should be improved.

That's why I propose the following.

Proposal

Introduce a new addNormalizer() method that allows us to add more than one normalization function associated with an option (just like lazy defaults now work).

Signature:

public function addNormalizer(string $option, \Closure $normalizer): self

Later, the solution to the above problem will be to use addNormalizer() instead of setNormalizer() in Type2, receiving $foo value already normalized by the parent type.

WDYT?


I've reviewed the Component code to analyze its implementation feasibility and it look good to me (draft ready yceruto@c5353a2)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0