8000 [DI] Documenting Abstract Bundle and Extension by yceruto · Pull Request #16801 · symfony/symfony-docs · GitHub
[go: up one dir, main page]

Skip to content

[DI] Documenting Abstract Bundle and Extension #16801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Rewrite the bundle docs a bit more
  • Loading branch information
wouterj committed May 22, 2022
commit 558b02eee1988a2cdb28f0236c43b7c3e9795a25
67 changes: 40 additions & 27 deletions bundles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,11 @@ Creating a Bundle
This section creates and enables a new bundle to show there are only a few steps required.
The new bundle is called AcmeTestBundle, where the ``Acme`` portion is an example
name that should be replaced by some "vendor" name that represents you or your
organization (e.g. ABCTestBundle for some company named ``ABC``).
organization (e.g. AbcTestBundle for some company named ``Abc``).

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

// Acme/TestBundle/src/AcmeTestBundle.php
// src/AcmeTestBundle.php
namespace Acme\TestBundle;

use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
Expand All @@ -62,9 +61,10 @@ called ``AcmeTestBundle.php``::

.. versionadded:: 6.1

The ``AbstractBundle`` was introduced in Symfony 6.1.
The :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle` was
introduced in Symfony 6.1.

.. warning::
.. caution::

If your bundle must be compatible with previous Symfony versions you have to
extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle` instead.
Expand Down Expand Up @@ -96,7 +96,7 @@ between all Symfony bundles. It follows a set of conventions, but is flexible
to be adjusted if needed:

``src/``
Contains mainly PHP classes related to the bundle logic (e.g. ``Controller/RandomController.php``).
Contains all PHP classes related to the bundle logic (e.g. ``Controller/RandomController.php``).

``config/``
Houses configuration, including routing configuration (e.g. ``routing.yaml``).
Expand All @@ -115,32 +115,44 @@ to be adjusted if needed:
``tests/``
Holds all tests for the bundle.

It's recommended to use the `PSR-4`_ autoload standard: use the namespace as key,
and the location of the bundle's main class (relative to ``composer.json``)
as value. As the main class is located in the ``src/`` directory of the bundle:
.. caution::

.. code-block:: json
The recommended bundle structure was changed in Symfony 5, read the
`Symfony 4.4 bundle documentation`_ for information about the old
structure.

{
"autoload": {
"psr-4": {
"Acme\\TestBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Acme\\TestBundle\\Tests\\": "tests/"
When using the new ``AbstractBundle`` class, the bundle defaults to the
new structure. Override the ``Bundle::getPath()`` method to change to
the old structure::

class AcmeTestBundle extends AbstractBundle
{
public function getPath(): string
{
return __DIR__;
}
}
}

A bundle can be as small or large as the feature it implements. It contains
only the files you need and nothing else.
.. tip::

As you move through the guides, you'll learn how to persist objects to a
database, create and validate forms, create translations for your application,
write tests and much more. Each of these has their own place and role within
the bundle.
It's recommended to use the `PSR-4`_ autoload standard: use the namespace as key,
and the location of the bundle's main class (relative to ``composer.json``)
as value. As the main class is located in the ``src/`` directory of the bundle:

.. code-block:: json

{
"autoload": {
"psr-4": {
"Acme\\TestBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Acme\\TestBundle\\Tests\\": "tests/"
}
}
}

Learn more
----------
Expand All @@ -152,4 +164,5 @@ Learn more
* :doc:`/bundles/prepend_extension`

.. _`third-party bundles`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories
.. _`Symfony 4.4 bundle documentation`: https://symfony.com/doc/4.4/bundles.html#bundle-directory-structure
.. _`PSR-4`: https://www.php-fig.org/psr/psr-4/
133 changes: 82 additions & 51 deletions bundles/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,88 @@ In your extension, you can load this and dynamically set its arguments::
// ... now use the flat $config array
}

Using the Bundle Class
----------------------

.. versionadded:: 6.1

The ``AbstractBundle`` class is introduced in Symfony 6.1.

Instead of creating an extension and configuration class, you can also
extend :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle` to
add this logic to the bundle class directly::

// src/AcmeSocialBundle.php
namespace Acme\SocialBundle;

use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeSocialBundle extends AbstractBundle
{
public function configure(DefinitionConfigurator $definition): void
{
$definition->rootNode()
->children()
->arrayNode('twitter')
->children()
->integerNode('client_id')->end()
->scalarNode('client_secret')->end()
->end()
->end() // twitter
->end()
;
}

public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
// Contrary to the Extension class, the "$config" variable is already merged
// and processed. You can use it directly to configure the service container.
$container->services()
->get('acme.social.twitter_client')
->arg(0, $config['twitter']['client_id'])
->arg(1, $config['twitter']['client_secret'])
;
}
}

.. note::

The ``configure()`` and ``loadExtension()`` methods are called only at compile time.

.. tip::

The ``AbstractBundle::configure()`` method also allows to import the
configuration definitino from one or more files::

// src/AcmeSocialBundle.php

// ...
class AcmeSocialBundle extends AbstractBundle
{
public function configure(DefinitionConfigurator $definition): void
{
$definition->import('../config/definition.php');
// you can also use glob patterns
//$definition->import('../config/definition/*.php');
}

// ...
}

.. code-block:: php

// config/definition.php
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;

return static function (DefinitionConfigurator $definition) {
$definition->rootNode()
->children()
->scalarNode('foo')->defaultValue('bar')->end()
->end()
;
};

Modifying the Configuration of Another Bundle
---------------------------------------------

Expand Down Expand Up @@ -431,57 +513,6 @@ Assuming the XSD file is called ``hello-1.0.xsd``, the schema location will be
<!-- ... -->
</container>

Defining Configuration directly in your Bundle class
----------------------------------------------------

.. versionadded:: 6.1

The ``AbstractBundle`` class is introduced in Symfony 6.1.

As another option, you can define the extension configuration directly in your Bundle
class by implementing :class:`Symfony\\Component\\Config\\Definition\\ConfigurableInterface`,
which is already supported when your bundle extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`::

use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeFooBundle 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');

// if the configuration is short, consider adding it in this class
$definition->rootNode()
->children()
->scalarNode('foo')->defaultValue('bar')->end()
->end()
;
}
}

