How to get Sonata Media Bundle to generate pictures in separate folders

Jozef Kétyi

I'm struggling with a problem that I need Sonata Media Bundle to upload pictures to different folders. What I'm trying to reach is to generate a picture structure as the following: if the picture has an ID for 1234567 the original image will be put into folder: "/web/uploads/1234/567/original' And all the generated thumbnails/sizes will be like: "/web/uploads/1234/567/100x130" and "/web/uploads/1234/567/200x100" depending on the generated size.

Is this folder structure generation possible in Sonata Media Bundle and if yes, how?

Thanks for the answers. Jozef

Jozef Kétyi

We worked out a "workaround" for this problem:

Basically what we have done is that we created our own Generator, Provider and Resizer to match our needs and than injected it back to the Media Bundle. The generator implements Generator Interface, the constructor only has one argument ($this->firstLevel = 1000;). Than:

public function generatePath(MediaInterface $media)
{
    $rep_first_level = (int) ($media->getId() / $this->firstLevel);
    $rep_second_level = (int) ($media->getId() % $this->firstLevel);

    return sprintf('%s/%04s/%03s', $media->getContext(), $rep_first_level, $rep_second_level);
}

This will create the sub directories in the preferred way "/web/uploads/1234/567" for image with ID "1234567".

Our provider extends ImageProvider and has only 1 instances:

protected function generateReferenceName(MediaInterface $media)
{
    $metadata = $media->getProviderMetadata();
    $fileName = $metadata['filename'];
    $temp = explode('.', $fileName);
    $name = $temp[0];
    return '/origos/' . $name . '.' . $media->getBinaryContent()->getExtension();
}

Our Resizer class will extend squareResizer. This was changed to be able to generate pictures (thumbnails) the exact size (100x100 and 190x100 also) by cropping the image. For this we created a new resizer which implements ResizerInterface:

<?php

namespace Sita\<YourBundle>\Resizer;

use Imagine\Image\ImagineInterface;
use Imagine\Image\Box;
use Imagine\Image\Point;
use Gaufrette\File;
use Sonata\MediaBundle\Model\MediaInterface;
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
use Sonata\MediaBundle\Resizer\ResizerInterface;

class <YourResizer> implements ResizerInterface
{
    /**
     * ImagineInterface
     */
    protected $adapter;

    /**
     * string
     */
    protected $mode;

    /**
     * @param ImagineInterface $adapter
     * @param string $mode
     * @param MetadataBuilderInterface $metadata
     */
    public function __construct(ImagineInterface $adapter, $mode, MetadataBuilderInterface $metadata)
    {
        $this->adapter = $adapter;
        $this->mode    = $mode;
        $this->metadata = $metadata;
    }

    /**
     * {@inheritdoc}
     */
    public function resize(MediaInterface $media, File $in, File $out, $format, array $settings)
    {
        if (!isset($settings['width'])) {
            throw new \RuntimeException(sprintf('Width parameter is missing in context "%s" for provider "%s"', $media->getContext(), $media->getProviderName()));
        }

        $image = $this->adapter->load($in->getContent());
        $size  = $media->getBox();

        if (null != $settings['height']) {
            $ratioWidth = $size->getWidth() / $settings['width'];
            $ratioHeight = $size->getHeight() / $settings['height'];
            $ratio = $ratioHeight > $ratioWidth ? $ratioWidth : $ratioHeight;

            $point = new Point(($size->getWidth() - $settings['width'] * $ratio) / 2, ($size->getHeight() - $settings['height'] * $ratio) / 2);

            $image->crop($point, new Box($settings['width'] * $ratio, $settings['height'] * $ratio));
            $size = $image->getSize();
        }

        $settings['height'] = (int) ($settings['width'] * $size->getHeight() / $size->getWidth());

        if ($settings['height'] < $size->getHeight() && $settings['width'] < $size->getWidth()) {
            $content = $image
                ->thumbnail(new Box($settings['width'], $settings['height']), $this->mode)
                ->get($format, array('quality' => $settings['quality']));
        } else {
            $content = $image->get($format, array('quality' => $settings['quality']));
        }

        $out->setContent($content, $this->metadata->get($media, $out->getName()));
    }

    /**
     * {@inheritdoc}
     */
    public function getBox(MediaInterface $media, array $settings)
    {
        $size = $media->getBox();

        if (null != $settings['height']) {

            if ($size->getHeight() > $size->getWidth()) {
                $higher = $size->getHeight();
                $lower  = $size->getWidth();
            } else {
                $higher = $size->getWidth();
                $lower  = $size->getHeight();
            }

            if ($higher - $lower > 0) {
                return new Box($lower, $lower);
            }
        }

        $settings['height'] = (int) ($settings['width'] * $size->getHeight() / $size->getWidth());

        if ($settings['height'] < $size->getHeight() && $settings['width'] < $size->getWidth()) {
            return new Box($settings['width'], $settings['height']);
        }

        return $size;
    }
}

