Using a Data Transformer in Symfony to handle duplicate tags

This is Part 3 in a series on setting up Tags in a ManyToMany association with an Entity in Symfony. You can read the previous two articles here:

  1. Managing Tags in ManyToMany association with Symfony
  2. Setting up Form Collection for Tags in Symfony

In the last article, I talked about the problem of duplicate Tags being added when creating a new Product. Here, I will show one way to handle that using a Data Transformer.

First, we will create the data transformer:

Your data transformer implements the DataTransformerInterface, which requires these two methods:

transform  allows you to alter the data going to your form. We don’t need to change this, so can just return the tags as is.

reverseTransform  allows you to alter the data submitted by your form. This is where we want to make our changes.

In the constructor, the ObjectManager  is injected for checking a Tag’s existence in the database.

In the reverseTransform  method, an empty ArrayCollection is created to store all tags we’ll be returning. For each submitted Tag, it checks for its presence in the Tag repository. If present, we replace the form submitted Tag with the one already present in the Tag repository. If not, then we allow the new form submitted Tag into the $tagCollection .

Finally, the $tagCollection  is returned.

You can do any other filtering you like there. But for the purposes of this tutorial, we’ll keep it simple like that.

The next thing we need to do is make a few changes to our ProductType:

We’ll be using the ObjectManager  and our newly created TagsToCollectionTransformer :

The ObjectManager  is injected in the constructor. (We’ll create a service for our ProductType class later)

We also get 'tags'  from our form builder, add our transformer to it using the addModelTransformer  method, and pass in the  ObjectManager  dependency to our TagsToCollectionTransformer .

Finally, we create a service for our ProductType  class which injects the ObjectManager  dependency to it:

Our form is now ready to roll.

Let’s create a controller for submitting new Products:

This should be pretty self-explanatory if you’re familiar with controllers, routes, form submissions, and persisting objects to the database in Symfony. Basically we just create our form with a new Product  object, and have the form handle the Request. If a form was submitted, and is valid, we persist the Product to the database. Finally, we render the submitProduct template which will be created later.

Note that while it’s not actually required to check $form->isSubmitted() , it is recommended per the Symfony best practices on form submissions for readability:

Second, we recommend using $form->isSubmitted() in the if statement for clarity. This isn’t technically needed, since isValid() first calls isSubmitted(). But without this, the flow doesn’t read well as it looks like the form is always processed (even on the GET request).

The last thing to do is create the Twig template submitProduct.html.twig which will display our form. Since we’ll be adding and removing Tags from Products, we’ll need some JavaScript to add/remove our  inputs for us. You could accomplish this using the example in the Symfony documentation, but in the next article, we’ll be using the jQuery Tag-it plugin instead.

Next article: How to use jQuery Tag-it plugin with a Symfony form