Home > Net >  Symfony5: Recommended pattern for using DataTransformers within Entities
Symfony5: Recommended pattern for using DataTransformers within Entities

Time:05-16

I am currently trying to make our Symfony project type-safe and thereby refactoring it a bit, because it uses some anti-patterns here and there. My problem is that I cannot come up with a good pattern for transforming entity properties. Let me elaborate:

Let's take our Language entities as an example:

  • Language
    • properties: int $id, string $locale, string $name
    • stores different languages (e.g. '1|de|German' or '2|en|English')
  • LanguageToken
    • properties: int $id, string $token
    • stores translation keys (=token) (e.g. '1|user_firstName')
  • LanguageTranslation
    • properties: int $id, Language $language, LanguageToken $token, string $translation
    • defines translations for a token in a specific language (e.g. '1|1|1|Vorname'), so we have ManyToOne relations here

The goal is now to transform Entity instances between their actual objects and a user-friendly string representation, which is e.g. used in the LanguageTranslation form to have a text field in which you can select a Language by its user-friendly string representation (which uses the name property) in combination with a typeahead.

To solve this problem, I have created a LanguageDataTransformer that transforms a Language entity to its user-friendly string in the transform-direction by simply returning the property or an empty string. The reverseTransform is quite trivial as well: It accepts the name and searches the database for a Language with the given name property and returns it or throws an exception if it does not exist. This DataTransformer is then applied to the correct form field by using the addModelTransformer-function.

While this works great to handle the translation between external form input and internal entity objects, we also "serialize" our entities in a generic way by translating them to a data array of their properties (unchanged for scalar types, but user-friendly string for objects) in a toArray() function. The inverse also exists to transform such a data array back to its entity object. This is required because we export/import the entities from/to different formats (e.g. JSON).

As this is basically the same transformation (e.g. for the language field) as in the form handling, I tried to reuse the already written DataTransformer. The problem now is that I am not able to embed the LanguageDataTransformer in the LanguageTranslation entity. The Transformer internally uses the EntityManager, so it needs to be instantiated with autowiring by Symfony. How do I make Symfony autowire the Transformer in my Entity? Especially given that the fromArray-method is static (because it is like a factory to construct the entity), but needs access to the instantiated transformer for the construction process.

I would be very glad about a hint to the correct pattern. Thanks in advance!

CodePudding user response:

I found my problem: Our Entities are instantiated manually (i.e. by calling something like new LanguageTranslation()), so Symfony cannot instantiate the required services for these Entities.

I have decided to extract the import/export logic (i.e. toArray and fromArray) for each Entity to a separate service class which can then be injected at the required import/export locations in the code. That way the required dependencies are injected by Symfony and the Entity or its data array are passed in as an argument respectively. This also leads to a better loose coupling and separation of concerns.

  • Related