Skip to content

Commit fa34e9e

Browse files
author
Thomas Rabaix
committed
Add NotificationBundle support to handle Thumbnail Generations
1 parent 20aea08 commit fa34e9e

15 files changed

+269
-20
lines changed

Consumer/CreateThumbnailConsumer.php

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata project.
5+
*
6+
* (c) Thomas Rabaix <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\MediaBundle\Consumer;
13+
14+
use Sonata\NotificationBundle\Consumer\ConsumerInterface;
15+
use Sonata\NotificationBundle\Consumer\ConsumerEvent;
16+
use Sonata\MediaBundle\Model\MediaManagerInterface;
17+
use Sonata\MediaBundle\Provider\Pool;
18+
use Sonata\MediaBundle\Thumbnail\ThumbnailInterface;
19+
use Sonata\NotificationBundle\Exception\HandlingException;
20+
21+
use Symfony\Component\DependencyInjection\ContainerInterface;
22+
23+
class CreateThumbnailConsumer implements ConsumerInterface
24+
{
25+
protected $mediaManager;
26+
27+
protected $pool;
28+
29+
protected $container;
30+
31+
/**
32+
* @param \Sonata\MediaBundle\Model\MediaManagerInterface $mediaManager
33+
* @param \Sonata\MediaBundle\Provider\Pool $pool
34+
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
35+
*/
36+
public function __construct(MediaManagerInterface $mediaManager, Pool $pool, ContainerInterface $container)
37+
{
38+
$this->mediaManager = $mediaManager;
39+
$this->pool = $pool;
40+
$this->container = $container;
41+
}
42+
43+
/**
44+
* @param \Sonata\NotificationBundle\Consumer\ConsumerEvent $event
45+
* @return void
46+
*/
47+
public function process(ConsumerEvent $event)
48+
{
49+
$media = $this->mediaManager->findOneBy(array(
50+
'id' => $event->getMessage()->getValue('mediaId')
51+
));
52+
53+
// solve race condition between message queue and database transaction
54+
$media->setProviderReference($event->getMessage()->getValue('providerReference'));
55+
56+
if (!$media) {
57+
throw new HandlingException(sprintf('Media not found - id: %s', $event->getMessage()->getValue('mediaId')));
58+
}
59+
60+
try {
61+
$this->getThumbnail($event)->generate($this->pool->getProvider($media->getProviderName()), $media);
62+
} catch(\LogicException $e) {
63+
throw new HandlingException(sprintf('Error while generating exception for media.id: %s', $event->getMessage()->getValue('mediaId')), 0, $e);
64+
}
65+
}
66+
67+
/**
68+
* @param \Sonata\NotificationBundle\Consumer\ConsumerEvent $event
69+
* @return \Sonata\MediaBundle\Thumbnail\ThumbnailInterface
70+
*/
71+
protected function getThumbnail(ConsumerEvent $event)
72+
{
73+
$thumbnail = $this->container->get($event->getMessage()->getValue('thumbnailId'));
74+
75+
if (!$thumbnail instanceof ThumbnailInterface) {
76+
throw new HandlingException(sprintf('Invalid thumbnail instance requested - id: %s', $event->getMessage()->getValue('thumbnailId')));
77+
}
78+
79+
return $thumbnail;
80+
}
81+
}

DependencyInjection/SonataMediaExtension.php

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public function load(array $configs, ContainerBuilder $container)
5454

5555
$bundles = $container->getParameter('kernel.bundles');
5656

57+
if (isset($bundles['SonataNotificationBundle'])) {
58+
$loader->load('consumer.xml');
59+
}
60+
5761
if (isset($bundles['SonataFormatterBundle'])) {
5862
$loader->load('formatter.xml');
5963
}

Listener/BaseMediaEventSubscriber.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ protected function getProvider(EventArgs $args)
6262

