Generating Image and File URLs 

File URL Providers 

To generate a URL for an image or a file, use the oro_attachment.provider.file_url service backed by a chain of classes that implement Oro\Bundle\AttachmentBundle\Provider\FileUrlProviderInterface with the following methods:

  • getFileUrl - to get a URL for downloading the specified file.

  • getFilteredImageUrl - to get a URL for displaying an image represented by the specified file and with an applied LiipImagine filter.

  • getResizedImageUrl - to get a URL for displaying an image represented by the specified file and resized to the specified width and height.

Note

The same methods for generating URLs are provided by the oro_attachment.manager service, which is the facade with common methods required when working with files.

// Generates a URL for resized image of $image with $filterName LiipImagine filter.
$imageUrl = $this->fileUrlProvider->getFilteredImageUrl($image, $filterName);
// Generates a URL for resized image of $image with $filterName LiipImagine filter and converted to 'webp' format.
// Extension 'webp' will be appended to the filename.
$webpImageUrl = $this->fileUrlProvider->getFilteredImageUrl($image, $filterName, 'webp');

Custom URL Provider 

In order to hook into the logic of generating a URL for a file or image, decorate service oro_attachment.provider.file_url with a class that implements the Oro\Bundle\AttachmentBundle\Provider\FileUrlProviderInterface interface. For example:

src/Acme/Bundle/DemoBundle/Provider/CustomUrlProvider.php 
namespace Acme\Bundle\DemoBundle\Provider;

