10000 minor #16801 [DI] Documenting Abstract Bundle and Extension (yceruto,… · symfony/symfony-docs@6be632e · GitHub
[go: up one dir, main page]

Skip to content

Commit 6be632e

Browse files
committed
minor #16801 [DI] Documenting Abstract Bundle and Extension (yceruto, wouterj)
This PR was merged into the 6.1 branch. Discussion ---------- [DI] Documenting Abstract Bundle and Extension Fixes #16669 PR symfony/symfony#43701 ping `@Nyholm` `@wouterj` as you were interested in this feature/doc. Any suggestion to improve the wording is more than welcome! --- I also updated other places to be aligned with the best practices doc. I didn't remove the current convention to create bundles/extensions/configurations because they are still valid until 6.4 becomes the last LTS available. However, it's ok for apps using AbstractExtension. Commits ------- 7a24f08 typo c426dbb Fix extension/config paths c85de08 Revert attribute route 558b02e Rewrite the bundle docs a bit more 7cf9bf4 Add warning d0cba72 Remove note after update the feature c2c47a2 Update bundle.rst c93db0b Jules' review 426e289 Documenting Abstract Bundle and Extension
2 parents ebbc61d + 7a24f08 commit 6be632e

File tree

6 files changed

+294
-42
lines changed

6 files changed

+294
-42
lines changed

bundles.rst

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,29 @@ Creating a Bundle
4646
This section creates and enables a new bundle to show there are only a few steps required.
4747
The new bundle is called AcmeTestBundle, where the ``Acme`` portion is an example
4848
name that should be replaced by some "vendor" name that represents you or your
49-
organization (e.g. ABCTestBundle for some company named ``ABC``).
49+
organization (e.g. AbcTestBundle for some company named ``Abc``).
5050

51-
Start by creating a ``src/Acme/TestBundle/`` directory and adding a new file
52-
called ``AcmeTestBundle.php``::
51+
Start by adding creating a new class called ``AcmeTestBundle``::
5352

54-
// src/Acme/TestBundle/AcmeTestBundle.php
55-
namespace App\Acme\TestBundle;
53+
// src/AcmeTestBundle.php
54+
namespace Acme\TestBundle;
5655

57-
use Symfony\Component\HttpKernel\Bundle\Bundle;
56+
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
5857

59-
class AcmeTestBundle extends Bundle
58+
class AcmeTestBundle extends AbstractBundle
6059
{
6160
}
6261

62+
.. versionadded:: 6.1
63+
64+
The :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle` was
65+
introduced in Symfony 6.1.
66+
67+
.. caution::
68+
69+
If your bundle must be compatible with previous Symfony versions you have to
70+
extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle` instead.
71+
6372
.. tip::
6473

6574
The name AcmeTestBundle follows the standard
@@ -74,7 +83,7 @@ of the bundle. Now that you've created the bundle, enable it::
7483
// config/bundles.php
7584
return [
7685
// ...
77-
App\Acme\TestBundle\AcmeTestBundle::class => ['all' => true],
86+
Acme\TestBundle\AcmeTestBundle::class => ['all' => true],
7887
];
7988

8089
And while it doesn't do anything yet, AcmeTestBundle is now ready to be used.
@@ -86,35 +95,64 @@ The directory structure of a bundle is meant to help to keep code consistent
8695
between all Symfony bundles. It follows a set of conventions, but is flexible
8796
to be adjusted if needed:
8897

89-
``Controller/``
90-
Contains the controllers of the bundle (e.g. ``RandomController.php``).
91-
92-
``DependencyInjection/``
93-
Holds certain Dependency Injection Extension classes, which may import service
94-
configuration, register compiler passes or more (this directory is not
95-
necessary).
98+
``src/``
99+
Contains all PHP classes related to the bundle logic (e.g. ``Controller/RandomController.php``).
96100

97-
``Resources/config/``
101+
``config/``
98102
Houses configuration, including routing configuration (e.g. ``routing.yaml``).
99103

100-
``Resources/views/``
101-
Holds templates organized by controller name (e.g. ``Random/index.html.twig``).
104+
``templates/``
105+
Holds templates organized by controller name (e.g. ``random/index.html.twig``).
106+
107+
``translations/``
108+
Holds translations organized by domain and locale (e.g. ``AcmeTestBundle.en.xlf``).
102109

103-
``Resources/public/``
110+
``public/``
104111
Contains web assets (images, stylesheets, etc) and is copied or symbolically
105112
linked into the project ``public/`` directory via the ``assets:install`` console
106113
command.
107114

108-
``Tests/``
115+
``tests/``
109116
Holds all tests for the bundle.
110117

111-
A bundle can be as small or large as the feature it implements. It contains
112-
only the files you need and nothing else.
118+
.. caution::
119+
120+
The recommended bundle structure was changed in Symfony 5, read the
121+
`Symfony 4.4 bundle documentation`_ for information about the old
122+
structure.
123+
124+
When using the new ``AbstractBundle`` class, the bundle defaults to the
125+
new structure. Override the ``Bundle::getPath()`` method to change to
126+
the old structure::
127+
128+
class AcmeTestBundle extends AbstractBundle
129+
{
130+
public function getPath(): string
131+
{
132+
return __DIR__;
133+
}
134+
}
135+
136+
.. tip::
113137

