paneedesign/storage-bundle

A Symfony bundle that provide tools to handle media storage locally or on S3.

Installs: 2 097

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 9

Forks: 1

Open Issues: 0

Type:symfony-bundle

v6.0.3 2021-04-12 07:19 UTC

README

Latest Stable Version Total Downloads Latest Unstable Version License

A Symfony bundle that provide tools to handle media storage locally or on S3.

Installation

Step 1: Download the Bundle

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

$ composer require "paneedesign/storage-bundle"

This command requires you to have Composer installed globally, as explained in the installation chapter of the Composer documentation.

Step 2: Enable the Bundle

Then, enable the bundle by adding it to the list of registered bundles in the app/AppKernel.php file of your project:

<?php
// app/AppKernel.php

// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...

            new \Fresh\DoctrineEnumBundle\FreshDoctrineEnumBundle(),
            new \Liip\ImagineBundle\LiipImagineBundle(),
            new \Knp\Bundle\GaufretteBundle\KnpGaufretteBundle(),
            new \PaneeDesign\DiscriminatorMapBundle\PedDiscriminatorMapBundle(),
            new \PaneeDesign\StorageBundle\PedStorageBundle(),
        );

        // ...
    }

    // ...
}

Step 3: Configurations

Add to .env

###> paneedesign/storage-bundle ###
STORAGE_ADAPTER=local
STORAGE_DIRECTORY=uploads
STORAGE_THUMBS_PREFIX=thumbs
STORAGE_LOCAL_ENDPOINT=/uploads
###< paneedesign/storage-bundle ###

or

###> paneedesign/storage-bundle ###
STORAGE_ADAPTER=amazon_s3
STORAGE_DIRECTORY=uploads
STORAGE_THUMBS_PREFIX=thumbs
STORAGE_AMAZON_S3_KEY=key
STORAGE_AMAZON_S3_SECRET=secret
STORAGE_AMAZON_S3_REGION=eu-west-2
STORAGE_AMAZON_S3_ENDPOINT=https://s3.amazonaws.com
STORAGE_AMAZON_S3_BUCKET_NAME=ped-local
STORAGE_AMAZON_S3_EXPIRE_AT="+1 hour"
###< paneedesign/storage-bundle ###

Copy under config/packeges following files:

  • config/packeges/ped_storage.yaml

and under config/routes:

  • config/routes/ped_storage.yaml

Set into config/packages/doctrine.yaml

//...

doctrine:
    dbal:
        types:
            enum_media_type: PaneeDesign\StorageBundle\DBAL\EnumMediaType
            enum_file_type: PaneeDesign\StorageBundle\DBAL\EnumFileType

and into config/packages/ped_discriminator_map.yaml

//...

ped_discriminator_map:
    maps:
        media:
            entity: PaneeDesign\StorageBundle\Entity\Media
            children:
                app_media: AppBundle\Entity\Media
        ...

Step 4: Use

You can store image or document and retrive full url of resource using this snippets:

<?php

declare(strict_types=1);

namespace App\Handler;

use App\Entity\Media;
use Gaufrette\Extras\Resolvable\UnresolvableObjectException;
use PaneeDesign\StorageBundle\DBAL\EnumFileType;
use PaneeDesign\StorageBundle\DBAL\EnumMediaType;
use PaneeDesign\StorageBundle\Entity\Media as PedMedia;
use PaneeDesign\StorageBundle\Handler\MediaHandler;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Routing\RouterInterface;

class StorageHandler
{
    /**
     * @var RouterInterface
     */
    protected $router;
    /**
     * @var MediaHandler
     */
    private $mediaHandler;

    /**
     * MediaManager constructor.
     *
     * @param MediaHandler    $mediaHandler
     * @param RouterInterface $router
     */
    public function __construct(MediaHandler $mediaHandler, RouterInterface $router)
    {
        $this->mediaHandler = $mediaHandler;
        $this->router = $router;
    }

    /**
     * @param PedMedia    $media
     * @param string|null $filter
     *
     * @return string
     */
    public function generateAbsoluteUri(PedMedia $media, ?string $filter = null)
    {
        $url = '';

        try {
            if (null !== $filter) {
                if ($media->hasFilter($filter)) {
                    $url = $media->getUrl($filter);
                } else {
                    $url = $this->router->generate('ped_storage_image', [
                        'key' => $media->getKey(),
                        'filter' => $filter,
                    ]);
                }
            } else {
                $url = $this->mediaHandler->getFullUrl($media->getFullKey());
            }
        } catch (UnresolvableObjectException $e) {
        } catch (\Exception $e) {
        }

        return $url ?: '';
    }

