Message-ID: <1187710670.2770.1485850748945.JavaMail.confluence@ip-10-127-227-164> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_2769_982210258.1485850748928" ------=_Part_2769_982210258.1485850748928 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html Exposing SiteAccess-aware configuration for your bundle

Exposing SiteAccess-aware configuration for your bundle

=20
=20
=20
=20

D= escription

Symfony Config component makes it possible to define semantic c= onfiguration, exposed to the end developer. This configuration is vali= dated by rules you define, e.g. validating type (string, array, integer, bo= olean, etc.). Usually, once validated and processed, this semantic configur= ation is then mapped to internal key/value parameters st= ored in the ServiceContainer.

eZ Platform uses this for its core configuration, but adds another confi= guration level, the siteaccess. For each defined siteacces= s, we need to be able to use the same configuration tree in order to define= siteaccess-specific config. These settings then need to be mapped to sitea= ccess-aware internal parameters that you can retrieve via the Co= nfigResolver. For this, internal keys need to follow the format = ;<namespace>.<scope>.<parameter_name>, = namespace being specific to your app/bundle, s= cope being the siteaccess, siteaccess group, default=  or globalparameter_name&= nbsp;being the actual setting identifier.

For more information on ConfigResolve= r, namespaces and scopes, see <= a href=3D"https://doc.ez.no/display/DEVELOPER/SiteAccess#SiteAccess-Configu= ration" rel=3D"nofollow">eZ Platform configuration basics.

The goal of this feature is to make it easy to implement a = siteaccess-aware semantic configuration and its mapping= to internal config for any eZ bundle developer.

Solu= tion

Semantic configuration parsing

An abstract Configuration&nbs= p;class has been added, simplifying the way to add a siteaccess settings tr= ee like the following:

 

ezplatform.yml or config.yml
=20
acme_demo:
    system:
        my_siteaccess:
            hello: "world"
            foo_setting:
                an_integer: 456
                enabled: true

        my_siteaccess_group:
            hello: "universe"
            foo_setting:
                foo: "bar"
                some: "thing"
                an_integer: 123
                enabled: false
=20

 

Class FQN is eZ\Bundle\EzPublishCoreBundle\Depen= dencyInjection\Configuration\SiteAccessAware\Configuration.All you have to do is to ex= tend it and use $this-&= gt;generateScopeBaseNode():<= /span>

=20
namespace Acme\DemoBundle\DependencyInjection;

use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAcc=
essAware\Configuration as SiteAccessConfiguration;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;

class Configuration extends SiteAccessConfiguration
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder =3D new TreeBuilder();
        $rootNode =3D $treeBuilder->root( 'acme_demo' );

        // $systemNode will then be the root of siteaccess aware settings.
        $systemNode =3D $this->generateScopeBaseNode( $rootNode );
        $systemNode
            ->scalarNode( 'hello' )->isRequired()->end()
            ->arrayNode( 'foo_setting' )
                ->children()
                    ->scalarNode( "foo" )->end()
                    ->scalarNode( "some" )->end()
                    ->integerNode( "an_integer" )->end()
                    ->booleanNode( "enabled" )->end()
                ->end()
            ->end();

        return $treeBuilder;
    }
}
=20

Default name for the siteaccess r= oot node is system, but you can customize it. F= or this, just pass the name you want to use as a second argument of $this->generateScopeBaseNode().

Mapping to internal settings

Semantic configuration must always be mapped to inter= nal key/value settings within the ServiceCont= ainer. This is usually done in the DIC extension.

For siteaccess-aware settings, new ConfigurationProcessor and Contextualizer classes have been introd= uced to ease the process.

=20
namespace Acme\DemoBundle\DependencyInjection;

use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAcc=
essAware\ConfigurationProcessor;
use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAcc=
essAware\ContextualizerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
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 AcmeDemoExtension extends Extension
{
    public function load( array $configs, ContainerBuilder $container )
    {
        $configuration =3D $this->getConfiguration( $configs, $container=
 );
        $config =3D $this->processConfiguration( $configuration, $config=
s );

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

        // "acme_demo" will be the namespace as used in ConfigResolver form=
at.
        $processor =3D new ConfigurationProcessor( $container, 'acme_demo' =
);
        $processor->mapConfig(
            $config,
            // Any kind of callable can be used here.
            // It will be called for each declared scope/SiteAccess.
            function ( $scopeSettings, $currentScope, ContextualizerInterfa=
ce $contextualizer )
            {
                // Will map "hello" setting to "acme_demo.<$currentScope=
>.hello" container parameter
                // It will then be possible to retrieve this parameter thro=
ugh ConfigResolver in the application code:
                // $helloSetting =3D $configResolver->getParameter( 'hel=
lo', 'acme_demo' );
                $contextualizer->setContextualParameter( 'hello', $curre=
ntScope, $scopeSettings['hello'] );
            }
        );

        // Now map "foo_setting" and ensure keys defined for "my_siteaccess=
" overrides the one for "my_siteaccess_group"
        // It is done outside the closure as it is needed only once.
        $processor->mapConfigArray( 'foo_setting', $config );
    }
}
=20
Tip

