Skip to content

Document how to embed a controller as a service #8039

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

Closed
Closed
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
32 changes: 32 additions & 0 deletions controller/service.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,38 @@ syntax:
You cannot drop the ``Action`` part of the method name when using the
single colon notation.

Embedding your controller in a template
---------------------------------------

If you want to :doc:`embed your controller in a template </templating/embedding_controllers>`,
you need to use the following syntax:

.. configuration-block::

.. code-block:: html+twig

{# app/Resources/views/base.html.twig #}

{# ... #}
<div id="sidebar">
{{ render(controller(
'AppBundle\\Controller\\HelloController:indexAction',
Copy link
Contributor Author

@greg0ire greg0ire Jun 14, 2017

Choose a reason for hiding this comment

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

Can anyone tell why these double backslashes are required?

)) }}
</div>

.. code-block:: html+php

<!-- app/Resources/views/base.html.php -->

<!-- ... -->
<div id="sidebar">
<?php echo $view['actions']->render(
new \Symfony\Component\HttpKernel\Controller\ControllerReference(
'AppBundle\\Controller\\HelloController:indexAction'
)
) ?>
</div>

.. _controller-service-invoke:

Invokable Controllers
Expand Down
32 changes: 32 additions & 0 deletions templating/embedding_controllers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,36 @@ string syntax for controllers (i.e. **bundle**:**controller**:**action**):
) ?>
</div>

If your controller should be used :doc:`as a service </controller/service>`,
you can reference it like this instead:
Copy link
Member

Choose a reason for hiding this comment

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

So this is interesting :). Starting in Symfony 3.3, the AppBundle:Article:recentArticles syntax should also work when your controller is a service... as long as your service id matches your class name (like it does in your example). In other words, unless you've registered your service with a non-class id (e.g. my_controller), you should be able to just use one syntax, and Symfony will automatically use your controller as a service if it is registered as one (or instantiate it like normal if it is not).

Given that, what made you create this PR? Were you seeing different behavior or were you confused by something?

Cheers!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried helping @marcverney set it up on via Slack, but it did not work for him. @marcverney , can you comment on your setup and on the error message you were getting?

Copy link
Contributor

Choose a reason for hiding this comment

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

Here it goes. I was trying to embed a controller action in a twig template, this way:

{{ render(controller('AppBundle:Default:completeSignup')) }}

Here is my controller action:

public function completeSignupAction(CategoryRepository $categoryRepository)
{
    // ...
}

And the CategoryRepository service definition

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    Domain\Model\CategoryRepository:
        factory: ['@doctrine.orm.entity_manager', 'getRepository']
        arguments: ['Domain\Model\Category']    

When I tried to access the page, I got the following error:

An exception has been thrown during the rendering of a template ("Controller "Infrastructure\UI\Symfony\AppBundle\Controller\DefaultController::completeSignupAction()" requires that you provide a value for the "$categoryRepository" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.").

I then thought that maybe injecting a service in a controller action was not supported for subrequests (as I think that's what render(controller()) does). I searched the docs but couldn't find anything about this, so I asked on the support Slack channel. This is where @greg0ire and others told me to try using the FQCN notation:

{{ render(controller('Infrastructure\\UI\\Symfony\\AppBundle\\Controller\\DefaultController:completeSignupAction')) }}

This worked and my $categoryRepository was correctly injected, so @greg0ire created this PR.

I think this sums it all up.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, and as for my setup, two things:

  • I'm trying to follow a ddd-loosely-inspired folder structure, so my AppBundle dir is not a direct child of src, plus not everything is inside AppBundle.
  • I'm not very comfortable with the autowiring magic yet, so an error in my service definitions would not come as a surprise.


.. configuration-block::

.. code-block:: html+twig

{# app/Resources/views/base.html.twig #}

{# ... #}
<div id="sidebar">
{{ render(controller(
'AppBundle\\Controller\\ArticleController:recentArticlesAction',
{ 'max': 3 }
)) }}
</div>

.. code-block:: html+php

<!-- app/Resources/views/base.html.php -->

<!-- ... -->
<div id="sidebar">
<?php echo $view['actions']->render(
new \Symfony\Component\HttpKernel\Controller\ControllerReference(
'AppBundle\\Controller\\ArticleController:recentArticlesAction',
array('max' => 3)
)
) ?>
</div>


The result of an embedded controler can also be :doc:`cached </http_cache/esi>`