It was a bit challenge to do the dependency injection, but here is the result:

Services:

    parameters:
    <yourBundle>.generator.<project>_generator.class: Sita\<yourBundle>\Generator\<project>Generator
    <yourBundle>.resizer.<project>_resizer.class: Sita\<yourBundle>\Resizer\<project>Resizer
    <yourBundle>.thumbnail.<project>_thumbnail.class: Sita\<yourBundle>\Thumbnail\<project>Thumbnail
    <yourBundle>.provider.<project>_provider.class: Sita\<yourBundle>\Provider\<project>Provider
services:
    <yourBundle>.generator.<project>_generator:
        class: %<yourBundle>.generator.<project>_generator.class%
        arguments:
            - ~

    <yourBundle>.resizer.<project>_resizer:
        class: %<yourBundle>.resizer.<project>_resizer.class%
        arguments:
            - @sonata.media.adapter.image.gd
            - %sonata.media.resizer.square.adapter.mode%
            - @sonata.media.metadata.proxy

    <yourBundle>.thumbnail.<project>_thumbnail:
        class: %<yourBundle>.thumbnail.<project>_thumbnail.class%
        arguments:
            - %sonata.media.thumbnail.format.default%

    <yourBundle>.provider.<project>:
        class: %<yourBundle>.provider.<project>_provider.class%
        arguments:
            - <yourBundle>.provider.<project>
            - ~
            - ~
            - ~
            - @<yourBundle>.thumbnail.<project>_thumbnail
            - ~
            - ~
            - ~
            - @sonata.media.metadata.proxy
        calls:
            - [setResizer, ["@<yourBundle>.resizer.<project>_resizer"]]
        tags:
            - { name: sonata.media.provider }

Configuration:

<?php

namespace <yourBundle>\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * This is the class that validates and merges configuration from your app/config files
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
 */
class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritdoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('<yourBundle>');

        $rootNode
            ->children()
                ->arrayNode('providers')
                    ->addDefaultsIfNotSet()
                    ->children()
                        ->arrayNode('<project>')
                            ->addDefaultsIfNotSet()
                            ->children()
                                ->scalarNode('service')->defaultValue('<yourBundle>.provider.<project>')->end()
                                ->scalarNode('resizer')->defaultValue('<yourBundle>.resizer.<project>_resizer')->end()
                                ->scalarNode('filesystem')->defaultValue('sonata.media.filesystem.local')->end()
                                ->scalarNode('cdn')->defaultValue('sonata.media.cdn.server')->end()
                                ->scalarNode('generator')->defaultValue('<yourBundle>.generator.<project>_generator')->end()
                                ->scalarNode('thumbnail')->defaultValue('<yourBundle>.thumbnail.<project>_thumbnail')->end()
                                ->scalarNode('adapter')->defaultValue('sonata.media.adapter.image.gd')->end()
                                ->arrayNode('allowed_extensions')
                                    ->prototype('scalar')->end()
                                    ->defaultValue(array('jpg', 'png', 'jpeg'))
                                ->end()
                                ->arrayNode('allowed_mime_types')
                                    ->prototype('scalar')->end()
                                    ->defaultValue(array(
                                        'image/pjpeg',
                                        'image/jpeg',
                                        'image/png',
                                        'image/x-png',
                                    ))
                                ->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();

        return $treeBuilder;
    }
}

and Extension:

<?php

namespace <yourBundle>\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

/**
 * This is the class that loads and manages your bundle configuration
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
 */