This method is a shortcut of the previous "Extension", "Configuration" and "TreeBuilder" convention,
now you also have the possibility to import configuration definition from an external file::

// 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()
;
};

.. note::

The "configure()" method is called only at compile time.

.. _`FrameworkBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
.. _`TwigBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
.. _`XML namespace`: https://en.wikipedia.org/wiki/XML_namespace
Expand Down
94 changes: 52 additions & 42 deletions bundles/extension.rst
F438
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ For instance, assume you have a file called ``services.xml`` in the
{
$loader = new XmlFileLoader(
$container,
new FileLocator(__DIR__.'/../Resources/config')
new FileLocator(__DIR__.'/../config')
);
$loader->load('services.xml');
}
Expand All @@ -111,6 +111,57 @@ The Extension is also the class that handles the configuration for that
particular bundle (e.g. the configuration in ``config/packages/<bundle_alias>.yaml``).
To read more about it, see the ":doc:`/bundles/configuration`" article.

Loading Services directly in your Bundle class
----------------------------------------------

.. versionadded:: 6.1

The ``AbstractBundle`` class is introduced in Symfony 6.1.

Alternatively, you can define and load services configuration directly in a
bundle class instead of creating a specific ``Extension`` class. You can do
this by extending from :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
and defining the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::loadExtension`
method::

// ...
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

class AcmeHelloBundle extends AbstractBundle
{
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
// load an XML, PHP or Yaml file
$container->import('../config/services.xml');

// you can also add or replace parameters and services
$container->parameters()
->set('acme_hello.phrase', $config['phrase'])
;

if ($config['scream']) {
$container->services()
->get('acme_hello.printer')
->class(ScreamingPrinter::class)
;
}
}
}

This method works similar to the ``Extension::load()`` method, but it uses
a new API to define and import service configuration.

.. note::

Contrary to the ``$configs`` parameter in ``Extension::load()``, the
``$config`` parameter is already merged and processed by the
``AbstractBundle``.

.. note::

The ``loadExtension()`` is called only at compile time.

Adding Classes to Compile
-------------------------

Expand Down Expand Up @@ -147,44 +198,3 @@ the full classmap executing the ``dump-autoload`` command of Composer.
This technique can't be used when the classes to compile use the ``__DIR__``
or ``__FILE__`` constants, because their values will change when loading
these classes from the ``classes.php`` file.

Loading Services directly in your Bundle class
----------------------------------------------

.. versionadded:: 6.1

The ``AbstractBundle`` class is introduced in Symfony 6.1.

Alternatively, you can define and load services configuration directly in a bundle class
by extending from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
and defining the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::loadExtension` method::

use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

class AcmeFooBundle 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 method is a shortcut of the previous "load()" method, but with more options
to define and import the service configuration with less effort. The ``$config``
argument is the previous ``$configs`` array but already merged and processed. And
through the ``$container`` configurator you can import the services configuration
from an external file in any supported format (php, yaml, xml) or simply define
them in place using the fluent interfaces.

.. note::

The "loadExtension()", as the "load()" method, are called only at compile time.
Loading
0