6363
/**
6464
* @param \Doctrine\Common\EventArgs $args
65-
* @return
65+
* @return void
6666
*/
6767
public function postUpdate(EventArgs $args)
6868
{
@@ -75,7 +75,7 @@ public function postUpdate(EventArgs $args)
7575

7676
/**
7777
* @param \Doctrine\Common\EventArgs $args
78-
* @return
78+
* @return void
7979
*/
8080
public function postRemove(EventArgs $args)
8181
{
@@ -88,7 +88,7 @@ public function postRemove(EventArgs $args)
8888

8989
/**
9090
* @param \Doctrine\Common\EventArgs $args
91-
* @return
91+
* @return void
9292
*/
9393
public function postPersist(EventArgs $args)
9494
{
@@ -101,7 +101,7 @@ public function postPersist(EventArgs $args)
101101

102102
/**
103103
* @param \Doctrine\Common\EventArgs $args
104-
* @return
104+
* @return void
105105
*/
106106
public function preUpdate(EventArgs $args)
107107
{
@@ -117,7 +117,7 @@ public function preUpdate(EventArgs $args)
117117

118118
/**
119119
* @param \Doctrine\Common\EventArgs $args
120-
* @return
120+
* @return void
121121
*/
122122
public function preRemove(EventArgs $args)
123123
{
@@ -130,7 +130,7 @@ public function preRemove(EventArgs $args)
130130

131131
/**
132132
* @param \Doctrine\Common\EventArgs $args
133-
* @return
133+
* @return void
134134
*/
135135
public function prePersist(EventArgs $args)
136136
{

Listener/ORM/MediaEventSubscriber.php

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
class MediaEventSubscriber extends BaseMediaEventSubscriber
1919
{
20-
2120
/**
2221
* @return array
2322
*/

Model/Media.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ abstract class Media implements MediaInterface
104104
*/
105105
protected $createdAt;
106106

107-
108107
protected $binaryContent;
109108

109+
protected $previousProviderReference;
110+
110111
/**
111112
* @var varchar $content_type
112113
*/
@@ -151,6 +152,7 @@ public static function getStatusList()
151152
*/
152153
public function setBinaryContent($binaryContent)
153154
{
155+
$this->previousProviderReference = $this->providerReference;
154156
$this->providerReference = null;
155157
$this->binaryContent = $binaryContent;
156158
}
@@ -552,4 +554,12 @@ public function getGalleryHasMedias()
552554
{
553555
return $this->galleryHasMedias;
554556
}
557+
558+
/**
559+
* {@inheritdoc}
560+
*/
561+
public function getPreviousProviderReference()
562+
{
563+
return $this->previousProviderReference;
564+
}
555565
}

Model/MediaInterface.php

+2
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,6 @@ function __toString();
350350
function setGalleryHasMedias($galleryHasMedias);
351351

352352
function getGalleryHasMedias();
353+
354+
function getPreviousProviderReference();
353355
}

Provider/FileProvider.php

+13-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function getReferenceFile(MediaInterface $media)
7474
public function buildEditForm(FormMapper $formMapper)
7575
{
7676
$formMapper->add('name');
77-
$formMapper->add('enabled');
77+
$formMapper->add('enabled', null, array('required' => false));
7878
$formMapper->add('authorName');
7979
$formMapper->add('cdnIsFlushable');
8080
$formMapper->add('description');
@@ -121,6 +121,16 @@ public function postUpdate(MediaInterface $media)
121121
return;
122122
}
123123

124+
// Delete the current file from the FS
125+
$oldMedia = clone $media;
126+
$oldMedia->setProviderReference($media->getPreviousProviderReference());
127+
128+
$path = $this->getReferenceImage($oldMedia);
129+
130+
if ($this->getFilesystem()->has($path)) {
131+
$this->getFilesystem()->delete($path);
132+
}
133+
124134
$this->fixBinaryContent($media);
125135

126136
$this->setFileContents($media);
@@ -313,6 +323,8 @@ public function validate(ErrorElement $errorElement, MediaInterface $media)
313323
$fileName = $media->getBinaryContent()->getClientOriginalName();
314324
} else if ($media->getBinaryContent() instanceof File) {
315325
$fileName = $media->getBinaryContent()->getFilename();
326+
} else {
327+
throw new \RuntimeException(sprintf('Invalid binary content type: %s', get_class($media->getBinaryContent())));
316328
}
317329

318330
if (!in_array(strtolower(pathinfo($fileName, PATHINFO_EXTENSION)), $this->allowedExtensions)) {

Provider/ImageProvider.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,13 @@ protected function doTransform(MediaInterface $media)
9090
*/
9191
public function generatePublicUrl(MediaInterface $media, $format)
9292
{
93-
return $this->thumbnail->generatePublicUrl($this, $media, $format);
93+
if ($format == 'reference') {
94+
$path = $this->getReferenceImage($media);
95+
} else {
96+
$path = $this->thumbnail->generatePublicUrl($this, $media, $format);
97+
}
98+
99+
return $this->getCdn()->getPath($path, $media->getCdnIsFlushable());
94100
}
95101

96102
/**

Provider/MediaProviderInterface.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function getFormat($name);
4444
function requireThumbnails();
4545

4646
/**
47-
* generated thumbnails linked to the media, a thumbnail is a format used on the website
47+
* Generated thumbnails linked to the media, a thumbnail is a format used on the website
4848
*
4949
* @param \Sonata\MediaBundle\Model\MediaInterface $media
5050
* @return void
@@ -54,7 +54,7 @@ function generateThumbnails(MediaInterface $media);
5454
/**
5555
* remove all linked thumbnails
5656
*
57-
* @param MediaInterface $media
57+
* @param \Sonata\MediaBundle\Model\MediaInterface $media
5858
* @return void
5959
*/
6060
function removeThumbnails(MediaInterface $media);

Resources/config/consumer.xml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="sonata.media.notification.create_thumbnail" class="Sonata\MediaBundle\Consumer\CreateThumbnailConsumer" >
9+
<tag name="sonata.notification.consumer" type="sonata.media.create_thumbnail" />
10+
11+
<argument type="service" id="sonata.media.manager.media" />
12+
<argument type="service" id="sonata.media.pool" />
13+
<argument type="service" id="service_container" />
14+
</service>
15+
16+
<service id="sonata.media.thumbnail.consumer.format" class="Sonata\MediaBundle\Thumbnail\ConsumerThumbnail" >
17+
<argument>sonata.media.thumbnail.format</argument>
18+
<argument type="service" id="sonata.media.thumbnail.format"/>
19+
<argument type="service" id="sonata.notification.backend" />
20+
</service>
21+
</services>
22+
</container>

Resources/doc/reference/extra.rst

+32-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,38 @@ Add the pixlr routing file
2525
.. code-block:: yaml
2626
2727
# app/config/routing.yml
28-
media_pixlr:
28+
sonata_media_pixlr:
2929
resource: '@SonataMediaBundle/Resources/config/routing/pixlr.xml'
3030
prefix: /admin/media
3131
32-
And now, you can edit any pictures from the admin section.
32+
And now, you can edit any pictures from the admin section.
33+
34+
Sonata Notification Bundle Integration
35+
======================================
36+
37+
The bundle provides a specific consumer to generate thumbnails through an asynchronous task. So there no processing
38+
time for the user after uploading a file.
39+
40+
In order to use this feature, you need to install the Sonata Notification Bundle and change the thumbnail configuration
41+
for each provider::
42+
43+
.. code-block:: yaml
44+
45+
sonata_media:
46+
47+
# ...
48+
49+
providers:
50+
# ...
51+
52+
image:
53+
thumbnail: sonata.media.thumbnail.consumer.format
54+
55+
vimeo:
56+
thumbnail: sonata.media.thumbnail.consumer.format
57+
58+
youtube:
59+
thumbnail: sonata.media.thumbnail.consumer.format
60+
61+
dailymotion:
62+
thumbnail: sonata.media.thumbnail.consumer.format

0 commit comments

Comments
 (0)