Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #43701 [HttpKernel] Simplifying Bundle/Extension config defin…
…ition (yceruto) This PR was merged into the 6.1 branch. Discussion ---------- [HttpKernel] Simplifying Bundle/Extension config definition | Q | A | ------------- | --- | Branch? | 6.1 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #40259, #42647, #43080 | License | MIT | Doc PR | - This PR aims to simplify DI extension/configuration definitions at the Bundle level (based on @Nyholm #40259 (comment)) Currently, the services and configuration definitions have to deal with some conventions: * Create the `DependencyInjection/` directory * Create the `DependencyInjection/Configuration.php` class to define the bundle config. * Create the `DependencyInjection/FooExtension.php` extension class and extend from `Extension` * In the `ExtensionInterface::load()` method to implement we have to: * Process the bundle configuration yourself `Configuration`, `Processor`, etc. * Create the specific `*FileLoader` & `FileLocator` instances to import services definition (have to deal with bundle path) * Prepend/append configs for other extensions requires implementing `PrependExtensionInterface`. * Redefine `Bundle::$name` to change the extension alias. Although it may not be a big problem to follow all these conventions (indeed, we have been doing it for years) it's true that there are limitations and it requires extra work to achieve them. Note: The following improvements don't pretend to deprecate the actual convention (at least so far) but simplify it with some benefits. --- To start using the following improvements your bundle must extend from the new abstract class `AbstractBundle` to autoconfigure all hooks and make this possible inside a bundle class. **The first improvement** offers the possibility to configure your bundle DI extension within the bundle class itself using `loadExtension()` method and the fluent `ContainerConfigurator` helper: ```php class FooBundle extends AbstractBundle { public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void { $container->parameters() ->set('foo', $config['foo']); $container->import('../config/services.php'); if ('bar' === $config['foo']) { $container->services() ->set(Parser::class); } } } ``` This new method `loadExtension()` (a same goal that `ExtensionInterface::load()`) contains now all new benefits you currently love for service definition/import/etc. Keep in mind that this configurator still works with a temporal container, so you can't access any extension config at this point (as before). And, the `$config` argument is the bundle's `Configuration` that you usually process on `ExtensionInterface::load()` but here it's given to you already merged and processed (ready to use). --- **The next improvement** comes when you want to prepend/append an extension config before all extensions are loaded & merged, then use the `prependExtension()` method: ```php class FooBundle extends AbstractBundle { public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void { // prepend $builder->prependExtensionConfig('framework', [ 'cache' => ['prefix_seed' => 'foo/bar'], ]); // append $container->extension('framework', [ 'cache' => ['prefix_seed' => 'foo/bar'], ]) // append from file $container->import('../config/packages/cache.php'); } } ``` This is the improved alternative to `PrependExtensionInterface` that you normally implement on extension classes. But using this method has bonus points, you can now use the `ContainerConfigurator` to append an extension config from an external file in any format (including the new PHP fluent-config feature). --- **Another improvement** is about `Configuration` definition. Here you can manage it directly within the bundle class using the `configuration()` method with new possibilities: ```php class FooBundle extends AbstractBundle { public function configure(DefinitionConfigurator $definition): void { // loads config definition from a file $definition->import('../config/definition.php'); // loads config definition from multiple files (when it's too long you can split it) $definition->import('../config/definition/*.php'); // defines config directly when it's short $definition->rootNode() ->children() ->scalarNode('foo')->defaultValue('bar')->end() ->end() ; } } ``` You don't have to create the `TreeBuilder` instance yourself anymore and remember the proper extension alias. Instead, you will use a new `DefinitionConfigurator` with the possibility to import configuration definitions from an external PHP file, and this config file can now live outside the `src/` directory of the bundle if desired: ```php // Acme/FooBundle/config/definition.php use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator; return static function (DefinitionConfigurator $definition) { $definition->rootNode() ->children() ->scalarNode('foo')->defaultValue('bar')->end() ->end() ; }; ``` And why not, you could also split your definition into several files if it's too long, or simply define the config directly in the method if it's short. --- **Last but not least** you can change the extension alias by redefining a new property that now belongs to the MicroBundle class: ```php class AcmeFooBundle extends AbstractBundle { protected string $extensionAlias = 'foo'; // alias used during the extension config loading // ... } ``` The default alias will be determined from your bundle name (in this case `acme_foo`), so the new way allows you to change that alias without either touching your bundle name or overriding any method. --- Note: The same feature has been implemented in a new `AbstractExtension` class for those applications applying the bundle-less approach and want to define configuration through an extension. Combining all these benefits I believe we gain a more simplified bundle structure while decreasing the learning curve. Commits ------- 7e8cf5d Simplifying bundle extension/config definition
- Loading branch information