Skip to content

Commit ed767c9

Browse files
committed
[Bundles] Promote the new bundle structure everywhere
1 parent ee119d0 commit ed767c9

File tree

3 files changed

+179
-146
lines changed

3 files changed

+179
-146
lines changed

bundles.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ of the bundle. Now that you've created the bundle, enable it::
8787

8888
And while it doesn't do anything yet, AcmeBlogBundle is now ready to be used.
8989

90+
.. _bundles-directory-structure:
91+
9092
Bundle Directory Structure
9193
--------------------------
9294

@@ -119,6 +121,8 @@ to be adjusted if needed:
119121
``translations/``
120122
Holds translations organized by domain and locale (e.g. ``AcmeBlogBundle.en.xlf``).
121123

124+
.. _bundles-legacy-directory-structure:
125+
122126
.. caution::
123127

124128
The recommended bundle structure was changed in Symfony 5, read the

bundles/configuration.rst

Lines changed: 105 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,114 @@ as integration of other related components:
4646
$framework->form()->enabled(true);
4747
};
4848
49+
There are two different ways of creating friendly configuration for a bundle:
50+
51+
#. :ref:`Using the main bundle class <bundle-friendly-config-bundle-class>`:
52+
this is recommended for new bundles and for bundles following the
53+
:ref:`recommended directory structure <bundles-directory-structure>`;
54+
#. :ref:`Using the Bundle extension class <bundle-friendly-config-extension>`:
55+
this was the traditional way of doing it, but nowadays it's only recommended for
56+
bundles following the :ref:`legacy directory structure <bundles-legacy-directory-structure>`.
57+
58+
.. _using-the-bundle-class:
59+
.. _bundle-friendly-config-bundle-class:
60+
61+
Using the AbstractBundle Class
62+
------------------------------
63+
64+
.. versionadded:: 6.1
65+
66+
The ``AbstractBundle`` class was introduced in Symfony 6.1.
67+
68+
In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
69+
class, you can add all the logic related to processing the configuration in that class::
70+
71+
// src/AcmeSocialBundle.php
72+
namespace Acme\SocialBundle;
73+
74+
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
75+
use Symfony\Component\DependencyInjection\ContainerBuilder;
76+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
77+
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
78+
79+
class AcmeSocialBundle extends AbstractBundle
80+
{
81+
public function configure(DefinitionConfigurator $definition): void
82+
{
83+
$definition->rootNode()
84+
->children()
85+
->arrayNode('twitter')
86+
->children()
87+
->integerNode('client_id')->end()
88+
->scalarNode('client_secret')->end()
89+
->end()
90+
->end() // twitter
91+
->end()
92+
;
93+
}
94+
95+
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
96+
{
97+
// the "$config" variable is already merged and processed so you can
98+
// use it directly to configure the service container (when defining an
99+
// extension class, you also have to do this merging and processing)
100+
$containerConfigurator->services()
101+
->get('acme.social.twitter_client')
102+
->arg(0, $config['twitter']['client_id'])
103+
->arg(1, $config['twitter']['client_secret'])
104+
;
105+
}
106+
}
107+
108+
.. note::
109+
110+
The ``configure()`` and ``loadExtension()`` methods are called only at compile time.
111+
112+
.. tip::
113+
114+
The ``AbstractBundle::configure()`` method also allows to import the
115+
configuration definition from one or more files::
116+
117+
// src/AcmeSocialBundle.php
118+
namespace Acme\SocialBundle;
119+
120+
// ...
121+
class AcmeSocialBundle extends AbstractBundle
122+
{
123+
public function configure(DefinitionConfigurator $definition): void
124+
{
125+
$definition->import('../config/definition.php');
126+
// you can also use glob patterns
127+
//$definition->import('../config/definition/*.php');
128+
}
129+
130+
// ...
131+
}
132+
133+
.. code-block:: php
134+
135+
// config/definition.php
136+
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
137+
138+
return static function (DefinitionConfigurator $definition): void {
139+
$definition->rootNode()
140+
->children()
141+
->scalarNode('foo')->defaultValue('bar')->end()
142+
->end()
143+
;
144+
};
145+
146+
.. _bundle-friendly-config-extension:
147+
49148
Using the Bundle Extension
50149
--------------------------
51150

151+
This is the traditional way of creating friendly configuration for bundles. For new
152+
bundles it's recommended to :ref:`use the main bundle class <bundle-friendly-config-bundle-class>`,
153+
but the traditional way of creating an extension class still works.
154+
52155
Imagine you are creating a new bundle - AcmeSocialBundle - which provides
53-
integration with Twitter. To make your bundle configurable to the user, you
156+
integration with X/Twitter. To make your bundle configurable to the user, you
54157
can add some configuration that looks like this:
55158

