Skip to content

Updates to DI config for 3.3 #7807

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 23 commits into from
May 5, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8433fc1
[WIP] Updates to DI config for 3.3
weaverryan Apr 15, 2017
2d11347
more tweaks
weaverryan Apr 28, 2017
105801c
adding note about autoconfigure
weaverryan Apr 28, 2017
049df7d
Adding details and usages of fetching the service as a controller arg
weaverryan Apr 28, 2017
9e84572
last tweaks from feedback
weaverryan Apr 28, 2017
c45daf4
fixing build problem
weaverryan Apr 28, 2017
2636bea
bad link
weaverryan Apr 28, 2017
9ab27f0
Add xml files
GuilhemN Apr 28, 2017
0e48bd8
[WIP] Updates to DI config for 3.3
weaverryan Apr 15, 2017
6e6ed94
more tweaks
weaverryan Apr 28, 2017
70178d1
adding note about autoconfigure
weaverryan Apr 28, 2017
45500b3
Adding details and usages of fetching the service as a controller arg
weaverryan Apr 28, 2017
759e9b2
last tweaks from feedback
weaverryan Apr 28, 2017
6de83e2
fixing build problem
weaverryan Apr 28, 2017
89e12de
bad link
weaverryan Apr 28, 2017
443aec2
Merge pull request #7857 from GuilhemN/patch-1
weaverryan May 2, 2017
bc7088d
Merge remote-tracking branch 'origin/di-3.3-changes' into di-3.3-changes
weaverryan May 2, 2017
ee27765
Adding versionadded
weaverryan May 2, 2017
5452c61
Adding section about public: false
weaverryan May 2, 2017
2229fd3
Merge remote-tracking branch 'origin/master' into di-3.3-changes
weaverryan May 2, 2017
cac3c6c
Merge remote-tracking branch 'origin/master' into di-3.3-changes
weaverryan May 5, 2017
12c4944
Tweaks after amazing review from @GuilhemN and @xabbuh
weaverryan May 5, 2017
22adfbd
removing duplicate target
weaverryan May 5, 2017
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
Tweaks after amazing review from @GuilhemN and @xabbuh
  • Loading branch information
weaverryan committed May 5, 2017
commit 12c49444c8be79f3988875fd619f958a0ff85e07
36 changes: 21 additions & 15 deletions controller.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ and many others that you'll learn about next.
You can extend either ``Controller`` or ``AbstractController``. The difference
is that when you extend ``AbstractController``, you can't access services directly
via ``$this->get()`` or ``$this->container->get()``. This forces you to write
more robust code to access services, but if you're not use, use ``Controller``.
more robust code to access services. But if you *do* need direct access to the
container, using ``Controller`` is fine.

.. versionadded:: 3.3
The ``AbstractController`` class was added in Symfony 3.3.
Expand Down Expand Up @@ -238,19 +239,19 @@ The Symfony templating system and Twig are explained more in the
.. _controller-accessing-services:
.. _accessing-other-services:

Fetching Services as Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fetching Services as Controller Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 3.3
The ability to type-hint an argument in order to receive a service was added
in Symfony 3.3.
The ability to type-hint a controller argument in order to receive a service
was added in Symfony 3.3.

Symfony comes *packed* with a lot of useful objects, called :doc:`services </service_container>`.
These are used for rendering templates, sending emails, querying the database and
any other "work" you can think of.

If you need a service, just type-hint an argument with its class (or interface) name.
Symfony will automatically pass you the service you need::
If you need a service in a controller, just type-hint an argument with its class
(or interface) name. Symfony will automatically pass you the service you need::

use Psr\Log\LoggerInterface
// ...
Expand All @@ -266,16 +267,15 @@ Symfony will automatically pass you the service you need::

Awesome!

What other services exist? Each page of the documentation will reveal more and more
services you can use. To list *all* services, use the ``debug:container`` console
What other services can you type-hint? To see them, use the ``debug:container`` console
command:

.. code-block:: terminal

$ php bin/console debug:container
$ php bin/console debug:container --types

If need to control the *exact* value of an argument, you can override your controller's
service config:
If you need control over the *exact* value of an argument, you can override your
controller's service config:

.. configuration-block::

Expand Down Expand Up @@ -334,22 +334,26 @@ service config:
])
;

You can of course also use normal :ref:`constructor injection <services-constructor-injection>`
in your controllers.

For more information about services, see the :doc:`/service_container` article.

.. note::
If this isn't working, make sure your controller is registered as a service,
:ref:`autoconfigured <services-autoconfigure>` and extends either
is :ref:`autoconfigured <services-autoconfigure>` and extends either
:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` or
:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController`. Or,
you can tag your service manually with ``controller.service_arguments``.
you can tag your service manually with ``controller.service_arguments``. All
of this is done for you in a fresh Symfony install.

