diff --git a/Doctrine/Driver/AnnotationDriver.php b/Doctrine/Driver/AnnotationDriver.php new file mode 100644 index 0000000..5002e0b --- /dev/null +++ b/Doctrine/Driver/AnnotationDriver.php @@ -0,0 +1,82 @@ +reader = $reader; + } + + private function getMogrifyMetadata(\ReflectionClass $class) + { + $classMetadata = new MergeableClassMetadata($class->getName()); + + foreach ($class->getProperties() as $property) { + $field = $this->reader->getPropertyAnnotation($property, self::MOGRIFY); + if(!is_null($field)) { + $propertyMetadata = new MogrifyMetadata($class->getName(), $property->getName()); + $propertyMetadata->params = $field->params; + + $classMetadata->addPropertyMetadata($propertyMetadata); + } + } + + return $classMetadata; + } + + private function getConvertMetadata(\ReflectionClass $class) + { + $classMetadata = new MergeableClassMetadata($class->getName()); + + foreach ($class->getProperties() as $property) { + $multiple = $this->reader->getPropertyAnnotation($property, self::CONVERT_MULTIPLE); + if(!is_null($multiple)) { + + $propertyMetadata = new ConvertMetadata($class->getName(), $property->getName()); + foreach($multiple->value as $convert) { + $propertyMetadata->addConvert($convert->params, $convert->targetProperty); + } + + } else { + + $convert = $this->reader->getPropertyAnnotation($property, self::CONVERT); + if(!is_null($convert)) { + $propertyMetadata = new ConvertMetadata($class->getName(), $property->getName()); + $propertyMetadata->addConvert($convert->params, $convert->targetProperty); + } + + } + + if(isset($propertyMetadata)) { + $classMetadata->addPropertyMetadata($propertyMetadata); + } + } + + return $classMetadata; + } + + public function loadMetadataForClass(\ReflectionClass $class) + { + $classMetadata = new MergeableClassMetadata($class->getName()); + $classMetadata->merge($this->getMogrifyMetadata($class)); + $classMetadata->merge($this->getConvertMetadata($class)); + + return $classMetadata; + } +} diff --git a/Doctrine/Driver/MogrifyDriver.php b/Doctrine/Driver/MogrifyDriver.php deleted file mode 100644 index 01382eb..0000000 --- a/Doctrine/Driver/MogrifyDriver.php +++ /dev/null @@ -1,38 +0,0 @@ -reader = $reader; - } - - public function loadMetadataForClass(\ReflectionClass $class) - { - $classMetadata = new MergeableClassMetadata($class->getName()); - - foreach ($class->getProperties() as $property) { - $field = $this->reader->getPropertyAnnotation($property, self::MOGRIFY); - - if(!is_null($field)) { - $propertyMetadata = new MogrifyMetadata($class->getName(), $property->getName()); - $propertyMetadata->params = $field->params; - - $classMetadata->addPropertyMetadata($propertyMetadata); - } - } - - return $classMetadata; - } -} diff --git a/Doctrine/Mapping/Convert.php b/Doctrine/Mapping/Convert.php new file mode 100644 index 0000000..38f8bbf --- /dev/null +++ b/Doctrine/Mapping/Convert.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Snowcap\ImBundle\Doctrine\Mapping; + +use Doctrine\Common\Annotations\Annotation; + +/** + * Annotation definition class + * + * @Annotation + * @Annotation\Target("PROPERTY") + * @codeCoverageIgnore + */ +class Convert extends Annotation +{ + /** + * Identfier of a pre-configured format or key-value pairs of IM parameters + * + * @Required + * @var string|array + */ + public $params; + + /** + * Property the converted image file is to be injected into + * + * @Required + * @var string + */ + public $targetProperty; +} diff --git a/Doctrine/Mapping/ConvertMultiple.php b/Doctrine/Mapping/ConvertMultiple.php new file mode 100644 index 0000000..1912574 --- /dev/null +++ b/Doctrine/Mapping/ConvertMultiple.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Snowcap\ImBundle\Doctrine\Mapping; + +use Doctrine\Common\Annotations\Annotation; + +/** + * Annotation definition class + * + * @Annotation + * @Annotation\Target("PROPERTY") + * @codeCoverageIgnore + */ +class ConvertMultiple extends Annotation +{ + /** + * @Required + * @var array<\Snowcap\ImBundle\Doctrine\Mapping\Convert> + */ + public $value; +} diff --git a/Doctrine/Metadata/ConvertMetadata.php b/Doctrine/Metadata/ConvertMetadata.php new file mode 100644 index 0000000..cd7642f --- /dev/null +++ b/Doctrine/Metadata/ConvertMetadata.php @@ -0,0 +1,43 @@ +params = $params; + $convert->targetProperty = $targetProperty; + $this->converts[] = $convert; + } + + public function getConverts() + { + return $this->converts; + } + + public function serialize() + { + return serialize(array( + $this->class, + $this->name, + $this->converts, + )); + } + + public function unserialize($str) + { + list($this->class, $this->name, $this->converts) = unserialize($str); + + $this->reflection = new \ReflectionProperty($this->class, $this->name); + $this->reflection->setAccessible(true); + } +} diff --git a/Listener/ConvertSubscriber.php b/Listener/ConvertSubscriber.php new file mode 100644 index 0000000..7118b72 --- /dev/null +++ b/Listener/ConvertSubscriber.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Snowcap\ImBundle\Listener; + +use Doctrine\Common\EventSubscriber; +use Metadata\MetadataFactoryInterface; +use Snowcap\ImBundle\Manager as ImManager; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Doctrine\ORM\Event\PreFlushEventArgs; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Snowcap\ImBundle\Doctrine\Metadata\ConvertMetadata; + +/** + * Event listener for Doctrine entities to evualuate and execute Convert and ConvertMultiple annotations + */ +class ConvertSubscriber implements EventSubscriber +{ + /** + * @var \Metadata\MetadataFactoryInterface + */ + private $metadataFactory; + + /** + * @var \Snowcap\ImBundle\Manager + */ + private $imManager; + + /** + * @var \Symfony\Component\PropertyAccess\PropertyAccessor + */ + private $propertyAccessor; + + /** + * @param MetadataFactoryInterface $metadataFactory + * @param ImManager $imManager The ImBundle manager instance + */ + public function __construct(MetadataFactoryInterface $metadataFactory, ImManager $imManager) + { + $this->metadataFactory = $metadataFactory; + $this->imManager = $imManager; + $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + public function getSubscribedEvents() + { + return array('prePersist', 'preFlush'); + } + + private function recomputeChangeSet($event) + { + $object = $event->getEntity(); + + $em = $event->getEntityManager(); + $uow = $em->getUnitOfWork(); + $metadata = $em->getClassMetadata(get_class($object)); + $uow->recomputeSingleEntityChangeSet($metadata, $object); + + return $this; + } + + /** + * @param PreFlushEventArgs $ea + */ + public function preFlush(PreFlushEventArgs $ea) + { + + } + + /** + * @param LifecycleEventArgs $ea + */ + public function prePersist(LifecycleEventArgs $ea) + { + $entity = $ea->getEntity(); + foreach ($this->metadataFactory->getMetadataForClass(get_class($entity))->propertyMetadata as $propertyMetadata) { + if($propertyMetadata instanceof ConvertMetadata) { + $this->convert($entity, $propertyMetadata); + } + } + + } + + /** + * @param $entity + * @param $propertyMetadata + */ + private function convert($entity, $propertyMetadata) + { + $file = $this->propertyAccessor->getValue($entity, $propertyMetadata->name); + if ($file instanceof \SplFileInfo) { + foreach ($propertyMetadata->getConverts() as $convert) { + // XXX: convert() only takes paths relative to web root, currently + $this->imManager->convert($convert->params, $file->getPathName()); + } + } + return null; + } +} diff --git a/Listener/MogrifySubscriber.php b/Listener/MogrifySubscriber.php index 2c09649..5b2758a 100644 --- a/Listener/MogrifySubscriber.php +++ b/Listener/MogrifySubscriber.php @@ -12,15 +12,15 @@ namespace Snowcap\ImBundle\Listener; use Doctrine\Common\EventSubscriber; -use Doctrine\ORM\Event\LifecycleEventArgs; -use Doctrine\ORM\Event\PreFlushEventArgs; use Metadata\MetadataFactoryInterface; use Snowcap\ImBundle\Manager as ImManager; use Symfony\Component\PropertyAccess\PropertyAccess; -use Symfony\Component\HttpFoundation\File\UploadedFile; +use Doctrine\ORM\Event\PreFlushEventArgs; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Snowcap\ImBundle\Doctrine\Metadata\MogrifyMetadata; /** - * Event listener for Doctrine entities to evualuate and execute ImBundle annotations + * Event listener for Doctrine entities to evualuate and execute Mogrify annotations */ class MogrifySubscriber implements EventSubscriber { @@ -70,8 +70,10 @@ public function preFlush(PreFlushEventArgs $ea) foreach ($entityMaps as $entities) { foreach ($entities as $entity) { - foreach ($this->metadataFactory->getMetadataForClass(get_class($entity))->propertyMetadata as $file) { - $this->mogrify($entity, $file); + foreach ($this->metadataFactory->getMetadataForClass(get_class($entity))->propertyMetadata as $propertyMetadata) { + if($propertyMetadata instanceof MogrifyMetadata) { + $this->mogrify($entity, $propertyMetadata); + } } } } @@ -83,20 +85,22 @@ public function preFlush(PreFlushEventArgs $ea) public function prePersist(LifecycleEventArgs $ea) { $entity = $ea->getEntity(); - foreach ($this->metadataFactory->getMetadataForClass(get_class($entity))->propertyMetadata as $file) { - $this->mogrify($entity, $file); + foreach ($this->metadataFactory->getMetadataForClass(get_class($entity))->propertyMetadata as $propertyMetadata) { + if($propertyMetadata instanceof MogrifyMetadata) { + $this->mogrify($entity, $propertyMetadata); + } } } /** * @param $entity - * @param $file + * @param $propertyMetadata */ - private function mogrify($entity, $file) + private function mogrify($entity, $propertyMetadata) { - $uploadedFile = $this->propertyAccessor->getValue($entity, $file->name); - if ($uploadedFile instanceof UploadedFile) { - $this->imManager->mogrify($file->params, $uploadedFile->getPathName()); + $file = $this->propertyAccessor->getValue($entity, $propertyMetadata->name); + if ($file instanceof \SplFileInfo) { + $this->imManager->mogrify($propertyMetadata->params, $file->getPathName()); } } } diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 8ea715e..849a2a1 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -16,8 +16,8 @@ services: tags: - { name: twig.extension } - snowcap_im.metadata.mogrify_driver: - class: Snowcap\ImBundle\Doctrine\Driver\MogrifyDriver + snowcap_im.metadata.annotation_driver: + class: Snowcap\ImBundle\Doctrine\Driver\AnnotationDriver public: false arguments: [@annotation_reader] @@ -27,19 +27,25 @@ services: arguments: - {} - snowcap_im.metadata.mogrify_factory: + snowcap_im.metadata.annotation_factory: class: Metadata\MetadataFactory public: false - arguments: [@snowcap_im.metadata.mogrify_driver] + arguments: [@snowcap_im.metadata.annotation_driver] calls: - [setCache, ["@snowcap_im.metadata.cache"]] snowcap_im.mogrify_subscriber: class: Snowcap\ImBundle\Listener\MogrifySubscriber - arguments: [@snowcap_im.metadata.mogrify_factory, @snowcap_im.manager] + arguments: [@snowcap_im.metadata.annotation_factory, @snowcap_im.manager] tags: - { name: doctrine.event_subscriber, priority: 1 } + snowcap_im.convert_subscriber: + class: Snowcap\ImBundle\Listener\ConvertSubscriber + arguments: [@snowcap_im.metadata.annotation_factory, @snowcap_im.manager] + tags: + - { name: doctrine.event_subscriber, priority: 2 } + snowcap_im.form_extension: class: Snowcap\ImBundle\Form\Extension\ImageTypeExtension arguments: [@snowcap_im.manager] diff --git a/docs/usage.rst b/docs/usage.rst index 5b27cf4..025692b 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -47,10 +47,10 @@ From a controller // to resize the source file $im->mogrify($format, $path); -In entities +In entities (annotations) ----------- -If you need to alter an uploaded image, you can add annotations on the file public property from your entity +If you need to alter an uploaded image, you can add annotations on the public file property from your entity .. code-block:: php @@ -70,7 +70,57 @@ When the form is submitted, the file will then be "thumbnailed" to 100x100 if bi The *params* attribute can contain * an array of ImageMagick key/values (like the example above) -* a format predefined in config +* a string identifier of a format predefined in your config + +Keep original +~~~~~~~~~~~~~~~~~~~~~~ + +If you want to create a thumbnail while keeping the original, you can use the *targetProperty* attribute + +.. code-block:: php + + // ... + use Snowcap\ImBundle\Doctrine\Mapping as SnowcapIm; + // ... + + /** + * + * @Assert\File(maxSize="6000000") + * @SnowcapIm\Convert(params={"thumbnail"="100x100>"}, targetProperty="thumbnail") + */ + public $file; + + public $thumbnail; + +This will create an image file in the cache directory. Just like for the *Mogrify* annotation, you can then use the $file->move() method as usual. As the convert operation will only be executed when the entity is persisted, it might make sense to move the created thumbnail from the cache to a permanent location. Otherwise the cache might be cleared and the database record of the thumbnail will be orphaned. + +Multiple resizes +~~~~~~~~~~~~~~~~~~~~~~ + +To resize the original not to be any wider than 1024 and create say a medium sized version at a width of 612 and a thumbnail at 100x100, you can combine the *ConvertMultiple*, *Convert* and *Mogrify* annotations + +.. code-block:: php + + // ... + use Snowcap\ImBundle\Doctrine\Mapping as SnowcapIm; + // ... + + /** + * + * @Assert\File(maxSize="6000000") + * @SnowcapIm\Mogrify(params={"resize"="1024"}) + * @SnowcapIm\ConvertMultiple({ + * @SnowcapIm\Convert(params={"resize"="612"}, targetProperty="medium"), + * @SnowcapIm\Convert(params={"thumbnail"="100x100>"}, targetProperty="thumbnail") + * }) + */ + public $file; + + public $medium; + + public $thumbnail; + +**Note: Convert operations are always executed before mogrify operations.** Clearing the cache ------------------ @@ -82,4 +132,3 @@ You can clear the cache with the following command-line task ./app/console snowcap:im:clear [age] Where the age argument - optional - will only clear cache older than the [age] days -