diff --git a/.travis.yml b/.travis.yml index e0d73070..bce20cb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ php: - hhvm env: - - SYMFONY_VERSION="~2.0.0" DOCTRINE_VERSION="~2.2.0" - SYMFONY_VERSION="~2.1.0" DOCTRINE_VERSION=">=2.2.3,<2.5-dev" - SYMFONY_VERSION="~2.2.0" DOCTRINE_VERSION="~2.2,>=2.2.3" - SYMFONY_VERSION="~2.3.0" DOCTRINE_VERSION=">=2.2.3,<2.4-dev" diff --git a/Adapter/AdapterInterface.php b/Adapter/AdapterInterface.php index 3d5fcc18..6b459b15 100644 --- a/Adapter/AdapterInterface.php +++ b/Adapter/AdapterInterface.php @@ -2,8 +2,6 @@ namespace Vich\UploaderBundle\Adapter; -use Doctrine\Common\EventArgs; - /** * AdapterInterface. * @@ -12,26 +10,26 @@ interface AdapterInterface { /** - * Gets the mapped object from the event arguments. + * Gets the mapped object from an event. * - * @param EventArgs $e The event arguments. - * @return object The mapped object. + * @param object $event The event argument. + * @return object The mapped object. */ - public function getObjectFromArgs(EventArgs $e); + public function getObjectFromEvent($event); /** - * Recomputes the change set for the object. + * Recomputes the change set for the given object. * - * @param EventArgs $e The event arguments. + * @param object $event The event arguments. */ - public function recomputeChangeSet(EventArgs $e); + public function recomputeChangeSet($event); /** - * Gets the reflection class for the object taking - * proxies into account. + * Gets class name for the object, taking proxies into account. + * + * @param object $object The object. * - * @param object $obj The object. - * @return \ReflectionClass The reflection class. + * @return string The FQCN of the className. */ - public function getReflectionClass($obj); + public function getClassName($object); } diff --git a/Adapter/ODM/MongoDB/MongoDBAdapter.php b/Adapter/ODM/MongoDB/MongoDBAdapter.php index 96a95f84..d33691df 100644 --- a/Adapter/ODM/MongoDB/MongoDBAdapter.php +++ b/Adapter/ODM/MongoDB/MongoDBAdapter.php @@ -3,7 +3,6 @@ namespace Vich\UploaderBundle\Adapter\ODM\MongoDB; use Vich\UploaderBundle\Adapter\AdapterInterface; -use Doctrine\Common\EventArgs; use Doctrine\ODM\MongoDB\Proxy\Proxy; /** @@ -16,33 +15,33 @@ class MongoDBAdapter implements AdapterInterface /** * {@inheritDoc} */ - public function getObjectFromArgs(EventArgs $e) + public function getObjectFromEvent($event) { - return $e->getDocument(); + return $event->getDocument(); } /** * {@inheritDoc} */ - public function recomputeChangeSet(EventArgs $e) + public function recomputeChangeSet($event) { - $obj = $this->getObjectFromArgs($e); + $object = $this->getObjectFromEvent($event); - $dm = $e->getDocumentManager(); + $dm = $event->getDocumentManager(); $uow = $dm->getUnitOfWork(); - $metadata = $dm->getClassMetadata(get_class($obj)); - $uow->recomputeSingleDocumentChangeSet($metadata, $obj); + $metadata = $dm->getClassMetadata(get_class($object)); + $uow->recomputeSingleDocumentChangeSet($metadata, $object); } /** * {@inheritDoc} */ - public function getReflectionClass($obj) + public function getClassName($object) { - if ($obj instanceof Proxy) { - return new \ReflectionClass(get_parent_class($obj)); + if ($object instanceof Proxy) { + return get_parent_class($object); } - return new \ReflectionClass($obj); + return get_class($object); } } diff --git a/Adapter/ORM/DoctrineORMAdapter.php b/Adapter/ORM/DoctrineORMAdapter.php index 325c2a35..74ee8020 100644 --- a/Adapter/ORM/DoctrineORMAdapter.php +++ b/Adapter/ORM/DoctrineORMAdapter.php @@ -3,7 +3,6 @@ namespace Vich\UploaderBundle\Adapter\ORM; use Vich\UploaderBundle\Adapter\AdapterInterface; -use Doctrine\Common\EventArgs; use Doctrine\Common\Persistence\Proxy; /** @@ -16,33 +15,33 @@ class DoctrineORMAdapter implements AdapterInterface /** * {@inheritDoc} */ - public function getObjectFromArgs(EventArgs $e) + public function getObjectFromEvent($event) { - return $e->getEntity(); + return $event->getEntity(); } /** * {@inheritDoc} */ - public function recomputeChangeSet(EventArgs $e) + public function recomputeChangeSet($event) { - $obj = $this->getObjectFromArgs($e); + $object = $this->getObjectFromEvent($event); - $em = $e->getEntityManager(); + $em = $event->getEntityManager(); $uow = $em->getUnitOfWork(); - $metadata = $em->getClassMetadata(get_class($obj)); - $uow->recomputeSingleEntityChangeSet($metadata, $obj); + $metadata = $em->getClassMetadata(get_class($object)); + $uow->recomputeSingleEntityChangeSet($metadata, $object); } /** * {@inheritDoc} */ - public function getReflectionClass($obj) + public function getClassName($object) { - if ($obj instanceof Proxy) { - return new \ReflectionClass(get_parent_class($obj)); + if ($object instanceof Proxy) { + return get_parent_class($object); } - return new \ReflectionClass($obj); + return get_class($object); } } diff --git a/Adapter/Propel/PropelAdapter.php b/Adapter/Propel/PropelAdapter.php new file mode 100644 index 00000000..12434586 --- /dev/null +++ b/Adapter/Propel/PropelAdapter.php @@ -0,0 +1,36 @@ + + */ +class PropelAdapter implements AdapterInterface +{ + /** + * {@inheritDoc} + */ + public function getObjectFromEvent($event) + { + return $event->getSubject(); + } + + /** + * {@inheritDoc} + */ + public function recomputeChangeSet($event) + { + } + + /** + * {@inheritDoc} + */ + public function getClassName($object) + { + return get_class($object); + } +} diff --git a/DependencyInjection/CompilerPass/RegisterPropelModelsPass.php b/DependencyInjection/CompilerPass/RegisterPropelModelsPass.php new file mode 100644 index 00000000..80442650 --- /dev/null +++ b/DependencyInjection/CompilerPass/RegisterPropelModelsPass.php @@ -0,0 +1,39 @@ + + */ +class RegisterPropelModelsPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if ($container->getParameter('vich_uploader.driver') !== 'propel') { + return; + } + + $metadata = $container->get('vich_uploader.metadata_reader'); + $classes = $metadata->getUploadableClasses(); + + foreach ($container->findTaggedServiceIds('vich_uploader.listener') as $id => $attributes) { + $listener = $container->getDefinition($id); + $listener->setClass($container->getDefinition($listener->getParent())->getClass()); + $listener->setPublic(true); + + foreach ($classes as $class) { + $listener->addTag('propel.event_subscriber', array( + 'class' => $class + )); + } + } + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index aa02d757..ca8755e8 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2,6 +2,7 @@ namespace Vich\UploaderBundle\DependencyInjection; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -22,12 +23,37 @@ public function getConfigTreeBuilder() $tb = new TreeBuilder(); $root = $tb->root('vich_uploader'); + $supportedDbDrivers = array('orm', 'odm', 'propel'); + $root ->children() - ->scalarNode('db_driver')->isRequired()->end() + ->scalarNode('db_driver') + ->isRequired() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return strtolower($v); }) + ->end() + ->validate() + ->ifNotInArray($supportedDbDrivers) + ->thenInvalid('The db driver %s is not supported. Please choose one of ' . implode(', ', $supportedDbDrivers)) + ->end() + ->end() ->scalarNode('storage')->defaultValue('vich_uploader.storage.file_system')->end() ->scalarNode('twig')->defaultTrue()->end() ->scalarNode('gaufrette')->defaultFalse()->end() + ->end() + ; + + $this->addMetadataSection($root); + $this->addMappingsSection($root); + + return $tb; + } + + protected function addMetadataSection(ArrayNodeDefinition $node) + { + $node + ->children() ->arrayNode('metadata') ->addDefaultsIfNotSet() ->fixXmlConfig('directory', 'directories') @@ -50,6 +76,13 @@ public function getConfigTreeBuilder() ->end() ->end() ->end() + ->end(); + } + + protected function addMappingsSection(ArrayNodeDefinition $node) + { + $node + ->children() ->arrayNode('mappings') ->useAttributeAsKey('id') ->prototype('array') @@ -64,9 +97,6 @@ public function getConfigTreeBuilder() ->end() ->end() ->end() - ->end() - ; - - return $tb; + ->end(); } } diff --git a/DependencyInjection/VichUploaderExtension.php b/DependencyInjection/VichUploaderExtension.php index b6f6512d..5e5cae1f 100644 --- a/DependencyInjection/VichUploaderExtension.php +++ b/DependencyInjection/VichUploaderExtension.php @@ -3,12 +3,15 @@ namespace Vich\UploaderBundle\DependencyInjection; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\Config\FileLocator; -use Vich\UploaderBundle\DependencyInjection\Configuration; use Symfony\Component\HttpKernel\Kernel; +use Vich\UploaderBundle\DependencyInjection\Configuration; + /** * VichUploaderExtension. * @@ -16,20 +19,9 @@ */ class VichUploaderExtension extends Extension { - /** - * @var array $tagMap - */ protected $tagMap = array( - 'orm' => 'doctrine.event_subscriber', - 'mongodb' => 'doctrine_mongodb.odm.event_subscriber' - ); - - /** - * @var array $adapterMap - */ - protected $adapterMap = array( - 'orm' => 'Vich\UploaderBundle\Adapter\ORM\DoctrineORMAdapter', - 'mongodb' => 'Vich\UploaderBundle\Adapter\ODM\MongoDB\MongoDBAdapter' + 'orm' => 'doctrine.event_subscriber', + 'mongodb' => 'doctrine_mongodb.odm.event_subscriber', ); /** @@ -41,23 +33,25 @@ class VichUploaderExtension extends Extension */ public function load(array $configs, ContainerBuilder $container) { - // Set correct doctrine subscriber event for versions of symfony before 2.1 - if (!defined('Symfony\Component\HttpKernel\Kernel::VERSION_ID') || Kernel::VERSION_ID < 20100) { - $this->tagMap['mongodb'] = 'doctrine.odm.mongodb.event_subscriber'; - } - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); - $driver = strtolower($config['db_driver']); - if (!in_array($driver, array_keys($this->tagMap))) { - throw new \InvalidArgumentException(sprintf( - 'Invalid "db_driver" configuration option specified: "%s"', - $driver - )); - } + $this->loadServicesFiles($container, $config); + $this->registerMetadataDirectories($container, $config); + $this->registerCacheStrategy($container, $config); + $this->registerEventListeners($container, $config); + + // define a few parameters + $container->setParameter('vich_uploader.driver', $config['db_driver']); + $container->setParameter('vich_uploader.mappings', $config['mappings']); + $container->setParameter('vich_uploader.storage_service', $config['storage']); + + // define the adapter to use + $container->setAlias('vich_uploader.adapter', 'vich_uploader.adapter.'.$config['db_driver']); + } + protected function loadServicesFiles(ContainerBuilder $container, array $config) + { $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $toLoad = array( @@ -75,16 +69,42 @@ public function load(array $configs, ContainerBuilder $container) if ($config['twig']) { $loader->load('twig.xml'); } + } - $mappings = isset($config['mappings']) ? $config['mappings'] : array(); - $container->setParameter('vich_uploader.mappings', $mappings); + protected function registerEventListeners(ContainerBuilder $container, array $config) + { + $driver = $config['db_driver']; + $servicesMap = array( + 'inject_on_load' => 'inject', + 'delete_on_update' => 'clean', + 'delete_on_remove' => 'remove', + ); - $container->setParameter('vich_uploader.storage_service', $config['storage']); - $container->setParameter('vich_uploader.adapter.class', $this->adapterMap[$driver]); - $container->getDefinition('vich_uploader.listener.uploader')->addTag($this->tagMap[$driver]); + foreach ($config['mappings'] as $name => $mapping) { + foreach ($servicesMap as $configOption => $service) { + if (!$mapping[$configOption]) { + continue; + } - $this->registerMetadataDirectories($container, $config); - $this->registerCacheStrategy($container, $config); + $definition = $container + ->setDefinition(sprintf('vich_uploader.listener.%s.%s', $service, $name), new DefinitionDecorator(sprintf('vich_uploader.listener.%s.%s', $service, $driver))) + ->replaceArgument(0, $name) + ->addTag('vich_uploader.listener'); + + if (isset($this->tagMap[$driver])) { + $definition->addTag($this->tagMap[$driver]); + } + } + + $definition = $container + ->setDefinition(sprintf('vich_uploader.listener.upload.%s', $name), new DefinitionDecorator(sprintf('vich_uploader.listener.upload.%s', $driver))) + ->replaceArgument(0, $name) + ->addTag('vich_uploader.listener'); + + if (isset($this->tagMap[$driver])) { + $definition->addTag($this->tagMap[$driver], array('priority' => -50)); + } + } } protected function registerMetadataDirectories(ContainerBuilder $container, array $config) @@ -94,7 +114,7 @@ protected function registerMetadataDirectories(ContainerBuilder $container, arra // directories $directories = array(); if ($config['metadata']['auto_detection']) { - foreach ($bundles as $name => $class) { + foreach ($bundles as $class) { $ref = new \ReflectionClass($class); $directory = dirname($ref->getFileName()).'/Resources/config/vich_uploader'; @@ -140,10 +160,8 @@ protected function registerCacheStrategy(ContainerBuilder $container, array $con ; $dir = $container->getParameterBag()->resolveValue($config['metadata']['file_cache']['dir']); - if (!file_exists($dir)) { - if (!$rs = @mkdir($dir, 0777, true)) { - throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $dir)); - } + if (!file_exists($dir) && !@mkdir($dir, 0777, true)) { + throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $dir)); } } else { $container->setAlias('vich_uploader.metadata.cache', new Alias($config['metadata']['cache'], false)); diff --git a/EventListener/Doctrine/BaseListener.php b/EventListener/Doctrine/BaseListener.php new file mode 100644 index 00000000..c66df029 --- /dev/null +++ b/EventListener/Doctrine/BaseListener.php @@ -0,0 +1,51 @@ + + */ +abstract class BaseListener +{ + /** + * @var string + */ + protected $mappingName; + + /** + * @var AdapterInterface $adapter + */ + protected $adapter; + + /** + * @var MetadataReader $metadata + */ + protected $metadata; + + /** + * @var UploaderHandler $handler + */ + protected $handler; + + /** + * Constructs a new instance of UploaderListener. + * + * @param string $mapping The mapping name. + * @param AdapterInterface $adapter The adapter. + * @param MetadataReader $metadata The metadata reader. + * @param UploaderHandler $handler The upload handler. + */ + public function __construct($mapping, AdapterInterface $adapter, MetadataReader $metadata, UploadHandler $handler) + { + $this->mapping = $mapping; + $this->adapter = $adapter; + $this->metadata = $metadata; + $this->handler = $handler; + } +} diff --git a/EventListener/Doctrine/CleanListener.php b/EventListener/Doctrine/CleanListener.php new file mode 100644 index 00000000..941a77f6 --- /dev/null +++ b/EventListener/Doctrine/CleanListener.php @@ -0,0 +1,43 @@ + + */ +class CleanListener extends BaseListener implements EventSubscriber +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public function getSubscribedEvents() + { + return array( + 'preUpdate', + ); + } + + /** + * Update the file and file name if necessary. + * + * @param EventArgs $event The event. + */ + public function preUpdate(EventArgs $event) + { + $object = $this->adapter->getObjectFromEvent($event); + + if ($this->metadata->isUploadable($this->adapter->getClassName($object))) { + $this->handler->clean($object, $this->mapping); + $this->adapter->recomputeChangeSet($event); + } + } +} diff --git a/EventListener/Doctrine/InjectListener.php b/EventListener/Doctrine/InjectListener.php new file mode 100644 index 00000000..91692e8d --- /dev/null +++ b/EventListener/Doctrine/InjectListener.php @@ -0,0 +1,42 @@ + + */ +class InjectListener extends BaseListener implements EventSubscriber +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public function getSubscribedEvents() + { + return array( + 'postLoad', + ); + } + + /** + * Populates uploadable fields from filename properties. + * + * @param EventArgs $event The event. + */ + public function postLoad(EventArgs $event) + { + $object = $this->adapter->getObjectFromEvent($event); + + if ($this->metadata->isUploadable($this->adapter->getClassName($object))) { + $this->handler->hydrate($object, $this->mapping); + } + } +} diff --git a/EventListener/Doctrine/RemoveListener.php b/EventListener/Doctrine/RemoveListener.php new file mode 100644 index 00000000..7005660a --- /dev/null +++ b/EventListener/Doctrine/RemoveListener.php @@ -0,0 +1,42 @@ + + */ +class RemoveListener extends BaseListener implements EventSubscriber +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public function getSubscribedEvents() + { + return array( + 'postRemove', + ); + } + + /** + * Removes the file if necessary. + * + * @param EventArgs $event The event. + */ + public function postRemove(EventArgs $event) + { + $object = $this->adapter->getObjectFromEvent($event); + + if ($this->metadata->isUploadable($this->adapter->getClassName($object))) { + $this->handler->delete($object, $this->mapping); + } + } +} diff --git a/EventListener/Doctrine/UploadListener.php b/EventListener/Doctrine/UploadListener.php new file mode 100644 index 00000000..ad838b92 --- /dev/null +++ b/EventListener/Doctrine/UploadListener.php @@ -0,0 +1,58 @@ + + */ +class UploadListener extends BaseListener implements EventSubscriber +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public function getSubscribedEvents() + { + return array( + 'prePersist', + 'preUpdate', + ); + } + + /** + * Checks for file to upload. + * + * @param EventArgs $event The event. + */ + public function prePersist(EventArgs $event) + { + $object = $this->adapter->getObjectFromEvent($event); + + if ($this->metadata->isUploadable($this->adapter->getClassName($object))) { + $this->handler->upload($object, $this->mapping); + } + } + + /** + * Update the file and file name if necessary. + * + * @param EventArgs $event The event. + */ + public function preUpdate(EventArgs $event) + { + $object = $this->adapter->getObjectFromEvent($event); + + if ($this->metadata->isUploadable($this->adapter->getClassName($object))) { + $this->handler->upload($object, $this->mapping); + $this->adapter->recomputeChangeSet($event); + } + } +} diff --git a/EventListener/Propel/BaseListener.php b/EventListener/Propel/BaseListener.php new file mode 100644 index 00000000..c86a7b91 --- /dev/null +++ b/EventListener/Propel/BaseListener.php @@ -0,0 +1,43 @@ + + */ +abstract class BaseListener +{ + /** + * @var string + */ + protected $mappingName; + + /** + * @var AdapterInterface $adapter + */ + protected $adapter; + + /** + * @var UploaderHandler $handler + */ + protected $handler; + + /** + * Constructs a new instance of BaseListener. + * + * @param string $mapping The mapping name. + * @param AdapterInterface $adapter The adapter. + * @param UploaderHandler $handler The upload handler. + */ + public function __construct($mapping, AdapterInterface $adapter, UploadHandler $handler) + { + $this->mapping = $mapping; + $this->adapter = $adapter; + $this->handler = $handler; + } +} diff --git a/EventListener/Propel/CleanListener.php b/EventListener/Propel/CleanListener.php new file mode 100644 index 00000000..63b3d36f --- /dev/null +++ b/EventListener/Propel/CleanListener.php @@ -0,0 +1,39 @@ + + */ +class CleanListener extends BaseListener implements EventSubscriberInterface +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public static function getSubscribedEvents() + { + return array( + 'propel.pre_update' => 'onUpload', + ); + } + + /** + * Update the file and file name if necessary. + * + * @param GenericEvent $event The event. + */ + public function onUpload(GenericEvent $event) + { + $object = $this->adapter->getObjectFromEvent($event); + $this->handler->clean($object, $this->mapping); + } +} diff --git a/EventListener/Propel/InjectListener.php b/EventListener/Propel/InjectListener.php new file mode 100644 index 00000000..bcb15d5c --- /dev/null +++ b/EventListener/Propel/InjectListener.php @@ -0,0 +1,39 @@ + + */ +class InjectListener extends BaseListener implements EventSubscriberInterface +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public static function getSubscribedEvents() + { + return array( + 'propel.post_hydrate' => 'onHydrate', + ); + } + + /** + * Populates uploadable fields from filename properties. + * + * @param GenericEvent $event The event. + */ + public function onHydrate(GenericEvent $event) + { + $object = $this->adapter->getObjectFromEvent($event); + $this->handler->hydrate($object, $this->mapping); + } +} diff --git a/EventListener/Propel/RemoveListener.php b/EventListener/Propel/RemoveListener.php new file mode 100644 index 00000000..5793ddbb --- /dev/null +++ b/EventListener/Propel/RemoveListener.php @@ -0,0 +1,39 @@ + + */ +class RemoveListener extends BaseListener implements EventSubscriberInterface +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public static function getSubscribedEvents() + { + return array( + 'propel.post_delete' => 'onDelete', + ); + } + + /** + * Removes the file when the object is deleted. + * + * @param GenericEvent $event The event. + */ + public function onDelete(GenericEvent $event) + { + $object = $this->adapter->getObjectFromEvent($event); + $this->handler->delete($object, $this->mapping); + } +} diff --git a/EventListener/Propel/UploadListener.php b/EventListener/Propel/UploadListener.php new file mode 100644 index 00000000..96ed2a37 --- /dev/null +++ b/EventListener/Propel/UploadListener.php @@ -0,0 +1,40 @@ + + */ +class UploadListener extends BaseListener implements EventSubscriberInterface +{ + /** + * The events the listener is subscribed to. + * + * @return array The array of events. + */ + public static function getSubscribedEvents() + { + return array( + 'propel.pre_insert' => 'onUpload', + 'propel.pre_update' => 'onUpload', + ); + } + + /** + * Update the file and file name if necessary. + * + * @param GenericEvent $event The event. + */ + public function onUpload(GenericEvent $event) + { + $object = $this->adapter->getObjectFromEvent($event); + $this->handler->upload($object, $this->mapping); + } +} diff --git a/EventListener/UploaderListener.php b/EventListener/UploaderListener.php deleted file mode 100644 index 12e06bbf..00000000 --- a/EventListener/UploaderListener.php +++ /dev/null @@ -1,129 +0,0 @@ - - */ -class UploaderListener implements EventSubscriber -{ - /** - * @var \Vich\UploaderBundle\Adapter\AdapterInterface $adapter - */ - protected $adapter; - - /** - * @var \Vich\UploaderBundle\Mapping\MappingReader $mapping - */ - protected $mapping; - - /** - * @var \Vich\UploaderBundle\Storage\StorageInterface $storage - */ - protected $storage; - - /** - * @var \Vich\UploaderBundle\Injector\FileInjectorInterface $injector - */ - protected $injector; - - /** - * Constructs a new instance of UploaderListener. - * - * @param \Vich\UploaderBundle\Adapter\AdapterInterface $adapter The adapter. - * @param \Vich\UploaderBundle\Mapping\MappingReader $mapping The mapping reader. - * @param \Vich\UploaderBundle\Storage\StorageInterface $storage The storage. - * @param \Vich\UploaderBundle\Injector\FileInjectorInterface $injector The injector. - */ - public function __construct(AdapterInterface $adapter, MappingReader $mapping, StorageInterface $storage, FileInjectorInterface $injector) - { - $this->adapter = $adapter; - $this->mapping = $mapping; - $this->storage = $storage; - $this->injector = $injector; - } - - /** - * The events the listener is subscribed to. - * - * @return array The array of events. - */ - public function getSubscribedEvents() - { - return array( - 'prePersist', - 'preUpdate', - 'postLoad', - 'postRemove', - ); - } - - /** - * Checks for file to upload. - * - * @param \Doctrine\Common\EventArgs $args The event arguments. - */ - public function prePersist(EventArgs $args) - { - $obj = $this->adapter->getObjectFromArgs($args); - - if ($this->mapping->isUploadable($this->adapter->getReflectionClass($obj))) { - $this->storage->upload($obj); - $this->injector->injectFiles($obj); - } - } - - /** - * Update the file and file name if necessary. - * - * @param EventArgs $args The event arguments. - */ - public function preUpdate(EventArgs $args) - { - $obj = $this->adapter->getObjectFromArgs($args); - - if ($this->mapping->isUploadable($this->adapter->getReflectionClass($obj))) { - $this->storage->upload($obj); - $this->injector->injectFiles($obj); - $this->adapter->recomputeChangeSet($args); - } - } - - /** - * Populates uploadable fields from filename properties - * if necessary. - * - * @param \Doctrine\Common\EventArgs $args - */ - public function postLoad(EventArgs $args) - { - $obj = $this->adapter->getObjectFromArgs($args); - - if ($this->mapping->isUploadable($this->adapter->getReflectionClass($obj))) { - $this->injector->injectFiles($obj); - } - } - - /** - * Removes the file if necessary. - * - * @param EventArgs $args The event arguments. - */ - public function postRemove(EventArgs $args) - { - $obj = $this->adapter->getObjectFromArgs($args); - - if ($this->mapping->isUploadable($this->adapter->getReflectionClass($obj))) { - $this->storage->remove($obj); - } - } -} diff --git a/Handler/UploadHandler.php b/Handler/UploadHandler.php new file mode 100644 index 00000000..829e7ef6 --- /dev/null +++ b/Handler/UploadHandler.php @@ -0,0 +1,108 @@ + + */ +class UploadHandler +{ + /** + * @var \Vich\UploaderBundle\Mapping\PropertyMappingFactory + */ + protected $factory; + + /** + * @var \Vich\UploaderBundle\Storage\StorageInterface $storage + */ + protected $storage; + + /** + * @var \Vich\UploaderBundle\Injector\FileInjectorInterface $injector + */ + protected $injector; + + /** + * Constructs a new instance of UploaderListener. + * + * @param \Vich\UploaderBundle\Mapping\PropertyMappingFactory $factory The mapping factory. + * @param \Vich\UploaderBundle\Storage\StorageInterface $storage The storage. + * @param \Vich\UploaderBundle\Injector\FileInjectorInterface $injector The injector. + */ + public function __construct(PropertyMappingFactory $factory, StorageInterface $storage, FileInjectorInterface $injector) + { + $this->factory = $factory; + $this->storage = $storage; + $this->injector = $injector; + } + + /** + * Checks for file to upload. + */ + public function upload($object, $mapping) + { + if (!$this->factory->hasMapping($object, $mapping)) { + return; + } + + $mapping = $this->factory->fromName($object, $mapping); + $file = $mapping->getFile($object); + + if ($file === null || !($file instanceof UploadedFile)) { + return; + } + + $this->storage->upload($object, $mapping); + $this->injector->injectFile($object, $mapping); + } + + /** + * Checks for file to remove before a new upload. + */ + public function clean($object, $mapping) + { + if (!$this->factory->hasMapping($object, $mapping)) { + return; + } + + $mapping = $this->factory->fromName($object, $mapping); + $file = $mapping->getFile($object); + + if ($file === null || !($file instanceof UploadedFile)) { + return; + } + + // if there already is a file for the given object, delete it if needed + if ($mapping->getFileName($object)) { + $this->storage->remove($object, $mapping); + } + } + + public function hydrate($object, $mapping) + { + if (!$this->factory->hasMapping($object, $mapping)) { + return; + } + + $mapping = $this->factory->fromName($object, $mapping); + $this->injector->injectFile($object, $mapping); + } + + public function delete($object, $mapping) + { + if (!$this->factory->hasMapping($object, $mapping)) { + return; + } + + $mapping = $this->factory->fromName($object, $mapping); + $this->storage->remove($object, $mapping); + } +} diff --git a/Injector/FileInjector.php b/Injector/FileInjector.php index 1f5f8beb..305b975d 100644 --- a/Injector/FileInjector.php +++ b/Injector/FileInjector.php @@ -2,10 +2,12 @@ namespace Vich\UploaderBundle\Injector; +use Symfony\Component\HttpFoundation\File\File; + use Vich\UploaderBundle\Injector\FileInjectorInterface; -use Vich\UploaderBundle\Mapping\PropertyMappingFactory; +use Vich\UploaderBundle\Mapping\PropertyMapping; use Vich\UploaderBundle\Storage\StorageInterface; -use Symfony\Component\HttpFoundation\File\File; + /** * FileInjector. * @@ -14,49 +16,33 @@ class FileInjector implements FileInjectorInterface { /** - * @var \Vich\UploaderBundle\Mapping\PropertyMappingFactory $factory - */ - protected $factory; - - /** - * @var \Vich\UploaderBundle\Storage\StorageInterface + * @var StorageInterface */ protected $storage; /** * Constructs a new instance of FileInjector. * - * @param \Vich\UploaderBundle\Mapping\PropertyMappingFactory $factory The factory. - * @param \Vich\UploaderBundle\Storage\StorageInterface $storage Storage. + * @param StorageInterface $storage Storage. */ - public function __construct(PropertyMappingFactory $factory, StorageInterface $storage) + public function __construct(StorageInterface $storage) { - $this->factory = $factory; $this->storage = $storage; } /** * {@inheritDoc} */ - public function injectFiles($obj) + public function injectFile($object, PropertyMapping $mapping) { - $mappings = $this->factory->fromObject($obj); - foreach ($mappings as $mapping) { - if (!$mapping->getInjectOnLoad()) { - continue; - } - - $field = $mapping->getProperty()->getName(); - try { - $path = $this->storage->resolvePath($obj, $field); - } catch (\InvalidArgumentException $e) { - continue; - } - - $mapping->getProperty()->setValue( - $obj, - new File($path, false) - ); + $field = $mapping->getFilePropertyName(); + + try { + $path = $this->storage->resolvePath($object, $field); + } catch (\InvalidArgumentException $e) { + return; } + + $mapping->setFile($object, new File($path, false)); } } diff --git a/Injector/FileInjectorInterface.php b/Injector/FileInjectorInterface.php index b3d3eb4f..e312b903 100644 --- a/Injector/FileInjectorInterface.php +++ b/Injector/FileInjectorInterface.php @@ -2,6 +2,8 @@ namespace Vich\UploaderBundle\Injector; +use Vich\UploaderBundle\Mapping\PropertyMapping; + /** * FileInjectorInterface. * @@ -14,7 +16,8 @@ interface FileInjectorInterface * with a populated Symfony\Component\HttpFoundation\File\File * instance if the field is configured for injection. * - * @param object $obj The object. + * @param object $object The object. + * @param PropertyMapping $mapping The mapping representing the field to populate. */ - public function injectFiles($obj); + public function injectFile($object, PropertyMapping $mapping); } diff --git a/Mapping/CachedPropertyMappingFactory.php b/Mapping/CachedPropertyMappingFactory.php new file mode 100644 index 00000000..d3f61330 --- /dev/null +++ b/Mapping/CachedPropertyMappingFactory.php @@ -0,0 +1,35 @@ + + */ +class CachedPropertyMappingFactory extends PropertyMappingFactory +{ + protected $cache = array(); + + public function fromObject($obj, $className = null) + { + $class = $this->getClassName($obj, $className); + + if (isset($this->cache[$class])) { + return $this->cache[$class]; + } + + return $this->cache[$class] = parent::fromObject($obj, $className); + } + + public function fromField($obj, $field, $className = null) + { + $class = $this->getClassName($obj, $className); + + if (isset($this->cache[$class.'::'.$field])) { + return $this->cache[$class.'::'.$field]; + } + + return $this->cache[$class.'::'.$field] = parent::fromField($obj, $field, $className);; + } +} diff --git a/Mapping/MappingReader.php b/Mapping/MappingReader.php deleted file mode 100644 index 89752f70..00000000 --- a/Mapping/MappingReader.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -class MappingReader -{ - /** - * @var AgnosticReader $reader - */ - protected $reader; - - /** - * Constructs a new instance of the MappingReader. - * - * @param MetadataFactoryInterface $reader The metadata reader. - */ - public function __construct(MetadataFactoryInterface $reader) - { - $this->reader = $reader; - } - - /** - * Tells if the given class is uploadable. - * - * @param Reflectionclass $class The class to test. - * - * @return bool - */ - public function isUploadable(\ReflectionClass $class) - { - $metadata = $this->reader->getMetadataForClass($class->name); - - return $metadata !== null; - } - - /** - * Attempts to read the uploadable fields. - * - * @param \ReflectionClass $class The reflection class. - * - * @return array A list of uploadable fields. - */ - public function getUploadableFields(\ReflectionClass $class) - { - $metadata = $this->reader->getMetadataForClass($class->getName()); - $classMetadata = $metadata->classMetadata[$class->getName()]; - - return $classMetadata->fields; - } - - /** - * Attempts to read the mapping of a specified property. - * - * @param \ReflectionClass $class The class. - * @param string $field The field - * - * @return null|array The field mapping. - */ - public function getUploadableField(\ReflectionClass $class, $field) - { - $fieldsMetadata = $this->getUploadableFields($class); - - return isset($fieldsMetadata[$field]) ? $fieldsMetadata[$field] : null; - } -} diff --git a/Mapping/PropertyMapping.php b/Mapping/PropertyMapping.php index d0a2dfb4..e93730e5 100644 --- a/Mapping/PropertyMapping.php +++ b/Mapping/PropertyMapping.php @@ -2,6 +2,8 @@ namespace Vich\UploaderBundle\Mapping; +use Symfony\Component\PropertyAccess\PropertyAccess; + use Vich\UploaderBundle\Naming\NamerInterface; use Vich\UploaderBundle\Naming\DirectoryNamerInterface; @@ -12,16 +14,6 @@ */ class PropertyMapping { - /** - * @var \ReflectionProperty $property - */ - protected $property; - - /** - * @var \ReflectionProperty $fileNameProperty - */ - protected $fileNameProperty; - /** * @var NamerInterface $namer */ @@ -43,49 +35,33 @@ class PropertyMapping protected $mappingName; /** - * Gets the reflection property that represents the - * annotated property. - * - * @return \ReflectionProperty The property. + * @var string $filePropertyPath */ - public function getProperty() - { - return $this->property; - } + protected $filePropertyPath; /** - * Sets the reflection property that represents the annotated - * property. - * - * @param \ReflectionProperty $property The reflection property. + * @var string $fileNamePropertyPath */ - public function setProperty(\ReflectionProperty $property) - { - $this->property = $property; - $this->property->setAccessible(true); - } + protected $fileNamePropertyPath; /** - * Gets the reflection property that represents the property - * which holds the file name for the mapping. - * - * @return \ReflectionProperty The reflection property. + * @var PropertyAccess $accessor */ - public function getFileNameProperty() + protected $accessor; + + public function __construct($filePropertyPath, $fileNamePropertyPath) { - return $this->fileNameProperty; + $this->filePropertyPath = $filePropertyPath; + $this->fileNamePropertyPath = $fileNamePropertyPath; } - /** - * Sets the reflection property that represents the property - * which holds the file name for the mapping. - * - * @param \ReflectionProperty $fileNameProperty The reflection property. - */ - public function setFileNameProperty(\ReflectionProperty $fileNameProperty) + protected function getAccessor() { - $this->fileNameProperty = $fileNameProperty; - $this->fileNameProperty->setAccessible(true); + if ($this->accessor !== null) { + return $this->accessor; + } + + return $this->accessor = PropertyAccess::getPropertyAccessor(); } /** @@ -179,84 +155,66 @@ public function setMappingName($mappingName) } /** - * Gets the name of the annotated property. - * - * @return string The name. - */ - public function getPropertyName() - { - return $this->property->getName(); - } - - /** - * Gets the value of the annotated property. + * Gets the file property value for the given object. * * @param object $obj The object. * @return UploadedFile The file. */ - public function getPropertyValue($obj) + public function getFile($obj) { - return $this->property->getValue($obj); + return $this->getAccessor()->getValue($obj, $this->filePropertyPath); } /** - * Gets the configured file name property name. + * Modifies the file property value for the given object. * - * @return string The name. + * @param object $obj The object. + * @param UploadedFile $file The new file. */ - public function getFileNamePropertyName() + public function setFile($obj, $file) { - return $this->fileNameProperty->getName(); + $this->getAccessor()->setValue($obj, $this->filePropertyPath, $file); } /** - * Gets the configured upload directory. + * Gets the fileName property of the given object. * - * @param null $obj - * @param null $field - * @return string The configured upload directory. + * @param object $obj The object. + * @return string The filename. */ - public function getUploadDir($obj = null, $field = null) + public function getFileName($obj) { - if ($this->hasDirectoryNamer()) { - return $this->getDirectoryNamer()->directoryName($obj, $field, $this->mapping['upload_destination']); - } - - return $this->mapping['upload_destination']; + return $this->getAccessor()->getValue($obj, $this->fileNamePropertyPath); } /** - * Determines if the file should be deleted upon removal of the - * entity. + * Modifies the fileName property of the given object. * - * @return bool True if delete on remove, false otherwise. + * @param object $obj The object. */ - public function getDeleteOnRemove() + public function setFileName($obj, $value) { - return $this->mapping['delete_on_remove']; + $this->getAccessor()->setValue($obj, $this->fileNamePropertyPath, $value); } /** - * Determines if the file should be deleted when the file is - * replaced by an other one. + * Gets the configured file property name. * - * @return bool True if delete on update, false otherwise. + * @return string The name. */ - public function getDeleteOnUpdate() + public function getFilePropertyName() { - return $this->mapping['delete_on_update']; + return $this->filePropertyPath; } /** - * Determines if the uploadable field should be injected with a - * Symfony\Component\HttpFoundation\File\File instance when - * the object is loaded from the datastore. + * Returns the raw upload destination, as given in the configuration. * - * @return bool True if the field should be injected, false otherwise. + * @return string The configured upload directory. */ - public function getInjectOnLoad() + public function getUploadDestination() { - return $this->mapping['inject_on_load']; + return $this->mapping['upload_destination']; } /** diff --git a/Mapping/PropertyMappingFactory.php b/Mapping/PropertyMappingFactory.php index ecb1da67..457ffc71 100644 --- a/Mapping/PropertyMappingFactory.php +++ b/Mapping/PropertyMappingFactory.php @@ -2,12 +2,13 @@ namespace Vich\UploaderBundle\Mapping; -use Vich\UploaderBundle\Mapping\MappingReader; -use Vich\UploaderBundle\Mapping\PropertyMapping; -use Vich\UploaderBundle\Adapter\AdapterInterface; +use Doctrine\Common\Persistence\Proxy; use Symfony\Component\DependencyInjection\ContainerInterface; + +use Vich\UploaderBundle\Adapter\AdapterInterface; use Vich\UploaderBundle\Mapping\Annotation\UploadableField; -use Doctrine\Common\Persistence\Proxy; +use Vich\UploaderBundle\Metadata\MetadataReader; +use Vich\UploaderBundle\Mapping\PropertyMapping; /** * PropertyMappingFactory. @@ -22,9 +23,9 @@ class PropertyMappingFactory protected $container; /** - * @var MappingReader $mapping + * @var MetadataReader $metadata */ - protected $mapping; + protected $metadata; /** * @var AdapterInterface $adapter @@ -39,39 +40,60 @@ class PropertyMappingFactory /** * Constructs a new instance of PropertyMappingFactory. * - * @param \Symfony\Component\DependencyInjection\ContainerInterface $container The container. - * @param \Vich\UploaderBundle\Mapping\MappingReader $mapping The mapping mapping. - * @param \Vich\UploaderBundle\Adapter\AdapterInterface $adapter The adapter. - * @param array $mappings The configured mappings. + * @param ContainerInterface $container The container. + * @param MetadataReader $metadata The metadata mapping. + * @param AdapterInterface $adapter The adapter. + * @param array $mappings The configured mappings. */ - public function __construct(ContainerInterface $container, MappingReader $mapping, AdapterInterface $adapter, array $mappings) + public function __construct(ContainerInterface $container, MetadataReader $metadata, AdapterInterface $adapter, array $mappings) { $this->container = $container; - $this->mapping = $mapping; + $this->metadata = $metadata; $this->adapter = $adapter; $this->mappings = $mappings; } + public function hasMapping($object, $mappingName) + { + $mappings = $this->fromObject($object); + + return isset($mappings[$mappingName]); + } + + public function fromName($object, $mappingName) + { + $mappings = $this->fromObject($object); + + if (!isset($mappings[$mappingName])) { + throw new \RuntimeException(sprintf('Mapping %s does not exist', $mappingName)); + } + + return $mappings[$mappingName]; + } + /** * Creates an array of PropetyMapping objects which contain the * configuration for the uploadable fields in the specified * object. * - * @param object $obj The object. - * @return array An array up PropertyMapping objects. + * @param object $obj The object. + * @param string $className The object's class. Mandatory if $obj can't be used to determine it. + * + * @return array An array up PropertyMapping objects. */ - public function fromObject($obj) + public function fromObject($obj, $className = null) { + // @todo nothing to do here if ($obj instanceof Proxy) { $obj->__load(); } - $class = $this->adapter->getReflectionClass($obj); + $class = $this->getClassName($obj, $className); $this->checkUploadable($class); $mappings = array(); - foreach ($this->mapping->getUploadableFields($class) as $field => $mappingData) { - $mappings[] = $this->createMapping($obj, $field, $mappingData); + foreach ($this->metadata->getUploadableFields($class) as $field => $mappingData) { + $mappings[$mappingData['mapping']] = $this->createMapping($obj, $field, $mappingData); } return $mappings; @@ -81,20 +103,23 @@ public function fromObject($obj) * Creates a property mapping object which contains the * configuration for the specified uploadable field. * - * @param object $obj The object. - * @param string $field The field. + * @param object $obj The object. + * @param string $field The field. + * @param string $className The object's class. Mandatory if $obj can't be used to determine it. + * * @return null|PropertyMapping The property mapping. */ - public function fromField($obj, $field) + public function fromField($obj, $field, $className = null) { + // @todo nothing to do here if ($obj instanceof Proxy) { $obj->__load(); } - $class = $this->adapter->getReflectionClass($obj); + $class = $this->getClassName($obj, $className); $this->checkUploadable($class); - $mappingData = $this->mapping->getUploadableField($class, $field); + $mappingData = $this->metadata->getUploadableField($class, $field); if ($mappingData === null) { return null; } @@ -105,12 +130,13 @@ public function fromField($obj, $field) /** * Checks to see if the class is uploadable. * - * @param \ReflectionClass $class The class. - * @throws \InvalidArgumentException + * @param string $class The class name (FQCN). + * + * @throws InvalidArgumentException */ - protected function checkUploadable(\ReflectionClass $class) + protected function checkUploadable($class) { - if (!$this->mapping->isUploadable($class)) { + if (!$this->metadata->isUploadable($class)) { throw new \InvalidArgumentException('The object is not uploadable.'); } } @@ -118,17 +144,15 @@ protected function checkUploadable(\ReflectionClass $class) /** * Creates the property mapping from the read annotation and configured mapping. * - * @param object $obj The object. - * @param string $fieldName The field name. - * @param \Vich\UploaderBundle\Annotation\UploadableField $mappingData The mapping data. + * @param object $obj The object. + * @param string $fieldName The field name. + * @param UploadableField $mappingData The mapping data. * - * @return PropertyMapping The property mapping. - * @throws \InvalidArgumentException + * @return PropertyMapping The property mapping. + * @throws InvalidArgumentException */ protected function createMapping($obj, $fieldName, array $mappingData) { - $class = $this->adapter->getReflectionClass($obj); - if (!array_key_exists($mappingData['mapping'], $this->mappings)) { throw new \InvalidArgumentException(sprintf( 'No mapping named "%s" configured.', $mappingData['mapping'] @@ -137,9 +161,7 @@ protected function createMapping($obj, $fieldName, array $mappingData) $config = $this->mappings[$mappingData['mapping']]; - $mapping = new PropertyMapping(); - $mapping->setProperty($class->getProperty($mappingData['propertyName'] ?: $fieldName)); - $mapping->setFileNameProperty($class->getProperty($mappingData['fileNameProperty'])); + $mapping = new PropertyMapping(isset($mappingData['propertyName']) ? $mappingData['propertyName'] : $fieldName, $mappingData['fileNameProperty']); $mapping->setMappingName($mappingData['mapping']); $mapping->setMapping($config); @@ -153,4 +175,25 @@ protected function createMapping($obj, $fieldName, array $mappingData) return $mapping; } + + /** + * Returns the className of the given object. + * + * @param object $object The object to inspect. + * @param string $className User specified className. + * + * @return string + */ + protected function getClassName($object, $className = null) + { + if ($className !== null) { + return $className; + } + + if (is_object($object)) { + return $this->adapter->getClassName($object); + } + + throw new \RuntimeException('Impossible to determine the class name. Either specify it explicitly or give an object'); + } } diff --git a/Metadata/Driver/AbstractFileDriver.php b/Metadata/Driver/AbstractFileDriver.php new file mode 100644 index 00000000..061dffa0 --- /dev/null +++ b/Metadata/Driver/AbstractFileDriver.php @@ -0,0 +1,69 @@ + + */ +abstract class AbstractFileDriver implements AdvancedDriverInterface +{ + /** + * @var FileLocatorInterface + */ + protected $locator; + + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + if (null === $path = $this->locator->findFileForClass($class, $this->getExtension())) { + return null; + } + + return $this->loadMetadataFromFile($path, $class); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if (!$this->locator instanceof AdvancedFileLocatorInterface) { + throw new \RuntimeException('Locator "%s" must be an instance of "AdvancedFileLocatorInterface".'); + } + + $classNames = array(); + foreach ($this->locator->findAllClasses($this->getExtension()) as $file) { + $metadata = $this->loadMetadataFromFile($file->getRealpath()); + $classNames[] = $metadata->name; + } + + return $classNames; + } + + /** + * Parses the content of the file, and converts it to the desired metadata. + * + * @param string $file + * @param \ReflectionClass $class + * + * @return \MetaData\ClassMetadata|null + */ + abstract protected function loadMetadataFromFile($file, \ReflectionClass $class = null); + + /** + * Returns the extension of the file. + * + * @return string + */ + abstract protected function getExtension(); +} diff --git a/Metadata/Driver/Annotation.php b/Metadata/Driver/AnnotationDriver.php similarity index 97% rename from Metadata/Driver/Annotation.php rename to Metadata/Driver/AnnotationDriver.php index 4ab2a7f1..56119bab 100644 --- a/Metadata/Driver/Annotation.php +++ b/Metadata/Driver/AnnotationDriver.php @@ -12,7 +12,7 @@ * * @author Kévin Gomez */ -class Annotation implements DriverInterface +class AnnotationDriver implements DriverInterface { const UPLOADABLE_ANNOTATION = 'Vich\UploaderBundle\Mapping\Annotation\Uploadable'; const UPLOADABLE_FIELD_ANNOTATION = 'Vich\UploaderBundle\Mapping\Annotation\UploadableField'; diff --git a/Metadata/Driver/ChainDriver.php b/Metadata/Driver/ChainDriver.php new file mode 100644 index 00000000..f4c29a49 --- /dev/null +++ b/Metadata/Driver/ChainDriver.php @@ -0,0 +1,51 @@ +drivers = $drivers; + } + + public function addDriver(DriverInterface $driver) + { + $this->drivers[] = $driver; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + foreach ($this->drivers as $driver) { + if (null !== ($metadata = $driver->loadMetadataForClass($class))) { + return $metadata; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $classes = array(); + foreach ($this->drivers as $driver) { + if (!$driver instanceof AdvancedDriverInterface) { + continue; + } + + $driverClasses = $driver->getAllClassNames(); + if (!empty($driverClasses)) { + $classes = array_merge($classes, $driverClasses); + } + } + + return $classes; + } +} diff --git a/Metadata/Driver/FileLocator.php b/Metadata/Driver/FileLocator.php index c0f0e370..98dfae6d 100644 --- a/Metadata/Driver/FileLocator.php +++ b/Metadata/Driver/FileLocator.php @@ -2,10 +2,10 @@ namespace Vich\UploaderBundle\Metadata\Driver; -use Metadata\Driver\FileLocatorInterface; +use Metadata\Driver\AdvancedFileLocatorInterface; use Symfony\Component\Finder\Finder; -class FileLocator implements FileLocatorInterface +class FileLocator implements AdvancedFileLocatorInterface { private $dirs; @@ -27,12 +27,13 @@ public function getDirs() */ public function findFileForClass(\ReflectionClass $class, $extension) { + $finder = new Finder(); + foreach ($this->dirs as $prefix => $dir) { if ('' !== $prefix && 0 !== strpos($class->getNamespaceName(), $prefix)) { continue; } - $finder = new Finder(); $files = $finder->files()->in($dir)->name(sprintf('*%s*.%s', $class->getShortName(), $extension)); if (count($files) !== 1) { @@ -46,4 +47,21 @@ public function findFileForClass(\ReflectionClass $class, $extension) return null; } + + /** + * Finds all possible metadata files. + * + * @param string $extension + * + * @return array + */ + public function findAllClasses($extension) + { + $files = Finder::create() + ->files() + ->name('*.'.$extension) + ->in($this->dirs); + + return iterator_to_array($files); + } } diff --git a/Metadata/Driver/XmlDriver.php b/Metadata/Driver/XmlDriver.php new file mode 100644 index 00000000..d62a871c --- /dev/null +++ b/Metadata/Driver/XmlDriver.php @@ -0,0 +1,63 @@ + + */ +class XmlDriver extends AbstractFileDriver +{ + /** + * {@inheritDoc} + */ + protected function loadMetadataFromFile($file, \ReflectionClass $class = null) + { + $previous = libxml_use_internal_errors(true); + $elem = simplexml_load_file($file); + libxml_use_internal_errors($previous); + + if (false === $elem) { + throw new RuntimeException(libxml_get_last_error()); + } + + $className = $this->guessClassName($file, $elem, $class); + $metadata = new ClassMetadata($className); + + foreach ($elem->children() as $field) { + $fieldMetadata = array( + 'mapping' => (string) $field->attributes()->mapping, + 'propertyName' => (string) $field->attributes()->name, + 'fileNameProperty' => (string) $field->attributes()->filename_property, + ); + + $metadata->fields[(string) $field->attributes()->name] = $fieldMetadata; + } + + return $metadata; + } + + /** + * {@inheritDoc} + */ + protected function getExtension() + { + return 'xml'; + } + + protected function guessClassName($file, \SimpleXMLElement $elem, \ReflectionClass $class = null) + { + if ($class === null) { + return (string) $elem->attributes()->class; + } + + if ($class->name !== (string) $elem->attributes()->class) { + throw new \RuntimeException(sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file)); + } + + return $class->name; + } +} diff --git a/Metadata/Driver/Yaml.php b/Metadata/Driver/YamlDriver.php similarity index 66% rename from Metadata/Driver/Yaml.php rename to Metadata/Driver/YamlDriver.php index 188ac847..affa3165 100644 --- a/Metadata/Driver/Yaml.php +++ b/Metadata/Driver/YamlDriver.php @@ -2,7 +2,6 @@ namespace Vich\UploaderBundle\Metadata\Driver; -use Metadata\Driver\AbstractFileDriver; use Symfony\Component\Yaml\Yaml as YmlParser; use Vich\UploaderBundle\Metadata\ClassMetadata; @@ -12,22 +11,18 @@ * * @author Kévin Gomez */ -class Yaml extends AbstractFileDriver +class YamlDriver extends AbstractFileDriver { /** * {@inheritDoc} */ - protected function loadMetadataFromFile(\ReflectionClass $class, $file) + protected function loadMetadataFromFile($file, \ReflectionClass $class = null) { $config = $this->loadMappingFile($file); + $className = $this->guessClassName($file, $config, $class); + $metadata = new ClassMetadata($className); - if (!isset($config[$class->name])) { - throw new \RuntimeException(sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file)); - } - - $metadata = new ClassMetadata($class->name); - - foreach ($config[$class->name] as $field => $mappingData) { + foreach ($config[$className] as $field => $mappingData) { $fieldMetadata = array( 'mapping' => $mappingData['mapping'], 'propertyName' => $field, @@ -52,4 +47,17 @@ protected function getExtension() { return 'yml'; } + + protected function guessClassName($file, array $config, \ReflectionClass $class = null) + { + if ($class === null) { + return current(array_keys($config)); + } + + if (!isset($config[$class->name])) { + throw new \RuntimeException(sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file)); + } + + return $class->name; + } } diff --git a/Metadata/MetadataReader.php b/Metadata/MetadataReader.php new file mode 100644 index 00000000..a0203453 --- /dev/null +++ b/Metadata/MetadataReader.php @@ -0,0 +1,82 @@ + + */ +class MetadataReader +{ + /** + * @var MetadataFactoryInterface $reader + */ + protected $reader; + + /** + * Constructs a new instance of the MetadataReader. + * + * @param MetadataFactoryInterface $reader The "low-level" metadata reader. + */ + public function __construct(MetadataFactoryInterface $reader) + { + $this->reader = $reader; + } + + /** + * Tells if the given class is uploadable. + * + * @param string $class The class name to test (FQCN). + * + * @return bool + */ + public function isUploadable($class) + { + return $this->reader->getMetadataForClass($class) !== null; + } + + /** + * Returns a list of uploadable classes. + * + * @return array A list of class names. + */ + public function getUploadableClasses() + { + return $this->reader->getAllClassNames(); + } + + /** + * Attempts to read the uploadable fields. + * + * @param string $class The class name (FQCN). + * + * @return array A list of uploadable fields. + */ + public function getUploadableFields($class) + { + $metadata = $this->reader->getMetadataForClass($class); + $classMetadata = $metadata->classMetadata[$class]; + + return $classMetadata->fields; + } + + /** + * Attempts to read the mapping of a specified property. + * + * @param string $class The class (FQCN). + * @param string $field The field + * + * @return null|array The field mapping. + */ + public function getUploadableField($class, $field) + { + $fieldsMetadata = $this->getUploadableFields($class); + + return isset($fieldsMetadata[$field]) ? $fieldsMetadata[$field] : null; + } +} diff --git a/Naming/DirectoryNamerInterface.php b/Naming/DirectoryNamerInterface.php index a87a9d8c..0fbdac83 100644 --- a/Naming/DirectoryNamerInterface.php +++ b/Naming/DirectoryNamerInterface.php @@ -2,8 +2,10 @@ namespace Vich\UploaderBundle\Naming; +use Vich\UploaderBundle\Mapping\PropertyMapping; + /** - * NamerInterface. + * DirectoryNamerInterface. * * @author Kevin bond */ @@ -12,10 +14,10 @@ interface DirectoryNamerInterface /** * Creates a directory name for the file being uploaded. * - * @param object $obj The object the upload is attached to. - * @param string $field The name of the uploadable field to generate a name for. - * @param string $uploadDir The upload directory set in config + * @param Propertymapping $mapping The mapping to use to manipulate the given object. + * @param object $object The object the upload is attached to. + * * @return string The directory name. */ - public function directoryName($obj, $field, $uploadDir); + public function name(PropertyMapping $mapping, $object); } diff --git a/Naming/NamerInterface.php b/Naming/NamerInterface.php index fff4b833..b00a8909 100644 --- a/Naming/NamerInterface.php +++ b/Naming/NamerInterface.php @@ -2,6 +2,8 @@ namespace Vich\UploaderBundle\Naming; +use Vich\UploaderBundle\Mapping\PropertyMapping; + /** * NamerInterface. * @@ -12,9 +14,10 @@ interface NamerInterface /** * Creates a name for the file being uploaded. * - * @param object $obj The object the upload is attached to. - * @param string $field The name of the uploadable field to generate a name for. + * @param Propertymapping $mapping The mapping to use to manipulate the given object. + * @param object $object The object the upload is attached to. + * * @return string The file name. */ - public function name($obj, $field); + public function name(PropertyMapping $mapping, $object); } diff --git a/Naming/OrignameNamer.php b/Naming/OrignameNamer.php index 5955d8de..8958594e 100644 --- a/Naming/OrignameNamer.php +++ b/Naming/OrignameNamer.php @@ -2,8 +2,7 @@ namespace Vich\UploaderBundle\Naming; -use Symfony\Component\HttpFoundation\File\File; -use Symfony\Component\HttpFoundation\File\UploadedFile; +use Vich\UploaderBundle\Mapping\PropertyMapping; /** * OrignameNamer @@ -15,16 +14,11 @@ class OrignameNamer implements NamerInterface /** * {@inheritDoc} */ - public function name($obj, $field) + public function name(PropertyMapping $mapping, $object) { - $refObj = new \ReflectionObject($obj); + $file = $mapping->getFile($object); - $refProp = $refObj->getProperty($field); - $refProp->setAccessible(true); - - $file = $refProp->getValue($obj); - - /** @var $file UploadedFile */ + /** @var $file \Symfony\Component\HttpFoundation\File\UploadedFile */ return uniqid().'_'.$file->getClientOriginalName(); } diff --git a/Naming/UniqidNamer.php b/Naming/UniqidNamer.php index 3f3b86df..234e81f1 100644 --- a/Naming/UniqidNamer.php +++ b/Naming/UniqidNamer.php @@ -3,6 +3,7 @@ namespace Vich\UploaderBundle\Naming; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Vich\UploaderBundle\Mapping\PropertyMapping; /** * UniqidNamer @@ -14,14 +15,9 @@ class UniqidNamer implements NamerInterface /** * {@inheritDoc} */ - public function name($obj, $field) + public function name(PropertyMapping $mapping, $object) { - $refObj = new \ReflectionObject($obj); - - $refProp = $refObj->getProperty($field); - $refProp->setAccessible(true); - - $file = $refProp->getValue($obj); + $file = $mapping->getFile($object); $name = uniqid(); if ($extension = $this->getExtension($file)) { diff --git a/Resources/config/adapter.xml b/Resources/config/adapter.xml index af37c005..75f4f690 100644 --- a/Resources/config/adapter.xml +++ b/Resources/config/adapter.xml @@ -6,7 +6,9 @@ - + + + diff --git a/Resources/config/factory.xml b/Resources/config/factory.xml index 8e3c12ae..71e15e26 100644 --- a/Resources/config/factory.xml +++ b/Resources/config/factory.xml @@ -4,11 +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"> + + Vich\UploaderBundle\Mapping\CachedPropertyMappingFactory + + - + - + %vich_uploader.mappings% diff --git a/Resources/config/injector.xml b/Resources/config/injector.xml index a455a329..ebede39d 100644 --- a/Resources/config/injector.xml +++ b/Resources/config/injector.xml @@ -11,7 +11,6 @@ - diff --git a/Resources/config/listener.xml b/Resources/config/listener.xml index 785a7d70..56e8e460 100644 --- a/Resources/config/listener.xml +++ b/Resources/config/listener.xml @@ -6,13 +6,45 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/mapping.xml b/Resources/config/mapping.xml index 26141131..daa7c32a 100644 --- a/Resources/config/mapping.xml +++ b/Resources/config/mapping.xml @@ -12,19 +12,23 @@ - + - + - + + + + + - + @@ -43,7 +47,7 @@ - + diff --git a/Storage/AbstractStorage.php b/Storage/AbstractStorage.php index 62e4a936..49f1f86a 100644 --- a/Storage/AbstractStorage.php +++ b/Storage/AbstractStorage.php @@ -31,131 +31,119 @@ public function __construct(PropertyMappingFactory $factory) /** * Do real upload * - * @param UploadedFile $file - * @param string $dir - * @param string $name - * - * @return boolean + * @param PropertyMapping $mapping The mapping representing the current object. + * @param UploadedFile $file The file being uploaded. + * @param string $destinationPath The destination path of the file. */ - abstract protected function doUpload(UploadedFile $file, $dir, $name); + abstract protected function doUpload(PropertyMapping $mapping, UploadedFile $file, $destinationPath); /** - * {@inheritDoc} + * Do real remove + * + * @param PropertyMapping $mapping The mapping representing the current object. + * @param string $path The path of the file to remove. + * + * @return boolean Whether the file has been removed or not. */ - public function upload($obj) - { - $mappings = $this->factory->fromObject($obj); - foreach ($mappings as $mapping) { - $file = $mapping->getPropertyValue($obj); - - if ($file === null || !($file instanceof UploadedFile)) { - continue; - } - - if ($mapping->getDeleteOnUpdate() && $mapping->getFileNameProperty()->getValue($obj)) { - $name = $mapping->getFileNameProperty()->getValue($obj); - $dir = $mapping->getUploadDir($obj, $mapping->getProperty()->getName()); - - $this->doRemove($dir, $name); - } - - if ($mapping->hasNamer()) { - $name = $mapping->getNamer()->name($obj, $mapping->getProperty()->getName()); - } else { - $name = $file->getClientOriginalName(); - } - - $dir = $mapping->getUploadDir($obj, $mapping->getProperty()->getName()); - - $this->doUpload($file, $dir, $name); - - $mapping->getFileNameProperty()->setValue($obj, $name); - } - } + abstract protected function doRemove(PropertyMapping $mapping, $path); /** - * Do real remove + * Do resolve path * * @param string $dir * @param string $name * - * @return boolean + * @return string */ - abstract protected function doRemove($dir, $name); + abstract protected function doResolvePath($dir, $path); /** * {@inheritDoc} */ - public function remove($obj) + public function upload($object, PropertyMapping $mapping) { - $mappings = $this->factory->fromObject($obj); + $file = $mapping->getFile($object); + + if ($file === null || !($file instanceof UploadedFile)) { + return; + } - /** @var $mapping PropertyMapping */ - foreach ($mappings as $mapping) { - if (!$mapping->getDeleteOnRemove()) { - continue; - } + // keep the original name by default + $name = $file->getClientOriginalName(); - $name = $mapping->getFileNameProperty()->getValue($obj); + // but use the namer if there is one + if ($mapping->hasNamer()) { + $name = $mapping->getNamer()->name($mapping, $object); + } - if (null === $name) { - continue; - } + // update the filename + $mapping->setFileName($object, $name); - $dir = $mapping->getUploadDir($obj, $mapping->getProperty()->getName()); + // determine the upload directory to use + if ($mapping->hasDirectoryNamer()) { + $dir = $mapping->getDirectoryNamer()->name($mapping, $object); + $name = $dir . DIRECTORY_SEPARATOR . $name; - $this->doRemove($dir, $name); + // store the complete path in the filename + // @note: we do this because the FileInjector needs the + // directory, and the DirectoryNamer might need the File object + // to compute it + $mapping->setFileName($object, $name); } + + // and finalize the upload + $this->doUpload($mapping, $file, $name); } /** - * Do resolve path - * - * @param string $dir - * @param string $name - * - * @return string + * {@inheritDoc} */ - abstract protected function doResolvePath($dir, $name); + public function remove($object, PropertyMapping $mapping) + { + $name = $mapping->getFileName($object); + + if (null !== $name) { + $this->doRemove($mapping, $name); + } + } /** * {@inheritDoc} */ - public function resolvePath($obj, $field) + public function resolvePath($obj, $field, $className = null) { - list($mapping, $name) = $this->getFileNamePropertyValue($obj, $field); - $dir = $mapping->getUploadDir($obj, $field); + list($mapping, $name) = $this->getFileName($obj, $field, $className); - return $this->doResolvePath($dir, $name); + return $this->doResolvePath($mapping->getUploadDestination(), $name); } /** * {@inheritDoc} */ - public function resolveUri($obj, $field) + public function resolveUri($obj, $field, $className = null) { - list($mapping, $name) = $this->getFileNamePropertyValue($obj, $field); + list($mapping, $filename) = $this->getFileName($obj, $field, $className); $uriPrefix = $mapping->getUriPrefix(); - return $name ? ($uriPrefix . '/' . $name) : ''; + return $filename ? ($uriPrefix . '/' . $filename) : ''; } - protected function getFileNamePropertyValue($obj, $field) + protected function getFileName($obj, $field, $className = null) { - $mapping = $this->factory->fromField($obj, $field); + $mapping = $this->factory->fromField($obj, $field, $className); if (null === $mapping) { throw new \InvalidArgumentException(sprintf( 'Unable to find uploadable field named: "%s"', $field )); } - $value = $mapping->getFileNameProperty()->getValue($obj); - if ($value === null) { + $name = $mapping->getFileName($obj); + if ($name === null) { throw new \InvalidArgumentException(sprintf( 'Unable to get filename property value: "%s"', $field )); } - return array($mapping, $value); + return array($mapping, $name); } } diff --git a/Storage/FileSystemStorage.php b/Storage/FileSystemStorage.php index 858772d0..4e407307 100644 --- a/Storage/FileSystemStorage.php +++ b/Storage/FileSystemStorage.php @@ -4,6 +4,8 @@ use Symfony\Component\HttpFoundation\File\UploadedFile; +use Vich\UploaderBundle\Mapping\PropertyMapping; + /** * FileSystemStorage. * @@ -14,26 +16,20 @@ class FileSystemStorage extends AbstractStorage /** * {@inheritDoc} */ - protected function doUpload(UploadedFile $file, $dir, $name) + protected function doUpload(PropertyMapping $mapping, UploadedFile $file, $destinationPath) { - $uploadDir = $this->getUploadDirectory($dir, $name); - $fileName = basename($name); + $uploadDir = $this->getUploadDirectory($mapping->getUploadDestination(), $destinationPath); + $fileName = basename($destinationPath); return $file->move($uploadDir, $fileName); } /** - * Do real remove - * - * @param string $dir - * @param string $name - * - * @internal param object $obj - * @return boolean + * {@inheritDoc} */ - protected function doRemove($dir, $name) + protected function doRemove(PropertyMapping $mapping, $path) { - $file = $dir . DIRECTORY_SEPARATOR . $name; + $file = $mapping->getUploadDestination() . DIRECTORY_SEPARATOR . $path; return file_exists($file) ? unlink($file) : false; } @@ -49,17 +45,18 @@ protected function doResolvePath($dir, $name) /** * {@inheritDoc} */ - public function resolveUri($obj, $field) + public function resolveUri($obj, $field, $className = null) { - list($mapping, $name) = $this->getFileNamePropertyValue($obj, $field); + list($mapping, $name) = $this->getFileName($obj, $field, $className); $uriPrefix = $mapping->getUriPrefix(); - $parts = explode($uriPrefix, $this->convertWindowsDirectorySeparator($mapping->getUploadDir($obj, $field))); + $parts = explode($uriPrefix, $this->convertWindowsDirectorySeparator($mapping->getUploadDestination())); - return sprintf('%s/%s', $uriPrefix . array_pop($parts), $name); + return sprintf('%s/%s', $uriPrefix . array_pop($parts), $this->convertWindowsDirectorySeparator($name)); } /** * @param $string + * * @return string */ protected function convertWindowsDirectorySeparator($string) @@ -75,6 +72,7 @@ protected function convertWindowsDirectorySeparator($string) * * @param $dir * @param $name + * * @return string */ protected function getUploadDirectory($dir, $name) diff --git a/Storage/GaufretteStorage.php b/Storage/GaufretteStorage.php index 15b1e9b8..91f08251 100644 --- a/Storage/GaufretteStorage.php +++ b/Storage/GaufretteStorage.php @@ -3,15 +3,14 @@ namespace Vich\UploaderBundle\Storage; use Gaufrette\Exception\FileNotFound; -use Vich\UploaderBundle\Mapping\PropertyMappingFactory; - -use Symfony\Component\HttpFoundation\File\UploadedFile; - -use Knp\Bundle\GaufretteBundle\FilesystemMap; - use Gaufrette\Stream\Local as LocalStream; use Gaufrette\StreamMode; use Gaufrette\Adapter\MetadataSupporter; +use Knp\Bundle\GaufretteBundle\FilesystemMap; +use Symfony\Component\HttpFoundation\File\UploadedFile; + +use Vich\UploaderBundle\Mapping\PropertyMapping; +use Vich\UploaderBundle\Mapping\PropertyMappingFactory; /** * GaufretteStorage. @@ -33,9 +32,9 @@ class GaufretteStorage extends AbstractStorage /** * Constructs a new instance of FileSystemStorage. * - * @param \Vich\UploaderBundle\Mapping\PropertyMappingFactory $factory The factory. - * @param FilesystemMap $filesystemMap Gaufrete filesystem factory. - * @param string $protocol Gaufrette stream wrapper protocol. + * @param PropertyMappingFactory $factory The factory. + * @param FilesystemMap $filesystemMap Gaufrete filesystem factory. + * @param string $protocol Gaufrette stream wrapper protocol. */ public function __construct(PropertyMappingFactory $factory, FilesystemMap $filesystemMap, $protocol = 'gaufrette') { @@ -45,31 +44,23 @@ public function __construct(PropertyMappingFactory $factory, FilesystemMap $file $this->protocol = $protocol; } - /** - * Get filesystem adapter by key - * - * @param string $key - * - * @return \Gaufrette\Filesystem - */ - protected function getFilesystem($key) - { - return $this->filesystemMap->get($key); - } - /** * {@inheritDoc} */ - protected function doUpload(UploadedFile $file, $dir, $name) + protected function doUpload(PropertyMapping $mapping, UploadedFile $file, $destinationPath) { - $filesystem = $this->getFilesystem($dir); + $fs = $this->getFilesystem($mapping->getUploadDestination()); - if ($filesystem->getAdapter() instanceof MetadataSupporter) { - $filesystem->getAdapter()->setMetadata($name, array('contentType' => $file->getMimeType())); + if ($fs->getAdapter() instanceof MetadataSupporter) { + $fs->getAdapter()->setMetadata($destinationPath, array('contentType' => $file->getMimeType())); } + // just to make sure that $dir is created before we start writing + // @todo: find a better way to do this + $fs->write($destinationPath, 'test'); + $src = new LocalStream($file->getPathname()); - $dst = $filesystem->createStream($name); + $dst = $fs->createStream($destinationPath); $src->open(new StreamMode('rb+')); $dst->open(new StreamMode('wb+')); @@ -86,12 +77,12 @@ protected function doUpload(UploadedFile $file, $dir, $name) /** * {@inheritDoc} */ - protected function doRemove($dir, $name) + protected function doRemove(PropertyMapping $mapping, $path) { - $adapter = $this->getFilesystem($dir); + $fs = $this->getFilesystem($mapping->getUploadDestination()); try { - return $adapter->delete($name); + return $fs->delete($path); } catch (FileNotFound $e) { return false; } @@ -104,4 +95,16 @@ protected function doResolvePath($dir, $name) { return $this->protocol.'://' . $dir . '/' . $name; } + + /** + * Get filesystem adapter by key + * + * @param string $key + * + * @return \Gaufrette\Filesystem + */ + protected function getFilesystem($key) + { + return $this->filesystemMap->get($key); + } } diff --git a/Storage/StorageInterface.php b/Storage/StorageInterface.php index 9fa2ef65..f539eb75 100644 --- a/Storage/StorageInterface.php +++ b/Storage/StorageInterface.php @@ -2,6 +2,8 @@ namespace Vich\UploaderBundle\Storage; +use Vich\UploaderBundle\Mapping\PropertyMapping; + /** * StorageInterface. * @@ -10,38 +12,42 @@ interface StorageInterface { /** - * Uploads the files in the uploadable fields of the - * specified object according to the property configuration. + * Uploads the files in the uploadable fields of the specified object + * according to the property configuration. * * @param object $obj The object. */ - public function upload($obj); + public function upload($object, PropertyMapping $mapping); /** - * Removes the files associated with the object if configured to - * do so. + * Removes the files associated with the object if configured to do so. * * @param object $obj The object. */ - public function remove($obj); + public function remove($object, PropertyMapping $mapping); /** - * Resolves the path for a file based on the specified object - * and field name. + * Resolves the path for a file based on the specified object and field + * name. + * + * @param object $obj The object. + * @param string $field The field. + * @param string $className The object's class. Mandatory if $obj can't be + * used to determine it. * - * @param object $obj The object. - * @param string $field The field. * @return string The path. */ - public function resolvePath($obj, $field); + public function resolvePath($obj, $field, $className = null); /** - * Resolves the uri for any based on the specified object - * and field name. + * Resolves the uri for any based on the specified object and field name. + * + * @param object $obj The object. + * @param string $field The field. + * @param string $className The object's class. Mandatory if $obj can't be + * used to determine it. * - * @param object $obj The object. - * @param string $field The field. * @return string The uri. */ - public function resolveUri($obj, $field); + public function resolveUri($obj, $field, $className = null); } diff --git a/Templating/Helper/UploaderHelper.php b/Templating/Helper/UploaderHelper.php index 195d3814..72487487 100644 --- a/Templating/Helper/UploaderHelper.php +++ b/Templating/Helper/UploaderHelper.php @@ -41,12 +41,14 @@ public function getName() * Gets the public path for the file associated with the * object. * - * @param object $obj The object. - * @param string $field The field. + * @param object $obj The object. + * @param string $field The field. + * @param string $className The object's class. Mandatory if $obj can't be used to determine it. + * * @return string The public asset path. */ - public function asset($obj, $field) + public function asset($obj, $field, $className = null) { - return $this->storage->resolveUri($obj, $field); + return $this->storage->resolveUri($obj, $field, $className); } } diff --git a/Tests/Adapter/ODM/MongoDB/MongoDBAdapterTest.php b/Tests/Adapter/ODM/MongoDB/MongoDBAdapterTest.php index bf260d7b..e55d3e7d 100644 --- a/Tests/Adapter/ODM/MongoDB/MongoDBAdapterTest.php +++ b/Tests/Adapter/ODM/MongoDB/MongoDBAdapterTest.php @@ -13,59 +13,51 @@ */ class MongoDBAdapterTest extends \PHPUnit_Framework_TestCase { + public static function setUpBeforeClass() + { + if (!class_exists('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs')) { + self::markTestSkipped('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs does not exist.'); + } + } + /** - * Test the getObjectFromArgs method. + * Test the getObjectFromEvent method. */ - public function testGetObjectFromArgs() + public function testGetObjectFromEvent() { - if (!class_exists('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs')) { - $this->markTestSkipped('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs does not exist.'); - } else { - $entity = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); + $entity = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); - $args = $this->getMockBuilder('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs') - ->disableOriginalConstructor() - ->getMock(); - $args - ->expects($this->once()) - ->method('getDocument') - ->will($this->returnValue($entity)); + $args = $this->getMockBuilder('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs') + ->disableOriginalConstructor() + ->getMock(); + $args + ->expects($this->once()) + ->method('getDocument') + ->will($this->returnValue($entity)); - $adapter = new MongoDBAdapter(); + $adapter = new MongoDBAdapter(); - $this->assertEquals($entity, $adapter->getObjectFromArgs($args)); - } + $this->assertEquals($entity, $adapter->getObjectFromEvent($args)); } /** - * Tests the getReflectionClass method. + * @dataProvider entityProvider */ - public function testGetReflectionClass() + public function testGetClassName($entity, $expectedClassName) { - if (!interface_exists('Doctrine\ODM\MongoDB\Proxy\Proxy')) { - $this->markTestSkipped('Doctrine\ODM\MongoDB\Proxy\Proxy does not exist.'); - } else { - $obj = new DummyEntity(); - $adapter = new MongoDBAdapter(); - $class = $adapter->getReflectionClass($obj); + $adapter = new MongoDBAdapter(); - $this->assertEquals($class->getName(), get_class($obj)); - } + $this->assertEquals($expectedClassName, $adapter->getClassName($entity)); } - /** - * Tests the getReflectionClass method with a proxy. - */ - public function testGetReflectionClassProxy() + public function entityProvider() { - if (!interface_exists('Doctrine\ODM\MongoDB\Proxy\Proxy')) { - $this->markTestSkipped('Doctrine\ODM\MongoDB\Proxy\Proxy does not exist.'); - } else { - $obj = new DummyEntityProxyMongo(); - $adapter = new MongoDBAdapter(); - $class = $adapter->getReflectionClass($obj); + $classicEntity = new DummyEntity(); + $proxiedEntity = new DummyEntityProxyMongo(); - $this->assertEquals($class->getName(), get_parent_class($obj)); - } + return array( + array($classicEntity, 'Vich\UploaderBundle\Tests\DummyEntity'), + array($proxiedEntity, 'Vich\UploaderBundle\Tests\DummyEntity'), + ); } } diff --git a/Tests/Adapter/ORM/DoctrineORMAdapterTest.php b/Tests/Adapter/ORM/DoctrineORMAdapterTest.php index 2741deac..271ca787 100644 --- a/Tests/Adapter/ORM/DoctrineORMAdapterTest.php +++ b/Tests/Adapter/ORM/DoctrineORMAdapterTest.php @@ -13,59 +13,54 @@ */ class DoctrineORMAdapterTest extends \PHPUnit_Framework_TestCase { + public static function setUpBeforeClass() + { + if (!class_exists('Doctrine\ORM\Event\LifecycleEventArgs')) { + self::markTestSkipped('Doctrine\ORM\Event\LifecycleEventArgs does not exist.'); + } + } + /** - * Test the getObjectFromArgs method. + * Test the getObjectFromEvent method. */ - public function testGetObjectFromArgs() + public function testGetObjectFromEvent() { - if (!class_exists('Doctrine\ORM\Event\LifecycleEventArgs')) { - $this->markTestSkipped('Doctrine\ORM\Event\LifecycleEventArgs does not exist.'); - } else { - $entity = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); + $entity = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); - $args = $this->getMockBuilder('Doctrine\ORM\Event\LifecycleEventArgs') - ->disableOriginalConstructor() - ->getMock(); - $args - ->expects($this->once()) - ->method('getEntity') - ->will($this->returnValue($entity)); + $args = $this->getMockBuilder('Doctrine\ORM\Event\LifecycleEventArgs') + ->disableOriginalConstructor() + ->getMock(); + $args + ->expects($this->once()) + ->method('getEntity') + ->will($this->returnValue($entity)); - $adapter = new DoctrineORMAdapter(); + $adapter = new DoctrineORMAdapter(); - $this->assertEquals($entity, $adapter->getObjectFromArgs($args)); - } + $this->assertEquals($entity, $adapter->getObjectFromEvent($args)); } /** * Tests the getReflectionClass method. */ - public function testGetReflectionClass() + public function testGetClassName() { - if (!interface_exists('Doctrine\ORM\Proxy\Proxy')) { - $this->markTestSkipped('Doctrine\ORM\Proxy\Proxy does not exist.'); - } else { - $obj = new DummyEntity(); - $adapter = new DoctrineORMAdapter(); - $class = $adapter->getReflectionClass($obj); + $obj = new DummyEntity(); + $adapter = new DoctrineORMAdapter(); + $class = $adapter->getClassName($obj); - $this->assertEquals($class->getName(), get_class($obj)); - } + $this->assertEquals('Vich\UploaderBundle\Tests\DummyEntity', $class); } /** * Tests the getReflectionClass method with a proxy. */ - public function testGetReflectionClassProxy() + public function testGetClassNameWithProxy() { - if (!interface_exists('Doctrine\ORM\Proxy\Proxy')) { - $this->markTestSkipped('Doctrine\ORM\Proxy\Proxy does not exist.'); - } else { - $obj = new DummyEntityProxyORM(); - $adapter = new DoctrineORMAdapter(); - $class = $adapter->getReflectionClass($obj); + $obj = new DummyEntityProxyORM(); + $adapter = new DoctrineORMAdapter(); + $class = $adapter->getClassName($obj); - $this->assertEquals($class->getName(), get_parent_class($obj)); - } + $this->assertEquals('Vich\UploaderBundle\Tests\DummyEntity', $class); } } diff --git a/Tests/Adapter/Propel/PropelAdapterTest.php b/Tests/Adapter/Propel/PropelAdapterTest.php new file mode 100644 index 00000000..1a7b5623 --- /dev/null +++ b/Tests/Adapter/Propel/PropelAdapterTest.php @@ -0,0 +1,46 @@ + + */ +class PropelAdapterTest extends \PHPUnit_Framework_TestCase +{ + protected $adapter; + + public function setUp() + { + $this->adapter = new PropelAdapter(); + } + + public function testGetObjectFromEvent() + { + $event = $this->getMock('\Symfony\Component\EventDispatcher\GenericEvent'); + $event + ->expects($this->once()) + ->method('getSubject') + ->will($this->returnValue(42)); + + $this->assertSame(42, $this->adapter->getObjectFromEvent($event)); + } + + public function testGetClassName() + { + $obj = new \DateTime(); + + $this->assertSame('DateTime', $this->adapter->getClassName($obj)); + } + + public function testRecomputeChangeset() + { + $event = $this->getMock('\Symfony\Component\EventDispatcher\GenericEvent'); + + // does nothing but should be callable + $this->adapter->recomputeChangeSet($event); + } +} diff --git a/Tests/EventListener/Doctrine/CleanListenerTest.php b/Tests/EventListener/Doctrine/CleanListenerTest.php new file mode 100644 index 00000000..8a53d02b --- /dev/null +++ b/Tests/EventListener/Doctrine/CleanListenerTest.php @@ -0,0 +1,80 @@ + + */ +class CleanListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new CleanListener(self::MAPPING_NAME, $this->adapter, $this->metadata, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertSame(array('preUpdate'), $events); + } + + /** + * Test the preUpdate method. + */ + public function testPreUpdate() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(true)); + + $this->handler + ->expects($this->once()) + ->method('clean') + ->with($this->object, self::MAPPING_NAME); + + $this->adapter + ->expects($this->once()) + ->method('recomputeChangeSet') + ->with($this->event); + + $this->listener->preUpdate($this->event); + } + + + /** + * Test that preUpdate skips non uploadable entity. + */ + public function testPreUpdateSkipsNonUploadable() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(false)); + + $this->handler + ->expects($this->never()) + ->method('clean'); + + $this->adapter + ->expects($this->never()) + ->method('recomputeChangeSet'); + + $this->listener->preUpdate($this->event); + } +} diff --git a/Tests/EventListener/Doctrine/InjectListenerTest.php b/Tests/EventListener/Doctrine/InjectListenerTest.php new file mode 100644 index 00000000..85372687 --- /dev/null +++ b/Tests/EventListener/Doctrine/InjectListenerTest.php @@ -0,0 +1,70 @@ + + */ +class InjectListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new InjectListener(self::MAPPING_NAME, $this->adapter, $this->metadata, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertSame(array('postLoad'), $events); + } + + /** + * Test the postLoad method. + */ + public function testPostLoad() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(true)); + + $this->handler + ->expects($this->once()) + ->method('hydrate') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->postLoad($this->event); + } + + /** + * Test that postLoad skips non uploadable entity. + */ + public function testPostLoadSkipsNonUploadable() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(false)); + + $this->handler + ->expects($this->never()) + ->method('hydrate', self::MAPPING_NAME); + + $this->listener->postLoad($this->event); + } +} diff --git a/Tests/EventListener/Doctrine/ListenerTestCase.php b/Tests/EventListener/Doctrine/ListenerTestCase.php new file mode 100644 index 00000000..ab85610e --- /dev/null +++ b/Tests/EventListener/Doctrine/ListenerTestCase.php @@ -0,0 +1,119 @@ + + */ +class ListenerTestCase extends \PHPUnit_Framework_TestCase +{ + const MAPPING_NAME = 'dummy_mapping'; + + /** + * @var \Vich\UploaderBundle\Adapter\AdapterInterface $adapter + */ + protected $adapter; + + /** + * @var \Vich\UploaderBundle\Metadata\MetadataReader $metadata + */ + protected $metadata; + + /** + * @var \Vich\UploaderBundle\Handler\UploadHandler $handler + */ + protected $handler; + + /** + * @var EventArgs + */ + protected $event; + + /** + * @var DummyEntity + */ + protected $object; + + /** + * @var DoctrineUploaderListener + */ + protected $listener; + + /** + * Sets up the test + */ + public function setUp() + { + $this->adapter = $this->getAdapterMock(); + $this->metadata = $this->getMetadataReaderMock(); + $this->handler = $this->getHandlerMock(); + $this->object = new DummyEntity(); + $this->event = $this->getEventMock(); + + // the adapter is always used to return the object + $this->adapter + ->expects($this->any()) + ->method('getObjectFromEvent') + ->with($this->event) + ->will($this->returnValue($this->object)); + + // in these tests, the adapter is always used with the same object + $this->adapter + ->expects($this->any()) + ->method('getClassName') + ->will($this->returnValue(get_class($this->object))); + } + + /** + * Creates a mock adapter. + * + * @return \Vich\UploaderBundle\Adapter\AdapterInterface The mock adapter. + */ + protected function getAdapterMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Adapter\AdapterInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Creates a mock metadata reader. + * + * @return \Vich\UploaderBundle\Metadata\MetadataReader The mock metadata reader. + */ + protected function getMetadataReaderMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Metadata\MetadataReader') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Creates a mock handler. + * + * @return \Vich\UploaderBundle\Handler\UploadHandler The handler mock. + */ + protected function getHandlerMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Handler\UploadHandler') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Creates a mock doctrine event + * + * @return \Doctrine\Common\EventArgs + */ + protected function getEventMock() + { + return $this->getMockBuilder('Doctrine\Common\EventArgs') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/Tests/EventListener/Doctrine/RemoveListenerTest.php b/Tests/EventListener/Doctrine/RemoveListenerTest.php new file mode 100644 index 00000000..938e1e86 --- /dev/null +++ b/Tests/EventListener/Doctrine/RemoveListenerTest.php @@ -0,0 +1,70 @@ + + */ +class RemoveListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new RemoveListener(self::MAPPING_NAME, $this->adapter, $this->metadata, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertSame(array('postRemove'), $events); + } + + /** + * Test the postRemove method. + */ + public function testPostRemove() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(true)); + + $this->handler + ->expects($this->once()) + ->method('delete') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->postRemove($this->event); + } + + /** + * Test that postRemove skips non uploadable entity. + */ + public function testPostRemoveSkipsNonUploadable() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(false)); + + $this->handler + ->expects($this->never()) + ->method('delete'); + + $this->listener->postRemove($this->event); + } +} diff --git a/Tests/EventListener/Doctrine/UploadListenerTest.php b/Tests/EventListener/Doctrine/UploadListenerTest.php new file mode 100644 index 00000000..4bdde089 --- /dev/null +++ b/Tests/EventListener/Doctrine/UploadListenerTest.php @@ -0,0 +1,116 @@ + + */ +class UploadListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new UploadListener(self::MAPPING_NAME, $this->adapter, $this->metadata, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertSame(array('prePersist', 'preUpdate'), $events); + } + + /** + * Tests the prePersist method. + */ + public function testPrePersist() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(true)); + + $this->handler + ->expects($this->once()) + ->method('upload') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->prePersist($this->event); + } + + /** + * Tests that prePersist skips non-uploadable entity. + */ + public function testPrePersistSkipsNonUploadable() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(false)); + + $this->handler + ->expects($this->never()) + ->method('upload'); + + $this->listener->prePersist($this->event); + } + + /** + * Test the preUpdate method. + */ + public function testPreUpdate() + { + $this->adapter + ->expects($this->once()) + ->method('recomputeChangeSet') + ->with($this->event); + + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(true)); + + $this->handler + ->expects($this->once()) + ->method('upload') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->preUpdate($this->event); + } + + /** + * Test that preUpdate skips non uploadable entity. + */ + public function testPreUpdateSkipsNonUploadable() + { + $this->metadata + ->expects($this->once()) + ->method('isUploadable') + ->with('Vich\UploaderBundle\Tests\DummyEntity') + ->will($this->returnValue(false)); + + $this->adapter + ->expects($this->never()) + ->method('recomputeChangeSet'); + + $this->handler + ->expects($this->never()) + ->method('upload'); + + $this->listener->preUpdate($this->event); + } +} diff --git a/Tests/EventListener/Propel/CleanListenerTest.php b/Tests/EventListener/Propel/CleanListenerTest.php new file mode 100644 index 00000000..1e5090df --- /dev/null +++ b/Tests/EventListener/Propel/CleanListenerTest.php @@ -0,0 +1,43 @@ + + */ +class CleanListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new CleanListener(self::MAPPING_NAME, $this->adapter, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertArrayHasKey('propel.pre_update', $events); + } + + public function testOnUpload() + { + $this->handler + ->expects($this->once()) + ->method('clean') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->onUpload($this->event); + } +} diff --git a/Tests/EventListener/Propel/InjectListenerTest.php b/Tests/EventListener/Propel/InjectListenerTest.php new file mode 100644 index 00000000..07830edd --- /dev/null +++ b/Tests/EventListener/Propel/InjectListenerTest.php @@ -0,0 +1,43 @@ + + */ +class InjectListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new InjectListener(self::MAPPING_NAME, $this->adapter, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertArrayHasKey('propel.post_hydrate', $events); + } + + public function testOnHydrate() + { + $this->handler + ->expects($this->once()) + ->method('hydrate') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->onHydrate($this->event); + } +} diff --git a/Tests/EventListener/Propel/ListenerTestCase.php b/Tests/EventListener/Propel/ListenerTestCase.php new file mode 100644 index 00000000..51c9a8dc --- /dev/null +++ b/Tests/EventListener/Propel/ListenerTestCase.php @@ -0,0 +1,100 @@ + + */ +class ListenerTestCase extends \PHPUnit_Framework_TestCase +{ + const MAPPING_NAME = 'dummy_mapping'; + + /** + * @var \Vich\UploaderBundle\Adapter\AdapterInterface $adapter + */ + protected $adapter; + + /** + * @var \Vich\UploaderBundle\Handler\UploadHandler $handler + */ + protected $handler; + + /** + * @var PropelUploaderListener $listener + */ + protected $listener; + + /** + * @var EventArgs + */ + protected $event; + + /** + * @var DummyEntity + */ + protected $object; + + /** + * Sets up the test + */ + public function setUp() + { + $this->adapter = $this->getAdapterMock(); + $this->handler = $this->getHandlerMock(); + $this->object = new DummyEntity(); + $this->event = $this->getEventMock(); + + // the adapter is always used to return the object + $this->adapter + ->expects($this->any()) + ->method('getObjectFromEvent') + ->with($this->event) + ->will($this->returnValue($this->object)); + + // in these tests, the adapter is always used with the same object + $this->adapter + ->expects($this->any()) + ->method('getClassName') + ->will($this->returnValue(get_class($this->object))); + } + + /** + * Creates a mock adapter. + * + * @return \Vich\UploaderBundle\Adapter\AdapterInterface The mock adapter. + */ + protected function getAdapterMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Adapter\AdapterInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Creates a mock handler. + * + * @return \Vich\UploaderBundle\Handler\UploadHandler The handler mock. + */ + protected function getHandlerMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Handler\UploadHandler') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Creates a mock event. + * + * @return \Symfony\Component\EventDispatcher\GenericEvent The mock event. + */ + protected function getEventMock() + { + return $this->getMockBuilder('\Symfony\Component\EventDispatcher\GenericEvent') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/Tests/EventListener/Propel/RemoveListenerTest.php b/Tests/EventListener/Propel/RemoveListenerTest.php new file mode 100644 index 00000000..39b8411a --- /dev/null +++ b/Tests/EventListener/Propel/RemoveListenerTest.php @@ -0,0 +1,43 @@ + + */ +class RemoveListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new RemoveListener(self::MAPPING_NAME, $this->adapter, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertArrayHasKey('propel.post_delete', $events); + } + + public function testOnDelete() + { + $this->handler + ->expects($this->once()) + ->method('delete') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->onDelete($this->event); + } +} diff --git a/Tests/EventListener/Propel/UploadListenerTest.php b/Tests/EventListener/Propel/UploadListenerTest.php new file mode 100644 index 00000000..1a1b64f0 --- /dev/null +++ b/Tests/EventListener/Propel/UploadListenerTest.php @@ -0,0 +1,44 @@ + + */ +class UploadListenerTest extends ListenerTestCase +{ + /** + * Sets up the test + */ + public function setUp() + { + parent::setUp(); + + $this->listener = new UploadListener(self::MAPPING_NAME, $this->adapter, $this->handler); + } + + /** + * Test the getSubscribedEvents method. + */ + public function testGetSubscribedEvents() + { + $events = $this->listener->getSubscribedEvents(); + + $this->assertArrayHasKey('propel.pre_update', $events); + $this->assertArrayHasKey('propel.pre_insert', $events); + } + + public function testOnUpload() + { + $this->handler + ->expects($this->once()) + ->method('upload') + ->with($this->object, self::MAPPING_NAME); + + $this->listener->onUpload($this->event); + } +} diff --git a/Tests/EventListener/UploaderListenerTest.php b/Tests/EventListener/UploaderListenerTest.php deleted file mode 100644 index 885d1bb6..00000000 --- a/Tests/EventListener/UploaderListenerTest.php +++ /dev/null @@ -1,426 +0,0 @@ - - */ -class UploaderListenerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Vich\UploaderBundle\Adapter\AdapterInterface $adapter - */ - protected $adapter; - - /** - * @var \Vich\UploaderBundle\Mapping\MappingReader $mapping - */ - protected $mapping; - - /** - * @var \Vich\UploaderBundle\Storage\StorageInterface $storage - */ - protected $storage; - - /** - * @var \Vich\UploaderBundle\Injector\FileInjectorInterface $injector - */ - protected $injector; - - /** - * Sets up the test - */ - public function setUp() - { - $this->adapter = $this->getAdapterMock(); - $this->mapping = $this->getMappingMock(); - $this->storage = $this->getStorageMock(); - $this->injector = $this->getInjectorMock(); - } - - /** - * Test the getSubscribedEvents method. - */ - public function testGetSubscribedEvents() - { - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $events = $listener->getSubscribedEvents(); - - $this->assertTrue(in_array('prePersist', $events)); - $this->assertTrue(in_array('preUpdate', $events)); - $this->assertTrue(in_array('postLoad', $events)); - $this->assertTrue(in_array('postRemove', $events)); - } - - /** - * Tests the prePersist method. - */ - public function testPrePersist() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(true)); - - $this->storage - ->expects($this->once()) - ->method('upload') - ->with($obj); - - $this->injector - ->expects($this->once()) - ->method('injectFiles') - ->with($obj); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->prePersist($args); - } - - /** - * Tests that prePersist skips non-uploadable entity. - */ - public function testPrePersistSkipsNonUploadable() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(false)); - - $this->storage - ->expects($this->never()) - ->method('upload'); - - $this->injector - ->expects($this->never()) - ->method('injectFiles'); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->prePersist($args); - } - - /** - * Test the preUpdate method. - */ - public function testPreUpdate() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->adapter - ->expects($this->once()) - ->method('recomputeChangeSet') - ->with($args); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(true)); - - $this->storage - ->expects($this->once()) - ->method('upload') - ->with($obj); - - $this->injector - ->expects($this->once()) - ->method('injectFiles') - ->with($obj); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->preUpdate($args); - } - - /** - * Test that preUpdate skips non uploadable entity. - */ - public function testPreUpdateSkipsNonUploadable() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(false)); - - $this->storage - ->expects($this->never()) - ->method('upload'); - - $this->adapter - ->expects($this->never()) - ->method('recomputeChangeSet'); - - $this->injector - ->expects($this->never()) - ->method('injectFiles'); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->preUpdate($args); - } - - /** - * Test the postLoad method. - */ - public function testPostLoad() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(true)); - - $this->injector - ->expects($this->once()) - ->method('injectFiles') - ->with($obj); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->postLoad($args); - } - - /** - * Test that postLoad skips non uploadable entity. - */ - public function testPostLoadSkipsNonUploadable() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(false)); - - $this->injector - ->expects($this->never()) - ->method('injectFiles'); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->postLoad($args); - } - - /** - * Test the postRemove method. - */ - public function testPostRemove() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(true)); - - $this->storage - ->expects($this->once()) - ->method('remove') - ->with($obj); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->postRemove($args); - } - - /** - * Test that postRemove skips non uploadable entity. - */ - public function testPostRemoveSkipsNonUploadable() - { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - - $args = $this->getMockBuilder('Doctrine\Common\EventArgs') - ->disableOriginalConstructor() - ->getMock(); - - $this->adapter - ->expects($this->once()) - ->method('getObjectFromArgs') - ->will($this->returnValue($obj)); - - $this->adapter - ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); - - $this->mapping - ->expects($this->once()) - ->method('isUploadable') - ->with($class) - ->will($this->returnValue(false)); - - $this->storage - ->expects($this->never()) - ->method('remove'); - - $listener = new UploaderListener($this->adapter, $this->mapping, $this->storage, $this->injector); - $listener->postRemove($args); - } - - /** - * Creates a mock adapter. - * - * @return \Vich\UploaderBundle\Adapter\AdapterInterface The mock adapter. - */ - protected function getAdapterMock() - { - return $this->getMockBuilder('Vich\UploaderBundle\Adapter\AdapterInterface') - ->disableOriginalConstructor() - ->getMock(); - } - - /** - * Creates a mock mapping reader. - * - * @return \Vich\UploaderBundle\Mapping\MappingReader The mock mapping reader. - */ - protected function getMappingMock() - { - return $this->getMockBuilder('Vich\UploaderBundle\Mapping\MappingReader') - ->disableOriginalConstructor() - ->getMock(); - } - - /** - * Creates a mock storage. - * - * @return \Vich\UploaderBundle\Storage\StorageInterface The mock storage. - */ - protected function getStorageMock() - { - return $this->getMockBuilder('Vich\UploaderBundle\Storage\StorageInterface') - ->disableOriginalConstructor() - ->getMock(); - } - - /** - * Creates a mock injector. - * - * @return \Vich\UploaderBundle\Injector\FileInjectorInterface The mock injector. - */ - protected function getInjectorMock() - { - return $this->getMockBuilder('Vich\UploaderBundle\Injector\FileInjectorInterface') - ->disableOriginalConstructor() - ->getMock(); - } -} diff --git a/Tests/Fixtures/file.txt b/Tests/Fixtures/file.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/Tests/Handler/UploadHandlerTest.php b/Tests/Handler/UploadHandlerTest.php new file mode 100644 index 00000000..4c7ade74 --- /dev/null +++ b/Tests/Handler/UploadHandlerTest.php @@ -0,0 +1,412 @@ + + */ +class UploadHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FileInjectorInterface + */ + protected $injector; + + /** + * @var PropertyMappingFactory + */ + protected $factory; + + /** + * @var UploadHandler + */ + protected $storage; + + /** + * @var UploadHandler + */ + protected $handler; + + /** + * @var DummyEntity + */ + protected $object; + + /** + * Sets up the test + */ + public function setUp() + { + $this->factory = $this->getPropertyMappingFactoryMock(); + $this->injector = $this->getFileInjectorMock(); + $this->storage = $this->getStorageMock(); + $this->object = new DummyEntity(); + + $this->handler = new UploadHandler($this->factory, $this->storage, $this->injector); + } + + public function testUploadDoesNothingIfMappingDoesntExistForObject() + { + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(false)); + + $this->storage + ->expects($this->never()) + ->method('upload'); + + $this->injector + ->expects($this->never()) + ->method('injectFile'); + + $this->handler->upload($this->object, 'dummy_mapping'); + } + + /** + * @dataProvider invalidUploadedFileProvider + */ + public function testUploadDoesNothingIfNoFileIsUploaded($file) + { + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($this->object) + ->will($this->returnValue($file)); + + $this->storage + ->expects($this->never()) + ->method('upload'); + + $this->injector + ->expects($this->never()) + ->method('injectFile'); + + $this->handler->upload($this->object, 'dummy_mapping'); + } + + public function testUpload() + { + $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($this->object) + ->will($this->returnValue($file)); + + $this->storage + ->expects($this->once()) + ->method('upload') + ->with($this->object, $mapping); + + $this->injector + ->expects($this->once()) + ->method('injectFile') + ->with($this->object, $mapping); + + $this->handler->upload($this->object, 'dummy_mapping'); + } + + public function testCleanDoesNothingIfMappingDoesntExistForObject() + { + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(false)); + + $this->storage + ->expects($this->never()) + ->method('remove'); + + $this->handler->clean($this->object, 'dummy_mapping'); + } + + public function testCleanDoesNothingIfNoOldFileIsPresent() + { + $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($this->object) + ->will($this->returnValue($file)); + $mapping + ->expects($this->once()) + ->method('getFileName') + ->with($this->object) + ->will($this->returnValue(null)); + + $this->storage + ->expects($this->never()) + ->method('remove'); + + $this->handler->clean($this->object, 'dummy_mapping'); + } + + /** + * @dataProvider invalidUploadedFileProvider + */ + public function testCleanDoesNothingIfNoFileIsUploaded($file) + { + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($this->object) + ->will($this->returnValue($file)); + + $this->storage + ->expects($this->never()) + ->method('remove'); + + $this->handler->clean($this->object, 'dummy_mapping'); + } + + public function invalidUploadedFileProvider() + { + $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File') + ->disableOriginalConstructor() + ->getMock(); + + return array( + array(null), + array('lala'), + array(new \DateTime()), + array($file), + ); + } + + public function testClean() + { + $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->disableOriginalConstructor() + ->getMock(); + + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($this->object) + ->will($this->returnValue($file)); + $mapping + ->expects($this->once()) + ->method('getFileName') + ->with($this->object) + ->will($this->returnValue('foo.txt')); + + $this->storage + ->expects($this->once()) + ->method('remove') + ->with($this->object, $mapping); + + $this->handler->clean($this->object, 'dummy_mapping'); + } + + public function testHydrateDoesNothingIfMappingDoesntExistForObject() + { + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(false)); + + $this->injector + ->expects($this->never()) + ->method('injectFile'); + + $this->handler->hydrate($this->object, 'dummy_mapping'); + } + + public function testHydrate() + { + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $this->injector + ->expects($this->once()) + ->method('injectFile') + ->with($this->object, $mapping); + + $this->handler->hydrate($this->object, 'dummy_mapping'); + } + + public function testDeleteDoesNothingIfMappingDoesntExistForObject() + { + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(false)); + + $this->injector + ->expects($this->never()) + ->method('remove'); + + $this->handler->delete($this->object, 'dummy_mapping'); + } + + public function testDelete() + { + $mapping = $this->getPropertyMappingMock(); + + $this->factory + ->expects($this->once()) + ->method('hasMapping') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue(true)); + + $this->factory + ->expects($this->once()) + ->method('fromName') + ->with($this->object, 'dummy_mapping') + ->will($this->returnValue($mapping)); + + $this->storage + ->expects($this->once()) + ->method('remove') + ->with($this->object, $mapping); + + $this->handler->delete($this->object, 'dummy_mapping'); + } + + /** + * Creates a mock property mapping factory + * + * @return \Vich\UploaderBundle\Mapping\PropertyMappingFactory + */ + protected function getPropertyMappingFactoryMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMappingFactory') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Gets a mock storage. + * + * @return \Vich\UploaderBundle\Storage\StorageInterface + */ + protected function getStorageMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Storage\StorageInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Gets a mock file injector. + * + * @return \Vich\UploaderBundle\Injector\FileInjectorInterface + */ + protected function getFileInjectorMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Injector\FileInjectorInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Gets a mock property mapping. + * + * @return \Vich\UploaderBundle\Mapping\PropertyMapping + */ + protected function getPropertyMappingMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/Tests/Injector/FileInjectorTest.php b/Tests/Injector/FileInjectorTest.php index 38d8017f..f84629ec 100644 --- a/Tests/Injector/FileInjectorTest.php +++ b/Tests/Injector/FileInjectorTest.php @@ -2,36 +2,48 @@ namespace Vich\UploaderBundle\Tests\Injector; -use Vich\UploaderBundle\Injector\FileInjector; +use org\bovigo\vfs\vfsStream; use Symfony\Component\HttpFoundation\File\File; + +use Vich\UploaderBundle\Injector\FileInjector; use Vich\UploaderBundle\Tests\DummyEntity; /** * FileInjectorTest. * - * @todo use vfsStream (http://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mocking-the-filesystem) - * * @author Dustin Dobervich */ class FileInjectorTest extends \PHPUnit_Framework_TestCase { /** - * @var Vich\UploaderBundle\Mapping\PropertyMappingFactory $factory + * @var \Vich\UploaderBundle\Storage\StorageInterface $storage */ - protected $factory; + protected $storage; /** - * @var Vich\UploaderBundle\Storage\GaufretteStorage $storage + * @var FileInjector */ - protected $storage; + protected $injector; + + /** + * @var vfsStreamDirectory + */ + protected $root; /** * Sets up the test. */ public function setUp() { - $this->factory = $this->getMockMappingFactory(); $this->storage = $this->getMockStorage(); + $this->root = vfsStream::setup('vich_uploader_bundle', null, array( + 'uploads' => array( + 'file.txt' => 'some content', + 'image.png' => 'some content', + ), + )); + + $this->injector = new FileInjector($this->storage); } /** @@ -39,224 +51,77 @@ public function setUp() */ public function testInjectsOneFile() { - $uploadDir = __DIR__ . '/..'; - $name = 'file.txt'; - - file_put_contents(sprintf('%s/%s', $uploadDir, $name), ''); - + $filePropertyName = 'file'; $obj = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('setValue'); - - $fileMapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') - ->disableOriginalConstructor() - ->getMock(); + $fileMapping = $this->getPropertyMappingMock(); $fileMapping ->expects($this->once()) - ->method('getInjectOnLoad') - ->will($this->returnValue(true)); + ->method('getFilePropertyName') + ->will($this->returnValue($filePropertyName)); $fileMapping - ->expects($this->exactly(2)) - ->method('getProperty') - ->will($this->returnValue($prop)); - - $this->factory ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($fileMapping))); + ->method('setFile') + ->with($this->equalTo($obj), $this->callback(function ($file) { + return $file instanceof File; + })); $this->storage ->expects($this->once()) ->method('resolvePath') - ->will($this->returnValue($uploadDir)); + ->with($this->equalTo($obj), $this->equalTo($filePropertyName)) + ->will($this->returnValue($this->getUploadDir() . DIRECTORY_SEPARATOR . 'file.txt')); - $inject = new FileInjector($this->factory, $this->storage); - $inject->injectFiles($obj); - - unlink(sprintf('%s/%s', $uploadDir, $name)); + $this->injector->injectFile($obj, $fileMapping); } - /** - * Test inject two files. - */ - public function testInjectTwoFiles() - { - $uploadDir = __DIR__ . '/..'; - $fileName = 'file.txt'; - $imageName = 'image.txt'; - - file_put_contents(sprintf('%s/%s', $uploadDir, $fileName), ''); - file_put_contents(sprintf('%s/%s', $uploadDir, $imageName), ''); - - $obj = $this->getMock('Vich\UploaderBundle\Tests\TwoFieldsDummyEntity'); - - $fileProp = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $fileProp - ->expects($this->once()) - ->method('setValue'); - - $imageProp = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $imageProp - ->expects($this->once()) - ->method('setValue'); - - $fileMapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') - ->disableOriginalConstructor() - ->getMock(); - $fileMapping - ->expects($this->once()) - ->method('getInjectOnLoad') - ->will($this->returnValue(true)); - $fileMapping - ->expects($this->exactly(2)) - ->method('getProperty') - ->will($this->returnValue($fileProp)); - - $imageMapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') - ->disableOriginalConstructor() - ->getMock(); - $imageMapping - ->expects($this->once()) - ->method('getInjectOnLoad') - ->will($this->returnValue(true)); - $imageMapping - ->expects($this->exactly(2)) - ->method('getProperty') - ->will($this->returnValue($imageProp)); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($fileMapping, $imageMapping))); - - $this->storage - ->expects($this->exactly(2)) - ->method('resolvePath') - ->will($this->returnValue($uploadDir)); - - $inject = new FileInjector($this->factory, $this->storage); - $inject->injectFiles($obj); - - unlink(sprintf('%s/%s', $uploadDir, $fileName)); - unlink(sprintf('%s/%s', $uploadDir, $imageName)); - } - - /** - * Test injection is skipped if inject_on_load is configured - * to false. - */ - public function testInjectionIsSkippedIfNotConfigured() - { - $obj = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); - - $fileMapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') - ->disableOriginalConstructor() - ->getMock(); - - $fileMapping - ->expects($this->once()) - ->method('getInjectOnLoad') - ->will($this->returnValue(false)); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($fileMapping))); - - $inject = new FileInjector($this->factory, $this->storage); - $inject->injectFiles($obj); - - $this->assertEquals(null, $obj->getFile()); - } - - /** - * Test that if the file name property returns a null value - * then no file is injected. - */ public function testPropertyIsNullWhenFileNamePropertyIsNull() { - $uploadDir = __DIR__ . '/..'; - $obj = $this->getMock('Vich\UploaderBundle\Tests\DummyEntity'); - $fileMapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') - ->disableOriginalConstructor() - ->getMock(); - + $fileMapping = $this->getPropertyMappingMock(); $fileMapping ->expects($this->once()) - ->method('getInjectOnLoad') - ->will($this->returnValue(true)); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - - $prop - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue('test_adapter')); - - $prop - ->expects($this->once()) - ->method('setValue'); - + ->method('getFilePropertyName') + ->will($this->returnValue('file')); $fileMapping - ->expects($this->exactly(2)) - ->method('getProperty') - ->will($this->returnValue($prop)); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($fileMapping))); + ->expects($this->never()) + ->method('setFile'); $this->storage ->expects($this->once()) ->method('resolvePath') - ->will($this->returnValue($uploadDir)); - - $inject = new FileInjector($this->factory, $this->storage); - $inject->injectFiles($obj); + ->will($this->throwException(new \InvalidArgumentException)); - $this->assertEquals(null, $obj->getFile()); + $this->injector->injectFile($obj, $fileMapping); } /** - * Gets a mock mapping factory. + * Gets a mocked property mapping. * - * @return Vich\UploaderBundle\Mapping\PropertyMappingFactory The factory. + * @return \Vich\UploaderBundle\Mapping\PropertyMapping The property. */ - protected function getMockMappingFactory() + protected function getPropertyMappingMock() { - return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMappingFactory') - ->disableOriginalConstructor() - ->getMock(); + return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') + ->disableOriginalConstructor() + ->getMock(); } /** * Gets a mock storage. * - * @return Vich\UploaderBundle\Storage\GaufretteStorage Storage + * @return \Vich\UploaderBundle\Storage\StorageInterface */ protected function getMockStorage() { - return $this->getMockBuilder('Vich\UploaderBundle\Storage\GaufretteStorage') - ->disableOriginalConstructor() - ->getMock() - ; + return $this->getMockBuilder('Vich\UploaderBundle\Storage\StorageInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + protected function getUploadDir() + { + return $this->root->url() . DIRECTORY_SEPARATOR . 'uploads'; } } diff --git a/Tests/Mapping/PropertyMappingFactoryTest.php b/Tests/Mapping/PropertyMappingFactoryTest.php index 83eae6fe..48dbed7f 100644 --- a/Tests/Mapping/PropertyMappingFactoryTest.php +++ b/Tests/Mapping/PropertyMappingFactoryTest.php @@ -18,84 +18,88 @@ class PropertyMappingFactoryTest extends \PHPUnit_Framework_TestCase protected $container; /** - * @var \Vich\UploaderBundle\Mapping\MappingReader $mapping + * @var \Vich\UploaderBundle\Metadata\MetadataReader $metadata */ - protected $mapping; + protected $metadata; /** * @var \Vich\UploaderBundle\Adapter\AdapterInterface $adapter */ protected $adapter; + /** + * @var DummyEntity + */ + protected $object; + /** * Sets up the test. */ public function setUp() { $this->container = $this->getContainerMock(); - $this->mapping = $this->getMappingMock(); + $this->metadata = $this->getMetadataReaderMock(); $this->adapter = $this->getAdapterMock(); + + $this->object = new DummyEntity(); } /** - * Tests that an exception is thrown if a non uploadable - * object is passed in. + * Tests that an exception is thrown if a non uploadable object is passed. * - * @expectedException \InvalidArgumentException + * @expectedException InvalidArgumentException + * @expectedExceptionMessage The object is not uploadable. */ public function testFromObjectThrowsExceptionIfNotUploadable() { - $obj = new \StdClass(); - $this->adapter ->expects($this->once()) - ->method('getReflectionClass') - ->will($this->returnValue(new \ReflectionClass($obj))); + ->method('getClassName') + ->will($this->returnValue('stdClass')); - $this->mapping + $this->metadata ->expects($this->once()) ->method('isUploadable') ->will($this->returnValue(false)); - $factory = new PropertyMappingFactory($this->container, $this->mapping, $this->adapter, array()); - $factory->fromObject($obj); + $factory = new PropertyMappingFactory($this->container, $this->metadata, $this->adapter, array()); + $factory->fromObject(new \stdClass()); } /** - * Test the fromObject method with one uploadable - * field. + * @dataProvider uploadableClassNameProvider */ - public function testFromObjectOneField() + public function testFromObjectWithValidMapping($givenClassName, $inferredClassName) { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - $mappings = array( 'dummy_file' => array( - 'upload_destination' => 'images', - 'delete_on_remove' => true, - 'delete_on_update' => true, - 'namer' => null, - 'inject_on_load' => true, - 'directory_namer' => null + 'upload_destination' => 'images', + 'namer' => null, + 'directory_namer' => null ) ); - $this->adapter - ->expects($this->any()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); + if ($givenClassName === null) { + $this->adapter + ->expects($this->once()) + ->method('getClassName') + ->will($this->returnValue($inferredClassName)); + } else { + $this->adapter + ->expects($this->never()) + ->method('getClassName'); + } - $this->mapping + $this->metadata ->expects($this->once()) ->method('isUploadable') - ->with($class) + ->with($inferredClassName) ->will($this->returnValue(true)); - $this->mapping + $this->metadata ->expects($this->once()) ->method('getUploadableFields') - ->with($class) + ->with($inferredClassName) ->will($this->returnValue(array( 'file' => array( 'mapping' => 'dummy_file', @@ -104,51 +108,58 @@ public function testFromObjectOneField() ) ))); - $factory = new PropertyMappingFactory($this->container, $this->mapping, $this->adapter, $mappings); - $mappings = $factory->fromObject($obj); + $factory = new PropertyMappingFactory($this->container, $this->metadata, $this->adapter, $mappings); + $mappings = $factory->fromObject($this->object, $givenClassName); $this->assertEquals(1, count($mappings)); - $mapping = $mappings[0]; + $mapping = $mappings['dummy_file']; $this->assertEquals('dummy_file', $mapping->getMappingName()); - $this->assertEquals('images', $mapping->getUploadDir()); + $this->assertEquals('images', $mapping->getUploadDestination()); $this->assertNull($mapping->getNamer()); $this->assertFalse($mapping->hasNamer()); - $this->assertTrue($mapping->getDeleteOnRemove()); - $this->assertTrue($mapping->getInjectOnLoad()); + } + + public function uploadableClassNameProvider() + { + $uploadableClassName = 'Vich\UploaderBundle\Tests\DummyEntity'; + + return array( + // given className, inferred className + array( null, $uploadableClassName), + array( $uploadableClassName, $uploadableClassName), + ); } /** * Test that an exception is thrown when an invalid mapping name * is specified. * - * @expectedException \InvalidArgumentException + * @expectedException InvalidArgumentException + * @expectedExceptionMessage No mapping named "dummy_file" configured. */ public function testThrowsExceptionOnInvalidMappingName() { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - $mappings = array( 'bad_name' => array() ); $this->adapter - ->expects($this->any()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); + ->expects($this->once()) + ->method('getClassName') + ->will($this->returnValue('Vich\UploaderBundle\Tests\DummyEntity')); - $this->mapping + $this->metadata ->expects($this->once()) ->method('isUploadable') - ->with($class) + ->with('Vich\UploaderBundle\Tests\DummyEntity') ->will($this->returnValue(true)); - $this->mapping + $this->metadata ->expects($this->once()) ->method('getUploadableFields') - ->with($class) + ->with('Vich\UploaderBundle\Tests\DummyEntity') ->will($this->returnValue(array( 'file' => array( 'mapping' => 'dummy_file', @@ -157,55 +168,46 @@ public function testThrowsExceptionOnInvalidMappingName() ) ))); - $factory = new PropertyMappingFactory($this->container, $this->mapping, $this->adapter, $mappings); - $mappings = $factory->fromObject($obj); + $factory = new PropertyMappingFactory($this->container, $this->metadata, $this->adapter, $mappings); + $factory->fromObject($this->object); } /** - * Test that the fromField method returns null when an invalid - * field name is specified. + * Test that the fromField method returns null when an invalid field name + * is specified. */ public function testFromFieldReturnsNullOnInvalidFieldName() { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - $this->adapter - ->expects($this->any()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); + ->expects($this->once()) + ->method('getClassName') + ->will($this->returnValue('Vich\UploaderBundle\Tests\DummyEntity')); - $this->mapping + $this->metadata ->expects($this->once()) ->method('isUploadable') - ->with($class) + ->with('Vich\UploaderBundle\Tests\DummyEntity') ->will($this->returnValue(true)); - $this->mapping + $this->metadata ->expects($this->once()) ->method('getUploadableField') - ->with($class) + ->with('Vich\UploaderBundle\Tests\DummyEntity') ->will($this->returnValue(null)); - $factory = new PropertyMappingFactory($this->container, $this->mapping, $this->adapter, array()); - $mapping = $factory->fromField($obj, 'oops'); + $factory = new PropertyMappingFactory($this->container, $this->metadata, $this->adapter, array()); + $mapping = $factory->fromField($this->object, 'oops'); $this->assertNull($mapping); } public function testConfiguredNamerRetrievedFromContainer() { - $obj = new DummyEntity(); - $class = new \ReflectionClass($obj); - $mappings = array( 'dummy_file' => array( - 'upload_destination' => 'images', - 'delete_on_remove' => true, - 'delete_on_update' => true, - 'namer' => 'my.custom.namer', - 'inject_on_load' => true, - 'directory_namer' => null + 'upload_destination' => 'images', + 'namer' => 'my.custom.namer', + 'directory_namer' => null ) ); @@ -218,20 +220,20 @@ public function testConfiguredNamerRetrievedFromContainer() ->will($this->returnValue($namer)); $this->adapter - ->expects($this->any()) - ->method('getReflectionClass') - ->will($this->returnValue($class)); + ->expects($this->once()) + ->method('getClassName') + ->will($this->returnValue('Vich\UploaderBundle\Tests\DummyEntity')); - $this->mapping + $this->metadata ->expects($this->once()) ->method('isUploadable') - ->with($class) + ->with('Vich\UploaderBundle\Tests\DummyEntity') ->will($this->returnValue(true)); - $this->mapping + $this->metadata ->expects($this->once()) ->method('getUploadableFields') - ->with($class) + ->with('Vich\UploaderBundle\Tests\DummyEntity') ->will($this->returnValue(array( 'file' => array( 'mapping' => 'dummy_file', @@ -240,17 +242,15 @@ public function testConfiguredNamerRetrievedFromContainer() ) ))); - $factory = new PropertyMappingFactory($this->container, $this->mapping, $this->adapter, $mappings); - $mappings = $factory->fromObject($obj); + $factory = new PropertyMappingFactory($this->container, $this->metadata, $this->adapter, $mappings); + $mappings = $factory->fromObject($this->object); $this->assertEquals(1, count($mappings)); - if (count($mappings) > 0) { - $mapping = $mappings[0]; + $mapping = $mappings['dummy_file']; - $this->assertEquals($namer, $mapping->getNamer()); - $this->assertTrue($mapping->hasNamer()); - } + $this->assertEquals($namer, $mapping->getNamer()); + $this->assertTrue($mapping->hasNamer()); } /** @@ -261,20 +261,20 @@ public function testConfiguredNamerRetrievedFromContainer() protected function getContainerMock() { return $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); } /** - * Creates a mock mapping reader. + * Creates a mock metadata reader. * - * @return \Vich\UploaderBundle\Mapping\MappingReader The mapping reader. + * @return \Vich\UploaderBundle\Metadata\MetadataReader The metadata reader. */ - protected function getMappingMock() + protected function getMetadataReaderMock() { - return $this->getMockBuilder('Vich\UploaderBundle\Mapping\MappingReader') - ->disableOriginalConstructor() - ->getMock(); + return $this->getMockBuilder('Vich\UploaderBundle\Metadata\MetadataReader') + ->disableOriginalConstructor() + ->getMock(); } /** @@ -285,7 +285,7 @@ protected function getMappingMock() protected function getAdapterMock() { return $this->getMockBuilder('Vich\UploaderBundle\Adapter\AdapterInterface') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); } } diff --git a/Tests/Mapping/ProperyMappingTest.php b/Tests/Mapping/PropertyMappingTest.php similarity index 60% rename from Tests/Mapping/ProperyMappingTest.php rename to Tests/Mapping/PropertyMappingTest.php index a9ad0e60..5c520376 100644 --- a/Tests/Mapping/ProperyMappingTest.php +++ b/Tests/Mapping/PropertyMappingTest.php @@ -17,16 +17,11 @@ class PropertyMappingTest extends \PHPUnit_Framework_TestCase */ public function testConfiguredMappingAccess() { - $prop = new PropertyMapping(); + $prop = new PropertyMapping('file', 'fileName'); $prop->setMapping(array( - 'delete_on_remove' => true, - 'delete_on_update' => true, 'upload_destination' => '/tmp', - 'inject_on_load' => true )); - $this->assertEquals($prop->getUploadDir(), '/tmp'); - $this->assertTrue($prop->getDeleteOnRemove()); - $this->assertTrue($prop->getInjectOnLoad()); + $this->assertEquals('/tmp', $prop->getUploadDestination()); } } diff --git a/Tests/Metadata/Driver/AnnotationTest.php b/Tests/Metadata/Driver/AnnotationDriverTest.php similarity index 91% rename from Tests/Metadata/Driver/AnnotationTest.php rename to Tests/Metadata/Driver/AnnotationDriverTest.php index 53d31994..dbbda8f6 100644 --- a/Tests/Metadata/Driver/AnnotationTest.php +++ b/Tests/Metadata/Driver/AnnotationDriverTest.php @@ -3,16 +3,16 @@ namespace Vich\UploaderBundle\Tests\Metadata\Driver; use Vich\UploaderBundle\Mapping\Annotation\UploadableField; -use Vich\UploaderBundle\Metadata\Driver\Annotation; +use Vich\UploaderBundle\Metadata\Driver\AnnotationDriver; use Vich\UploaderBundle\Tests\DummyEntity; use Vich\UploaderBundle\Tests\TwoFieldsDummyEntity; /** - * AnnotationTest + * AnnotationDriverTest * * @author Kévin Gomez */ -class AnnotationTest extends \PHPUnit_Framework_TestCase +class AnnotationDriverTest extends \PHPUnit_Framework_TestCase { public function testReadUploadableAnnotation() { @@ -31,7 +31,7 @@ public function testReadUploadableAnnotation() 'fileNameProperty' => 'fileName' )))); - $driver = new Annotation($reader); + $driver = new AnnotationDriver($reader); $metadata = $driver->loadMetadataForClass(new \ReflectionClass($entity)); $this->assertInstanceOf('\Vich\UploaderBundle\Metadata\ClassMetadata', $metadata); @@ -58,7 +58,7 @@ public function testReadUploadableAnnotationReturnsNullWhenNonePresent() ->expects($this->never()) ->method('getPropertyAnnotation'); - $driver = new Annotation($reader); + $driver = new AnnotationDriver($reader); $metadata = $driver->loadMetadataForClass(new \ReflectionClass($entity)); $this->assertNull($metadata); @@ -88,7 +88,7 @@ public function testReadTwoUploadableFields() 'fileNameProperty' => 'imageName' )))); - $driver = new Annotation($reader); + $driver = new AnnotationDriver($reader); $metadata = $driver->loadMetadataForClass(new \ReflectionClass($entity)); $this->assertEquals(array( @@ -115,7 +115,7 @@ public function testReadNoUploadableFieldsWhenNoneExist() ->method('getClassAnnotation') ->will($this->returnValue('something not null')); - $driver = new Annotation($reader); + $driver = new AnnotationDriver($reader); $metadata = $driver->loadMetadataForClass(new \ReflectionClass($entity)); $this->assertEmpty($metadata->fields); diff --git a/Tests/Metadata/Driver/YamlTest.php b/Tests/Metadata/Driver/YamlDriverTest.php similarity index 89% rename from Tests/Metadata/Driver/YamlTest.php rename to Tests/Metadata/Driver/YamlDriverTest.php index 5eb08f76..a8e45a56 100644 --- a/Tests/Metadata/Driver/YamlTest.php +++ b/Tests/Metadata/Driver/YamlDriverTest.php @@ -3,14 +3,14 @@ namespace Vich\UploaderBundle\Tests\Metadata\Driver; use Doctrine\ORM\Mapping\ClassMetadata; -use Vich\UploaderBundle\Metadata\Driver\Yaml; +use Vich\UploaderBundle\Metadata\Driver\YamlDriver; /** - * YamlTest + * YamlDriverTest * * @author Kévin Gomez */ -class YamlTest extends \PHPUnit_Framework_TestCase +class YamlDriverTest extends \PHPUnit_Framework_TestCase { /** * @expectedException RuntimeException @@ -20,7 +20,7 @@ public function testInconsistentYamlFile() $rClass = new \ReflectionClass('\DateTime'); $driver = $this->getDriver($rClass); - $driver->mapping_content = array(); + $driver->mappingContent = array(); $driver->loadMetadataForClass($rClass); } @@ -33,7 +33,7 @@ public function testLoadMetadataForClass($mapping, $expectedMetadata) $rClass = new \ReflectionClass('\DateTime'); $driver = $this->getDriver($rClass); - $driver->mapping_content = array( + $driver->mappingContent = array( $rClass->name => $mapping ); @@ -47,7 +47,7 @@ public function testLoadMetadataForClass($mapping, $expectedMetadata) protected function getDriver(\ReflectionClass $class, $found = true) { $fileLocator = $this->getMock('\Metadata\Driver\FileLocatorInterface'); - $driver = new TestableYaml($fileLocator); + $driver = new TestableYamlDriver($fileLocator); $fileLocator ->expects($this->once()) @@ -109,12 +109,12 @@ public function fieldsProvider() } } -class TestableYaml extends Yaml +class TestableYamlDriver extends YamlDriver { - public $mapping_content; + public $mappingContent; protected function loadMappingFile($file) { - return $this->mapping_content; + return $this->mappingContent; } } diff --git a/Tests/Naming/OrignameNamerTest.php b/Tests/Naming/OrignameNamerTest.php index e2b2da01..ba0abc02 100644 --- a/Tests/Naming/OrignameNamerTest.php +++ b/Tests/Naming/OrignameNamerTest.php @@ -1,10 +1,9 @@ setFile($file); + $mapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') + ->disableOriginalConstructor() + ->getMock(); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($entity) + ->will($this->returnValue($file)); + $namer = new OrignameNamer(); - $this->assertRegExp($pattern, $namer->name($entity, 'file')); + $this->assertRegExp($pattern, $namer->name($mapping, $entity)); } } diff --git a/Tests/Naming/UniqidNamerTest.php b/Tests/Naming/UniqidNamerTest.php index 63d7e07f..cd85a98b 100644 --- a/Tests/Naming/UniqidNamerTest.php +++ b/Tests/Naming/UniqidNamerTest.php @@ -1,6 +1,6 @@ setFile($file); + $mapping = $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') + ->disableOriginalConstructor() + ->getMock(); + + $mapping + ->expects($this->once()) + ->method('getFile') + ->with($entity) + ->will($this->returnValue($file)); + $namer = new UniqidNamer(); - $this->assertRegExp($pattern, $namer->name($entity, 'file')); + $this->assertRegExp($pattern, $namer->name($mapping, $entity)); } } diff --git a/Tests/Storage/FileSystemStorageTest.php b/Tests/Storage/FileSystemStorageTest.php index be7a94d6..7e00a826 100644 --- a/Tests/Storage/FileSystemStorageTest.php +++ b/Tests/Storage/FileSystemStorageTest.php @@ -2,6 +2,8 @@ namespace Vich\UploaderBundle\Tests\Storage; +use org\bovigo\vfs\vfsStream; + use Vich\UploaderBundle\Storage\FileSystemStorage; use Vich\UploaderBundle\Tests\DummyEntity; @@ -18,512 +20,171 @@ class FileSystemStorageTest extends \PHPUnit_Framework_TestCase protected $factory; /** - * Sets up the test. + * @var PropertyMapping */ - public function setUp() - { - $this->factory = $this->getFactoryMock(); - } + protected $mapping; /** - * Tests the upload method skips a mapping which has a null - * uploadable property value. + * @var DummyEntity */ - public function testUploadSkipsMappingOnNullFile() - { - $obj = new DummyEntity(); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $mapping - ->expects($this->once()) - ->method('getPropertyValue') - ->will($this->returnValue(null)); - - $mapping - ->expects($this->never()) - ->method('hasNamer'); - - $mapping - ->expects($this->never()) - ->method('getNamer'); - - $mapping - ->expects($this->never()) - ->method('getFileNameProperty'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - - $storage = new FileSystemStorage($this->factory); - $storage->upload($obj); - } + protected $object; /** - * Tests the upload method skips a mapping which has an uploadable - * field property value that is not an instance of UploadedFile. + * @var FileSystemStorage */ - public function testUploadSkipsMappingOnNonUploadedFileInstance() - { - $obj = new DummyEntity(); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() - ->getMock(); - - $mapping - ->expects($this->once()) - ->method('getPropertyValue') - ->will($this->returnValue($file)); - - $mapping - ->expects($this->never()) - ->method('hasNamer'); - - $mapping - ->expects($this->never()) - ->method('getNamer'); - - $mapping - ->expects($this->never()) - ->method('getFileNameProperty'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - - $storage = new FileSystemStorage($this->factory); - $storage->upload($obj); - } + protected $storage; /** - * Test the remove method does not remove a file that is configured - * to not be deleted upon removal of the entity. + * @var vfsStreamDirectory */ - public function testRemoveSkipsConfiguredNotToDeleteOnRemove() - { - $obj = new DummyEntity(); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(false)); - - $mapping - ->expects($this->never()) - ->method('getFileNameProperty'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - - $storage = new FileSystemStorage($this->factory); - $storage->remove($obj); - } + protected $root; /** - * Test the remove method skips trying to remove a file whose file name - * property value returns null. + * Sets up the test. */ - public function testRemoveSkipsNullFileNameProperty() + public function setUp() { - $obj = new DummyEntity(); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue(null)); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(true)); - - $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); + $this->factory = $this->getFactoryMock(); + $this->mapping = $this->getMappingMock(); + $this->object = new DummyEntity(); + $this->storage = new FileSystemStorage($this->factory); - $mapping - ->expects($this->never()) - ->method('getUploadDir'); + $this->root = vfsStream::setup('vich_uploader_bundle', null, array( + 'uploads' => array( + 'test.txt' => 'some content' + ), + )); $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - - $storage = new FileSystemStorage($this->factory); - $storage->remove($obj); - } - /** - * Test the remove method skips trying to remove a file that no longer exists - */ - public function testRemoveSkipsNonExistingFile() - { - $obj = new DummyEntity(); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - - $propertyMock = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - - $propertyMock - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue(null)); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(true)); - - $mapping - ->expects($this->once()) - ->method('getUploadDir') - ->will($this->returnValue('/tmp')); - - $mapping ->expects($this->any()) - ->method('getProperty') - ->will($this->returnValue($propertyMock)); - - $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - - $storage = new FileSystemStorage($this->factory); - try { - $storage->remove($obj); - } catch (\Exception $e) { - $this->fail('We tried to remove nonexistent file'); - } + ->method('fromObject') + ->with($this->object) + ->will($this->returnValue(array($this->mapping))); } /** - * Test the resolve path method. + * @dataProvider invalidFileProvider + * @group upload */ - public function testResolvePath() + public function testUploadSkipsMappingOnInvalidFile() { - $obj = new DummyEntity(); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - - $mapping - ->expects($this->once()) - ->method('getUploadDir') - ->will($this->returnValue('/tmp')); - - $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $this->factory - ->expects($this->once()) - ->method('fromField') - ->with($obj, 'file') - ->will($this->returnValue($mapping)); - - $storage = new FileSystemStorage($this->factory); - $path = $storage->resolvePath($obj, 'file'); - - $this->assertEquals(sprintf('/tmp%sfile.txt', DIRECTORY_SEPARATOR), $path); + $this->mapping + ->expects($this->once()) + ->method('getFile') + ->will($this->returnValue(null)); + $this->mapping + ->expects($this->never()) + ->method('hasNamer'); + $this->mapping + ->expects($this->never()) + ->method('getNamer'); + $this->mapping + ->expects($this->never()) + ->method('getFileName'); + + $this->storage->upload($this->object, $this->mapping); } - /** - * Test the resolve uri - * - * @dataProvider resolveUriDataProvider - */ - public function testResolveUri($uploadDir, $uri) + public function invalidFileProvider() { - $obj = new DummyEntity(); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - - $mapping - ->expects($this->once()) - ->method('getUploadDir') - ->will($this->returnValue($uploadDir)); - - $mapping - ->expects($this->once()) - ->method('getUriPrefix') - ->will($this->returnValue('/uploads')); - - $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $this->factory - ->expects($this->once()) - ->method('fromField') - ->with($obj, 'file') - ->will($this->returnValue($mapping)); - - $storage = new FileSystemStorage($this->factory); - $path = $storage->resolveUri($obj, 'file'); - - $this->assertEquals($uri, $path); - } + $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File') + ->disableOriginalConstructor() + ->getMock(); - public function resolveUriDataProvider() - { return array( - array( - '/abs/path/web/uploads', - '/uploads/file.txt' - ), - array( - 'c:\abs\path\web\uploads', - '/uploads/file.txt' - ), - array( - '/abs/path/web/project/web/uploads', - '/uploads/file.txt' - ), - array( - '/abs/path/web/project/web/uploads/custom/dir', - '/uploads/custom/dir/file.txt' - ), + // skipped because null + array( null ), + // skipped because not even a file + array( new \DateTime() ), + // skipped because not instance of UploadedFile + array( $file ), ); } /** - * Test the resolve path method throws exception - * when an invaid field name is specified. + * Test that file upload moves uploaded file to correct directory and with + * correct filename. * - * @expectedException \InvalidArgumentException - */ - public function testResolvePathThrowsExceptionOnInvalidFieldName() - { - $obj = new DummyEntity(); - - $this->factory - ->expects($this->once()) - ->method('fromField') - ->with($obj, 'oops') - ->will($this->returnValue(null)); - - $storage = new FileSystemStorage($this->factory); - $storage->resolvePath($obj, 'oops'); - } - - /** - * Test that file upload moves uploaded file to correct directory and with correct filename + * @group upload */ public function testUploadedFileIsCorrectlyMoved() { - $obj = new DummyEntity(); - $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') ->disableOriginalConstructor() ->getMock(); - $file - ->expects($this->any()) + ->expects($this->once()) ->method('getClientOriginalName') ->will($this->returnValue('filename.txt')); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - - $name = new \stdClass(); - - $prop - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue($name)); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $mapping - ->expects($this->any()) - ->method('getProperty') - ->will($this->returnValue($prop)); - - $mapping + $this->mapping ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $mapping - ->expects($this->once()) - ->method('getPropertyValue') - ->with($obj) + ->method('getFile') + ->with($this->object) ->will($this->returnValue($file)); - $mapping - ->expects($this->once()) - ->method('getDeleteOnUpdate') - ->will($this->returnValue(false)); - - $mapping - ->expects($this->once()) - ->method('hasNamer') - ->will($this->returnValue(false)); - - $mapping + $this->mapping ->expects($this->once()) - ->method('getUploadDir') - ->with($obj,$name) + ->method('getUploadDestination') ->will($this->returnValue('/dir')); - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - $file ->expects($this->once()) ->method('move') ->with('/dir', 'filename.txt'); - $storage = new FileSystemStorage($this->factory); - $storage->upload($obj); + $this->storage->upload($this->object, $this->mapping); } /** - * Test file upload when filename contains directories - * @dataProvider filenameWithDirectoriesDataProvider + * Test file upload when filename contains directories + * + * @dataProvider filenameWithDirectoriesDataProvider + * @group upload */ public function testFilenameWithDirectoriesIsUploadedToCorrectDirectory($dir, $filename, $expectedDir, $expectedFileName) { - $obj = new DummyEntity(); - $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') ->disableOriginalConstructor() ->getMock(); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - - $name = new \stdClass(); - $namer = $this->getMockBuilder('Vich\UploaderBundle\Naming\NamerInterface') ->disableOriginalConstructor() ->getMock(); - $namer ->expects($this->once()) ->method('name') - ->with($obj, $name) + ->with($this->mapping, $this->object) ->will($this->returnValue($filename)); - $prop - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue($name)); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $mapping + $this->mapping ->expects($this->once()) ->method('getNamer') ->will($this->returnValue($namer)); - $mapping - ->expects($this->any()) - ->method('getProperty') - ->will($this->returnValue($prop)); - - $mapping + $this->mapping ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $mapping - ->expects($this->once()) - ->method('getPropertyValue') - ->with($obj) + ->method('getFile') + ->with($this->object) ->will($this->returnValue($file)); - $mapping - ->expects($this->once()) - ->method('getDeleteOnUpdate') - ->will($this->returnValue(false)); - - $mapping + $this->mapping ->expects($this->once()) ->method('hasNamer') ->will($this->returnValue(true)); - $mapping + $this->mapping ->expects($this->once()) - ->method('getUploadDir') - ->with($obj,$name) + ->method('getUploadDestination') ->will($this->returnValue($dir)); - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - $file ->expects($this->once()) ->method('move') ->with($expectedDir, $expectedFileName); - $storage = new FileSystemStorage($this->factory); - $storage->upload($obj); + $this->storage->upload($this->object, $this->mapping); } @@ -546,15 +207,174 @@ public function filenameWithDirectoriesDataProvider() } /** - * Creates a mock factory. + * @dataProvider removeDataProvider + * @group remove + */ + public function testRemove($uploadDir, $fileName) + { + $this->mapping + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue($fileName)); + + // if the file should be deleted and we have its name, then we need the + // upload dir + if ($fileName !== null) { + $this->mapping + ->expects($this->once()) + ->method('getUploadDestination') + ->will($this->returnValue($this->root->url() . DIRECTORY_SEPARATOR . $uploadDir)); + } else { + $this->mapping + ->expects($this->never()) + ->method('getUploadDestination'); + } + + $this->storage->remove($this->object, $this->mapping); + + // the file should have been deleted + if ($fileName !== null) { + $this->assertFalse($this->root->hasChild($uploadDir . DIRECTORY_SEPARATOR . $fileName)); + } + } + + public function removeDataProvider() + { + return array( + // uploadDir fileName + // no file present + array(null, null), + // present in the filesystem + array('/uploads', 'test.txt'), + // file already deleted + array('/uploads', 'file.txt'), + ); + } + + /** + * Test the resolve path method. + * + * @group resolvePath + */ + public function testResolvePath() + { + $this->mapping + ->expects($this->once()) + ->method('getUploadDestination') + ->will($this->returnValue('/tmp')); + $this->mapping + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue('file.txt')); + + $this->factory + ->expects($this->once()) + ->method('fromField') + ->with($this->object, 'file') + ->will($this->returnValue($this->mapping)); + + $path = $this->storage->resolvePath($this->object, 'file'); + + $this->assertEquals(sprintf('/tmp%sfile.txt', DIRECTORY_SEPARATOR), $path); + } + + /** + * Test the resolve path method throws exception + * when an invaid field name is specified. + * + * @expectedException InvalidArgumentException + * @group resolvePath + */ + public function testResolvePathThrowsExceptionOnInvalidFieldName() + { + $this->factory + ->expects($this->once()) + ->method('fromField') + ->with($this->object, 'oops') + ->will($this->returnValue(null)); + + $this->storage->resolvePath($this->object, 'oops'); + } + + /** + * Test the resolve uri + * + * @dataProvider resolveUriDataProvider + * @group resolveUri + */ + public function testResolveUri($uploadDir, $file, $uri) + { + $this->mapping + ->expects($this->once()) + ->method('getUploadDestination') + ->will($this->returnValue($uploadDir)); + + $this->mapping + ->expects($this->once()) + ->method('getUriPrefix') + ->will($this->returnValue('/uploads')); + + $this->mapping + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue($file)); + + $this->factory + ->expects($this->once()) + ->method('fromField') + ->with($this->object, 'file') + ->will($this->returnValue($this->mapping)); + + $this->assertEquals($uri, $this->storage->resolveUri($this->object, 'file')); + } + + public function resolveUriDataProvider() + { + return array( + array( + '/abs/path/web/uploads', + 'file.txt', + '/uploads/file.txt' + ), + array( + 'c:\abs\path\web\uploads', + 'file.txt', + '/uploads/file.txt' + ), + array( + '/abs/path/web/project/web/uploads', + 'file.txt', + '/uploads/file.txt' + ), + array( + '/abs/path/web/project/web/uploads/foo', + 'custom/dir/file.txt', + '/uploads/foo/custom/dir/file.txt' + ), + array( + '/abs/path/web/project/web/uploads/custom/dir', + 'file.txt', + '/uploads/custom/dir/file.txt' + ), + ); + } + + /** + * Creates a mock mapping-factory. * * @return \Vich\UploaderBundle\Mapping\PropertyMappingFactory The factory. */ protected function getFactoryMock() { return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMappingFactory') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); } + protected function getMappingMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') + ->disableOriginalConstructor() + ->getMock(); + } } diff --git a/Tests/Storage/GaufretteStorageTest.php b/Tests/Storage/GaufretteStorageTest.php index 99602aaa..d5dfcbd2 100644 --- a/Tests/Storage/GaufretteStorageTest.php +++ b/Tests/Storage/GaufretteStorageTest.php @@ -2,8 +2,10 @@ namespace Vich\UploaderBundle\Tests\Storage; -use Vich\UploaderBundle\Storage\GaufretteStorage; use Gaufrette\Exception\FileNotFound; +use org\bovigo\vfs\vfsStream; + +use Vich\UploaderBundle\Storage\GaufretteStorage; use Vich\UploaderBundle\Tests\DummyEntity; /** @@ -23,6 +25,11 @@ class GaufretteStorageTest extends \PHPUnit_Framework_TestCase */ protected $filesystemMap; + /** + * @var vfsStreamDirectory + */ + protected $root; + /** * Sets up the test. */ @@ -30,6 +37,11 @@ public function setUp() { $this->factory = $this->getFactoryMock(); $this->filesystemMap = $this->getFilesystemMapMock(); + $this->root = vfsStream::setup('vich_uploader_bundle', null, array( + 'uploads' => array( + 'file.txt' => 'some content', + ), + )); } /** @@ -40,33 +52,22 @@ public function testUploadSkipsMappingOnNullFile() { $obj = new DummyEntity(); - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $mapping - ->expects($this->once()) - ->method('getPropertyValue') - ->will($this->returnValue(null)); - + $mapping = $this->getMappingMock(); $mapping - ->expects($this->never()) - ->method('hasNamer'); + ->expects($this->once()) + ->method('getFile') + ->will($this->returnValue(null)); $mapping - ->expects($this->never()) - ->method('getNamer'); + ->expects($this->never()) + ->method('hasNamer'); $mapping - ->expects($this->never()) - ->method('getFileNameProperty'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); + ->expects($this->never()) + ->method('getNamer'); $storage = new GaufretteStorage($this->factory, $this->filesystemMap); - $storage->upload($obj); + $storage->upload($obj, $mapping); } /** @@ -77,66 +78,27 @@ public function testUploadSkipsMappingOnNonUploadedFileInstance() { $obj = new DummyEntity(); - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); + $mapping = $this->getMappingMock(); $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\File') - ->disableOriginalConstructor() - ->getMock(); - - $mapping - ->expects($this->once()) - ->method('getPropertyValue') - ->will($this->returnValue($file)); - - $mapping - ->expects($this->never()) - ->method('hasNamer'); - - $mapping - ->expects($this->never()) - ->method('getNamer'); + ->disableOriginalConstructor() + ->getMock(); $mapping - ->expects($this->never()) - ->method('getFileNameProperty'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - - $storage = new GaufretteStorage($this->factory, $this->filesystemMap); - $storage->upload($obj); - } - - /** - * Test the remove method does not remove a file that is configured - * to not be deleted upon removal of the entity. - */ - public function testRemoveSkipsConfiguredNotToDeleteOnRemove() - { - $obj = new DummyEntity(); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); + ->expects($this->once()) + ->method('getFile') + ->will($this->returnValue($file)); $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(false)); + ->expects($this->never()) + ->method('hasNamer'); $mapping - ->expects($this->never()) - ->method('getFileNameProperty'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); + ->expects($this->never()) + ->method('getNamer'); $storage = new GaufretteStorage($this->factory, $this->filesystemMap); - $storage->remove($obj); + $storage->upload($obj, $mapping); } /** @@ -147,38 +109,18 @@ public function testRemoveSkipsNullFileNameProperty() { $obj = new DummyEntity(); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue(null)); - - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(true)); - + $mapping = $this->getMappingMock(); $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue(null)); $mapping - ->expects($this->never()) - ->method('getUploadDir'); - - $this->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); + ->expects($this->never()) + ->method('getUploadDestination'); $storage = new GaufretteStorage($this->factory, $this->filesystemMap); - $storage->remove($obj); + $storage->remove($obj, $mapping); } /** @@ -188,32 +130,23 @@ public function testResolvePath() { $obj = new DummyEntity(); - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); + $mapping = $this->getMappingMock(); $mapping - ->expects($this->once()) - ->method('getUploadDir') - ->will($this->returnValue('filesystemKey')); + ->expects($this->once()) + ->method('getUploadDestination') + ->will($this->returnValue('filesystemKey')); $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue('file.txt')); $this->factory - ->expects($this->once()) - ->method('fromField') - ->with($obj, 'file') - ->will($this->returnValue($mapping)); + ->expects($this->once()) + ->method('fromField') + ->with($obj, 'file') + ->will($this->returnValue($mapping)); $storage = new GaufretteStorage($this->factory, $this->filesystemMap); $path = $storage->resolvePath($obj, 'file'); @@ -228,32 +161,23 @@ public function testResolvePathWithChangedProtocol() { $obj = new DummyEntity(); - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); - - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->once()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); + $mapping = $this->getMappingMock(); $mapping - ->expects($this->once()) - ->method('getUploadDir') - ->will($this->returnValue('filesystemKey')); + ->expects($this->once()) + ->method('getUploadDestination') + ->will($this->returnValue('filesystemKey')); $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); + ->expects($this->once()) + ->method('getFileName') + ->will($this->returnValue('file.txt')); $this->factory - ->expects($this->once()) - ->method('fromField') - ->with($obj, 'file') - ->will($this->returnValue($mapping)); + ->expects($this->once()) + ->method('fromField') + ->with($obj, 'file') + ->will($this->returnValue($mapping)); $storage = new GaufretteStorage($this->factory, $this->filesystemMap, 'data'); $path = $storage->resolvePath($obj, 'file'); @@ -266,38 +190,17 @@ public function testResolvePathWithChangedProtocol() */ public function testThatRemoveMethodDoesDeleteFile() { - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); + $mapping = $this->getMappingMock(); $obj = new DummyEntity(); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->any()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - $prop - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue('nameProperty')); - - $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(true)); $mapping ->expects($this->any()) - ->method('getUploadDir') + ->method('getUploadDestination') ->will($this->returnValue('filesystemKey')); $mapping ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - $mapping - ->expects($this->once()) - ->method('getProperty') - ->will($this->returnValue($prop)); + ->method('getFileName') + ->will($this->returnValue('file.txt')); $filesystem = $this->getFilesystemMock(); $filesystem @@ -312,15 +215,8 @@ public function testThatRemoveMethodDoesDeleteFile() ->with('filesystemKey') ->will($this->returnValue($filesystem)); - $this - ->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - $storage = new GaufretteStorage($this->factory, $this->filesystemMap); - $storage->remove($obj, 'file'); + $storage->remove($obj, $mapping); } /** @@ -328,38 +224,17 @@ public function testThatRemoveMethodDoesDeleteFile() */ public function testRemoveNotFoundFile() { - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); + $mapping = $this->getMappingMock(); $obj = new DummyEntity(); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - $prop - ->expects($this->any()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - $prop - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue('nameProperty')); - - $mapping - ->expects($this->once()) - ->method('getDeleteOnRemove') - ->will($this->returnValue(true)); $mapping ->expects($this->any()) - ->method('getUploadDir') + ->method('getUploadDestination') ->will($this->returnValue('filesystemKey')); $mapping ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - $mapping - ->expects($this->once()) - ->method('getProperty') - ->will($this->returnValue($prop)); + ->method('getFileName') + ->will($this->returnValue('file.txt')); $filesystem = $this->getFilesystemMock(); $filesystem @@ -375,15 +250,8 @@ public function testRemoveNotFoundFile() ->with('filesystemKey') ->will($this->returnValue($filesystem)); - $this - ->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - $storage = new GaufretteStorage($this->factory, $this->filesystemMap); - $storage->remove($obj, 'file'); + $storage->remove($obj, $mapping); } /** @@ -397,10 +265,10 @@ public function testResolvePathThrowsExceptionOnInvalidFieldName() $obj = new DummyEntity(); $this->factory - ->expects($this->once()) - ->method('fromField') - ->with($obj, 'oops') - ->will($this->returnValue(null)); + ->expects($this->once()) + ->method('fromField') + ->with($obj, 'oops') + ->will($this->returnValue(null)); $storage = new GaufretteStorage($this->factory, $this->filesystemMap); $storage->resolvePath($obj, 'oops'); @@ -410,62 +278,35 @@ public function testUploadSetsMetadataWhenUsingMetadataSupporterAdapter() { $obj = new DummyEntity(); - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); + $mapping = $this->getMappingMock(); $adapter = $this->getMockBuilder('\Gaufrette\Adapter\MetadataSupporter') ->disableOriginalConstructor() ->setMethods(array('setMetadata', 'getMetadata')) ->getMock(); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - - $prop - ->expects($this->any()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - - $prop - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue('nameProperty')); - $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') ->disableOriginalConstructor() ->getMock(); - $file ->expects($this->once()) ->method('getClientOriginalName') ->will($this->returnValue('filename')); - $file ->expects($this->once()) ->method('getPathname') - ->will($this->returnValue(__DIR__ . '/../Fixtures/file.txt')); + ->will($this->returnValue($this->getUploadDir() . DIRECTORY_SEPARATOR . 'file.txt')); $mapping ->expects($this->once()) - ->method('getPropertyValue') + ->method('getFile') ->will($this->returnValue($file)); $mapping ->expects($this->once()) - ->method('getUploadDir') + ->method('getUploadDestination') ->will($this->returnValue('filesystemKey')); - $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $mapping - ->expects($this->once()) - ->method('getProperty') - ->will($this->returnValue($prop)); - $filesystem = $this ->getMockBuilder('\Gaufrette\Filesystem') ->disableOriginalConstructor() @@ -509,13 +350,6 @@ public function testUploadSetsMetadataWhenUsingMetadataSupporterAdapter() ->with('filesystemKey') ->will($this->returnValue($filesystem)); - $this - ->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - $adapter ->expects($this->once()) ->method('setMetadata') @@ -527,68 +361,41 @@ public function testUploadSetsMetadataWhenUsingMetadataSupporterAdapter() ->will($this->returnValue($adapter)); $storage = new GaufretteStorage($this->factory, $filesystemMap); - $storage->upload($obj); + $storage->upload($obj, $mapping); } public function testUploadDoesNotSetMetadataWhenUsingNonMetadataSupporterAdapter() { $obj = new DummyEntity(); - $mapping = $this->getMock('Vich\UploaderBundle\Mapping\PropertyMapping'); + $mapping = $this->getMappingMock(); $adapter = $this->getMockBuilder('\Gaufrette\Adapter\Apc') ->disableOriginalConstructor() ->getMock(); - $prop = $this->getMockBuilder('\ReflectionProperty') - ->disableOriginalConstructor() - ->getMock(); - - $prop - ->expects($this->any()) - ->method('getValue') - ->with($obj) - ->will($this->returnValue('file.txt')); - - $prop - ->expects($this->any()) - ->method('getName') - ->will($this->returnValue('nameProperty')); - $file = $this->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') ->disableOriginalConstructor() ->getMock(); - $file ->expects($this->once()) ->method('getClientOriginalName') ->will($this->returnValue('filename')); - $file ->expects($this->once()) ->method('getPathname') - ->will($this->returnValue(__DIR__ . '/../Fixtures/file.txt')); + ->will($this->returnValue($this->getUploadDir() . DIRECTORY_SEPARATOR . 'file.txt')); $mapping ->expects($this->once()) - ->method('getPropertyValue') + ->method('getFile') ->will($this->returnValue($file)); $mapping ->expects($this->once()) - ->method('getUploadDir') + ->method('getUploadDestination') ->will($this->returnValue('filesystemKey')); - $mapping - ->expects($this->once()) - ->method('getFileNameProperty') - ->will($this->returnValue($prop)); - - $mapping - ->expects($this->once()) - ->method('getProperty') - ->will($this->returnValue($prop)); - $filesystem = $this ->getMockBuilder('\Gaufrette\Filesystem') ->disableOriginalConstructor() @@ -632,13 +439,6 @@ public function testUploadDoesNotSetMetadataWhenUsingNonMetadataSupporterAdapter ->with('filesystemKey') ->will($this->returnValue($filesystem)); - $this - ->factory - ->expects($this->once()) - ->method('fromObject') - ->with($obj) - ->will($this->returnValue(array($mapping))); - $adapter ->expects($this->never()) ->method('setMetadata') @@ -650,7 +450,7 @@ public function testUploadDoesNotSetMetadataWhenUsingNonMetadataSupporterAdapter ->will($this->returnValue($adapter)); $storage = new GaufretteStorage($this->factory, $filesystemMap); - $storage->upload($obj); + $storage->upload($obj, $mapping); } /** @@ -691,4 +491,16 @@ protected function getFilesystemMock() ->disableOriginalConstructor() ->getMock(); } + + protected function getMappingMock() + { + return $this->getMockBuilder('Vich\UploaderBundle\Mapping\PropertyMapping') + ->disableOriginalConstructor() + ->getMock(); + } + + protected function getUploadDir() + { + return $this->root->url() . DIRECTORY_SEPARATOR . 'uploads'; + } } diff --git a/Twig/Extension/UploaderExtension.php b/Twig/Extension/UploaderExtension.php index f711406f..4414667a 100644 --- a/Twig/Extension/UploaderExtension.php +++ b/Twig/Extension/UploaderExtension.php @@ -59,12 +59,14 @@ public function getFunctions() * Gets the public path for the file associated with the uploadable * object. * - * @param object $obj The object. - * @param string $field The field. + * @param object $obj The object. + * @param string $field The field. + * @param string $className The object's class. Mandatory if $obj can't be used to determine it. + * * @return string The public path. */ - public function asset($obj, $field) + public function asset($obj, $field, $className = null) { - return $this->helper->asset($obj, $field); + return $this->helper->asset($obj, $field, $className); } } diff --git a/VichUploaderBundle.php b/VichUploaderBundle.php index 66dfd036..76642085 100644 --- a/VichUploaderBundle.php +++ b/VichUploaderBundle.php @@ -2,8 +2,11 @@ namespace Vich\UploaderBundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; +use Vich\UploaderBundle\DependencyInjection\CompilerPass\RegisterPropelModelsPass; + /** * VichUploaderBundle. * @@ -11,4 +14,13 @@ */ class VichUploaderBundle extends Bundle { + /** + * {@inheritdoc} + */ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $container->addCompilerPass(new RegisterPropelModelsPass()); + } } diff --git a/composer.json b/composer.json index 68a6925e..4b78214f 100644 --- a/composer.json +++ b/composer.json @@ -9,19 +9,27 @@ { "name": "Dustin Dobervich", "email": "ddobervich@gmail.com" + }, + { + "name": "Kévin Gomez", + "email": "contact@kevingomez.fr" } ], "require": { "php": ">=5.3.2", - "symfony/framework-bundle": "~2.0", + "symfony/framework-bundle": "~2.1", "jms/metadata": "~1.5", - "symfony/finder": ">=2.0" + "symfony/property-access": ">=2.2,<3.0", + "symfony/finder": "~2.0" }, "require-dev": { + "mikey179/vfsStream": "~1.0", "doctrine/orm": "@stable", "doctrine/mongodb-odm": "@dev", "knplabs/knp-gaufrette-bundle": "*", "phpunit/phpunit": "~3.7", + "propel/propel1": ">=1.6", + "willdurand/propel-eventdispatcher-bundle": "~1.0", "symfony/yaml": "@stable" }, "suggest": { @@ -29,6 +37,7 @@ "doctrine/doctrine-bundle": "*", "doctrine/mongodb-odm-bundle": "*", "knplabs/knp-gaufrette-bundle": "*", + "willdurand/propel-eventdispatcher-bundle": ">=1.2", "symfony/yaml": "@stable" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f53c420f..00b2cfdf 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,6 +1,16 @@ - + @@ -14,8 +24,9 @@ ./Resources ./Tests + ./vendor - +