.. _accessing-other-services:
.. _controller-access-services-directly:

Accessing the Container Directly
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If extending the base ``Controller`` class, you can access any Symfony service
If you extend the base ``Controller`` class, you can access any Symfony service
via the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::get`
method. Here are several common services you might need::

Expand All @@ -364,6 +368,8 @@ method. Here are several common services you might need::

If you receive an eror like:

.. code-block:: text

You have requested a non-existent service "my_service_id"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will render the error message as a block quote. Do we really want that? I suggest to use a text code block instead.


Check to make sure the service exists (use :ref:`debug:container <container-debug-container>`)
Expand Down
2 changes: 1 addition & 1 deletion email.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ an email is pretty straightforward::

$mailer->send($message);

// or, you can also fetch the mailer service in this way
// or, you can also fetch the mailer service this way
// $this->get('mailer')->send($message);

return $this->render(...);
Expand Down
47 changes: 29 additions & 18 deletions service_container.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Fetching and using Services

The moment you start a Symfony app, your container *already* contains many services.
These are like *tools*, waiting for you to take advantage of them. In your controller,
you can "ask" for a service from the container by type-hinting an argument wit the
you can "ask" for a service from the container by type-hinting an argument with the
service's class or interface name. Want to :doc:`log </logging>` something? No problem::

// src/AppBundle/Controller/ProductController.php
Expand Down Expand Up @@ -155,7 +155,7 @@ the service container *how* to instantiate it:

# loads services from whatever directories you want (you can update this!)
AppBundle\:
resource: '../../src/AppBundle/{Service,EventDispatcher,Twig,Form}'
resource: '../../src/AppBundle/{Service,Command,Form,EventSubscriber,Twig,Security}'
Copy link
Contributor

@theofidry theofidry May 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for being late, but what about giving an absolute path here with %kernel.root_dir% for example? I always find those relative paths a bit confusing as it's not always obvious relative to what. Alternatively, maybe we should add an example with both:

AppBundle\:
    # glob with path relative to this file
    resource: '../../src/AppBundle/{Service,Command,Form,EventSubscriber,Twig,Security}'
    # or
    resource: '%kernel.root_path%/../../src/AppBundle/{Service,Command,Form,EventSubscriber,Twig,Security}'

The case above may not be a good one since it makes it extra verbose, but I've seen quite a few projects where they are using a custom parameter to point to src, making it less verbose. Either way, you're still showing that parameters are supported in the glob.

Also I don't see anywhere mentioned that it's a glob that's being used.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%kernel.project_dir% might be even simpler

'%kernel.project_dir%/src/AppBundle/{Service,Command,Form,EventSubscriber,Twig,Security}'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better, I forgot we were adding this in 3.3!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a big gain, that makes one more thing to learn for newcomers while relative paths are understandable by everyone.


.. code-block:: xml

Expand All @@ -171,14 +171,20 @@ the service container *how* to instantiate it:
<defaults autowire="true" autoconfigure="true" public="false" />

<!-- Load services from whatever directories you want (you can update this!) -->
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,EventDispatcher,Twig,Form}" />
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,Command,Form,EventSubscriber,Twig,Security}" />
</services>
</container>

.. code-block:: php

// app/config/services.php
// _defaults and loading entire directories is not possible with PHP configuration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep a concrete example here for those who really want to use php:

// you have to define your services one-by-one
use AppBundle/Service/MessageGenerator;

$container->autowire(MessageGenerator::class)
    ->setAutoconfigured(true)
    ->setPublic(false);

// you need to define your servicess one-by-one
use AppBundle/Service/MessageGenerator;

$container->autowire(MessageGenerator::class)
->setAutoconfigured(true)
->setPublic(false);

.. versionadded:: 3.3
The ``_defaults`` key and ability to load services from a directory were added
Expand Down Expand Up @@ -228,18 +234,20 @@ be its class name in this case::
}
}

However, this only works if you set your service to be :ref:`public <container-public>`.
However, this only works if you make your service :ref:`public <container-public>`.

.. caution::

Service ids are case-insensitive (e.g. ``AppBundle\Service\MessageGenerator``
and ``appbundle\service\messagegenerator`` refer to the same service). But this
was deprecated in Symfony 3.3. Starting in 4.0, service ids will be case sensitive.

.. _services-constructor-injection:

Injecting Services/Config into a Service
----------------------------------------

What if need to access the ``logger`` service from within ``MessageGenerator``?
What if you need to access the ``logger`` service from within ``MessageGenerator``?
Your service does *not* have access to the container directly, so you can't fetch
it via ``$this->container->get()``.

Expand Down Expand Up @@ -272,17 +280,17 @@ when instantiating the ``MessageGenerator``. How does it know to do this?
:doc:`Autowiring </service_container/autowiring>`. The key is the ``LoggerInterface``
type-hint in your ``__construct()`` method and the ``autowire: true`` config in
``services.yml``. When you type-hint an argument, the container will automatically
find the matching service. If it can't or there is any ambiguity, you'll see a clear
exception with a helpful suggestion.
find the matching service. If it can't, you'll see a clear exception with a helpful
suggestion.

Be sure to read more about :doc:`autowiring </service_container/autowiring>`.

.. tip::

How should you know to use ``LoggerInterface`` for the type-hint? The best way
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there must be an alias for it (see previous comment)

is by reading the docs for whatever feature you're using. You can also use the
``php bin/console debug:container`` console command to get a hint
to the class name for a service.
``php bin/console debug:container --types`` console command to get a list of
available type-hints.

Handling Multiple Services
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -337,7 +345,7 @@ the new ``Updates`` sub-directory:

# registers all classes in Services & Updates directories
AppBundle\:
resource: '../../src/AppBundle/{Service,Updates,EventDispatcher,Twig,Form}'
resource: '../../src/AppBundle/{Service,Updates,Command,Form,EventSubscriber,Twig,Security}'

.. code-block:: xml

Expand All @@ -352,7 +360,7 @@ the new ``Updates`` sub-directory:
<!-- ... -->

<!-- Registers all classes in Services & Updates directories -->
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,Updates,EventDispatcher,Twig,Form}" />
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,Updates,Command,Form,EventSubscriber,Twig,Security}" />
</services>
</container>

Expand All @@ -370,7 +378,7 @@ Now, you can use the service immediately::
}

Thanks to autowiring and your type-hints in ``__construct()``, the container creates
the ``SiteUpdateManager`` object and passes it the correct arguments. In most cases,
the ``SiteUpdateManager`` object and passes it the correct argument. In most cases,
this works perfectly.

Manually Wiring Arguments
Expand Down Expand Up @@ -428,7 +436,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume

# same as before
AppBundle\:
resource: '../../src/AppBundle/{Service,Updates}'
resource: '../../src/AppBundle/{Service,Updates,Command,Form,EventSubscriber,Twig,Security}'

# explicitly configure the service
AppBundle\Updates\SiteUpdateManager:
Expand All @@ -448,7 +456,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume
<!-- ... -->

<!-- Same as before -->
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,Updates}" />
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,Updates,Command,Form,EventSubscriber,Twig,Security}" />

<!-- Explicitly configure the service -->
<service id="AppBundle\Updates\SiteUpdateManager">
Expand Down Expand Up @@ -526,6 +534,8 @@ and reference it with the ``%parameter_name%`` syntax:

.. code-block:: php

// app/config/services.php
use AppBundle\Updates\SiteUpdateManager;
$container->setParameter('admin_email', 'manager@example.com');

$container->autowire(SiteUpdateManager::class)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing use statement

Expand Down Expand Up @@ -659,7 +669,7 @@ The autoconfigure Option
Above, we've set ``autoconfigure: true`` in the ``_defaults`` section so that it
applies to all services defined in that file. With this setting, the container will
automatically apply certain configuration to your services, based on your service's
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configurations? or a certain configuration?

*class*. The is mostly used to *auto-tag* your services.
*class*. This is mostly used to *auto-tag* your services.

For example, to create a Twig Extension, you need to create a class, register it
as a service, and :doc:`tag </service_container/tags>` it with ``twig.extension``:
Expand Down Expand Up @@ -702,7 +712,8 @@ as a service, and :doc:`tag </service_container/tags>` it with ``twig.extension`
->addTag('twig.extension');

But, with ``autoconfigure: true``, you don't need the tag. In fact, all you need
to do is load your service from the ``Twig`` directory:
to do is load your service from the ``Twig`` directory, which is already loaded
by default in a fresh Symfony install:

.. configuration-block::

Expand All @@ -716,7 +727,7 @@ to do is load your service from the ``Twig`` directory:

# load your services from the Twig directory
AppBundle\:
resource: '../../src/AppBundle/{Service,EventDispatcher,Twig,Form}'
resource: '../../src/AppBundle/{Service,Updates,Command,Form,EventSubscriber,Twig,Security}'

.. code-block:: xml

Expand All @@ -731,7 +742,7 @@ to do is load your service from the ``Twig`` directory:
<defaults autowire="true" autoconfigure="true" />

<!-- Load your services-->
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,EventDispatcher,Twig,Form}" />
<prototype namespace="AppBundle\" resource="../../src/AppBundle/{Service,Updates,Command,Form,EventSubscriber,Twig,Security}" />
</services>
</container>

Expand Down