use Oro\Bundle\AttachmentBundle\Entity\File;
use Oro\Bundle\AttachmentBundle\Provider\FileUrlProviderInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class CustomUrlProvider implements FileUrlProviderInterface
{
    private FileUrlProviderInterface $innerFileUrlProvider;

    /**
     * @param FileUrlProviderInterface $innerFileUrlProvider
     */
    public function __construct(FileUrlProviderInterface $innerFileUrlProvider)
    {
        $this->innerFileUrlProvider = $innerFileUrlProvider;
    }

    /**
     * @inheritDoc
     */
    public function getFileUrl(File $file, string $action = self::FILE_ACTION_GET, int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
    {
        return 'custom url here';
    }

    /**
     * @inheritDoc
     */
    public function getResizedImageUrl(File $file, int $width, int $height, string $format = '', int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
    {
        return 'custom url here';
    }

    /**
     * @inheritDoc
     */
    public function getFilteredImageUrl(File $file, string $filterName, string $format = '', int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
    {
        if (/* custom condition here */) {
            /* custom logic here */
            return 'http://example.org/my-custom-url.png';
        }

        // Pass the control to the decorated class if the custom condition is not satisfied.
        return $this->innerFileUrlProvider->getFilteredImageUrl($file, $filterName, $format, $referenceType);
    }
}
src/Acme/Bundle/DemoBundle/Resources/config/services.yml 
services:
    acme.custom_file_url_provider:
        class: Acme\Bundle\DemoBundle\Provider\CustomUrlProvider
        decorates: oro_attachment.provider.file_url
        arguments:
            - '@.inner'

Custom Filename Provider 

To hook into the logic of generating a filename for a file or image, decorate service oro_attachment.provider.file_name with a class that implements the Oro\Bundle\AttachmentBundle\Provider\FileNameProviderInterface interface. For example:

src/Acme/Bundle/DemoBundle/Provider/CustomFileNameProvider.php 
namespace Acme\Bundle\DemoBundle\Provider;

use Oro\Bundle\AttachmentBundle\Entity\File;
use Oro\Bundle\AttachmentBundle\Provider\FileNameProviderInterface;
use Oro\Bundle\AttachmentBundle\Tools\FilenameExtensionHelper;
use Oro\Bundle\AttachmentBundle\Tools\FilenameSanitizer;
use Oro\Bundle\CatalogBundle\Entity\Category;
use Oro\Bundle\CatalogBundle\Entity\Repository\CategoryRepository;
use Oro\Bundle\EntityBundle\ORM\DoctrineHelper;
use Oro\Bundle\ProductBundle\Entity\Brand;
use Oro\Bundle\ProductBundle\Entity\Product;
use Oro\Bundle\ProductBundle\Entity\ProductImage;

class CustomFileNameProvider implements FileNameProviderInterface
{
    private const SEPARATOR = '-';

    private FileNameProviderInterface $innerProvider;
    private DoctrineHelper            $doctrineHelper;

    /**
     * @param FileNameProviderInterface $innerProvider
     * @param DoctrineHelper $doctrineHelper
     */
    public function __construct(
        FileNameProviderInterface $innerProvider,
        DoctrineHelper $doctrineHelper
    ) {
        $this->innerProvider  = $innerProvider;
        $this->doctrineHelper = $doctrineHelper;
    }

    /**
     * @inheritDoc
     */
    public function getFileName(File $file): string
    {
        if (!$this->isApplicable($file)) {
            return $this->innerProvider->getFileName($file);
        }

        return $this->getNameWithFormat($file);
    }

    /**
     * @inheritDoc
     */
    public function getFilteredImageName(File $file, string $filterName, string $format = ''): string
    {
        if (!$this->isApplicable($file)) {
            return $this->innerProvider->getFilteredImageName($file, $filterName, $format);
        }

        return $this->getNameWithFormat($file, $format);
    }

    /**
     * @inheritDoc
     */
    public function getResizedImageName(File $file, int $width, int $height, string $format = ''): string
    {
        if (!$this->isApplicable($file)) {
            return $this->innerProvider->getResizedImageName($file, $width, $height, $format);
        }

        return $this->getNameWithFormat($file, $format);
    }

    /**
     * Provider is applicable for files uploaded as Product Images.
     *
     * @param File $file
     * @return bool
     */
    private function isApplicable(File $file): bool
    {
        return $file->getParentEntityClass() === ProductImage::class
            && $file->getOriginalFilename();
    }

    /**
     * @param File $file
     * @param string $format
     * @return string
     */
    private function getNameWithFormat(File $file, string $format = ''): string
    {
        $extension = $file->getExtension() ?? pathinfo($file->getFilename(), PATHINFO_EXTENSION);
        $filename  = str_replace(
            '.' . $extension,
            '',
            $file->getFilename()
        );

        $parentEntity = $this->getParentEntity($file);
        if ($parentEntity instanceof ProductImage) {
            $product = $parentEntity->getProduct();

            $brandName = $this->getBrandName($product);
            if ($brandName) {
                $filename .= self::SEPARATOR . $brandName;
            }

            $categoryTitle = $this->getCategoryTitle($product);
            if ($categoryTitle) {
                $filename .= self::SEPARATOR . $categoryTitle;
            }

            $filename .= self::SEPARATOR . $product->getDefaultName();
        }

        $filename .= '.' . $extension;
        $filename = FilenameExtensionHelper::addExtension($filename, $format);

        return FilenameSanitizer::sanitizeFilename($filename);
    }

    /**
     * @param File $file
     * @return ProductImage|null
     */
    private function getParentEntity(File $file): ?ProductImage
    {
        $parentEntityClass = $file->getParentEntityClass();
        if (!$parentEntityClass) {
            return null;
        }

        $parentEntityId = $file->getParentEntityId();
        if (!$parentEntityId) {
            return null;
        }

        return $this->doctrineHelper->getEntity($parentEntityClass, $parentEntityId);
    }

    /**
     * @param Product $product
     * @return string|null
     */
    private function getCategoryTitle(Product $product): ?string
    {
        /** @var CategoryRepository $repository */
        $repository = $this->doctrineHelper->getEntityRepository('OroCatalogBundle:Category');
        $category   = $repository->findOneByProduct($product);

        if ($category instanceof Category) {
            return $category->getDenormalizedDefaultTitle();
        }

        return null;
    }

    /**
     * @param Product $product
     * @return string|null
     */
    private function getBrandName(Product $product): ?string
    {
        $brand = $product->getBrand();
        if ($brand instanceof Brand) {
            return $brand->getDefaultName();
        }

        return null;
    }
}
src/Acme/Bundle/DemoBundle/Resources/config/services.yml 
services:
    acme.provider.custom_filename_provider:
        class: Acme\Bundle\DemoBundle\Provider\CustomFileNameProvider
        decorates: oro_attachment.provider.file_name
        decoration_priority: -200
        arguments:
            - '@.inner'
            - '@oro_entity.doctrine_helper'

Image and File URLs in TWIG 

OroAttachmentBundle provides the following TWIG functions for images and files:

  • file_url - to get a URL for downloading the specified file. Uses Oro\Bundle\AttachmentBundle\Provider\FileUrlProviderInterface::getFileUrl() under-the-hood.

  • file_size - to get a formatted size for the specified file.

  • resized_image_url - to get a URL for displaying a image represented by the specified file and resized to the specified width and height. Uses Oro\Bundle\AttachmentBundle\Provider\FileUrlProviderInterface::getFileUrl() under the hood.

  • filtered_image_url - to get a URL for displaying a image represented by the specified file and with applied LiipImagine filter. Uses Oro\Bundle\AttachmentBundle\Provider\FileUrlProviderInterface::getFileUrl() under the hood.

  • oro_attachment_icon - to get a CSS class adding an icon for the specified file depending on its mime type.

  • oro_type_is_image - to check if the specified file represents an image.

  • oro_file_icons_config - to get a full list of icons CSS classes by mime types.

  • oro_file_view - to get a rendered view of a file. Uses @OroAttachment/Twig/file.html.twig under-the-hood.

  • oro_image_view - to get a rendered view of an image. Uses @OroAttachment/Twig/image.html.twig under-the-hood.

  • oro_resized_picture_sources - to get a collection of sources (URLs) to the resized image represented by the specified file. Should be used in a <picture> tag.

  • oro_filtered_picture_sources - to get a collection of sources (URLs) to the resized image represented by the specified file, with the LiipImagine filter applied. Should be used in a <picture> tag.

  • oro_file_title - to get a title (e.g. original filename) for the specified file. Uses Oro\Bundle\AttachmentBundle\Provider\FileTitleProviderInterface under-the-hood.

See Oro\Bundle\AttachmentBundle\Twig\FileExtension for more information on functions arguments.