You can map simple settings by calling $processor->mapSetting()= , without having to call $processor->mapConfig() wit= h a callable.

=20
$processor =3D new ConfigurationProcessor( $container, 'acme_demo' =
);
$processor->mapSetting( 'hello', $config );
=20

Important

Always ensure you have defined and loaded default settings.

@AcmeDemoBundle/Resources/config/default_settings.yml
=20
parameters:
    acme_demo.default.hello: world
    acme_demo.default.foo_setting:
        foo: ~
        some: ~
        planets: [Earth]
        an_integer: 0
        enabled: false
        j_adore: les_sushis
=20

Merging hash values between scopes

When you define a hash as semantic config, you sometimes don't want the = siteaccess settings to replace the default or group values, but en= rich them by appending new entries. This is made possible by usin= g $processor->mapConfigArray(), which needs to be call= ed outside the closure (before or after), in order to be called only once.<= /p>

Consider the following default config:

default_settings.yml
=20
parameters:
    acme_demo.default.foo_setting:
        foo: ~
        some: ~
        planets: [Earth]
        an_integer: 0
        enabled: false
        j_adore: les_sushis
=20


And then this semantic config:

 

ezplatform.yml or config.yml
=20
acme_demo:
    system:
        sa_group:
            foo_setting:
                foo: bar
                some: thing
                an_integer: 123

        # Assuming "sa1" is part of "sa_group"
        sa1:
            foo_setting:
                an_integer: 456
                enabled: true
                j_adore: le_saucisson
=20


What we want here is that keys defined for foo_s= etting are merged between default/group/siteaccess:

Expected result
=20
parameters:
    acme_demo.sa1.foo_setting:
        foo: bar
        some: thing
        planets: [Earth]
        an_integer: 456
        enabled: true
        j_adore: le_saucisson
=20

Merge from second level

In the example above, entries were merged in respect to the scope order = of precedence. However, if we define the planets key= forsa1, it will completely override the default value since t= he merge process is done at only 1 level.

You can add another level by passing ContextualizerInterface:= :MERGE_FROM_SECOND_LEVEL as an option (3rd argument) to$c= ontextualizer->mapConfigArray().

default_settings.yml
=20
parameters:
    acme_demo.default.foo_setting:
        foo: ~
        some: ~
        planets: [Earth]
        an_integer: 0
        enabled: false
        j_adore: [les_sushis]
=20
Semantic config (ezplatform.yml / config.yml)
=20
acme_demo:
    system:
        sa_group:
            foo_setting:
                foo: bar
                some: thing
                planets: [Mars, Venus]
                an_integer: 123

        # Assuming "sa1" is part of "sa_group"
        sa1:
            foo_setting:
                an_integer: 456
                enabled: true
                j_adore: [le_saucisson, la_truite_a_la_vapeur]
=20

Result of using ContextualizerInterface::MERGE_FROM_SECOND_LE= VEL option:

=20
parameters:
    acme_demo.sa1.foo_setting:
        foo: bar
        some: thing
        planets: [Earth, Mars, Venus]
        an_integer: 456
        enabled: true
        j_adore: [les_suhis, le_saucisson, la_truite_a_la_vapeur]
=20

There is also another option, ContextualizerInterface::UNIQUE, to be used when you want to ensure your array setting has unique values= . It will only work on normal arrays though, not hashes.

L= imitations

A few limitation exist with this scope hash merge:

  • Semantic setting name and internal name will be the same (like foo_setting in the examples above).
  • Applicable to first level semantic parameter only (i.e. settings right = under the siteaccess name).
  • Merge is not recursive. Only second level merge is possible by using&nb= sp;ContextualizerInterface::MERGE_FROM_SECOND_LEVEL optio= n.

Dedicated mapper object

Instead of passing a callable to $processor->mapConf= ig(), an instance of eZ\Bundle\EzPublishCoreBundle\Depend= encyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface can be passed.

This can be useful if you have a lot of configuration to map and d= on't want to pollute your DIC extension class (better for maintenance).

Merging hash values between scopes

As specified above, $contextualizer->ma= pConfigArray() is not to be used within the <= em>scope loop, like for simple values. When using a closure/call= able, you usually call it before or after $processor->= mapConfig(). For mapper objects, a dedicated interface can be = used: HookableConfigurationMapperInterface, = which defines 2 methods: preMap() and&n= bsp;postMap().


 

=20
=20
=20
=20

= In this topic:

=20
=20
=20
------=_Part_2769_982210258.1485850748928--