|
| 1 | +.. index:: |
| 2 | + single: Configuration; Semantic |
| 3 | + single: Bundle; Extension configuration |
| 4 | + |
| 5 | +How to simplify configuration of multiple Bundle |
| 6 | +================================================ |
| 7 | + |
| 8 | +Especially when building reusable and extensible applications developers are |
| 9 | +often faced with a choice, either create a single Bundle or create multiple |
| 10 | +Bundles. Creating a single Bundle has the draw back that its impossible for |
| 11 | +users to choose to remove functionality they are not using. Creating multiple |
| 12 | +Bundles has the draw back that configuration becomes more tedious and settings |
| 13 | +often need to be repeated for various Bundles. |
| 14 | + |
| 15 | +Using the below approach it is possible to remove the disadvantage of the |
| 16 | +multiple Bundle approach, by enabling a single Extension to prepend the settings |
| 17 | +for any Bundle. It can use the settings defined in the ``app/config/config.yml`` |
| 18 | +to prepend settings just as if they would have been written explicitly by the |
| 19 | +user in the application configuration. |
| 20 | + |
| 21 | +For example, this could be used to configure the entity manager name to use in |
| 22 | +multiple Bundle. Or it can be used to enable an optional feature that depends |
| 23 | +on another Bundle being loaded as well. |
| 24 | + |
| 25 | +In order for an Extension to be able to do this it needs to implement |
| 26 | +:class:`Symfony\\Component\\DependencyInjection\\Compiler\\PrependExtensionInterface`:: |
| 27 | + |
| 28 | + // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php |
| 29 | + namespace Acme\HelloBundle\DependencyInjection; |
| 30 | + |
| 31 | + use Symfony\Component\HttpKernel\DependencyInjection\Extension; |
| 32 | + use Symfony\Component\\DependencyInjection\PrependExtensionInterface; |
| 33 | + use Symfony\Component\DependencyInjection\ContainerBuilder; |
| 34 | + |
| 35 | + class AcmeHelloExtension extends Extension implements PrependExtensionInterface |
| 36 | + { |
| 37 | + // ... |
| 38 | + |
| 39 | + public function prepend(ContainerBuilder $container) |
| 40 | + { |
| 41 | + // ... |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | +Inside the :method:`Symfony\\Component\\DependencyInjection\\Compiler\\PrependExtensionInterface::prepend()` |
| 46 | +method developers have full access to the :class:`Symfony\Component\DependencyInjection\ContainerBuilder` |
| 47 | +instance just before the :method:`Symfony\Component\HttpKernel\DependencyInjection\ExtensionInterface:load()` |
| 48 | +method is called on the registered Bundle Extensions. In order to prepend settings |
| 49 | +to a Bundle extension developers can use the |
| 50 | +:method:`Symfony\Component\DependencyInjection\ContainerBuilder:prependExtensionConfig()` |
| 51 | +method on the :class:`Symfony\Component\DependencyInjection\ContainerBuilder` |
| 52 | +instance. As this method only prepends settings, any other settings done explicitly |
| 53 | +inside the ``app/config/config.yml`` would override these prepended settings. |
| 54 | + |
| 55 | +The following example illustrates how to prepend |
| 56 | +a configuration setting in multiple Bundles as well as disable a flag in multiple Bundles |
| 57 | +in case specific other Bundle is not registered:: |
| 58 | + |
| 59 | + public function prepend(ContainerBuilder $container) |
| 60 | + { |
| 61 | + // get all Bundles |
| 62 | + $bundles = $container->getParameter('kernel.bundles'); |
| 63 | + // determine if AcmeGoodbyeBundle is registered |
| 64 | + if (!isset($bundles['AcmeGoodbyeBundle'])) { |
| 65 | + // disable AcmeGoodbyeBundle in Bundles |
| 66 | + $config = array('use_acme_goodbye' => false); |
| 67 | + foreach ($container->getExtensions() as $name => $extension) { |
| 68 | + switch ($name) { |
| 69 | + case 'acme_something': |
| 70 | + case 'acme_other': |
| 71 | + // set use_acme_goodbye to false in the config of acme_something and acme_other |
| 72 | + // note that if the user manually configured use_acme_goodbye to true in the |
| 73 | + // app/config/config.yml then the setting would in the end be true and not false |
| 74 | + $container->prependExtensionConfig($name, $config); |
| 75 | + break; |
| 76 | + } |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + // process the configuration of AcmeHelloExtension |
| 81 | + $configs = $container->getExtensionConfig($this->getAlias()); |
| 82 | + // use the Configuration class to generate a config array with the settings ``acme_hello`` |
| 83 | + $config = $this->processConfiguration(new Configuration(), $configs); |
| 84 | + |
| 85 | + // check if entity_manager_name is set in the ``acme_hello`` configuration |
| 86 | + if (isset($config['entity_manager_name'])) { |
| 87 | + // prepend the acme_something settings with the entity_manager_name |
| 88 | + $config = array('entity_manager_name' => $config['entity_manager_name']); |
| 89 | + $container->prependExtensionConfig('acme_something', $config); |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | +The above would be the equivalent of writing the following into the ``app/config/config.yml`` |
| 94 | +in case ``AcmeGoodbyeBundle`` is not registered and the ``entity_manager_name`` setting |
| 95 | +for ``acme_hello`` is set to ``non_default``:: |
| 96 | + |
| 97 | +.. configuration-block:: |
| 98 | + |
| 99 | + .. code-block:: yaml |
| 100 | +
|
| 101 | + # app/config/config.yml |
| 102 | +
|
| 103 | + acme_something: |
| 104 | + # ... |
| 105 | + use_acme_goodbye: false |
| 106 | + entity_manager_name: non_default |
| 107 | +
|
| 108 | + acme_other: |
| 109 | + # ... |
| 110 | + use_acme_goodbye: false |
| 111 | +
|
| 112 | + .. code-block:: xml |
| 113 | +
|
| 114 | + <!-- app/config/config.xml --> |
| 115 | +
|
| 116 | + <acme-something:config use-acme-goodbye="false"> |
| 117 | + <acme-something:entity-manager-name>non_default</acme-something:entity-manager-name> |
| 118 | + </acme-something:config> |
| 119 | +
|
| 120 | + <acme-other:config use-acme-goodbye="false" /> |
| 121 | +
|
| 122 | + .. code-block:: php |
| 123 | +
|
| 124 | + // app/config/config.php |
| 125 | +
|
| 126 | + $container->loadFromExtension('acme_something', array( |
| 127 | + ..., |
| 128 | + 'use_acme_goodbye' => false, |
| 129 | + 'entity_manager_name' => 'non_default', |
| 130 | + )); |
| 131 | + $container->loadFromExtension('acme_other', array( |
| 132 | + ..., |
| 133 | + 'use_acme_goodbye' => false, |
| 134 | + )); |
| 135 | +
|
0 commit comments