    /**
     * @param int           $entityId
     * @param string        $type
     * @param UploadedFile  $media
     * @param PedMedia|null $image
     * @param string|null   $mediaType
     *
     * @throws \Exception
     *
     * @return Media
     */
    public function storeImage(
        int $entityId,
        string $type,
        UploadedFile $media,
        ?PedMedia $image = null,
        ?string $mediaType = EnumMediaType::PROFILE
    ): Media {
        $hasPublicAccess = false;
        $allowedMimeTypes = [
            'image/jpeg',
            'image/png',
            'image/gif',
        ];

        $uploader = $this->getUploader($entityId, $type, EnumFileType::IMAGE, $hasPublicAccess, $allowedMimeTypes);

        if (null === $image) {
            $image = new Media();
            $image->setType($mediaType);
        } else {
            $image->clearFilters();
            $uploader->remove($image);
        }

        $uploader->save($media);

        $image = new Media();
        $image->setKey($uploader->getKey());
        $image->setPath($uploader->getFullKey(''));
        $image->setFileType($uploader->getFileType());
        $image->setSize($media->getSize());
        $image->setIsPublic($uploader->getHasPublicAccess());

        return $image;
    }

    /**
     * @param int           $entityId
     * @param string        $type
     * @param UploadedFile  $media
     * @param PedMedia|null $document
     * @param string|null   $mediaType
     *
     * @throws \Exception
     *
     * @return Media
     */
    public function storeDocument(
        int $entityId,
        string $type,
        UploadedFile $media,
        ?PedMedia $document = null,
        ?string $mediaType = EnumMediaType::DOCUMENT
    ): Media {
        $hasPublicAccess = true;
        $allowedMimeTypes = [
            'image/jpeg',
            'image/png',
            'image/gif',
            'application/pdf',
        ];

        $uploader = $this->getUploader($entityId, $type, EnumFileType::DOCUMENT, $hasPublicAccess, $allowedMimeTypes);

        if (null === $document) {
            $document = new Media();
            $document->setType($mediaType);
        } else {
            $document->clearFilters();
            $uploader->remove($document);
        }

        $uploader->save($media);

        $document = new Media();
        $document->setKey($uploader->getKey());
        $document->setPath($uploader->getFullKey(''));
        $document->setFileType($uploader->getFileType());
        $document->setSize($media->getSize());
        $document->setIsPublic($uploader->getHasPublicAccess());

        return $document;
    }

    /**
     * @param int    $entityId
     * @param string $type
     * @param string $fileType
     * @param array  $allowedMimeTypes
     * @param bool   $hasPublicAccess
     *
     * @return MediaHandler
     */
    private function getUploader(
        int $entityId,
        string $type,
        string $fileType,
        bool $hasPublicAccess,
        array $allowedMimeTypes = []
    ): MediaHandler {
        /* @var MediaHandler $uploader */
        $uploader = $this->mediaHandler
            ->setId($entityId)
            ->setType($type)
            ->setFileType($fileType)
            ->setAllowedMimeTypes($allowedMimeTypes)
            ->setHasPublicAccess($hasPublicAccess);

        return $uploader;
    }
}

Bonus

Resize:

Required:

  • filter

In config/packages/liip_imagine.yaml define filter as:

liip_imagine:
    #filters
    filter_sets:
        # 1x1
        icon_1-1:
            quality: 75
            filters:
                relative_resize:
                    heighten: 40
                thumbnail:
                    size: [40, 40]
                    mode: outbound
                    allow_upscale: true
        small_1-1:
            quality: 75
            filters:
                relative_resize:
                    heighten: 400
                thumbnail:
                    size: [400, 400]
                    mode: outbound
                    allow_upscale: true
        medium_1-1:
            quality: 85
            filters:
                relative_resize:
                    heighten: 800
                thumbnail:
                    size: [800, 800]
                    mode: outbound
                    allow_upscale: true
        large_1-1:
            quality: 90
            filters:
                relative_resize:
                    heighten: 1200
                thumbnail:
                    size: [1200, 1200]
                    mode: outbound
                    allow_upscale: true
        xlarge_1-1:
            quality: 95
            filters:
                relative_resize:
                    heighten: 1600
                thumbnail:
                    size: [1600, 1600]
                    mode: outbound
                    allow_upscale: true

so when you call this url http://example.com/image/5dc350316d2ee.jpeg?filter=icon_1-1 you have a square image scaled to dimension 40x40px

Crop:

Required:

  • filter
  • start-x
  • start-y
  • width
  • height

when you call this url http://example.com/image/5dc350316d2ee.jpeg?filter=crop_medium_1-1&start-x=20&start-y=50&width=800&height=800 combine scale option of medium_1-1 with crop info.

So you have a crop of original image from point [20,50] to [820,850] (width and height equal 800px) and scale to 800x800px

Rotate:

Required:

  • filter
  • angle

when you call this url http://example.com/image/5dc350316d2ee.jpeg?filter=rotate_medium_1-1&angle=90 combine scale option of medium_1-1 with rotation angle.

So original image is rotated of 90° and scaled to 800x800px

Authors

👤 Fabiano Roberto fabiano.roberto@ped.technology

👤 Luigi Cardamone luigi.cardamone@ped.technology

🤝 Contributing

Contributions, issues and feature requests are welcome!
Feel free to check issues page.

Show your support

Give a ⭐️ if this project helped you!

This README was generated with ❤️ by readme-md-generator