From a36be9303c897bf37a53070d9c69e45194b81f08 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Fri, 2 Jan 2015 13:49:16 +0900 Subject: [PATCH 01/19] Rename gulpfile.js --- Gulpfile.js => gulpfile.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Gulpfile.js => gulpfile.js (100%) diff --git a/Gulpfile.js b/gulpfile.js similarity index 100% rename from Gulpfile.js rename to gulpfile.js From 331f2cdc3b7218883608925d10280890fe025214 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Fri, 2 Jan 2015 14:14:40 +0900 Subject: [PATCH 02/19] [VichUploader] add File configuration --- src/VichUploader/Configuration/File.php | 62 +++++++++++++++++++ tests/VichUploader/Configuration/FileTest.php | 41 ++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/VichUploader/Configuration/File.php create mode 100644 tests/VichUploader/Configuration/FileTest.php diff --git a/src/VichUploader/Configuration/File.php b/src/VichUploader/Configuration/File.php new file mode 100644 index 0000000..7f12379 --- /dev/null +++ b/src/VichUploader/Configuration/File.php @@ -0,0 +1,62 @@ + + */ +class File +{ + /** + * @var string + */ + private $property; + + /** + * @var string + */ + private $exportTo; + + /** + * @var null|string + */ + private $filter; + + /** + * @param string $property + * @param string $exportTo + * @param string $filter + */ + public function __construct($property, $exportTo = null, $filter = null) + { + $this->property = $property; + $this->exportTo = $exportTo ?: $property; + $this->filter = $filter; + } + + /** + * @return string + */ + public function getProperty() + { + return $this->property; + } + + /** + * @return string + */ + public function getExportTo() + { + return $this->exportTo; + } + + /** + * @return null|string + */ + public function getFilter() + { + return $this->filter; + } +} diff --git a/tests/VichUploader/Configuration/FileTest.php b/tests/VichUploader/Configuration/FileTest.php new file mode 100644 index 0000000..cec2e57 --- /dev/null +++ b/tests/VichUploader/Configuration/FileTest.php @@ -0,0 +1,41 @@ + + */ +class FileTest extends \PHPUnit_Framework_TestCase +{ + /** + * @test + */ + public function test() + { + $file = new File('property'); + $this->assertEquals('property', $file->getProperty()); + $this->assertEquals('property', $file->getExportTo()); + $this->assertEquals(null, $file->getFilter()); + } + + /** + * @test + */ + public function testExportTo() + { + $file = new File('property', 'export_to'); + $this->assertEquals('property', $file->getProperty()); + $this->assertEquals('export_to', $file->getExportTo()); + } + + /** + * @test + */ + public function testFilter() + { + $file = new File('property', null, 'filter'); + $this->assertEquals('filter', $file->getFilter()); + } +} From c15d2ed12752e94eb7db3c52a843dc2091530bb9 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 15:34:19 +0900 Subject: [PATCH 03/19] [VichUplaoder] Add filter feature --- src/VichUploader/Configuration.php | 55 +++++++++------- src/VichUploader/EventSubscriber.php | 38 ++++++----- src/VichUploader/UriResolverException.php | 12 ++++ src/VichUploader/UriResolverInterface.php | 23 +++++++ tests/VichUploader/ConfigurationTest.php | 62 ++++++++++++++++++ tests/VichUploader/EventSubscriberTest.php | 76 ++++++++++++++++------ 6 files changed, 203 insertions(+), 63 deletions(-) create mode 100644 src/VichUploader/UriResolverException.php create mode 100644 src/VichUploader/UriResolverInterface.php create mode 100644 tests/VichUploader/ConfigurationTest.php diff --git a/src/VichUploader/Configuration.php b/src/VichUploader/Configuration.php index cf7cc86..a70f665 100644 --- a/src/VichUploader/Configuration.php +++ b/src/VichUploader/Configuration.php @@ -2,6 +2,8 @@ namespace Hshn\SerializerExtraBundle\VichUploader; +use Hshn\SerializerExtraBundle\VichUploader\Configuration\File; + /** * @author Shota Hoshino */ @@ -18,24 +20,22 @@ class Configuration private $class; /** - * @var array + * @var File[] */ - private $attributes; + private $files; /** * @param string $class - * @param array $attributes + * @param array $files * @param int $maxDepth */ - public function __construct($class, array $attributes = [], $maxDepth = -1) + public function __construct($class, array $files = [], $maxDepth = -1) { $this->class = $class; - $this->attributes = []; + $this->files = []; $this->maxDepth = $maxDepth; - foreach ($attributes as $attribute => $vars) { - $this->setAttribute($attribute, $vars['alias']); - } + $this->addFiles($files); } /** @@ -55,37 +55,42 @@ public function getMaxDepth() } /** - * @param string $attribute - * @param string $alias + * @param array $files * - * @return Configuration + * @return $this */ - public function setAttribute($attribute, $alias = null) + public function addFiles(array $files) { - $this->attributes[$attribute] = $alias ?: $attribute; + foreach ($files as $file) { + $this->addFile($file); + } return $this; } /** - * @return array + * @param File $file + * + * @return $this */ - public function getAttributes() + public function addFile(File $file) { - return $this->attributes; + foreach ($this->files as $f) { + if ($f->getExportTo() === $file->getExportTo()) { + throw new \InvalidArgumentException(sprintf('File destination "%s" has already been specified by others', $f->getExportTo())); + } + } + + $this->files[] = $file; + + return $this; } /** - * @param string $attribute - * - * @return string + * @return File[] */ - public function getAlias($attribute) + public function getFiles() { - if (array_key_exists($attribute, $this->attributes)) { - return $this->attributes[$attribute]; - } - - throw new \LogicException('Invalid attribute "%s"'); + return $this->files; } } diff --git a/src/VichUploader/EventSubscriber.php b/src/VichUploader/EventSubscriber.php index 183758c..bb18eb0 100644 --- a/src/VichUploader/EventSubscriber.php +++ b/src/VichUploader/EventSubscriber.php @@ -4,12 +4,12 @@ use Hshn\SerializerExtraBundle\AbstractContextAwareEventSubscriber; use Hshn\SerializerExtraBundle\ContextMatcher\MatcherFactory; +use Hshn\SerializerExtraBundle\VichUploader\Configuration\File; use JMS\Serializer\EventDispatcher\Events; use JMS\Serializer\EventDispatcher\ObjectEvent; use JMS\Serializer\JsonSerializationVisitor; use Vich\UploaderBundle\Mapping\PropertyMapping; use Vich\UploaderBundle\Mapping\PropertyMappingFactory; -use Vich\UploaderBundle\Storage\StorageInterface; /** * @author Shota Hoshino @@ -32,28 +32,28 @@ public static function getSubscribedEvents() private $propertyMappingFactory; /** - * @var StorageInterface + * @var ConfigurationRepository */ - private $storage; + private $configurationRepository; /** - * @var ConfigurationRepository + * @var UriResolverInterface */ - private $configurationRepository; + private $uriResolver; /** * @param MatcherFactory $matcherFactory * @param PropertyMappingFactory $propertyMappingFactory - * @param StorageInterface $storage * @param ConfigurationRepository $configurationRepository + * @param UriResolverInterface $uriResolver */ - public function __construct(MatcherFactory $matcherFactory, PropertyMappingFactory $propertyMappingFactory, StorageInterface $storage, ConfigurationRepository $configurationRepository) + public function __construct(MatcherFactory $matcherFactory, PropertyMappingFactory $propertyMappingFactory, ConfigurationRepository $configurationRepository, UriResolverInterface $uriResolver) { parent::__construct($matcherFactory); $this->propertyMappingFactory = $propertyMappingFactory; - $this->storage = $storage; $this->configurationRepository = $configurationRepository; + $this->uriResolver = $uriResolver; } /** @@ -73,10 +73,13 @@ public function onPostSerialize(ObjectEvent $event) /** @var $visitor JsonSerializationVisitor */ $visitor = $event->getVisitor(); $object = $event->getObject(); - $attributes = $this->getAttributes($configuration, $object); + $files = $this->getFiles($configuration, $object); - foreach ($attributes as $attribute => $alias) { - $visitor->addData($alias, $this->storage->resolveUri($object, $attribute, $configuration->getClass())); + foreach ($files as $file) { + try { + $visitor->addData($file->getExportTo(), $this->uriResolver->resolve($object, $file, $configuration->getClass())); + } catch (UriResolverException $e) { + } } } @@ -108,21 +111,20 @@ private function buildContextMatcher(Configuration $configuration) * @param Configuration $configuration * @param object $object * - * @return array + * @return File[] */ - private function getAttributes(Configuration $configuration, $object) + private function getFiles(Configuration $configuration, $object) { - if ($attributes = $configuration->getAttributes()) { - return $attributes; + if ($files = $configuration->getFiles()) { + return $files; } /** @var $mappings PropertyMapping[] */ $mappings = $this->propertyMappingFactory->fromObject($object, $configuration->getClass()); foreach ($mappings as $mapping) { - $attribute = $mapping->getFilePropertyName(); - $attributes[$attribute] = $attribute; + $files[] = new File($mapping->getFilePropertyName()); } - return $attributes; + return $files; } } diff --git a/src/VichUploader/UriResolverException.php b/src/VichUploader/UriResolverException.php new file mode 100644 index 0000000..510f3c2 --- /dev/null +++ b/src/VichUploader/UriResolverException.php @@ -0,0 +1,12 @@ + + */ +class UriResolverException extends \Exception +{ +} diff --git a/src/VichUploader/UriResolverInterface.php b/src/VichUploader/UriResolverInterface.php new file mode 100644 index 0000000..feab6bf --- /dev/null +++ b/src/VichUploader/UriResolverInterface.php @@ -0,0 +1,23 @@ + + */ +interface UriResolverInterface +{ + /** + * @param object $object + * @param File $file + * @param string $className + * + * @return string + * @throws UriResolverException + */ + public function resolve($object, File $file, $className = null); +} diff --git a/tests/VichUploader/ConfigurationTest.php b/tests/VichUploader/ConfigurationTest.php new file mode 100644 index 0000000..8f78672 --- /dev/null +++ b/tests/VichUploader/ConfigurationTest.php @@ -0,0 +1,62 @@ + + */ +class ConfigurationTest extends \PHPUnit_Framework_TestCase +{ + /** + * @test + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage "b" has already + */ + public function testThrowExceptionWhenAttemptToAddDuplicatedDestinationFile() + { + $config = new Configuration('Foo', [ + $this->getFile('a'), + $this->getFile('b'), + $this->getFile('c'), + ]); + + $this->assertCount(3, $config->getFiles()); + $config->addFile($this->getFile('b')); + } + + /** + * @test + */ + public function testAddFile() + { + $config = new Configuration('Foo'); + $this->assertCount(0, $config->getFiles()); + + $config->addFile($fileA = $this->getFile('a')); + $this->assertCount(1, $config->getFiles()); + + $config->addFile($fileB = $this->getFile('b')); + $this->assertCount(2, $config->getFiles()); + + $this->assertContains($fileA, $config->getFiles()); + $this->assertContains($fileB, $config->getFiles()); + } + + /** + * @param $exportTo + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFile($exportTo) + { + $file = $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\Configuration\File')->disableOriginalConstructor()->getMock(); + + $file + ->expects($this->any()) + ->method('getExportTo') + ->will($this->returnValue($exportTo)); + + return $file; + } +} diff --git a/tests/VichUploader/EventSubscriberTest.php b/tests/VichUploader/EventSubscriberTest.php index 76682ac..14077a7 100644 --- a/tests/VichUploader/EventSubscriberTest.php +++ b/tests/VichUploader/EventSubscriberTest.php @@ -3,7 +3,7 @@ namespace Hshn\SerializerExtraBundle\VichUploader; - +use Hshn\SerializerExtraBundle\VichUploader\Configuration\File; /** * @author Shota Hoshino @@ -28,20 +28,20 @@ class EventSubscriberTest extends \PHPUnit_Framework_TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $storage; + private $configurationRepository; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $configurationRepository; + private $uriResolver; protected function setUp() { $this->subscriber = new EventSubscriber( $this->matcherFactory = $this->getMock('Hshn\SerializerExtraBundle\ContextMatcher\MatcherFactory'), $this->mappingFactory = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMappingFactory')->disableOriginalConstructor()->getMock(), - $this->storage = $this->getMock('Vich\UploaderBundle\Storage\StorageInterface'), - $this->configurationRepository = $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\ConfigurationRepository')->disableOriginalConstructor()->getMock() + $this->configurationRepository = $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\ConfigurationRepository')->disableOriginalConstructor()->getMock(), + $this->uriResolver = $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverInterface') ); } @@ -95,7 +95,7 @@ public function testDoNotExportUnlessMatchesContextMatcher() /** * @test */ - public function testExportUsingAttributes() + public function testExportUsingFiles() { $event = $this->getObjectEvent('mocked type'); @@ -130,18 +130,23 @@ public function testExportUsingAttributes() $configuration ->expects($this->once()) - ->method('getAttributes') - ->will($this->returnValue([ - 'foo' => 'bar', - 'bar' => 'baz', + ->method('getFiles') + ->will($this->returnValue($files = [ + $this->getFile('foo'), + $this->getFile('bar'), + $this->getFile('baz'), ])); $this - ->storage - ->expects($this->exactly(2)) - ->method('resolveUri') - ->with($this->identicalTo($object), $this->logicalOr('foo', 'bar'), $class) - ->will($this->onConsecutiveCalls('http://foo', 'http://bar')); + ->uriResolver + ->expects($this->exactly(3)) + ->method('resolve') + ->with($this->identicalTo($object), call_user_func_array([$this, 'logicalOr'], $files), $class) + ->will($this->onConsecutiveCalls( + $this->throwException($this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverException')), + 'http://foo', + 'http://bar' + )); $visitor ->expects($this->exactly(2)) @@ -154,7 +159,7 @@ public function testExportUsingAttributes() /** * @test */ - public function testExportUsingNoAttributes() + public function testExportUsingNoFiles() { $event = $this->getObjectEvent('mocked type'); @@ -189,7 +194,7 @@ public function testExportUsingNoAttributes() $configuration ->expects($this->once()) - ->method('getAttributes') + ->method('getFiles') ->will($this->returnValue([])); $this @@ -203,10 +208,24 @@ public function testExportUsingNoAttributes() ])); $this - ->storage + ->uriResolver ->expects($this->exactly(2)) - ->method('resolveUri') - ->with($this->identicalTo($object), $this->logicalOr('foo', 'bar'), $class) + ->method('resolve') + ->with( + $this->identicalTo($object), + $this->logicalAnd( + $this->isInstanceOf('Hshn\SerializerExtraBundle\VichUploader\Configuration\File'), + $this->callback(function (File $file) { + + $this->assertThat($file->getExportTo(), $this->logicalOr('foo', 'bar')); + $this->assertThat($file->getProperty(), $this->logicalOr('foo', 'bar')); + $this->assertNull($file->getFilter()); + + return true; + }) + ), + $class + ) ->will($this->onConsecutiveCalls('http://foo', 'http://bar')); $visitor @@ -312,4 +331,21 @@ private function getObjectEvent($type) return $event; } + + /** + * @param $exportTo + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFile($exportTo) + { + $file = $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\Configuration\File')->disableOriginalConstructor()->getMock(); + + $file + ->expects($this->atLeastOnce()) + ->method('getExportTo') + ->will($this->returnValue($exportTo)); + + return $file; + } } From c0d73d5cbb8c4c4de9d690a93b3a7b0a68f9644c Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 16:06:17 +0900 Subject: [PATCH 04/19] [VichUploader] Add storage uri resolver --- .../ResolvingUriFailedException.php | 13 ++++ .../UriResolver/StorageResolver.php | 38 +++++++++ .../UriResolver/StorageResolverTest.php | 77 +++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/VichUploader/UriResolver/ResolvingUriFailedException.php create mode 100644 src/VichUploader/UriResolver/StorageResolver.php create mode 100644 tests/VichUploader/UriResolver/StorageResolverTest.php diff --git a/src/VichUploader/UriResolver/ResolvingUriFailedException.php b/src/VichUploader/UriResolver/ResolvingUriFailedException.php new file mode 100644 index 0000000..8fde5cf --- /dev/null +++ b/src/VichUploader/UriResolver/ResolvingUriFailedException.php @@ -0,0 +1,13 @@ + + */ +class ResolvingUriFailedException extends UriResolverException +{ +} diff --git a/src/VichUploader/UriResolver/StorageResolver.php b/src/VichUploader/UriResolver/StorageResolver.php new file mode 100644 index 0000000..5cd7eee --- /dev/null +++ b/src/VichUploader/UriResolver/StorageResolver.php @@ -0,0 +1,38 @@ + + */ +class StorageResolver implements UriResolverInterface +{ + /** + * @var StorageInterface + */ + private $storage; + + /* + * @param StorageInterface $storageInterface + */ + public function __construct(StorageInterface $storage) + { + $this->storage = $storage; + } + + /** + * {@inheritdoc} + */ + public function resolve($object, File $file, $className = null) + { + if (null === $uri = $this->storage->resolveUri($object, $file->getProperty(), $className)) { + throw new ResolvingUriFailedException(sprintf('The object "%s" has no file at property "%s"', get_class($object), $file->getProperty())); + } + + return $uri; + } +} diff --git a/tests/VichUploader/UriResolver/StorageResolverTest.php b/tests/VichUploader/UriResolver/StorageResolverTest.php new file mode 100644 index 0000000..eb98428 --- /dev/null +++ b/tests/VichUploader/UriResolver/StorageResolverTest.php @@ -0,0 +1,77 @@ + + */ +class StorageResolverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var StorageResolver + */ + private $resolver; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storage; + + protected function setUp() + { + $this->resolver = new StorageResolver( + $this->storage = $this->getMock('Vich\UploaderBundle\Storage\StorageInterface') + ); + } + + /** + * @test + * @expectedException \Hshn\SerializerExtraBundle\VichUploader\UriResolver\ResolvingUriFailedException + */ + public function testThrowExceptionUnlessFileExists() + { + $this + ->storage + ->expects($this->once()) + ->method('resolveUri') + ->with($object = new \stdClass(), $property = 'foo', $class = 'Boo') + ->will($this->returnValue(null)); + + $file = $this->getFile('foo'); + + $this->resolver->resolve($object, $file, $class); + } + + /** + * @test + */ + public function testResolve() + { + $this + ->storage + ->expects($this->once()) + ->method('resolveUri') + ->with($object = new \stdClass(), $property = 'foo', $class = 'Boo') + ->will($this->returnValue($uri = 'http://foo/bar')); + + $file = $this->getFile('foo'); + + $this->assertEquals($uri, $this->resolver->resolve($object, $file, $class)); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFile($property) + { + $file = $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\Configuration\File')->disableOriginalConstructor()->getMock(); + + $file + ->expects($this->atLeastOnce()) + ->method('getProperty') + ->will($this->returnValue($property)); + + return $file; + } +} From e76814ef7c714663af11034477559cdf7ca72bc8 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 18:21:08 +0900 Subject: [PATCH 05/19] Install liip/imagine-bundle as a dev dependency --- composer.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0f06cd5..756b548 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,12 @@ "symfony/browser-kit": "*", "jms/serializer": "*", "jms/serializer-bundle": "*", - "vich/uploader-bundle": "~0.10" + "vich/uploader-bundle": "~0.10", + "liip/imagine-bundle": "~1.0" }, "suggest": { - "vich/uploader-bundle": "~0.10" + "vich/uploader-bundle": "~0.10", + "liip/imagine-bundle": "to apply a filter to uploaded images before serializing a file as an uri" }, "autoload": { "psr-4": { "Hshn\\SerializerExtraBundle\\": "src" } From 67de9ce420a2cc7c6a66e4d9bc7d5c59b6773e25 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 18:30:42 +0900 Subject: [PATCH 06/19] Fix vich/uploader-bundle suggestion --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 756b548..e57f184 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "liip/imagine-bundle": "~1.0" }, "suggest": { - "vich/uploader-bundle": "~0.10", + "vich/uploader-bundle": "to serialize uploaded files as an uri", "liip/imagine-bundle": "to apply a filter to uploaded images before serializing a file as an uri" }, "autoload": { From acbc6ab9827f06de102e3c716c949faa587cc2a7 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 16:41:07 +0900 Subject: [PATCH 07/19] [VichUploader] Add apply filter uri resolver --- .../UriResolver/ApplyFilterResolver.php | 51 ++++++++ .../UriResolver/ApplyFilterResolverTest.php | 123 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 src/VichUploader/UriResolver/ApplyFilterResolver.php create mode 100644 tests/VichUploader/UriResolver/ApplyFilterResolverTest.php diff --git a/src/VichUploader/UriResolver/ApplyFilterResolver.php b/src/VichUploader/UriResolver/ApplyFilterResolver.php new file mode 100644 index 0000000..9d63add --- /dev/null +++ b/src/VichUploader/UriResolver/ApplyFilterResolver.php @@ -0,0 +1,51 @@ + + */ +class ApplyFilterResolver implements UriResolverInterface +{ + /** + * @var StorageInterface + */ + private $storage; + + /** + * @var CacheManager + */ + private $cacheManager; + + /** + * @param StorageInterface $storage + * @param CacheManager $cacheManager + */ + public function __construct(StorageInterface $storage, CacheManager $cacheManager) + { + $this->storage = $storage; + $this->cacheManager = $cacheManager; + } + + /** + * {@inheritdoc} + */ + public function resolve($object, File $file, $className = null) + { + if (null === $filter = $file->getFilter()) { + throw new ResolvingUriFailedException('Could not resolve URI of the file that has no filter'); + } + + if (null === $path = $this->storage->resolvePath($object, $file->getProperty(), $className)) { + throw new ResolvingUriFailedException(sprintf('The object "%s" has no file at property "%s"', $className ?: get_class($object), $file->getProperty())); + } + + return $this->cacheManager->getBrowserPath($path, $filter); + } +} diff --git a/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php b/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php new file mode 100644 index 0000000..a7aed52 --- /dev/null +++ b/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php @@ -0,0 +1,123 @@ + + */ +class ApplyFilterResolverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ApplyFilterResolver + */ + private $resolver; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $storage; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $cacheManager; + + protected function setUp() + { + $this->resolver = new ApplyFilterResolver( + $this->storage = $this->getMock('Vich\UploaderBundle\Storage\StorageInterface'), + $this->cacheManager = $this->getMockBuilder('Liip\ImagineBundle\Imagine\Cache\CacheManager')->disableOriginalConstructor()->getMock() + ); + } + + /** + * @test + * @expectedException \Hshn\SerializerExtraBundle\VichUploader\UriResolver\ResolvingUriFailedException + */ + public function testThrowExceptionUnlessFileHasFilter() + { + $file = $this->getFile(null); + + $this->resolver->resolve(new \stdClass(), $file); + } + + /** + * @test + * @expectedException \Hshn\SerializerExtraBundle\VichUploader\UriResolver\ResolvingUriFailedException + */ + public function testThrowExceptionUnlessResolveFilePath() + { + $file = $this->getFileWithProperty('foo', 'bar'); + + $this + ->storage + ->expects($this->once()) + ->method('resolvePath') + ->will($this->returnValue(null)); + + $this->resolver->resolve(new \stdClass(), $file); + } + + /** + * @test + */ + public function testResolve() + { + $file = $this->getFileWithProperty($filter = 'bar', $property = 'foo'); + + $this + ->storage + ->expects($this->once()) + ->method('resolvePath') + ->with($object = new \stdClass(), $property, $class = 'ClassName') + ->will($this->returnValue($actualPath = '/path/to/file')); + + $this + ->cacheManager + ->expects($this->once()) + ->method('getBrowserPath') + ->with($actualPath, $filter) + ->will($this->returnValue($actualUri = 'http://foo/bar')); + + $this->resolver->resolve($object, $file, $class); + } + + /** + * @param $filter + * @param $property + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFileWithProperty($filter, $property) + { + $file = $this->getFile($filter); + + $file + ->expects($this->atLeastOnce()) + ->method('getProperty') + ->will($this->returnValue($property)); + + return $file; + } + + /** + * @param $filter + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFile($filter) + { + $file = $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\Configuration\File')->disableOriginalConstructor()->getMock(); + + $file + ->expects($this->atLeastOnce()) + ->method('getFilter') + ->will($this->returnValue($filter)); + + return $file; + } + +} From 1a9e718969f6e074a6500c9b0336d7a6bb2e02b2 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 17:03:45 +0900 Subject: [PATCH 08/19] [VichUploader] Add chain uri resolver --- .../UriResolver/ChainResolver.php | 43 ++++++ .../UriResolver/ChainResolverTest.php | 123 ++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/VichUploader/UriResolver/ChainResolver.php create mode 100644 tests/VichUploader/UriResolver/ChainResolverTest.php diff --git a/src/VichUploader/UriResolver/ChainResolver.php b/src/VichUploader/UriResolver/ChainResolver.php new file mode 100644 index 0000000..5fb91ec --- /dev/null +++ b/src/VichUploader/UriResolver/ChainResolver.php @@ -0,0 +1,43 @@ + + */ +class ChainResolver implements UriResolverInterface +{ + /** + * @var UriResolverInterface[] + */ + private $resolvers; + + /** + * @param UriResolverInterface[] $resolvers + */ + public function __construct(array $resolvers) + { + $this->resolvers = $resolvers; + } + + /** + * {@inheritdoc} + */ + public function resolve($object, File $file, $className = null) + { + foreach ($this->resolvers as $resolver) { + try { + return $resolver->resolve($object, $file, $className); + } catch (UriResolverException $e) { + } + } + + throw new ResolvingUriFailedException; + } +} diff --git a/tests/VichUploader/UriResolver/ChainResolverTest.php b/tests/VichUploader/UriResolver/ChainResolverTest.php new file mode 100644 index 0000000..f924b9e --- /dev/null +++ b/tests/VichUploader/UriResolver/ChainResolverTest.php @@ -0,0 +1,123 @@ + + */ +class ChainResolverTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ChainResolver + */ + private $resolver; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $primary; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $secondary; + + protected function setUp() + { + $this->resolver = new ChainResolver([ + $this->primary = $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverInterface'), + $this->secondary = $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverInterface') + ]); + } + + /** + * @test + * @expectedException \Hshn\SerializerExtraBundle\VichUploader\UriResolverException + */ + public function testThrowExceptionUnlessResolveUri() + { + $object = new \stdClass(); + $file = $this->getFile(); + $class = 'Foo'; + + $this + ->primary + ->expects($this->once()) + ->method('resolve') + ->willThrowException($this->getException()); + + $this + ->secondary + ->expects($this->once()) + ->method('resolve') + ->willThrowException($this->getException()); + + $this->resolver->resolve($object, $file, $class); + } + + /** + * @test + */ + public function testResolveByPrimary() + { + $object = new \stdClass(); + $file = $this->getFile(); + $class = 'Foo'; + + $this + ->primary + ->expects($this->once()) + ->method('resolve') + ->willReturn($uri = 'http://foo/bar'); + + $this + ->secondary + ->expects($this->never()) + ->method('resolve'); + + $this->assertEquals($uri, $this->resolver->resolve($object, $file, $class)); + } + + /** + * @test + */ + public function testResolveBySecondary() + { + $object = new \stdClass(); + $file = $this->getFile(); + $class = 'Foo'; + + $this + ->primary + ->expects($this->once()) + ->method('resolve') + ->willThrowException($this->getException()); + + $this + ->secondary + ->expects($this->once()) + ->method('resolve') + ->willReturn($uri = 'http://foo/bar'); + + $this->assertEquals($uri, $this->resolver->resolve($object, $file, $class)); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getException() + { + return $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverException'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getFile() + { + return $this->getMockBuilder('Hshn\SerializerExtraBundle\VichUploader\Configuration\File')->disableOriginalConstructor()->getMock(); + } +} From a1fb8fa114bbab73568c399eab49d5dcf4451f5f Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 19:13:45 +0900 Subject: [PATCH 09/19] Ensuring that the dependent bundles are enabled --- .../HshnSerializerExtraExtension.php | 14 ++++++++ .../HshnSerializerExtraExtensionTest.php | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/DependencyInjection/HshnSerializerExtraExtension.php b/src/DependencyInjection/HshnSerializerExtraExtension.php index 3f9944f..93b46a5 100644 --- a/src/DependencyInjection/HshnSerializerExtraExtension.php +++ b/src/DependencyInjection/HshnSerializerExtraExtension.php @@ -76,6 +76,8 @@ private function loadAuthority(ContainerBuilder $container, LoaderInterface $loa */ private function loadVichUploader(ContainerBuilder $container, LoaderInterface $loader, array $config) { + $this->ensureBundleEnabled($container, 'VichUploaderBundle'); + $loader->load('vich_uploader.xml'); $configurations = []; @@ -92,4 +94,16 @@ private function loadVichUploader(ContainerBuilder $container, LoaderInterface $ $container->getDefinition('hshn.serializer_extra.vich_uploader.configuration_repository')->addArgument($configurations); } + + /** + * @param ContainerBuilder $container + * @param string $bundle + */ + private function ensureBundleEnabled(ContainerBuilder $container, $bundle) + { + $bundles = $container->getParameter('kernel.bundles'); + if (!isset($bundles[$bundle])) { + throw new \RuntimeException(sprintf('The HshnSerializerExtraBundle requires the %s to enable integration of it, please make sure to enable it in your AppKernel', $bundle)); + } + } } diff --git a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php index c81018f..13b8f61 100644 --- a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php +++ b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php @@ -3,6 +3,7 @@ namespace Hshn\SerializerExtraBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Reference; /** @@ -77,11 +78,24 @@ public function testOverridingAuthorityConfigs() $this->assertEquals('my_authority', $definition->getArgument(0)); } + /** + * @test + * @expectedException \RuntimeException + * @expectedExceptionMessage VichUploaderBundle + */ + public function testThrowExceptionUnlessVichUploaderBundleIsEnabled() + { + $this->loadExtension([ + 'vich_uploader' => [] + ]); + } + /** * @test */ public function testVichUploaderConfigs() { + $this->enableBundle(['VichUploaderBundle']); $this->loadExtension([ 'vich_uploader' => [ 'classes' => [ @@ -102,11 +116,30 @@ public function testVichUploaderConfigs() $this->assertCount(2, $definition->getArgument(0)); } + /** + * @param array $bundles + */ + private function enableBundle(array $bundles) + { + $enabledBundles = []; + try { + $enabledBundles = $this->container->getParameter('kernel.bundles'); + } catch (InvalidArgumentException $e) { + } + + foreach ($bundles as $bundle) { + $enabledBundles[$bundle] = true; + } + + $this->container->setParameter('kernel.bundles', $enabledBundles); + } + /** * @param array $config */ private function loadExtension(array $config) { + $this->enableBundle([]); $this->extension->load([ 'hshn_serializer_extra' => $config ], $this->container); From 6a4dc31758c8c2e58824e80202ebf1bef8c29c99 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sat, 3 Jan 2015 21:12:31 +0900 Subject: [PATCH 10/19] [VichUploader] Update configurations --- src/DependencyInjection/Configuration.php | 13 ++-- .../HshnSerializerExtraExtension.php | 30 ++++++-- src/Resources/config/vich_uploader.xml | 1 + .../HshnSerializerExtraExtensionTest.php | 71 +++++++++++++++++-- 4 files changed, 95 insertions(+), 20 deletions(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 1e6ae0f..a6cc063 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -51,17 +51,12 @@ public function getConfigTreeBuilder() ->useAttributeAsKey('class') ->prototype('array') ->children() - ->arrayNode('attributes') - ->useAttributeAsKey('attribute') + ->arrayNode('files') ->prototype('array') ->children() - ->scalarNode('alias')->defaultNull()->end() - ->end() - ->beforeNormalization() - ->ifString() - ->then(function ($v) { - return ['alias' => $v]; - }) + ->scalarNode('property')->cannotBeEmpty()->end() + ->scalarNode('export_to')->defaultNull()->end() + ->scalarNode('filter')->defaultNull()->end() ->end() ->end() ->end() diff --git a/src/DependencyInjection/HshnSerializerExtraExtension.php b/src/DependencyInjection/HshnSerializerExtraExtension.php index 93b46a5..b005437 100644 --- a/src/DependencyInjection/HshnSerializerExtraExtension.php +++ b/src/DependencyInjection/HshnSerializerExtraExtension.php @@ -83,11 +83,10 @@ private function loadVichUploader(ContainerBuilder $container, LoaderInterface $ $configurations = []; foreach ($config['classes'] as $class => $vars) { $id = sprintf('hshn.serializer_extra.vich_uploader.configuration.%s', md5($class)); - $container->setDefinition($id, $definition = new DefinitionDecorator('hshn.serializer_extra.vich_uploader.configuration')); - $definition - ->addArgument($class) - ->addArgument($vars['attributes']) - ->addArgument($vars['max_depth']); + + $definition = new DefinitionDecorator('hshn.serializer_extra.vich_uploader.configuration'); + $definition->setArguments([$class, $this->createVichUploaderFileConfig($container, $id, $vars['files']), $vars['max_depth']]); + $container->setDefinition($id, $definition); $configurations[] = new Reference($id); } @@ -95,6 +94,27 @@ private function loadVichUploader(ContainerBuilder $container, LoaderInterface $ $container->getDefinition('hshn.serializer_extra.vich_uploader.configuration_repository')->addArgument($configurations); } + /** + * @param ContainerBuilder $container + * @param string $prefix + * @param array $files + * + * @return Reference[] + */ + private function createVichUploaderFileConfig(ContainerBuilder $container, $prefix, array $files) + { + $references = []; + foreach ($files as $i => $file) { + $id = "{$prefix}.file{$i}"; + $container->setDefinition($id, $definition = new DefinitionDecorator('hshn.serializer_extra.vich_uploader.configuration.file')); + $definition->setArguments([$file['property'], $file['export_to'], $file['filter']]); + + $references[] = new Reference($id); + } + + return $references; + } + /** * @param ContainerBuilder $container * @param string $bundle diff --git a/src/Resources/config/vich_uploader.xml b/src/Resources/config/vich_uploader.xml index c251563..dc4bf85 100644 --- a/src/Resources/config/vich_uploader.xml +++ b/src/Resources/config/vich_uploader.xml @@ -6,6 +6,7 @@ + diff --git a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php index 13b8f61..8470590 100644 --- a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php +++ b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php @@ -99,21 +99,80 @@ public function testVichUploaderConfigs() $this->loadExtension([ 'vich_uploader' => [ 'classes' => [ - 'MyClass_A' => [ - 'attributes' => [ - 'foo' => 'bar', - 'name' => null, + 'Foo' => [ + 'files' => [ + ['property' => 'foo', 'export_to' => 'bar'], + ['property' => 'foo', 'filter' => 'baz'] ], 'max_depth' => 1, ], - 'MyClass_B' => null, + 'Bar' => [ + 'files' => [ + ['property' => 'foo'] + ], + ], ], ] ]); $this->container->hasDefinition('hshn.serializer_extra.vich_uploader.configuration_repository'); $definition = $this->container->getDefinition('hshn.serializer_extra.vich_uploader.configuration_repository'); - $this->assertCount(2, $definition->getArgument(0)); + $this->assertCount(2, $configurations = $definition->getArgument(0)); + + $this->assertVichUploaderConfig($configurations[0], 'Foo', [ + $this->isFileConfiguration('foo', 'bar'), + $this->isFileConfiguration('foo', null, 'baz') + ], 1); + + $this->assertVichUploaderConfig($configurations[1], 'Bar', [ + $this->isFileConfiguration('foo') + ]); + } + + /** + * @param Reference $reference + * @param string $expectedClass + * @param \PHPUnit_Framework_Constraint[] $expectedFiles + * @param int $expectedMaxDepth + * @param string $message + */ + private function assertVichUploaderConfig(Reference $reference, $expectedClass, array $expectedFiles = [], $expectedMaxDepth = -1, $message = '') + { + $id = (string) $reference; + $this->assertRegExp('/^hshn\.serializer_extra\.vich_uploader\.configuration\.\w+$/', $id, $message); + + $definition = $this->container->getDefinition($id); + $this->assertEquals($expectedClass, $definition->getArgument(0), $message); + $this->assertCount(count($expectedFiles), $files = $definition->getArgument(1)); + foreach ($files as $i => $file) { + $this->assertThat($file, $expectedFiles[$i], $message); + } + $this->assertEquals($expectedMaxDepth, $definition->getArgument(2)); + } + + /** + * @param string $property + * @param string $exportTo + * @param string $filter + * + * @return \PHPUnit_Framework_Constraint + */ + private function isFileConfiguration($property, $exportTo = null, $filter = null) + { + return $this->logicalAnd( + $this->isInstanceOf('Symfony\Component\DependencyInjection\Reference'), + $this->callback(function (Reference $reference) use ($property, $exportTo, $filter) { + $id = (string) $reference; + $this->assertRegExp('/^hshn\.serializer_extra\.vich_uploader\.configuration\.\w+\.file\d+$/', $id); + + $definition = $this->container->getDefinition($id); + $this->assertEquals($property, $definition->getArgument(0)); + $this->assertEquals($exportTo, $definition->getArgument(1)); + $this->assertEquals($filter, $definition->getArgument(2)); + + return true; + }) + ); } /** From 09a0a81bdd6dd4483f62e3cf150f97ea56ee6e3e Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 00:17:36 +0900 Subject: [PATCH 11/19] [VichUploader] Update extension for filtering images --- .../HshnSerializerExtraExtension.php | 31 +++++++++--- src/Resources/config/liip_imagine.xml | 16 ++++++ src/Resources/config/vich_uploader.xml | 6 ++- .../HshnSerializerExtraExtensionTest.php | 49 ++++++++++++++++++- 4 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 src/Resources/config/liip_imagine.xml diff --git a/src/DependencyInjection/HshnSerializerExtraExtension.php b/src/DependencyInjection/HshnSerializerExtraExtension.php index b005437..671f385 100644 --- a/src/DependencyInjection/HshnSerializerExtraExtension.php +++ b/src/DependencyInjection/HshnSerializerExtraExtension.php @@ -2,13 +2,13 @@ namespace Hshn\SerializerExtraBundle\DependencyInjection; +use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\DefinitionDecorator; +use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\Loader; /** * This is the class that loads and manages your bundle configuration @@ -77,31 +77,36 @@ private function loadAuthority(ContainerBuilder $container, LoaderInterface $loa private function loadVichUploader(ContainerBuilder $container, LoaderInterface $loader, array $config) { $this->ensureBundleEnabled($container, 'VichUploaderBundle'); - $loader->load('vich_uploader.xml'); + $isFilterSpecified = false; $configurations = []; foreach ($config['classes'] as $class => $vars) { $id = sprintf('hshn.serializer_extra.vich_uploader.configuration.%s', md5($class)); $definition = new DefinitionDecorator('hshn.serializer_extra.vich_uploader.configuration'); - $definition->setArguments([$class, $this->createVichUploaderFileConfig($container, $id, $vars['files']), $vars['max_depth']]); + $definition->setArguments([$class, $this->createVichUploaderFileConfig($container, $id, $vars['files'], $isFilterSpecified), $vars['max_depth']]); $container->setDefinition($id, $definition); $configurations[] = new Reference($id); } $container->getDefinition('hshn.serializer_extra.vich_uploader.configuration_repository')->addArgument($configurations); + + if ($isFilterSpecified) { + $this->loadLiipImagineFilter($container, $loader); + } } /** * @param ContainerBuilder $container * @param string $prefix * @param array $files + * @param bool $isFilterSpecified * - * @return Reference[] + * @return \Symfony\Component\DependencyInjection\Reference[] */ - private function createVichUploaderFileConfig(ContainerBuilder $container, $prefix, array $files) + private function createVichUploaderFileConfig(ContainerBuilder $container, $prefix, array $files, &$isFilterSpecified) { $references = []; foreach ($files as $i => $file) { @@ -110,11 +115,25 @@ private function createVichUploaderFileConfig(ContainerBuilder $container, $pref $definition->setArguments([$file['property'], $file['export_to'], $file['filter']]); $references[] = new Reference($id); + $isFilterSpecified = $isFilterSpecified || $file['filter'] !== null; } return $references; } + /** + * @param ContainerBuilder $container + * @param LoaderInterface $loader + */ + private function loadLiipImagineFilter(ContainerBuilder $container, LoaderInterface $loader) + { + $this->ensureBundleEnabled($container, 'LiipImagineBundle'); + $loader->load('liip_imagine.xml'); + + $container->setAlias('hshn.serializer_extra.vich_uploader.uri_resolver', 'hshn.serializer_extra.vich_uploader.uri_resolver.imagine_filter'); + } + + /** * @param ContainerBuilder $container * @param string $bundle diff --git a/src/Resources/config/liip_imagine.xml b/src/Resources/config/liip_imagine.xml new file mode 100644 index 0000000..c4211db --- /dev/null +++ b/src/Resources/config/liip_imagine.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/src/Resources/config/vich_uploader.xml b/src/Resources/config/vich_uploader.xml index dc4bf85..8946ec5 100644 --- a/src/Resources/config/vich_uploader.xml +++ b/src/Resources/config/vich_uploader.xml @@ -7,10 +7,14 @@ + + + + - + diff --git a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php index 8470590..e7acf4e 100644 --- a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php +++ b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php @@ -92,10 +92,53 @@ public function testThrowExceptionUnlessVichUploaderBundleIsEnabled() /** * @test + * @expectedException \RuntimeException + * @expectedExceptionMessage LiipImagineBundle */ - public function testVichUploaderConfigs() + public function testThrowsExceptionWhenUsingFiltersUnlessLiipImagineBundleIsEnabled() { $this->enableBundle(['VichUploaderBundle']); + $this->loadExtension([ + 'vich_uploader' => [ + 'classes' => [ + 'Foo' => [ + 'files' => [ + ['property' => 'bar', 'filter' => 'baz'], + ], + ], + ], + ], + ]); + } + + /** + * @test + */ + public function testVichUploaderConfigsWithoutFilter() + { + $this->enableBundle(['VichUploaderBundle']); + $this->loadExtension([ + 'vich_uploader' => [ + 'classes' => [ + 'Foo' => [ + 'files' => [ + ['property' => 'bar'], + ], + ], + ], + ], + ]); + + $alias = $this->container->getAlias('hshn.serializer_extra.vich_uploader.uri_resolver'); + $this->assertEquals('hshn.serializer_extra.vich_uploader.uri_resolver.storage', (string) $alias); + } + + /** + * @test + */ + public function testVichUploaderConfigs() + { + $this->enableBundle(['VichUploaderBundle', 'LiipImagineBundle']); $this->loadExtension([ 'vich_uploader' => [ 'classes' => [ @@ -115,6 +158,9 @@ public function testVichUploaderConfigs() ] ]); + $alias = $this->container->getAlias('hshn.serializer_extra.vich_uploader.uri_resolver'); + $this->assertEquals('hshn.serializer_extra.vich_uploader.uri_resolver.imagine_filter', (string) $alias); + $this->container->hasDefinition('hshn.serializer_extra.vich_uploader.configuration_repository'); $definition = $this->container->getDefinition('hshn.serializer_extra.vich_uploader.configuration_repository'); $this->assertCount(2, $configurations = $definition->getArgument(0)); @@ -147,6 +193,7 @@ private function assertVichUploaderConfig(Reference $reference, $expectedClass, foreach ($files as $i => $file) { $this->assertThat($file, $expectedFiles[$i], $message); } + $this->assertEquals($expectedMaxDepth, $definition->getArgument(2)); } From e5cb21d39ce95e5512790ea44d8d73fd4591379b Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 00:30:08 +0900 Subject: [PATCH 12/19] [VichUploader] Update functional tests --- tests/Functional/config/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Functional/config/config.yml b/tests/Functional/config/config.yml index af9df0f..21000e5 100644 --- a/tests/Functional/config/config.yml +++ b/tests/Functional/config/config.yml @@ -37,8 +37,8 @@ hshn_serializer_extra: vich_uploader: classes: Hshn\SerializerExtraBundle\Functional\Bundle\Entity\Picture: - attributes: - file: file_url + files: + - { property: file, export_to: file_url } services: post_voter: From 73639505be67c63917452c7423bef35d1b64a73c Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 01:51:24 +0900 Subject: [PATCH 13/19] Separate configurations for functional tests --- tests/Functional/AuthorityTest.php | 8 +++++ tests/Functional/TestKernel.php | 43 ++++++++++++++++++++++- tests/Functional/VichUploaderTest.php | 8 +++++ tests/Functional/WebTestCase.php | 14 ++++++-- tests/Functional/config/authority.yml | 15 ++++++++ tests/Functional/config/config.yml | 18 ---------- tests/Functional/config/vich_uploader.yml | 9 +++++ 7 files changed, 93 insertions(+), 22 deletions(-) create mode 100644 tests/Functional/config/authority.yml create mode 100644 tests/Functional/config/vich_uploader.yml diff --git a/tests/Functional/AuthorityTest.php b/tests/Functional/AuthorityTest.php index d4c72a1..1e6689c 100644 --- a/tests/Functional/AuthorityTest.php +++ b/tests/Functional/AuthorityTest.php @@ -9,6 +9,14 @@ */ class AuthorityTest extends WebTestCase { + /** + * {@inheritdoc} + */ + public static function getConfiguration() + { + return 'authority'; + } + /** * @test * @dataProvider provideTests diff --git a/tests/Functional/TestKernel.php b/tests/Functional/TestKernel.php index 3490538..801dab6 100644 --- a/tests/Functional/TestKernel.php +++ b/tests/Functional/TestKernel.php @@ -8,6 +8,7 @@ use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\Kernel; use Vich\UploaderBundle\VichUploaderBundle; @@ -16,6 +17,20 @@ */ class TestKernel extends Kernel { + /** + * @var string + */ + private $config; + + /** + * @param string $config + */ + public function __construct($config) + { + $this->config = $config; + parent::__construct('test', true); + } + /** * {@inheritdoc} */ @@ -36,6 +51,32 @@ public function registerBundles() */ public function registerContainerConfiguration(LoaderInterface $loader) { - $loader->load(__DIR__.'/config/config.yml'); + $loader->load(__DIR__.'/config/'.$this->config.'.yml'); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return parent::getName().Container::camelize($this->config).str_replace('.', '', Kernel::VERSION); } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return $this->config; + } + + /** + * {@inheritdoc} + */ + public function unserialize($config) + { + $this->__construct($config); + } + + } diff --git a/tests/Functional/VichUploaderTest.php b/tests/Functional/VichUploaderTest.php index eb570d6..df377d5 100644 --- a/tests/Functional/VichUploaderTest.php +++ b/tests/Functional/VichUploaderTest.php @@ -9,6 +9,14 @@ */ class VichUploaderTest extends WebTestCase { + /** + * {@inheritdoc} + */ + public static function getConfiguration() + { + return 'vich_uploader'; + } + public function test() { $client = $this->createClient(); diff --git a/tests/Functional/WebTestCase.php b/tests/Functional/WebTestCase.php index 131c051..67e69d7 100644 --- a/tests/Functional/WebTestCase.php +++ b/tests/Functional/WebTestCase.php @@ -8,13 +8,21 @@ /** * @author Shota Hoshino */ -class WebTestCase extends BaseWebTestCase +abstract class WebTestCase extends BaseWebTestCase { /** * {@inheritdoc} */ - protected static function getKernelClass() + protected static function createKernel(array $options = array()) { - return 'Hshn\SerializerExtraBundle\Functional\TestKernel'; + return new TestKernel(static::getConfiguration()); + } + + /** + * @return string + */ + public static function getConfiguration() + { + throw new \LogicException(sprintf('Override "%s"', __METHOD__)); } } diff --git a/tests/Functional/config/authority.yml b/tests/Functional/config/authority.yml new file mode 100644 index 0000000..790a5c0 --- /dev/null +++ b/tests/Functional/config/authority.yml @@ -0,0 +1,15 @@ +imports: + - { resource: config.yml } + +hshn_serializer_extra: + authority: + classes: + Hshn\SerializerExtraBundle\Functional\Bundle\Entity\Post: + attributes: OWNER + max_depth: 0 + +services: + post_voter: + class: Hshn\SerializerExtraBundle\Functional\Bundle\Security\Voter\PostVoter + tags: + - { name: security.voter } diff --git a/tests/Functional/config/config.yml b/tests/Functional/config/config.yml index 21000e5..c0ef065 100644 --- a/tests/Functional/config/config.yml +++ b/tests/Functional/config/config.yml @@ -27,21 +27,3 @@ vich_uploader: uri_prefix: /images upload_destination: %kernel.cache_dir%/images namer: vich_uploader.namer_uniqid - -hshn_serializer_extra: - authority: - classes: - Hshn\SerializerExtraBundle\Functional\Bundle\Entity\Post: - attributes: OWNER - max_depth: 0 - vich_uploader: - classes: - Hshn\SerializerExtraBundle\Functional\Bundle\Entity\Picture: - files: - - { property: file, export_to: file_url } - -services: - post_voter: - class: Hshn\SerializerExtraBundle\Functional\Bundle\Security\Voter\PostVoter - tags: - - { name: security.voter } diff --git a/tests/Functional/config/vich_uploader.yml b/tests/Functional/config/vich_uploader.yml new file mode 100644 index 0000000..ac1ab85 --- /dev/null +++ b/tests/Functional/config/vich_uploader.yml @@ -0,0 +1,9 @@ +imports: + - { resource: config.yml } + +hshn_serializer_extra: + vich_uploader: + classes: + Hshn\SerializerExtraBundle\Functional\Bundle\Entity\Picture: + files: + - { property: file, export_to: file_url } From a9776addb9cfbb7371990fd4033050944673e62b Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 02:06:46 +0900 Subject: [PATCH 14/19] [VichUploader] Fix imagine filter service definitions --- src/Resources/config/liip_imagine.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Resources/config/liip_imagine.xml b/src/Resources/config/liip_imagine.xml index c4211db..6817801 100644 --- a/src/Resources/config/liip_imagine.xml +++ b/src/Resources/config/liip_imagine.xml @@ -4,13 +4,15 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + - - + + + + From d3f2c935cb093ece22104e1bc11a29eb0089f46a Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 02:08:02 +0900 Subject: [PATCH 15/19] [VichUploader] Change file namer to default --- tests/Functional/VichUploaderTest.php | 2 +- tests/Functional/config/config.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Functional/VichUploaderTest.php b/tests/Functional/VichUploaderTest.php index df377d5..1f32f8d 100644 --- a/tests/Functional/VichUploaderTest.php +++ b/tests/Functional/VichUploaderTest.php @@ -31,6 +31,6 @@ public function test() $content = json_decode($content, true); $this->assertArrayHasKey('name', $content); $this->assertArrayHasKey('file_url', $content); - $this->assertRegExp('/\/images\/[^\/]+\.php/', $content['file_url']); + $this->assertEquals('/images/symfony.png', $content['file_url']); } } diff --git a/tests/Functional/config/config.yml b/tests/Functional/config/config.yml index c0ef065..711b69d 100644 --- a/tests/Functional/config/config.yml +++ b/tests/Functional/config/config.yml @@ -26,4 +26,3 @@ vich_uploader: picture: uri_prefix: /images upload_destination: %kernel.cache_dir%/images - namer: vich_uploader.namer_uniqid From 10745359956955e08e8a3aee918cef3dd3c68f56 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 03:08:02 +0900 Subject: [PATCH 16/19] [VichUploader] Fix ApplyFilterResolver to resolve a file uri not a path --- .../HshnSerializerExtraExtension.php | 2 +- src/Resources/config/liip_imagine.xml | 8 +-- .../UriResolver/ApplyFilterResolver.php | 22 +++----- .../HshnSerializerExtraExtensionTest.php | 2 +- .../UriResolver/ApplyFilterResolverTest.php | 55 +++++-------------- 5 files changed, 24 insertions(+), 65 deletions(-) diff --git a/src/DependencyInjection/HshnSerializerExtraExtension.php b/src/DependencyInjection/HshnSerializerExtraExtension.php index 671f385..cf6efc7 100644 --- a/src/DependencyInjection/HshnSerializerExtraExtension.php +++ b/src/DependencyInjection/HshnSerializerExtraExtension.php @@ -130,7 +130,7 @@ private function loadLiipImagineFilter(ContainerBuilder $container, LoaderInterf $this->ensureBundleEnabled($container, 'LiipImagineBundle'); $loader->load('liip_imagine.xml'); - $container->setAlias('hshn.serializer_extra.vich_uploader.uri_resolver', 'hshn.serializer_extra.vich_uploader.uri_resolver.imagine_filter'); + $container->setAlias('hshn.serializer_extra.vich_uploader.uri_resolver', 'hshn.serializer_extra.vich_uploader.uri_resolver.apply_filter'); } diff --git a/src/Resources/config/liip_imagine.xml b/src/Resources/config/liip_imagine.xml index 6817801..713c547 100644 --- a/src/Resources/config/liip_imagine.xml +++ b/src/Resources/config/liip_imagine.xml @@ -5,14 +5,8 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + - - - - - - diff --git a/src/VichUploader/UriResolver/ApplyFilterResolver.php b/src/VichUploader/UriResolver/ApplyFilterResolver.php index 9d63add..66ac498 100644 --- a/src/VichUploader/UriResolver/ApplyFilterResolver.php +++ b/src/VichUploader/UriResolver/ApplyFilterResolver.php @@ -6,7 +6,6 @@ use Hshn\SerializerExtraBundle\VichUploader\Configuration\File; use Hshn\SerializerExtraBundle\VichUploader\UriResolverInterface; use Liip\ImagineBundle\Imagine\Cache\CacheManager; -use Vich\UploaderBundle\Storage\StorageInterface; /** * @author Shota Hoshino @@ -14,9 +13,9 @@ class ApplyFilterResolver implements UriResolverInterface { /** - * @var StorageInterface + * @var UriResolverInterface */ - private $storage; + private $originalResolver; /** * @var CacheManager @@ -24,12 +23,12 @@ class ApplyFilterResolver implements UriResolverInterface private $cacheManager; /** - * @param StorageInterface $storage + * @param UriResolverInterface $originalResolver * @param CacheManager $cacheManager */ - public function __construct(StorageInterface $storage, CacheManager $cacheManager) + public function __construct(UriResolverInterface $originalResolver, CacheManager $cacheManager) { - $this->storage = $storage; + $this->originalResolver = $originalResolver; $this->cacheManager = $cacheManager; } @@ -38,14 +37,9 @@ public function __construct(StorageInterface $storage, CacheManager $cacheManage */ public function resolve($object, File $file, $className = null) { - if (null === $filter = $file->getFilter()) { - throw new ResolvingUriFailedException('Could not resolve URI of the file that has no filter'); - } + $uri = $this->originalResolver->resolve($object, $file, $className); + $filter = $file->getFilter(); - if (null === $path = $this->storage->resolvePath($object, $file->getProperty(), $className)) { - throw new ResolvingUriFailedException(sprintf('The object "%s" has no file at property "%s"', $className ?: get_class($object), $file->getProperty())); - } - - return $this->cacheManager->getBrowserPath($path, $filter); + return $filter === null ? $uri : $this->cacheManager->getBrowserPath($uri, $filter); } } diff --git a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php index e7acf4e..8d241f2 100644 --- a/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php +++ b/tests/DependencyInjection/HshnSerializerExtraExtensionTest.php @@ -159,7 +159,7 @@ public function testVichUploaderConfigs() ]); $alias = $this->container->getAlias('hshn.serializer_extra.vich_uploader.uri_resolver'); - $this->assertEquals('hshn.serializer_extra.vich_uploader.uri_resolver.imagine_filter', (string) $alias); + $this->assertEquals('hshn.serializer_extra.vich_uploader.uri_resolver.apply_filter', (string) $alias); $this->container->hasDefinition('hshn.serializer_extra.vich_uploader.configuration_repository'); $definition = $this->container->getDefinition('hshn.serializer_extra.vich_uploader.configuration_repository'); diff --git a/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php b/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php index a7aed52..f6e13df 100644 --- a/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php +++ b/tests/VichUploader/UriResolver/ApplyFilterResolverTest.php @@ -18,7 +18,7 @@ class ApplyFilterResolverTest extends \PHPUnit_Framework_TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $storage; + private $originalResolver; /** * @var \PHPUnit_Framework_MockObject_MockObject @@ -28,37 +28,26 @@ class ApplyFilterResolverTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->resolver = new ApplyFilterResolver( - $this->storage = $this->getMock('Vich\UploaderBundle\Storage\StorageInterface'), + $this->originalResolver = $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverInterface'), $this->cacheManager = $this->getMockBuilder('Liip\ImagineBundle\Imagine\Cache\CacheManager')->disableOriginalConstructor()->getMock() ); } /** * @test - * @expectedException \Hshn\SerializerExtraBundle\VichUploader\UriResolver\ResolvingUriFailedException */ - public function testThrowExceptionUnlessFileHasFilter() + public function testDoNotFilterUnlessFileHasFilter() { $file = $this->getFile(null); - $this->resolver->resolve(new \stdClass(), $file); - } - - /** - * @test - * @expectedException \Hshn\SerializerExtraBundle\VichUploader\UriResolver\ResolvingUriFailedException - */ - public function testThrowExceptionUnlessResolveFilePath() - { - $file = $this->getFileWithProperty('foo', 'bar'); - $this - ->storage + ->originalResolver ->expects($this->once()) - ->method('resolvePath') - ->will($this->returnValue(null)); + ->method('resolve') + ->with($object = new \stdClass(), $file, $class = 'Foo') + ->willReturn($expectedUri = 'http://localhost/foo/bar'); - $this->resolver->resolve(new \stdClass(), $file); + $this->assertEquals($expectedUri, $this->resolver->resolve($object, $file, $class)); } /** @@ -66,14 +55,14 @@ public function testThrowExceptionUnlessResolveFilePath() */ public function testResolve() { - $file = $this->getFileWithProperty($filter = 'bar', $property = 'foo'); + $file = $this->getFile($filter = 'bar'); $this - ->storage + ->originalResolver ->expects($this->once()) - ->method('resolvePath') - ->with($object = new \stdClass(), $property, $class = 'ClassName') - ->will($this->returnValue($actualPath = '/path/to/file')); + ->method('resolve') + ->with($object = new \stdClass(), $file, $class = 'ClassName') + ->willReturn($actualPath = '/path/to/file'); $this ->cacheManager @@ -85,24 +74,6 @@ public function testResolve() $this->resolver->resolve($object, $file, $class); } - /** - * @param $filter - * @param $property - * - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getFileWithProperty($filter, $property) - { - $file = $this->getFile($filter); - - $file - ->expects($this->atLeastOnce()) - ->method('getProperty') - ->will($this->returnValue($property)); - - return $file; - } - /** * @param $filter * From 5ee5cd20dca3d4e0f3f194c28a839bb3f77dbf79 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 04:27:16 +0900 Subject: [PATCH 17/19] [VichUploader] Add functional tests for imagine filter --- .../Bundle/Controller/PictureController.php | 4 +- .../Bundle/Resources/images/symfony.png | Bin 0 -> 24081 bytes tests/Functional/TestKernel.php | 2 + tests/Functional/VichUploaderFilterTest.php | 52 ++++++++++++++++++ tests/Functional/config/config.yml | 2 +- .../{routing.yml => routing/default.yml} | 0 .../config/routing/vich_uploader_filter.yml | 5 ++ .../config/vich_uploader_filter.yml | 31 +++++++++++ 8 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 tests/Functional/Bundle/Resources/images/symfony.png create mode 100644 tests/Functional/VichUploaderFilterTest.php rename tests/Functional/config/{routing.yml => routing/default.yml} (100%) create mode 100644 tests/Functional/config/routing/vich_uploader_filter.yml create mode 100644 tests/Functional/config/vich_uploader_filter.yml diff --git a/tests/Functional/Bundle/Controller/PictureController.php b/tests/Functional/Bundle/Controller/PictureController.php index 1035394..e1ae834 100644 --- a/tests/Functional/Bundle/Controller/PictureController.php +++ b/tests/Functional/Bundle/Controller/PictureController.php @@ -16,9 +16,9 @@ class PictureController extends Controller public function showAction() { $temp = tempnam(sys_get_temp_dir(), md5(__CLASS__)); - file_put_contents($temp, file_get_contents(__FILE__)); + file_put_contents($temp, file_get_contents(__DIR__.'/../Resources/images/symfony.png')); - $picture = new Picture(new UploadedFile($temp, 'PictureController.php', null, null, UPLOAD_ERR_OK, true)); + $picture = new Picture(new UploadedFile($temp, 'symfony.png', null, null, UPLOAD_ERR_OK, true)); $this->getUploadHandler()->upload($picture, 'file'); diff --git a/tests/Functional/Bundle/Resources/images/symfony.png b/tests/Functional/Bundle/Resources/images/symfony.png new file mode 100644 index 0000000000000000000000000000000000000000..1a12f9efb8307e3b6ab9073b82bc6fdd648692cc GIT binary patch literal 24081 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 + */ +class VichUploaderFilterTest extends WebTestCase +{ + /** + * {@inheritdoc} + */ + public static function getConfiguration() + { + return 'vich_uploader_filter'; + } + + public function test() + { + $client = $this->createClient(); + + /** @var $cacheManager CacheManager */ + $cacheManager = $client->getContainer()->get('liip_imagine.cache.manager'); + $cacheManager->remove(); + + $client->request('GET', '/picture/show'); + + $response = $client->getResponse(); + $this->assertEquals($response::HTTP_OK, $response->getStatusCode()); + + $content = $response->getContent(); + $this->assertJson($content); + + $content = json_decode($content, true); + $this->assertArrayHasKey('name', $content); + $this->assertArrayHasKey('file_url', $content); + $this->assertArrayHasKey('thumb_url', $content); + $this->assertEquals('/images/symfony.png', $content['file_url']); + + // generating a thumbnail + $client->request('GET', $content['thumb_url']); + + $response = $client->getResponse(); + + // should redirect to generated + $this->assertEquals($response::HTTP_MOVED_PERMANENTLY, $response->getStatusCode()); + $this->assertContains('thumb/images/symfony.png', $response->headers->get('Location')); + } +} diff --git a/tests/Functional/config/config.yml b/tests/Functional/config/config.yml index 711b69d..560a758 100644 --- a/tests/Functional/config/config.yml +++ b/tests/Functional/config/config.yml @@ -2,7 +2,7 @@ framework: test: ~ secret: secret router: - resource: "%kernel.root_dir%/config/routing.yml" + resource: "%kernel.root_dir%/config/routing/default.yml" security: providers: diff --git a/tests/Functional/config/routing.yml b/tests/Functional/config/routing/default.yml similarity index 100% rename from tests/Functional/config/routing.yml rename to tests/Functional/config/routing/default.yml diff --git a/tests/Functional/config/routing/vich_uploader_filter.yml b/tests/Functional/config/routing/vich_uploader_filter.yml new file mode 100644 index 0000000..60526f8 --- /dev/null +++ b/tests/Functional/config/routing/vich_uploader_filter.yml @@ -0,0 +1,5 @@ +_liip_imagine: + resource: "@LiipImagineBundle/Resources/config/routing.xml" + +_main: + resource: default.yml diff --git a/tests/Functional/config/vich_uploader_filter.yml b/tests/Functional/config/vich_uploader_filter.yml new file mode 100644 index 0000000..6d6d5db --- /dev/null +++ b/tests/Functional/config/vich_uploader_filter.yml @@ -0,0 +1,31 @@ +imports: + - { resource: config.yml } + +framework: + router: + resource: "%kernel.root_dir%/config/routing/vich_uploader_filter.yml" + +liip_imagine: + resolvers: + default: + web_path: + web_root: %kernel.cache_dir% + + loaders: + default: + filesystem: + data_root: %kernel.cache_dir% + + filter_sets: + thumb: + quality: 75 + filters: + thumbnail: { size: [120, 90], mode: outbound } + +hshn_serializer_extra: + vich_uploader: + classes: + Hshn\SerializerExtraBundle\Functional\Bundle\Entity\Picture: + files: + - { property: file, export_to: file_url } + - { property: file, export_to: thumb_url, filter: thumb } From f8dcaf20a2c7b19d074a41916edc031102fc6975 Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Sun, 4 Jan 2015 05:01:01 +0900 Subject: [PATCH 18/19] Fix test code for HHVM --- tests/VichUploader/EventSubscriberTest.php | 2 +- tests/VichUploader/UriResolver/ChainResolverTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/VichUploader/EventSubscriberTest.php b/tests/VichUploader/EventSubscriberTest.php index 14077a7..857edae 100644 --- a/tests/VichUploader/EventSubscriberTest.php +++ b/tests/VichUploader/EventSubscriberTest.php @@ -143,7 +143,7 @@ public function testExportUsingFiles() ->method('resolve') ->with($this->identicalTo($object), call_user_func_array([$this, 'logicalOr'], $files), $class) ->will($this->onConsecutiveCalls( - $this->throwException($this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverException')), + $this->throwException($this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverException', ['getMessage'])), 'http://foo', 'http://bar' )); diff --git a/tests/VichUploader/UriResolver/ChainResolverTest.php b/tests/VichUploader/UriResolver/ChainResolverTest.php index f924b9e..479ea26 100644 --- a/tests/VichUploader/UriResolver/ChainResolverTest.php +++ b/tests/VichUploader/UriResolver/ChainResolverTest.php @@ -110,7 +110,7 @@ public function testResolveBySecondary() */ private function getException() { - return $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverException'); + return $this->getMock('Hshn\SerializerExtraBundle\VichUploader\UriResolverException', ['getMessage']); } /** From 9e6203cbcd43f599c1123bd61f249a1a923cb80e Mon Sep 17 00:00:00 2001 From: Shota Hoshino Date: Mon, 5 Jan 2015 00:32:21 +0900 Subject: [PATCH 19/19] Update doc --- README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d98fe6..8293c47 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ HshnSerializerExtraBundle This bundle provides some extra features for serialization. -### Exporting authorities of the classes +### Exporting authorities of objects ```yaml # app/config.yml @@ -77,3 +77,56 @@ class User $serializer->serialize($blog, 'json'); // will export the blog authorities (depth 0) $serializer->serialize($user, 'json'); // will NOT export the blog authorities (depth 1) ``` + +### Export files as URLs + +> This feature require [VichUploaderBundle](https://github.com/dustin10/VichUploaderBundle) + +```yaml +# app/config.yml +hshn_serializer_extra: + vich_uploader: + classes: + AcmeBundle\Entity\Blog: + files: + - { property: picture } + - { property: picture, export_to: image } +``` + +```php +/** @var $serializer JMS\Serializer\Serializer */ +$json = $serializer->serialize($blog, 'json'); +``` + +Generated URLs will be exported when serializing an object. + +```json +{ + "picture": "/url/to/picture", + "image": "/url/to/picture" +} +``` + +### Export images as URLs + +> This feature require [LiipImagineBundle](https://github.com/liip/LiipImagineBundle) + +Adding a filter name specification to a file configuration. + +```yaml +# app/config.yml +hshn_serializer_extra: + vich_uploader: + classes: + AcmeBundle\Entity\Blog: + files: + - { property: picture } + - { property: picture, export_to: image, filter: thumbnail } +``` + +```json +{ + "picture": "/url/to/picture", + "image": "/url/to/thumbnail/picture" +} +```