phpsymfonysonata-adminsonata-media-bundle

Sonata Admin Image Field


I am using the Sonata Admin back-end and I would like to add a new image field to my user entity which is an avatar. Since I am already using the SonataMediaBundle I followed this tutorial: https://sonata-project.org/blog/2013/10/11/mediabundle-mediatype-improved

Here is my entity configuration:

/**
 * @var \Application\Sonata\MediaBundle\Entity\Media
 *
 * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", cascade={"all"}, fetch="LAZY")
 * @ORM\JoinColumn(name="avatar_id", referencedColumnName="id")
 */
protected $avatar;

Unfortunately I have many problems:

  1. In my back-end the preview is not shown: enter image description here
  2. If I delete the media in the gallery I receive this error when editing the user: Entity of type 'Application\Sonata\MediaBundle\Entity\Media' for IDs id(6) was not found
  3. The resulting API (generated with FOSRestBundle) is unusable by the client:
"avatar": {
        "provider_metadata": {
            "filename": "Test.png"
        },
        "name": "Test.png",
        "description": null,
        "enabled": false,
        "provider_name": "sonata.media.provider.image",
        "provider_status": 1,
        "provider_reference": "325564b03489a6473e7c9def01dc58bab611eccb.png",
        "width": 1430,
        "height": 321,
        "length": null,
        "copyright": null,
        "author_name": null,
        "context": "default",
        "cdn_is_flushable": null,
        "cdn_flush_at": null,
        "cdn_status": null,
        "updated_at": "2017-08-08T12:31:19+02:00",
        "created_at": "2017-08-08T12:31:19+02:00",
        "content_type": "image/png",
        "size": 24978,
        "id": 7
    }

Solution

  • I resolved all the 3 problems! I put here my solutions for all those who have the same difficulties.

    1. In my back-end the preview is not shown:

    As explained here I have to add a custom form widget to my config.yml file:

    twig:
        # Sonata form themes
        form_themes:
            - 'SonataMediaBundle:Form:media_widgets.html.twig'
    

    And in my UserAdmin:

    ->with('Profile')
        ->add('avatar', 'sonata_media_type', array(
            'provider' => 'sonata.media.provider.image',
            'context'  => 'default',
        ))
    ->end()
    

    Now the preview will be shown :)

    1. If I delete the media in the gallery I receive this error when editing the user: Entity of type 'Application\Sonata\MediaBundle\Entity\Media' for IDs id(6) was not found

    As explained here I need to add onDelete="SET NULL" on my entity:

    /**
     * @var \Application\Sonata\MediaBundle\Entity\Media
     *
     * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", cascade={"persist"}, fetch="LAZY")
     * @ORM\JoinColumn(name="avatar_id", referencedColumnName="id", onDelete="SET NULL")
     */
    protected $avatar;
    
    1. The resulting API (generated with FOSRestBundle) is unusable by the client:

    This one was very tricky but I was able to implement a custom JMS handler getting started from this post.

    I peeked into the SonataMediaBundle source code and I found this snippet:

    /**
     * Returns media urls for each format.
     *
     * @ApiDoc(
     *  requirements={
     *      {"name"="id", "dataType"="integer", "requirement"="\d+", "description"="media id"}
     *  },
     *  statusCodes={
     *      200="Returned when successful",
     *      404="Returned when media is not found"
     *  }
     * )
     *
     * @param $id
     *
     * @return array
     */
    public function getMediumFormatsAction($id)
    {
        $media = $this->getMedium($id);
    
        $formats = array(MediaProviderInterface::FORMAT_REFERENCE);
        $formats = array_merge($formats, array_keys($this->mediaPool->getFormatNamesByContext($media->getContext())));
    
        $provider = $this->mediaPool->getProvider($media->getProviderName());
    
        $properties = array();
        foreach ($formats as $format) {
            $properties[$format]['url'] = $provider->generatePublicUrl($media, $format);
            $properties[$format]['properties'] = $provider->getHelperProperties($media, $format);
        }
    
        return $properties;
    }
    

    So I included it into my source and the complete handler is the following:

    <?php
    
    namespace AppBundle\Serializer;
    
    use Application\Sonata\MediaBundle\Entity\Media;
    use JMS\Serializer\Context;
    use JMS\Serializer\GraphNavigator;
    use JMS\Serializer\Handler\SubscribingHandlerInterface;
    use JMS\Serializer\JsonSerializationVisitor;
    use Sonata\MediaBundle\Provider\MediaProviderInterface;
    
    class MediaHandler implements SubscribingHandlerInterface
    {
        private $mediaPool;
    
        public function __construct($mediaPool)
        {
            $this->mediaPool = $mediaPool;
        }
    
        public static function getSubscribingMethods()
        {
            return array(
                array(
                    'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                    'format'    => 'json',
                    'type'      => 'Application\Sonata\MediaBundle\Entity\Media',
                    'method'    => 'serializeToJson',
                ),
            );
        }
    
        public function serializeToJson(JsonSerializationVisitor $visitor, Media $media, array $type, Context $context)
        {
            $formats = array(MediaProviderInterface::FORMAT_REFERENCE);
            $formats = array_merge($formats, array_keys($this->mediaPool->getFormatNamesByContext($media->getContext())));
    
            $provider = $this->mediaPool->getProvider($media->getProviderName());
    
            $properties = array();
            foreach ($formats as $format) {
                $properties[$format]['url']        = $provider->generatePublicUrl($media, $format);
                $properties[$format]['properties'] = $provider->getHelperProperties($media, $format);
            }
    
            return $properties;
        }
    }
    

    Service settings:

    app.serializer.media:
        class: AppBundle\Serializer\MediaHandler
        arguments:
          - '@sonata.media.pool'
        tags:
            - { name: jms_serializer.subscribing_handler }
    

    And that's all!