tagssonatasymfony-2.4sonata-media-bundle

Integrate Sonata Media Bundle (Media Entity) and Sonata Classiffication Bundle (Tags Entity)


I need to integrate this two bundles to be able to add Tags to an Media Entity (Image, Video, etc.).

I'm using:

"sonata-project/media-bundle":"version": "2.3.1" "sonata-project/classification-bundle":"version": "2.2.1" "symfony/symfony":"version": "v2.4.10"


Solution

  • In the sonata sandbox they demonstrate overriding entities.

    I'd suggest reviewing their appbundle directory structure and configs. You may have to use the current 2.3 branch folder layout but the concept is the same.

    the examples below will assume you are overriding/extending every entity. If you are only planning on overriding the media entity then I believe you would just need to change the namespace for the AppBundle\Entity\Classification\Tag to Sonata\ClassificationBundle\Model\Tag (not tested)

    you could add extra properties to the media entity located here

    AppBundle\Entity\Media\Media.php

     /**
     * @var ArrayCollection|\AppBundle\Entity\Classification\Tag[]
     */
    protected $tags;
    
    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        parent::__construct();
        $this->tags           = new ArrayCollection();
    }
    
    /**
     * @return ArrayCollection|\AppBundle\Entity\Classification\Tag[]
     */
    public function getTags()
    {
        return $this->tags;
    }
    
    /**
     * @param ArrayCollection|\AppBundle\Entity\Classification\Tag[] $tags
     */
    public function setTags($tags)
    {
        $this->tags = $tags;
    }
    

    then edit the doctrine xml located here to include these new relationships

    AppBundle\Resources\config\doctrine\Media.Media.orm.xml

    <many-to-many field="tags" target-entity="\AppBundle\Entity\Classification\Tag">
            <cascade>
                <cascade-persist/>
            </cascade>
            <join-table name="media__media_tag">
                <join-columns>
                    <join-column name="media_id" referenced-column-name="id" nullable="false" unique="false" />
                </join-columns>
                <inverse-join-columns>
                    <join-column name="tag_id" referenced-column-name="id" column-definition="INT NULL" />
                </inverse-join-columns>
            </join-table>
        </many-to-many>
    

    notice we're creating a new join table called media__media_tag this is following the existing patter and prefixing the table with media__ and media_tag indicates the relationship.

    we have solved the part of extending the current schema. You'll then need to tell the bundle to use your class instead as seen here (this could be in your app/config/config.yml instead of being imported from app/config/sonata/sonata_media.yml like the sandbox

    sonata_media:
    class:
        media:             AppBundle\Entity\Media\Media
    

    The last step would be to add the property to the MediaAdmin class for management. This part is a little more tricky and I'm not sure if its the most ideal solution.

    MediaBundle has an admin class for each storage model ORM|ODM|PHPCR implementing the abstract class BaseMediaAdmin unfortunately we would have to extend each one used. I beleive ORM is the most common so we'll extend that one

    what we are looking to do is add a form field for the tags

    so creating a new directory Admin inside AppBundle and a class called MediaAdmin (or whatever you like as long as it ends in Admin) and extend the class Sonata\MediaBundle\Admin\ORM\MediaAdmin. The example below we override configureFormFields and call the parent before adding the field for tags.

    AppBundle\Admin\MediaAdmin.php

    namespace AppBundle\Admin;
    
    class MediaAdmin extends \Sonata\MediaBundle\Admin\ORM
    {
    
        /**
         * {@inheritdoc}
         */
        protected function configureFormFields(FormMapper $formMapper)
        {
            parent::configureFormFields($formMapper);
    
            $formMapper->add('tags', 'sonata_type_model', array('multiple' => true, 'by_reference' => false));
        }
    

    then we need to add a compiler pass to override the MediaAdmin service with our class.

    AppBundle\AppBundle.php

    namespace AppBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    use AppBundle\DependencyInjection\Compiler\OverrideServiceCompilerPass;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    
    class AppBundle extends Bundle
    {
        public function build(ContainerBuilder $container)
        {
            parent::build($container);
    
            $container->addCompilerPass(new OverrideServiceCompilerPass());
        }
    }
    

    AppBundle\DependencyInjection\Compiler\OverrideServiceCompilerPass.php

    namespace AppBundle\DependencyInjection\Compiler;
    
    use AppBundle\Admin\MediaAdmin;
    use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Reference;
    
    class OverrideServiceCompilerPass implements CompilerPassInterface
    {
        public function process(ContainerBuilder $container)
        {
            $definition = $container->getDefinition('sonata.media.admin.media');
            $definition->setClass(MediaAdmin::class);
        }
    }
    

    if you wanted to add a tag filter you could override configureDatagridFilters, but this should be everything needed to get you started.