FabienPennequin / FPNTagBundle

This bundle allows to tag your Doctrine entities easily
http://knpbundles.com/FabienPennequin/FPNTagBundle
76 stars 50 forks source link

Handle forms #7

Open ghost opened 12 years ago

ghost commented 12 years ago

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 commented 12 years ago

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

mweimerskirch commented 12 years ago

@gimler Any progress on that datatransformer you could share?

damienalexandre commented 12 years ago

:+1:

mweimerskirch commented 12 years ago

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 commented 12 years ago

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 commented 11 years ago

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 commented 11 years ago

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 commented 10 years ago

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 commented 10 years ago

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

tvanc commented 9 years ago

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 commented 9 years ago

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