Skip to content

Handle forms #7

Open
ghost opened this Issue May 22, 2012 · 11 comments

9 participants

@ghost
ghost commented May 22, 2012

Which is the best practise to handle forms?
I have a taggable Entity and i want a field how i can add tags to this entity in a form.

@gimler
gimler commented May 30, 2012

The best way is to use a datatransformer i work on that at the moment

@mweimerskirch

@gimler Any progress on that datatransformer you could share?

@damienalexandre

👍

@mweimerskirch

I looked into writing a "data transformer" class for this bundle, but I don't think this can work. Data transformers only convert one value to another value. Thus, they only get the value of a specific field and do not have a reference to the object itself. However, the way the TagBundle works would require such a reference to the object (for methods such as "addTag($fooTag, $object)", "saveTagging($object)" or "loadTagging($object)".).

@mweimerskirch

I wrote a bundle to handle the "plain text"-to-"tag object" transformation in forms when using the SonataAdminBundle. https://github.com/mweimerskirch/MWTagAdminBundle
It's just a prototype though.
Depends on #13.

In order to make the transformation work I push the entire object through the transformer instead of just the "tags" field. I'm not sure this is the best solution, but it's the only one I got to work.

@oschettler

I now have an integration of FPNTagBundle with a CRUD form, using a DataTransformer and a custom form widget - a simple text field for now. This code is part of a showcase I prepared for a workshop, so please adapt the namespaces to your own bundle.

The DataTransformer looks like this:

<?php
/**
 * @see http://symfony.com/doc/current/cookbook/form/data_transformers.html
 */
namespace FUxCon2013\ProjectsBundle\Form;

use Symfony\Component\Form\DataTransformerInterface;

class TagsTransformer implements DataTransformerInterface
{
    private $tagManager;

    public function __construct($tagManager)
    { $this->tagManager = $tagManager; }

    public function transform($tags)
    { return join(', ', $tags->toArray()); }

    public function reverseTransform($tags)
    {
        return $this->tagManager->loadOrCreateTags(
            $this->tagManager->splitTagNames($tags)
        );
    }
}

With this, I implement a custom widget like so:

<?php
namespace FUxCon2013\ProjectsBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use DoctrineExtensions\Taggable\TagManager;

class TagsType extends AbstractType
{
    public function __construct(TagManager $tagManager)
    { $this->tagManager = $tagManager; }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new TagsTransformer($this->tagManager);
        $builder->addModelTransformer($transformer);
    }

    public function getParent()
    { return 'text'; }

    public function getName()
    { return 'tags_entry'; }
}

Once you have registered this new type in app/config/config.yml:

services:
    fuxcon2013.form.tags_entry:
        class: FUxCon2013\ProjectsBundle\Form\TagsType
        arguments: [ "@fpn_tag.tag_manager" ]
        tags:
            - { name: form.type, alias: tags_entry }

... you can use it in your edit form:

<?php
namespace FUxCon2013\ProjectsBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ProjectType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    { 
        $builder->add('tags', 'tags_entry'); 
    }
}

Good luck with tagging,
Olav

@Matzz
Matzz commented Jun 21, 2013

This transformer is ok but you still need to invoke loadTagging($entity) before crating form and saveTagging($entity) after flushing entity. What is more, transformer require setTags method in entity:

    public function setTags($tags) {
      $this->tags->clear();
      foreach($tags as $tag) {
        $this->tags->add($tag);
      }
    }
@andrew-jones

I'm having a issue here where if I don't update the taggable entity the tags don't get updated. Say my entity has a name. If I leave name the same, but send a new tag with the entity when i persist the entity no update is done so postUpdate isn't run. If I do change name then the entity is updated, postUpdate called and the tags are updated correctly. @Matzz Is this what your setTags function on the entity is for? Where/When should I call it?

@jbouzekri
Collaborator

What you could do is to listen on the onFlush events to call the method saveTagging on the entity.

You could take a look at the wrapper of this bundle done by fogs : https://github.com/fogs/tagging-bundle
and its doctrine event subscriber : https://github.com/fogs/tagging-bundle/blob/master/EventListener/TaggableSubscriber.php

@truribe
truribe commented Jan 10, 2015

Wish I'd seen your comment earlier @oschettler as I ended up writing exactly the same code, minus names and inconsequential style differences and then coming here to complain that the functionality wasn't part of the bundle already.

@Matzz The transformer itself that @oschettler posted doesn't require the setTags() method. The entity and form components of symfony require the setTags() method. @jbouzekri's solution is a great one for saving the tags after flushing automatically. Another idea for functionality that maybe ought to be part of the bundle by default.

@dramentol

@oschettler, your solution may work, but I don't think the data transformer should be responsable of creating new tags. What happens if there are some form errors? You have already persisted those tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.