LiipImagineBundle icon indicating copy to clipboard operation
LiipImagineBundle copied to clipboard

Reset settings when the config is divided into parts

Open peter-gribanov opened this issue 7 years ago • 2 comments

I want to put the filter configurations into modules, but in this case the global settings of resolvers and loaders will no longer apply to all filters.

Preconditions

# php -v
PHP 7.2.8 (cli) (built: Jul 21 2018 07:56:11) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.8, Copyright (c) 1999-2018, by Zend Technologies
# composer show --latest 'liip/*'
liip/imagine-bundle 2.1.0 2.1.0 This bundle provides an image manipulation abstraction toolkit for Symfony-based projects.

Steps to reproduce

  1. Create global config /config/packages/liip_imagine.yml with:
liip_imagine:
    resolvers:
        default:
            web_path:
                cache_prefix: 'media/suit'
    filter_sets:
        autocomplete:
            filters:
                fixed: { width: 60, height: 60 }
  1. Create config in module with:
liip_imagine:
    filter_sets:
        news_cover_in_list:
            filters:
                fixed: { width: 800, height: 600 }
  1. Then use this filters in template:
<img src="{{ 'media/test.jpg' | imagine_filter('autocomplete') }}" alt="" />
<img src="{{ 'media/test.jpg' | imagine_filter('news_cover_in_list') }}" alt="" />

Expected result

<img src="/media/suit/autocomplete/media/test.jpg" alt="" />
<img src="/media/suit/news_cover_in_list/media/test.jpg" alt="" />

Actual result

<img src="/media/cache/autocomplete/media/test.jpg" alt="" />
<img src="/media/cache/news_cover_in_list/media/test.jpg" alt="" />

peter-gribanov avatar Oct 12 '18 09:10 peter-gribanov

i also had this problem (and also 2 friends of mine @vworld, @MaxValEh)

we split our config in edition.yaml and app.yaml where edition.yaml are our global defaults and app.yaml are the settings for the current project/app.

our kernel.php looks a little bit different:

    protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
    {
        parent::configureContainer($container, $loader);

        $confDir = $this->getProjectDir().'/config';
        $loader->load($confDir.'/{edition}/*'.self::CONFIG_EXTS, 'glob');
        $loader->load($confDir.'/{edition}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
        $loader->load($confDir.'/{app}/*'.self::CONFIG_EXTS, 'glob');
        $loader->load($confDir.'/{app}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
    }

edition.yaml

liip_imagine:
    driver: "gd"
    resolvers:
        default:
            web_path:
                web_root: '%kernel.project_dir%/public' 
                cache_prefix: 'cache/images'
    loaders:
        default:
            filesystem:
                data_root:
                    - '%kernel.project_dir%/assets/images'

app.yaml:

liip_imagine:
    loaders:
        default:
            filesystem:
                data_root:
                    - '%kernel.project_dir%/var/data/uploads/photo-app'
php bin/console debug:config liip_imagine
liip_imagine:
    driver: gd
    resolvers:
        default:
            web_path:
                web_root: '/src/web'
                cache_prefix: media/cache
    loaders:
        default:
            filesystem:
                data_root:
                    - '/src/assets/images'
                    - '/src/var/data/uploads/photo-app'

so the data_root root gets correctly merged but the web_path is wrong now.

the reason is how the bundle handles the config.

vendor/liip/imagine-bundle/DependencyInjection/Configuration.php

        $rootNode
            ->beforeNormalization()
                ->ifTrue(function ($v) {
                    return
                        empty($v['loaders']) ||
                        empty($v['loaders']['default']) ||
                        empty($v['resolvers']) ||
                        empty($v['resolvers']['default']);
                })
                ->then(function ($v) {
                    if (empty($v['loaders'])) {
                        $v['loaders'] = [];
                    }

                    if (false === is_array($v['loaders'])) {
                        throw new \LogicException('Loaders has to be array');
                    }

                    if (false === array_key_exists('default', $v['loaders'])) {
                        $v['loaders']['default'] = ['filesystem' => null];
                    }

                    if (empty($v['resolvers'])) {
                        $v['resolvers'] = [];
                    }

                    if (false === is_array($v['resolvers'])) {
                        throw new \LogicException('Resolvers has to be array');
                    }

                    if (false === array_key_exists('default', $v['resolvers'])) {
                        $v['resolvers']['default'] = ['web_path' => null];
                    }

                    return $v;
                })
            ->end();

if you remove the following part from liip imagines code

//                    if (false === array_key_exists('default', $v['resolvers'])) {
//                        $v['resolvers']['default'] = ['web_path' => null];
//                    }

it works.

quite a unpleasent situation, the current error handling is correct but kills hierarchical configuration. i rather would go for hierarchical config instead of "better" error handling, feels like the better DX.

having only the following config

liip_imagine:
    loaders:
        default:
            filesystem:
                data_root:
                    - '%kernel.project_dir%/var/data/uploads/photo-app'

still creates a valid config (no exceptions, no errors) where its clear that i have no resolvers configured.

liip_imagine:
    driver: gd
    loaders:
        default:
            filesystem:
                data_root:
                    - '/src/var/data/uploads/photo-app'
                locator: filesystem
                bundle_resources:
                    enabled: false
                    access_control_type: blacklist
                    access_control_list: {  }
    resolvers: {}

the current behavior feels like symfony1 magic.

c33s avatar Aug 26 '19 13:08 c33s

The problem is that additional configs are loaded after the main config.

# main config
liip_imagine:
    resolvers:
        default:
            flysystem:
                filesystem_service: oneup_flysystem.profile_photos_filesystem
                root_url: "http://images.example.com"
                cache_prefix: media/cache
                visibility: public
    loaders:
        default:
            flysystem:
                filesystem_service: oneup_flysystem.profile_photos_filesystem
# additional config
liip_imagine:
    filter_sets:
        autocomplete:
            quality: 80
            format: jpg
            filters:
                background: { color: '#ffffff' }
                fixed: { width: 60, height: 60 }

Additional configs doesn't have options resolvers and loaders and a normalizer is launched for them. The normalizer adds options to the additional config and after the main and additional config are merged, the data in the main config is overwritten.

# ./bin/console debug:config liip_imagine
liip_imagine:
    resolvers:
        default:
            web_path:
                web_root: /application/app/../web
                cache_prefix: media/cache
    loaders:
        default:
            flysystem:
                filesystem_service: oneup_flysystem.profile_photos_filesystem
            filesystem:
                locator: filesystem
                data_root:
                    - /application/app/../web
                bundle_resources:
                    enabled: false
                    access_control_type: blacklist
                    access_control_list: {  }

I agree with @c33s. I would generally remove the normalization here.

peter-gribanov avatar Sep 10 '19 14:09 peter-gribanov