Skip to content

Commit 8ad1c2c

Browse files
committed
Merge branch '7.0' into 7.1
* 7.0: [Bundles] Promote the new bundle structure everywhere
2 parents 4a2c77d + 17aeba4 commit 8ad1c2c

File tree

3 files changed

+179
-138
lines changed

3 files changed

+179
-138
lines changed

bundles.rst

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

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

85+
.. _bundles-directory-structure:
86+
8587
Bundle Directory Structure
8688
--------------------------
8789

@@ -114,6 +116,8 @@ to be adjusted if needed:
114116
``translations/``
115117
Holds translations organized by domain and locale (e.g. ``AcmeBlogBundle.en.xlf``).
116118

119+
.. _bundles-legacy-directory-structure:
120+
117121
.. caution::
118122

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

bundles/configuration.rst

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

bundles/extension.rst

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

0 commit comments

Comments
 (0)