Skip to content

Latest commit

 

History

History
52 lines (39 loc) · 2.58 KB

factory_value_object.md

File metadata and controls

52 lines (39 loc) · 2.58 KB

Initializing Objects

When a form is not initialized with existing data (for example, because the object is to be created based on the user submitted data), the Form component, by default, will create a new empty instance by calling the constructor of the configured data class.

This approach does not work for models that require some initial data to be passed to the constructor (for example, to ensure that the internal state of the object is always valid).

The factory option can be used to tell the Form component to create the initial data object in a more sophisticated manner. The value passed here can be any of the following:

  • When given a string, it must refer to an existing class whose constructor will be called (and thus must be public).

  • When given an array, the value must be a valid callable.

  • Finally, the value can be a closure. In this case, the form data is passed as arguments to this anonymous function allowing for full flexibility on how to create the initial object.

The immutable and data_class options are mutually exclusive, meaning you cannot set them both at the same time. If you do so, an InvalidConfigurationException will be thrown. This can be confusing when you don't specify a data_class in your form type. The underlying Symfony\Component\Form\Extension\Core\Type\FormType class will attempt to automatically set data_class when you pass an existing object when creating the form. You can prevent any confusion or mistakes by always explicitly setting data_class to null when you use the immutable option as shown in the example below.

Mapping Value Objects

When working with value objects, you have to pass all data the value object consists of as a whole to be sure that it is always in a valid state and cannot be changed. This however means that every time a submitted form is mapped to your model, a new value object must be created instead of manipulating the existing data.

Therefore, besides configuring how to create new instances (see the factory option above) you also need to tell the form that the underlying model is immutable using the option with the same name:

// ...

class PriceType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefault('data_class', null);
        $resolver->setDefault('factory', Price::class);
        $resolver->setDefault('immutable', true);
    }
}