Download the PHP package elao/form-simple-object-mapper without Composer
On this page you can find all versions of the php package elao/form-simple-object-mapper. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download elao/form-simple-object-mapper
More information about elao/form-simple-object-mapper
Files in elao/form-simple-object-mapper
Package form-simple-object-mapper
Short Description Ease mapping immutable/value objects to Symfony Forms
License MIT
Homepage https://github.com/Elao/FormSimpleObjectMapper
Informations about the package form-simple-object-mapper
:warning: This package is abandoned in favor of using https://github.com/sensiolabs-de/rich-model-forms-bundle with the factory
option and a callable.
See https://github.com/sensiolabs-de/rich-model-forms-bundle/blob/master/docs/factory_value_object.md#initializing-objects for more infos.
Symfony Form Simple Object Mapper
This library aims to ease immutable or value objects mapping with the Symfony Form component, based on Bernhard Schussek (Webmozart)'s blog post: "Value Objects in Symfony Forms", until a decision on https://github.com/symfony/symfony/pull/19367 is made.
Table of Contents
- Installation
- With Symfony Full Stack framework
- With Symfony Form component only
- Usage
- Advanced usage
- Using a callback
- Handle conversion errors
- Convert form data to null
- Map an object to the form
Installation
With Symfony Full Stack framework
With Symfony Form component only
Register the type extension within the form factory, by using the FormFactoryBuilder
:
Usage
The library aims to provide a solution to not modify your domain or application models only for satisfying the Symfony Form component requirements.
The way your classes are designed should not be denatured because of infrastructure constraints (the libraries you're using in your project).
This is particularly true when using a command bus, such as thephpleague/tactician.
Imagine a simple AddItemToCartCommand
command:
Your controller will look like:
Although this works great, you're forced to create the AddItemToCartCommand
object from form's data, and validation has been processed on raw form data instead of you're object. You're not manipulating objects inside form neither, which can be an issue when dealing with more complex forms and form events.
As the form is responsible to map the request to objects with a meaning in your app, it makes sense to delegate the creation of our command from the request to the Form component.
Thus, you'll create a form type similar to the following one:
And your new controller:
Although it works perfectly for creating the form, this won't work natively when submitting it:
Neither the property "reference" nor one of the methods "setReference()", "set()" or "call()" exist and have public access in class "AddItemToCartCommand".
This is explained by the fact the Form component uses by default the PropertyPathMapper
, which tries to access and set object properties by using different means, as public getters/setters or public properties (It makes use of the Symfony PropertyAccess
component internally).
As most of our commands, AddItemToCartCommand
is designed as an immutable object. It's meant to preserve the command integrity once created and validated, in order to safely process it inside our handlers. Hence, despite the fact the PropertyPathMapper
is able to read properties through the provided getters, the command object does not have any setter. Thus, the PropertyPathMapper
is unable to map submitted form datas to our object. We must tell the Form component how to proceed (see Bernhard Schussek (Webmozart)'s blog post: "Value Objects in Symfony Forms" for a complete exlanation and examples on how to achieve that with data mappers).
Of course you could add setters or make the command properties public to workaround this limitation, but as stated above:
Your classes should not be denatured because of infrastructure constraints.
We've seen the PropertyPathMapper
is perfectly able to read our object, and map its properties to the form. Hence come the new SimpleObjectMapper
and simple_object_mapper
option:
Your only job is to tell the form how to create an instance of your object according to submitted data, by implementing FormDataToObjectConverterInterface::convertFormDataToObject(array $data, $originalData)
:
- The first
$data
argument of this method is an array of data submitted to the form. - The second
$originalData
argument is the original data you gave to the form when creating it, which can be reused to recreate your object from data not present in the form itself.
This code is more or less the one we've written in the first controller version, but the logic is moved where it belongs: inside the form type.
As a bonus, the object is properly validated by the Symfony Validator component.
Advanced usage
Using a callback
Instead of implementing the FormDataToObjectConverterInterface
, you can simply pass a callable as the simple_object_mapper
option value:
Handle conversion errors
If you're unable to convert form's data to an object, due to unexpected or missing data, you should throw a TransformationFailedException
.
This exception is gracefully handled by the form component by catching it and transforming it to a form error.
The error message displayed is the one set in the invalid_message
component.
Structural validation should be ensured by using proper form types (i.e: IntegerType
for an integer field) and domain validation by validation rules using teh Symfony Validator component.
Convert form data to null
When it makes sense, it's up to you to add your own logic inside FormDataToObjectConverterInterface::convertFormDataToObject()
in order to return null
instead of an instance of your object according to submitted data:
Map an object to the form
Mapping the object to the form is usually not something you should care if your immutable object has proper getters. The default PropertyPathhMapper
implementation will do the job perfectly.
However, for most advanced usages, an ObjectToFormDataConverterInterface
interface can also be implemented, allowing to skip the original mapper (in most cases the PropertyPathMapper
) implementation, allowing to map the data to the form yourself by converting to value object to an array of form data indexed by field name:
:memo: Remember, the
TransformationFailedException
message is not used to render the form error. It'll use theinvalid_message
option value instead. However, it's useful to set it for debugging purpose.:v: By using a proper
ChoiceType
field, this exception should never occur and a proper message will be shown about the unexpected field value.