56159
.. configuration-block::
@@ -110,7 +213,7 @@ load correct services and parameters inside an "Extension" class.
110213

111214
If a bundle provides an Extension class, then you should *not* generally
112215
override any service container parameters from that bundle. The idea
113-
is that if an Extension class is present, every setting that should be
216+
is that if an extension class is present, every setting that should be
114217
configurable should be present in the configuration made available by
115218
that class. In other words, the extension class defines all the public
116219
configuration settings for which backward compatibility will be maintained.
@@ -315,94 +418,6 @@ In your extension, you can load this and dynamically set its arguments::
315418
// ... now use the flat $config array
316419
}
317420

318-
.. _using-the-bundle-class:
319-
320-
Using the AbstractBundle Class
321-
------------------------------
322-
323-
.. versionadded:: 6.1
324-
325-
The ``AbstractBundle`` class was introduced in Symfony 6.1.
326-
327-
As an alternative, instead of creating an extension and configuration class as
328-
shown in the previous section, you can also extend
329-
:class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle` to add this
330-
logic to the bundle class directly::
331-
332-
// src/AcmeSocialBundle.php
333-
namespace Acme\SocialBundle;
334-
335-
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
336-
use Symfony\Component\DependencyInjection\ContainerBuilder;
337-
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
338-
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
339-
340-
class AcmeSocialBundle extends AbstractBundle
341-
{
342-
public function configure(DefinitionConfigurator $definition): void
343-
{
344-
$definition->rootNode()
345-
->children()
346-
->arrayNode('twitter')
347-
->children()
348-
->integerNode('client_id')->end()
349-
->scalarNode('client_secret')->end()
350-
->end()
351-
->end() // twitter
352-
->end()
353-
;
354-
}
355-
356-
public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
357-
{
358-
// Contrary to the Extension class, the "$config" variable is already merged
359-
// and processed. You can use it directly to configure the service container.
360-
$containerConfigurator->services()
361-
->get('acme.social.twitter_client')
362-
->arg(0, $config['twitter']['client_id'])
363-
->arg(1, $config['twitter']['client_secret'])
364-
;
365-
}
366-
}
367-
368-
.. note::
369-
370-
The ``configure()`` and ``loadExtension()`` methods are called only at compile time.
371-
372-
.. tip::
373-
374-
The ``AbstractBundle::configure()`` method also allows to import the
375-
configuration definition from one or more files::
376-
377-
// src/AcmeSocialBundle.php
378-
namespace Acme\SocialBundle;
379-
380-
// ...
381-
class AcmeSocialBundle extends AbstractBundle
382-
{
383-
public function configure(DefinitionConfigurator $definition): void
384-
{
385-
$definition->import('../config/definition.php');
386-
// you can also use glob patterns
387-
//$definition->import('../config/definition/*.php');
388-
}
389-
390-
// ...
391-
}
392-
393-
.. code-block:: php
394-
395-
// config/definition.php
396-
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
397-
398-
return static function (DefinitionConfigurator $definition): void {
399-
$definition->rootNode()
400-
->children()
401-
->scalarNode('foo')->defaultValue('bar')->end()
402-
->end()
403-
;
404-
};
405-
406421
Modifying the Configuration of Another Bundle
407422
---------------------------------------------
408423

bundles/extension.rst

Lines changed: 70 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,77 @@ file used by the application but in the bundles themselves. This article
66
explains how to create and load service files using the bundle directory
77
structure.
88

9+
There are two different ways of doing it:
10+
11+
#. :ref:`Load your services in the main bundle class <bundle-load-services-bundle-class>`:
12+
this is recommended for new bundles and for bundles following the
13+
:ref:`recommended directory structure <bundles-directory-structure>`;
14+
#. :ref:`Create an extension class to load the service configuration files <bundle-load-services-extension>`:
15+
this was the traditional way of doing it, but nowadays it's only recommended for
16+
bundles following the :ref:`legacy directory structure <bundles-legacy-directory-structure>`.
17+
18+
.. _bundle-load-services-bundle-class:
19+
20+
Loading Services Directly in your Bundle Class
21+
----------------------------------------------
22+
23+
.. versionadded:: 6.1
24+
25+
The ``AbstractBundle`` class was introduced in Symfony 6.1.
26+
27+
In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
28+
class, you can define the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::loadExtension`
29+
method to load service definitions from configuration files::
30+
31+
// ...
32+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
33+
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
34+
35+
class AcmeHelloBundle extends AbstractBundle
36+
{
37+
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
38+
{
39+
// load an XML, PHP or YAML file
40+
$container->import('../config/services.xml');
41+
42+
// you can also add or replace parameters and services
43+
$container->parameters()
44+
->set('acme_hello.phrase', $config['phrase'])
45+
;
46+
47+
if ($config['scream']) {
48+
$container->services()
49+
->get('acme_hello.printer')
50+
->class(ScreamingPrinter::class)
51+
;
52+
}
53+
}
54+
}
55+
56+
This method works similar to the ``Extension::load()`` method explained below,
57+
but it uses a new simpler API to define and import service configuration.
58+
59+
.. note::
60+
61+
Contrary to the ``$configs`` parameter in ``Extension::load()``, the
62+
``$config`` parameter is already merged and processed by the
63+
``AbstractBundle``.
64+
65+
.. note::
66+
67+
The ``loadExtension()`` is called only at compile time.
68+
69+
.. _bundle-load-services-extension:
70+
971
Creating an Extension Class
1072
---------------------------
1173

