FPNTagBundle icon indicating copy to clipboard operation
FPNTagBundle copied to clipboard

Handle forms

Open ghost opened this issue 12 years ago • 11 comments

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.

ghost avatar May 22 '12 13:05 ghost

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

gimler avatar May 30 '12 06:05 gimler

@gimler Any progress on that datatransformer you could share?

mweimerskirch avatar Jul 16 '12 14:07 mweimerskirch

:+1:

damienalexandre avatar Jul 31 '12 15:07 damienalexandre

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 avatar Aug 06 '12 18:08 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.

mweimerskirch avatar Aug 06 '12 20:08 mweimerskirch

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

oschettler avatar Jun 12 '13 15:06 oschettler

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);
      }
    }

Matzz avatar Jun 21 '13 17:06 Matzz

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?

andrew-jones avatar May 07 '14 07:05 andrew-jones

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

jbouzekri avatar May 16 '14 15:05 jbouzekri

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.

tvanc avatar Jan 10 '15 21:01 tvanc

@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.

dramentol avatar Apr 27 '15 08:04 dramentol