114-
As you move through the guides, you'll learn how to persist objects to a
115-
database, create and validate forms, create translations for your application,
116-
write tests and much more. Each of these has their own place and role within
117-
the bundle.
138+
It's recommended to use the `PSR-4`_ autoload standard: use the namespace as key,
139+
and the location of the bundle's main class (relative to ``composer.json``)
140+
as value. As the main class is located in the ``src/`` directory of the bundle:
141+
142+
.. code-block:: json
143+
144+
{
145+
"autoload": {
146+
"psr-4": {
147+
"Acme\\TestBundle\\": "src/"
148+
}
149+
},
150+
"autoload-dev": {
151+
"psr-4": {
152+
"Acme\\TestBundle\\Tests\\": "tests/"
153+
}
154+
}
155+
}
118156
119157
Learn more
120158
----------
@@ -126,3 +164,5 @@ Learn more
126164
* :doc:`/bundles/prepend_extension`
127165

128166
.. _`third-party bundles`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories
167+
.. _`Symfony 4.4 bundle documentation`: https://symfony.com/doc/4.4/bundles.html#bundle-directory-structure
168+
.. _`PSR-4`: https://www.php-fig.org/psr/psr-4/

bundles/configuration.rst

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,88 @@ In your extension, you can load this and dynamically set its arguments::
315315
// ... now use the flat $config array
316316
}
317317

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

bundles/extension.rst

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ follow these conventions (but later you'll learn how to skip them if needed):
3030

3131
This is how the extension of an AcmeHelloBundle should look like::
3232

33-
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
33+
// src/DependencyInjection/AcmeHelloExtension.php
3434
namespace Acme\HelloBundle\DependencyInjection;
3535

3636
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -87,7 +87,7 @@ but it is more common if you put these definitions in a configuration file
8787
(using the YAML, XML or PHP format).
8888

8989
For instance, assume you have a file called ``services.xml`` in the
90-
``Resources/config/`` directory of your bundle, your ``load()`` method looks like::
90+
``config/`` directory of your bundle, your ``load()`` method looks like::
9191

9292
use Symfony\Component\Config\FileLocator;
9393
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@@ -97,7 +97,7 @@ For instance, assume you have a file called ``services.xml`` in the
9797
{
9898
$loader = new XmlFileLoader(
9999
$container,
100-
new FileLocator(__DIR__.'/../Resources/config')
100+
new FileLocator(__DIR__.'/../../config')
101101
);
102102
$loader->load('services.xml');
103103
}
@@ -111,6 +111,57 @@ The Extension is also the class that handles the configuration for that
111111
particular bundle (e.g. the configuration in ``config/packages/<bundle_alias>.yaml``).
112112
To read more about it, see the ":doc:`/bundles/configuration`" article.
113113

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

bundles/override.rst

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ features of a bundle.
1212

1313
The bundle overriding mechanism means that you cannot use physical paths to
1414
refer to bundle's resources (e.g. ``__DIR__/config/services.xml``). Always
15-
use logical paths in your bundles (e.g. ``@FooBundle/Resources/config/services.xml``)
15+
use logical paths in your bundles (e.g. ``@FooBundle/config/services.xml``)
1616
and call the :ref:`locateResource() method <http-kernel-resource-locator>`
1717
to turn them into physical paths when needed.
1818

@@ -23,12 +23,12 @@ Templates
2323

2424
Third-party bundle templates can be overridden in the
2525
``<your-project>/templates/bundles/<bundle-name>/`` directory. The new templates
26-
must use the same name and path (relative to ``<bundle>/Resources/views/``) as
26+
must use the same name and path (relative to ``<bundle>/templates/``) as
2727
the original templates.
2828

29-
For example, to override the ``Resources/views/Registration/confirmed.html.twig``
30-
template from the FOSUserBundle, create this template:
31-
``<your-project>/templates/bundles/FOSUserBundle/Registration/confirmed.html.twig``
29+
For example, to override the ``templates/registration/confirmed.html.twig``
30+
template from the AcmeUserBundle, create this template:
31+
``<your-project>/templates/bundles/AcmeUserBundle/registration/confirmed.html.twig``
3232

3333
.. caution::
3434

@@ -43,9 +43,9 @@ extend from the original template, not from the overridden one:
4343

4444
.. code-block:: twig
4545
46-
{# templates/bundles/FOSUserBundle/Registration/confirmed.html.twig #}
46+
{# templates/bundles/AcmeUserBundle/registration/confirmed.html.twig #}
4747
{# the special '!' prefix avoids errors when extending from an overridden template #}
48-
{% extends "@!FOSUser/Registration/confirmed.html.twig" %}
48+
{% extends "@!AcmeUser/registration/confirmed.html.twig" %}
4949
5050
{% block some_block %}
5151
...
@@ -173,7 +173,7 @@ For this reason, you can override any bundle translation file from the main
173173
``translations/`` directory, as long as the new file uses the same domain.
174174

175175
For example, to override the translations defined in the
176-
``Resources/translations/FOSUserBundle.es.yml`` file of the FOSUserBundle,
177-
create a ``<your-project>/translations/FOSUserBundle.es.yml`` file.
176+
``translations/AcmeUserBundle.es.yaml`` file of the AcmeUserBundle,
177+
create a ``<your-project>/translations/AcmeUserBundle.es.yaml`` file.
178178

179179
.. _`the Doctrine documentation`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/inheritance-mapping.html#overrides

0 commit comments

Comments
 (0)
0