12-
In order to load service configuration, you have to create a Dependency
13-
Injection (DI) Extension for your bundle. By default, the Extension class must
14-
follow these conventions (but later you'll learn how to skip them if needed):
74+
This is the traditional way of loading service definitions in bundles. For new
75+
bundles it's recommended to :ref:`load your services in the main bundle class <bundle-load-services-bundle-class>`,
76+
but the traditional way of creating an extension class still works.
77+
78+
A depdendency injection extension is defined as a class that follows these
79+
conventions (later you'll learn how to skip them if needed):
1580

1681
* It has to live in the ``DependencyInjection`` namespace of the bundle;
1782

@@ -20,7 +85,7 @@ follow these conventions (but later you'll learn how to skip them if needed):
2085
:class:`Symfony\\Component\\DependencyInjection\\Extension\\Extension` class;
2186

2287
* The name is equal to the bundle name with the ``Bundle`` suffix replaced by
23-
``Extension`` (e.g. the Extension class of the AcmeBundle would be called
88+
``Extension`` (e.g. the extension class of the AcmeBundle would be called
2489
``AcmeExtension`` and the one for AcmeHelloBundle would be called
2590
``AcmeHelloExtension``).
2691

@@ -70,7 +135,7 @@ class name to underscores (e.g. ``AcmeHelloExtension``'s DI alias is
70135
``acme_hello``).
71136

72137
Using the ``load()`` Method
73-
---------------------------
138+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
74139

75140
In the ``load()`` method, all services and parameters related to this extension
76141
will be loaded. This method doesn't get the actual container instance, but a
@@ -108,57 +173,6 @@ The Extension is also the class that handles the configuration for that
108173
particular bundle (e.g. the configuration in ``config/packages/<bundle_alias>.yaml``).
109174
To read more about it, see the ":doc:`/bundles/configuration`" article.
110175

111-
Loading Services directly in your Bundle class
112-
----------------------------------------------
113-
114-
.. versionadded:: 6.1
115-
116-
The ``AbstractBundle`` class was introduced in Symfony 6.1.
117-
118-
Alternatively, you can define and load services configuration directly in a
119-
bundle class instead of creating a specific ``Extension`` class. You can do
120-
this by extending from :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
121-
and defining the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::loadExtension`
122-
method::
123-
124-
// ...
125-
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
126-
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
127-
128-
class AcmeHelloBundle extends AbstractBundle
129-
{
130-
public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
131-
{
132-
// load an XML, PHP or Yaml file
133-
$containerConfigurator->import('../config/services.xml');
134-
135-
// you can also add or replace parameters and services
136-
$containerConfigurator->parameters()
137-
->set('acme_hello.phrase', $config['phrase'])
138-
;
139-
140-
if ($config['scream']) {
141-
$containerConfigurator->services()
142-
->get('acme_hello.printer')
143-
->class(ScreamingPrinter::class)
144-
;
145-
}
146-
}
147-
}
148-
149-
This method works similar to the ``Extension::load()`` method, but it uses
150-
a new API to define and import service configuration.
151-
152-
.. note::
153-
154-
Contrary to the ``$configs`` parameter in ``Extension::load()``, the
155-
``$config`` parameter is already merged and processed by the
156-
``AbstractBundle``.
157-
158-
.. note::
159-
160-
The ``loadExtension()`` is called only at compile time.
161-
162176
Adding Classes to Compile
163177
-------------------------
164178

0 commit comments

Comments
 (0)