Skip to content

[Form] inherit_data and event dispatching ... #8607

Closed
@rande

Description

@rande

@bschussek this was supposed to be a blog post, but never get published as it is too much technical ....

While migrating the FormatterBundle to Symfony2.3, some small details can take a bit longer to fix than expecting. There is one main issue with the Form Component which makes it pretty impossible for a FormType to access the different fields.

Let's explain how we try to avoid a BC break in the SonataFormatterBundle:

The Formatter Bundle provides a easy way to convert raw markup (markdow, textile, ...) into a final state. The visual representation is

In order to make it work a FormatterType is used. The type allows to define a format (mardown, html, etc ...) to be applied on a target field by reading the data from a source field. So if you have a Post entity, the entity will have :

  • a content field : the transformed field
  • a rawContent field : the source field
  • a contentFormatter field: the format field

As < Symfony2.3, it was possible for a FromType to retrieve the parent form builder. However from 2.3, the FormBuilder::getParent is not available anymore, due to the fact that the parent builder can be changed so it is very hard to rely on it (on some very specific edge cases).

The parent builder was useful to attach an event listener on the parent form to apply the transformation once the form is being posted.

As of Symfony2.3, The FormatterType is broken ... let's see the others options we have:

The inherit_data option can be a solution ~ from the PHP doc: "Sets whether the form should read and write the data of its parent.".
But by looking at the Form class, the Form::setData is not called if the field is defined as ‘inherit_data’. This method is responsible for notifying listeners when data is set. So by using the inherit_data, it is not possible to use FormEvents::PRE_SET_DATA event inside the FormatterType::buildForm

public function getData()
{
    if ($this->config->getInheritData()) {
        if (!$this->parent) {
            throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
        }

        return $this->parent->getData();
    }

    if (!$this->defaultDataSet) {
        $this->setData($this->config->getData());
    }

    return $this->modelData;
}

After digging into the Form Component source code, the internal event dispatcher is set per FormBuilder and while a FormType::buildForm is called, the callee method does not copy the event dispatcher from the FormatterType builder to the parent (when inherit_data is set to true). This can be the expected behavior as inherited_data means "Sets whether the form should read and write the data of its parent.", we can expect the event dispatcher being shared. By looking at the code, this change is not as easy as it might look. While a Form is being built by using the FormBuilder and the FormType the options are not always available.

So in order to find a solution and keep going, we introduce a BC break in the FormatterType.

The solution: pass the parent event dispatcher to the form type. This might not be the best solution, but at least it will work as before and might break on some edge cases (can someone point me to an edge case?)

The BC break and the fix: sonata-project/SonataFormatterBundle@2d1a412

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