Using a DataTransformer to save tags for an object in Symfony

Today I want to show you how tags can be edited and saved comfortably using a form and a DataTransformer. My Document looks like this:

namespace MyName\MyBundle\Document;
 
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Validator\Constraints as Assert;
 
/**
 * @MongoDB\Document()
 */
class Post
{
    /**
     * @MongoDB\Collection
     */
    private $tags = array();
 
    public function getTags()
    {
        return $this->tags;
    }
 
    public function setTags(array $tags)
    {
        $this->tags = $tags;
    }
 
    // other fields like title and text and other setters/getters
}

Now I want that the user can edit a Post with a form. There I want the user to type a list of tags, separated by commas. This means I have to get the form input, convert it to an array and save this array when the Post is saved. When I want to display the form I have to take the array of tags and transform it to a string with comma-separated tags. In Symfony I don’t have to do this manually in my controller. I can use such a DataTransformer:

namespace MyName\MyBundle\Form\DataTransformer;
 
use Symfony\Component\Form\DataTransformerInterface;
 
class TagsTransformer implements DataTransformerInterface
{
    /**
      * Transforms the Document's value to a value for the form field
      */
    public function transform($tags)
    {
        if (!$tags) {
            $tags = array(); // default value
        }
 
        return implode(', ', $tags); // concatenate the tags to one string
    }
 
    /**
      * Transforms the value the users has typed to a value that suits the field in the Document
      */
    public function reverseTransform($tags)
    {
        if (!$tags) {
            $tags = ''; // default
        }
 
        return array_filter(array_map('trim', explode(',', $tags)));
        // 1. Split the string with commas
        // 2. Remove whitespaces around the tags
        // 3. Remove empty elements (like in "tag1,tag2, ,,tag3,tag4")
    }
}

Now you can create a new “tags” form type and use this transformer:

namespace MyName\MyBundle\Form\Type;
 
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use MyName\MyBundle\Form\DataTransformer\TagsTransformer;
 
class TagsType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->appendClientTransformer(new TagsTransformer());
    }
 
    public function getParent(array $options)
    {
        return 'text';
    }
 
    public function getName()
    {
        return 'tags';
    }
}

This new type can be used in the form type of the document:

namespace MyName\MyBundle\Form\Type;
 
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\Event\DataEvent;
 
use MyName\MyBundle\Form\Type\TagsType;
 
class PostDocumentType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('title');
        $builder->add('date');
        $builder->add('text');
        $builder->add('tags', new TagsType()); // add tags field
    }
 
    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' => 'MyName\MyBundle\Document\Post',
        );
    }
 
    public function getName()
    {
        return 'post_document';
    }
}

And that’s it. Never ever waste time parsing a list of tags :)

3 thoughts on “Using a DataTransformer to save tags for an object in Symfony

  1. this is bound to builder-> however i wonder if it can be used as a pattern elsewhere, what are other use cases and if you could elaborate where this can fit in the bigger designer tools picture.

    1. I think it is some kind of Strategy pattern. You define several strategies (the transformers) and then some data is passed to them. They return the data in a transformed state. Use cases are everywhere you want to provide a flexible way of customizing the transformation of data.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">