Thanks to visit codestin.com
Credit goes to github.com

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 all commits
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
92 changes: 66 additions & 26 deletions bundles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,29 @@ 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 ``src/Acme/TestBundle/`` directory and adding a new file
called ``AcmeTestBundle.php``::
Start by adding creating a new class called ``AcmeTestBundle``::

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

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeTestBundle extends Bundle
class AcmeTestBundle extends AbstractBundle
{
}

.. versionadded:: 6.1

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

.. caution::

If your bundle must be compatible with previous Symfony versions you have to
extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle` instead.

.. tip::

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

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

``Controller/``
Contains the controllers of the bundle (e.g. ``RandomController.php``).

``DependencyInjection/``
Holds certain Dependency Injection Extension classes, which may import service
configuration, register compiler passes or more (this directory is not
necessary).
``src/``
Contains all PHP classes related to the bundle logic (e.g. ``Controller/RandomController.php``).

``Resources/config/``
``config/``
Houses configuration, including routing configuration (e.g. ``routing.yaml``).

``Resources/views/``
Holds templates organized by controller name (e.g. ``Random/index.html.twig``).
``templates/``
Holds templates organized by controller name (e.g. ``random/index.html.twig``).

``translations/``
Holds translations organized by domain and locale (e.g. ``AcmeTestBundle.en.xlf``).

``Resources/public/``
``public/``
Contains web assets (images, stylesheets, etc) and is copied or symbolically
linked into the project ``public/`` directory via the ``assets:install`` console
command.

``Tests/``
``tests/``
Holds all tests for the bundle.

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

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

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__;
}
}

.. 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 @@ -126,3 +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/
82 changes: 82 additions & 0 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 definition 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
57 changes: 54 additions & 3 deletions bundles/extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ follow these conventions (but later you'll learn how to skip them if needed):

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

// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
// src/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;

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

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

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
Expand All @@ -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
18 changes: 9 additions & 9 deletions bundles/override.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ features of a bundle.

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

Expand All @@ -23,12 +23,12 @@ Templates

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

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

.. caution::

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

.. code-block:: twig

{# templates/bundles/FOSUserBundle/Registration/confirmed.html.twig #}
{# templates/bundles/AcmeUserBundle/registration/confirmed.html.twig #}
{# the special '!' prefix avoids errors when extending from an overridden template #}
{% extends "@!FOSUser/Registration/confirmed.html.twig" %}
{% extends "@!AcmeUser/registration/confirmed.html.twig" %}

{% block some_block %}
...
Expand Down Expand Up @@ -173,7 +173,7 @@ For this reason, you can override any bundle translation file from the main
``translations/`` directory, as long as the new file uses the same domain.

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

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