class <yourBundle>Extension extends Extension
{
    /**
     * {@inheritdoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');

        $container->getDefinition('<yourBundle>.provider.<project>')
            ->replaceArgument(1, new Reference($config['providers']['<project>']['filesystem']))
            ->replaceArgument(2, new Reference($config['providers']['<project>']['cdn']))
            ->replaceArgument(3, new Reference($config['providers']['<project>']['generator']))
            ->replaceArgument(4, new Reference($config['providers']['<project>']['thumbnail']))
            ->replaceArgument(5, array_map('strtolower', $config['providers']['<project>']['allowed_extensions']))
            ->replaceArgument(6, $config['providers']['<project>']['allowed_mime_types'])
            ->replaceArgument(7, new Reference($config['providers']['<project>']['adapter']))
        ;

    }
}

And finally the config.yml from the symfony config file:

sonata_media:
    # if you don't use default namespace configuration
    #class:
    #    media: MyVendor\MediaBundle\Entity\Media
    #    gallery: MyVendor\MediaBundle\Entity\Gallery
    #    gallery_has_media: MyVendor\MediaBundle\Entity\GalleryHasMedia
    default_context: default
    db_driver: doctrine_orm # or doctrine_mongodb, doctrine_phpcr
    contexts:
        default:  # the default context is mandatory
            providers:
                - <yourBundle>.provider.<project>

            formats:
                small: { width: 100 , quality: 70}
                big:   { width: 500 , quality: 70}
                100x100: { width: 100 , height: 100 , quality: 100 }
                126x190: { width: 126 , height: 190 , quality: 100 }
                190x126: { width: 190 , height: 126 , quality: 100 }
                190x56: { width: 190 , height: 56 , quality: 100 }

    cdn:
        server:
            path: /uploads/media # http://media.sonata-project.org/

    filesystem:
        local:
            directory:  %kernel.root_dir%/../web/uploads/media
            create:     true

<yourBundle>:

I know this is not the clearest work, but it does the job, good for now :)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

How to get Sonata Media Bundle to generate pictures in separate folders

From Dev

Sonata Media Bundle Override

From Dev

error in sonata media bundle

From Dev

How to override default upload path of sonata media bundle in symfony?

From Dev

Sonata Media Bundle - How to extend FormatThumbnail.php

From Dev

How to override default upload path of sonata media bundle in symfony?

From Dev

Sonata Media Bundle Installation error

From Dev

Sonata Media Bundle with Sonata Admin Bundle 3.0 (or 2.4)

From Dev

Issue Configuring Sonata Media Bundle with MongoDB

From Dev

Issue Configuring Sonata Media Bundle with MongoDB

From Dev

API Post to Sonata Media Bundle Symfony 3

From Dev

Symfony 3 - Sonata Aplication Media Bundle error

From Dev

How to get images listed in sonata admin bundle backend

From Dev

How to use Sonata Notification Bundle?

From Dev

How to use Sonata Notification Bundle?

From Dev

Sonata admin bundle preview image from some entity in list mapper without sonata media bundle

From Dev

Symfony 2 Sonata Media bundle: saving media file image without sonata admin

From Dev

Symfony Sonata Media Bundle add images/videos to a user

From Dev

Sonata Media Bundle + AWS S3 - specify subdirectory?

From Dev

how to rename file upload with sonata bundle

From Dev

How to display a photo in Sonata Admin Bundle

From Dev

How to validation to sonata media type in symfony 2

From Dev

Can't get webpack require.ensure chunking method to work with react-router and generate separate bundle files

From Dev

Can't get webpack require.ensure chunking method to work with react-router and generate separate bundle files

From Dev

how to override a css of sonata admin bundle in symfony2

From Dev

How to upload image and display on edit of profile using sonata admin bundle

From Dev

how to include an entity join in sonata bundle admin list view

From Dev

Sonata Admin Bundle and roles may explain to me how it works

From Dev

How to add filter to configureListField in Sonata Admin Bundle (createQuery method)

Related Related

  1. 1

    How to get Sonata Media Bundle to generate pictures in separate folders

  2. 2

    Sonata Media Bundle Override

  3. 3

    error in sonata media bundle

  4. 4

    How to override default upload path of sonata media bundle in symfony?

  5. 5

    Sonata Media Bundle - How to extend FormatThumbnail.php

  6. 6

    How to override default upload path of sonata media bundle in symfony?

  7. 7

    Sonata Media Bundle Installation error

  8. 8

    Sonata Media Bundle with Sonata Admin Bundle 3.0 (or 2.4)

  9. 9

    Issue Configuring Sonata Media Bundle with MongoDB

  10. 10

    Issue Configuring Sonata Media Bundle with MongoDB

  11. 11

    API Post to Sonata Media Bundle Symfony 3

  12. 12

    Symfony 3 - Sonata Aplication Media Bundle error

  13. 13

    How to get images listed in sonata admin bundle backend

  14. 14

    How to use Sonata Notification Bundle?

  15. 15

    How to use Sonata Notification Bundle?

  16. 16

    Sonata admin bundle preview image from some entity in list mapper without sonata media bundle

  17. 17

    Symfony 2 Sonata Media bundle: saving media file image without sonata admin

  18. 18

    Symfony Sonata Media Bundle add images/videos to a user

  19. 19

    Sonata Media Bundle + AWS S3 - specify subdirectory?

  20. 20

    how to rename file upload with sonata bundle

  21. 21

    How to display a photo in Sonata Admin Bundle

  22. 22

    How to validation to sonata media type in symfony 2

  23. 23

    Can't get webpack require.ensure chunking method to work with react-router and generate separate bundle files

  24. 24

    Can't get webpack require.ensure chunking method to work with react-router and generate separate bundle files

  25. 25

    how to override a css of sonata admin bundle in symfony2

  26. 26

    How to upload image and display on edit of profile using sonata admin bundle

  27. 27

    how to include an entity join in sonata bundle admin list view

  28. 28

    Sonata Admin Bundle and roles may explain to me how it works

  29. 29

    How to add filter to configureListField in Sonata Admin Bundle (createQuery method)

HotTag

Archive