diff --git a/controller.rst b/controller.rst index b44b8273672..102ca934e3f 100644 --- a/controller.rst +++ b/controller.rst @@ -4,42 +4,12 @@ Controller ========== -A controller is a PHP function you create that reads information from the Symfony's +A controller is a PHP function you create that reads information from the ``Request`` object and creates and returns a ``Response`` object. The response could be an HTML page, JSON, XML, a file download, a redirect, a 404 error or anything else you can dream up. The controller executes whatever arbitrary logic *your application* needs to render the content of a page. -See how simple this is by looking at a Symfony controller in action. -This renders a page that prints a lucky (random) number:: - - // src/Controller/LuckyController.php - namespace App\Controller; - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\Routing\Annotation\Route; - - class LuckyController - { - /** - * @Route("/lucky/number") - */ - public function numberAction() - { - $number = mt_rand(0, 100); - - return new Response( - 'Lucky number: '.$number.'' - ); - } - } - -But in the real world, your controller will probably do a lot of work in order to -create the response. It might read information from the request, load a database -resource, send an email or set information on the user's session. -But in all cases, the controller will eventually return the ``Response`` object -that will be delivered back to the client. - .. tip:: If you haven't already created your first working page, check out @@ -64,9 +34,9 @@ class:: class LuckyController { /** - * @Route("/lucky/number/{max}") + * @Route("/lucky/number/{max}", name="app_lucky_number") */ - public function numberAction($max) + public function number($max) { $number = mt_rand(0, $max); @@ -76,7 +46,7 @@ class:: } } -The controller is the ``numberAction()`` method, which lives inside a +The controller is the ``number()`` method, which lives inside a controller class ``LuckyController``. This controller is pretty straightforward: @@ -89,12 +59,10 @@ This controller is pretty straightforward: must return. * *line 7*: The class can technically be called anything - but should end in the - word ``Controller`` (this isn't *required*, but some shortcuts rely on this). + word ``Controller`` -* *line 12*: Each action method in a controller class is suffixed with ``Action`` - (again, this isn't *required*, but some shortcuts rely on this). This method - is allowed to have a ``$max`` argument thanks to the ``{max}`` - :doc:`wildcard in the route `. +* *line 12*: The action method is allowed to have a ``$max`` argument thanks to the + ``{max}`` :doc:`wildcard in the route `. * *line 16*: The controller creates and returns a ``Response`` object. @@ -105,7 +73,8 @@ Mapping a URL to a Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to *view* the result of this controller, you need to map a URL to it via -a route. This was done above with the ``@Route("/lucky/number/{max}")`` annotation. +a route. This was done above with the ``@Route("/lucky/number/{max}")`` +:ref:`route annotation `. To see your page, go to this URL in your browser: @@ -121,20 +90,23 @@ For more information on routing, see :doc:`/routing`. The Base Controller Classes & Services -------------------------------------- -For convenience, Symfony comes with two optional base +To make life nicer, Symfony comes with two optional base :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` and -:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController` -classes. You can extend either to get access to a number of `helper methods`_. +:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController`. +You can extend either to get access to some `helper methods`_. -Add the ``use`` statement atop the ``Controller`` class and then modify -``LuckyController`` to extend it:: +Add the ``use`` statement atop your controller class and then modify +``LuckyController`` to extend it: + +.. code-block:: diff // src/Controller/LuckyController.php namespace App\Controller; - use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; - class LuckyController extends Controller + - class LuckyController + + class LuckyController extends Controller { // ... } @@ -146,11 +118,12 @@ and many others that you'll learn about next. .. tip:: - 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 *do* need direct access to the - container, using ``Controller`` is fine. + What's the difference between ``Controller`` or ``AbstractController``? Not much: + both are identical, except that ``AbstractController`` is more restrictive: it + does not allow you to access services directly via ``$this->get()`` or + ``$this->container->get()``. This forces you to write more robust code to access + services. But if you *do* need direct access to the container, using ``Controller`` + is fine. .. index:: single: Controller; Redirecting @@ -161,7 +134,7 @@ Generating URLs The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::generateUrl` method is just a helper method that generates the URL for a given route:: - $url = $this->generateUrl('blog_show', array('slug' => 'slug-value')); + $url = $this->generateUrl('app_lucky_number', array('max' => 10)); Redirecting ~~~~~~~~~~~ @@ -169,43 +142,33 @@ Redirecting If you want to redirect the user to another page, use the ``redirectToRoute()`` and ``redirect()`` methods:: + use Symfony\Component\HttpFoundation\RedirectResponse; + + // ... public function indexAction() { // redirect to the "homepage" route return $this->redirectToRoute('homepage'); + // redirectToRoute is a shortcut for: + // return new RedirectResponse($this->generateUrl('homepage')); + // do a permanent - 301 redirect return $this->redirectToRoute('homepage', array(), 301); // redirect to a route with parameters - return $this->redirectToRoute('blog_show', array('slug' => 'my-page')); + return $this->redirectToRoute('app_lucky_number', array('max' => 10)); // redirect externally return $this->redirect('http://symfony.com/doc'); } -For more information, see the :doc:`Routing article `. - .. caution:: The ``redirect()`` method does not check its destination in any way. If you - redirect to some URL provided by the end-users, your application may be open + redirect to a URL provided by end-users, your application may be open to the `unvalidated redirects security vulnerability`_. - -.. tip:: - - The ``redirectToRoute()`` method is simply a shortcut that creates a - ``Response`` object that specializes in redirecting the user. It's - equivalent to:: - - use Symfony\Component\HttpFoundation\RedirectResponse; - - public function indexAction() - { - return new RedirectResponse($this->generateUrl('homepage')); - } - .. index:: single: Controller; Rendering templates @@ -221,24 +184,17 @@ object for you:: // renders templates/lucky/number.html.twig return $this->render('lucky/number.html.twig', array('name' => $name)); -Templates can also live in deeper sub-directories. Just try to avoid -creating unnecessarily deep structures:: - - // renders templates/lottery/lucky/number.html.twig - return $this->render('lottery/lucky/number.html.twig', array( - 'name' => $name, - )); - -The Symfony templating system and Twig are explained more in the +Templating and Twig are explained more in the :doc:`Creating and Using Templates article `. .. index:: single: Controller; Accessing services .. _controller-accessing-services: +.. _accessing-other-services: -Fetching Services as Controller Arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Fetching Services +~~~~~~~~~~~~~~~~~ Symfony comes *packed* with a lot of useful objects, called :doc:`services `. These are used for rendering templates, sending emails, querying the database and @@ -334,26 +290,12 @@ in your controllers. For more information about services, see the :doc:`/service_container` article. -.. _controller-service-arguments-tag: - -.. note:: - If this isn't working, make sure your controller is registered as a service, - is :ref:`autoconfigured ` and extends either - :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` or - :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController`. If - you use the :ref:`services.yaml configuration from the Symfony Standard Edition `, - then your controllers are already registered as services and autoconfigured. - - If you're not using the default configuration, you can tag your service manually - with ``controller.service_arguments``. - -.. _accessing-other-services: .. _controller-access-services-directly: Accessing the Container Directly ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you extend the base ``Controller`` class, you can access any Symfony service +If you extend the base ``Controller`` class, you can access :ref:`public services ` via the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::get` method. Here are several common services you might need:: @@ -382,40 +324,44 @@ and that it's :ref:`public `. Managing Errors and 404 Pages ----------------------------- -When things are not found, you should play well with the HTTP protocol and -return a 404 response. To do this, you'll throw a special type of exception. -If you're extending the base ``Controller`` or the base ``AbstractController`` -class, do the following:: +When things are not found, you should return a 404 response. To do this, throw a +special type of exception:: + + use Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException; + // ... public function indexAction() { // retrieve the object from database $product = ...; if (!$product) { throw $this->createNotFoundException('The product does not exist'); + + // the above is just a shortcut for: + // throw new NotFoundHttpException('The product does not exist'); } return $this->render(...); } -The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createNotFoundException` +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerTrait::createNotFoundException` method is just a shortcut to create a special :class:`Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException` object, which ultimately triggers a 404 HTTP response inside Symfony. -Of course, you're free to throw any ``Exception`` class in your controller - -Symfony will automatically return a 500 HTTP response code. +Of course, you can throw any ``Exception`` class in your controller: Symfony will +automatically return a 500 HTTP response code. .. code-block:: php throw new \Exception('Something went wrong!'); In every case, an error page is shown to the end user and a full debug -error page is shown to the developer (i.e. when you're using the ``index.php`` -front controller - see :ref:`page-creation-environments`). +error page is shown to the developer (i.e. when you're in "Debug" mode - see +:ref:`page-creation-environments`). -You'll want to customize the error page your user sees. To do that, see -the :doc:`/controller/error_pages` article. +To customize the error page that's shown to the user, see the +:doc:`/controller/error_pages` article. .. _controller-request-argument: @@ -448,54 +394,28 @@ Request object. Managing the Session -------------------- -Symfony provides a nice session object that you can use to store information -about the user between requests. By default, Symfony stores the token in a -cookie and writes the attributes to a file by using native PHP sessions. - -First, enable sessions in your configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - # ... - - session: - # With this config, PHP's native session handling is used - handler_id: ~ +Symfony provides a session service that you can use to store information +about the user between requests. Session storage and other configuration can +be controlled under the :ref:`framework.session configuration `. - .. code-block:: xml +First, activate the session by uncommenting the ``session`` key in ``config/packages/framework.yaml``: - - - +.. code-block:: diff - - - - - - - .. code-block:: php + # config/packages/framework.yaml + framework: + # ... - // config/packages/framework.php - $container->loadFromExtension('framework', array( - 'session' => array( - // ... - 'handler_id' => null, - ), - )); + - #session: + - # # The native PHP session handler will be used + - # handler_id: ~ + + session: + + # The native PHP session handler will be used + + handler_id: ~ + # ... -To retrieve the session, add the :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface` -type-hint to your argument and Symfony will provide you with a session:: +To get the session, add an argument and type-hint it with +:class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface`:: use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -511,12 +431,16 @@ type-hint to your argument and Symfony will provide you with a session:: $filters = $session->get('filters', array()); } +.. versionadded:: 3.3 + The ability to request a ``Session`` instance in controllers was introduced + in Symfony 3.3. + Stored attributes remain in the session for the remainder of that user's session. .. tip:: Every ``SessionInterface`` implementation is supported. If you have your - own implementation, type-hint this in the arguments instead. + own implementation, type-hint this in the argument instead. For more info, see :doc:`/session`. @@ -603,11 +527,9 @@ read any flash messages from the session using ``app.flashes()``: -.. note:: - - It's common to use ``notice``, ``warning`` and ``error`` as the keys of the - different types of flash messages, but you can use any key that fits your - needs. +It's common to use ``notice``, ``warning`` and ``error`` as the keys of the +different types of flash messages, but you can use any key that fits your +needs. .. tip:: @@ -623,7 +545,7 @@ read any flash messages from the session using ``app.flashes()``: The Request and Response Object ------------------------------- -As mentioned :ref:`earlier `, the framework will +As mentioned :ref:`earlier `, Symfony will pass the ``Request`` object to any controller argument that is type-hinted with the ``Request`` class:: @@ -662,10 +584,7 @@ some nice methods for getting and setting response headers. The header names are normalized so that using ``Content-Type`` is equivalent to ``content-type`` or even ``content_type``. -The only requirement for a controller is to return a ``Response`` object. -The :class:`Symfony\\Component\\HttpFoundation\\Response` class is an -abstraction around the HTTP response - the text-based message filled with -headers and content that's sent back to the client:: +The only requirement for a controller is to return a ``Response`` object:: use Symfony\Component\HttpFoundation\Response; @@ -676,26 +595,15 @@ headers and content that's sent back to the client:: $response = new Response(''); $response->headers->set('Content-Type', 'text/css'); -There are special classes that make certain kinds of responses easier: - -* For files, there is :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`. - See :ref:`component-http-foundation-serving-files`. - -* For streamed responses, there is - :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse`. - See :ref:`streaming-response`. +There are special classes that make certain kinds of responses easier. Some of these +are mentioned below. To learn more about the ``Request`` and ``Response`` (and special +``Response`` classes), see the :ref:`HttpFoundation component documentation `. -.. seealso:: +Returning JSON Response +~~~~~~~~~~~~~~~~~~~~~~~ - Now that you know the basics you can continue your research on Symfony - ``Request`` and ``Response`` object in the - :ref:`HttpFoundation component documentation `. - -JSON Helper -~~~~~~~~~~~ - -To return JSON from a controller, use the ``json()`` helper method on the base controller. -This returns a special ``JsonResponse`` object that encodes the data automatically:: +To return JSON from a controller, use the ``json()`` helper method. This returns a +special ``JsonResponse`` object that encodes the data automatically:: // ... public function indexAction() @@ -708,11 +616,11 @@ This returns a special ``JsonResponse`` object that encodes the data automatical } If the :doc:`serializer service ` is enabled in your -application, contents passed to ``json()`` are encoded with it. Otherwise, +application, it will be used to serialize the data to JSON. Otherwise, the :phpfunction:`json_encode` function is used. -File helper -~~~~~~~~~~~ +Streaming File Responses +~~~~~~~~~~~~~~~~~~~~~~~~ You can use the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::file` helper to serve a file from inside a controller:: diff --git a/page_creation.rst b/page_creation.rst index 3f198eda84e..34251dad928 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -89,6 +89,8 @@ to creating a page? return a ``Response`` object. You'll learn more about :doc:`controllers ` in their own section, including how to return JSON responses. +.. _annotation-routes: + Annotation Routes ----------------- @@ -164,6 +166,14 @@ To get a list of *all* of the routes in your system, use the ``debug:router`` co $ php bin/console debug:router +You should see your *one* route so far: + +================== ======== ======== ====== =============== + Name Method Scheme Host Path +================== ======== ======== ====== =============== + app_lucky_number ANY ANY ANY /lucky/number +================== ======== ======== ====== =============== + You'll learn about many more commands as you continue! The Web Debug Toolbar: Debugging Dream @@ -201,15 +211,17 @@ First, install Twig: $ composer require twig Second, make sure that ``LuckyController`` extends Symfony's base -:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class:: +:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class: + +.. code-block:: diff // src/Controller/LuckyController.php // ... - // --> add this new use statement - use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; - class LuckyController extends Controller + - class LuckyController + + class LuckyController extends Controller { // ... } @@ -266,8 +278,8 @@ project: ``src/`` All your PHP code lives here. -99% of the time, you'll be working in ``src/`` (PHP files) or ``config/`` (everything -else). As you keep reading, you'll learn what can be done inside each of these. +Most of the time, you'll be working in ``src/`` (PHP files) or ``config/`` As you +keep reading, you'll learn what can be done inside each of these. So what about the other directories in the project? @@ -287,6 +299,9 @@ So what about the other directories in the project? This is the document root for your project: you put any publicly accessible files here. +And when you install new packages, new directories will be created automatically +when needed. + What's Next? ------------ diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index fdb8b257711..df49de99512 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -688,6 +688,8 @@ The value can be one of: ``true`` is recommended in the development environment, while ``false`` or ``null`` might be preferred in production. +.. _config-framework-session: + session ~~~~~~~ diff --git a/routing.rst b/routing.rst index 843cd507808..732c847c5be 100644 --- a/routing.rst +++ b/routing.rst @@ -4,33 +4,25 @@ Routing ======= -Beautiful URLs are an absolute must for any serious web application. This -means leaving behind ugly URLs like ``index.php?article_id=57`` in favor -of something like ``/read/intro-to-symfony``. +Beautiful URLs are a must for any serious web application. This means leaving behind +ugly URLs like ``index.php?article_id=57`` in favor of something like ``/read/intro-to-symfony``. Having flexibility is even more important. What if you need to change the -URL of a page from ``/blog`` to ``/news``? How many links should you need to +URL of a page from ``/blog`` to ``/news``? How many links would you need to hunt down and update to make the change? If you're using Symfony's router, the change is simple. -The Symfony router lets you define creative URLs that you map to different -areas of your application. By the end of this article, you'll be able to: - -* Create complex routes that map to controllers -* Generate URLs inside templates and controllers -* Load routing resources from bundles (or anywhere else) -* Debug your routes - .. index:: single: Routing; Basics -Routing Examples ----------------- +.. _routing-creating-routes: + +Creating Routes +--------------- -A *route* is a map from a URL path to a controller. For example, suppose -you want to match any URL like ``/blog/my-post`` or ``/blog/all-about-symfony`` -and send it to a controller that can look up and render that blog entry. -The route is simple: +A *route* is a map from a URL path to a controller. Suppose you want one route that +matches ``/blog`` exactly and another more dynamic route that can match *any* URL +like ``/blog/my-post`` or ``/blog/all-about-symfony``:: .. configuration-block:: @@ -49,7 +41,7 @@ The route is simple: * * @Route("/blog", name="blog_list") */ - public function listAction() + public function list() { // ... } @@ -59,7 +51,7 @@ The route is simple: * * @Route("/blog/{slug}", name="blog_show") */ - public function showAction($slug) + public function show($slug) { // $slug will equal the dynamic part of the URL // e.g. at /blog/yay-routing, then $slug='yay-routing' @@ -73,11 +65,11 @@ The route is simple: # config/routes.yaml blog_list: path: /blog - defaults: { _controller: AppBundle:Blog:list } + controller: App\Controller\BlogController::list blog_show: path: /blog/{slug} - defaults: { _controller: AppBundle:Blog:show } + controller: App\Controller\BlogController::show .. code-block:: xml @@ -89,11 +81,11 @@ The route is simple: http://symfony.com/schema/routing/routing-1.0.xsd"> - AppBundle:Blog:list + App\Controller\BlogController::list - AppBundle:Blog:show + App\Controller\BlogController::show @@ -102,25 +94,26 @@ The route is simple: // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; + use App\Controller\BlogController; $collection = new RouteCollection(); $collection->add('blog_list', new Route('/blog', array( - '_controller' => 'AppBundle:Blog:list', + '_controller' => [BlogController::class, 'list'] ))); $collection->add('blog_show', new Route('/blog/{slug}', array( - '_controller' => 'AppBundle:Blog:show', + '_controller' => [BlogController::class, 'show'] ))); return $collection; Thanks to these two routes: -* If the user goes to ``/blog``, the first route is matched and ``listAction()`` +* If the user goes to ``/blog``, the first route is matched and ``list()`` is executed; -* If the user goes to ``/blog/*``, the second route is matched and ``showAction()`` +* If the user goes to ``/blog/*``, the second route is matched and ``show()`` is executed. Because the route path is ``/blog/{slug}``, a ``$slug`` variable is - passed to ``showAction()`` matching that value. For example, if the user goes to + passed to ``show()`` matching that value. For example, if the user goes to ``/blog/yay-routing``, then ``$slug`` will equal ``yay-routing``. Whenever you have a ``{placeholder}`` in your route path, that portion becomes a @@ -128,25 +121,14 @@ wildcard: it matches *any* value. Your controller can now *also* have an argumen called ``$placeholder`` (the wildcard and argument names *must* match). Each route also has an internal name: ``blog_list`` and ``blog_show``. These can -be anything (as long as each is unique) and don't have any meaning yet. -Later, you'll use it to generate URLs. +be anything (as long as each is unique) and don't have any meaning yet. You'll +use them later to :ref:`generate URLs `. .. sidebar:: Routing in Other Formats The ``@Route`` above each method is called an *annotation*. If you'd rather - configure your routes in YAML, XML or PHP, that's no problem! - - In these formats, the ``_controller`` "defaults" value is a special key that - tells Symfony which controller should be executed when a URL matches this route. - The ``_controller`` string is called the - :ref:`logical name `. It follows a pattern that - points to a specific PHP class and method, in this case the - ``App\Controller\BlogController::listAction`` and - ``App\Controller\BlogController::showAction`` methods. - -This is the goal of the Symfony router: to map the URL of a request to a -controller. Along the way, you'll learn all sorts of tricks that make mapping -even the most complex URLs easy. + configure your routes in YAML, XML or PHP, that's no problem! Just create a + new routing file (e.g. ``routes.xml``) and Symfony will automatically use it. .. _routing-requirements: @@ -181,7 +163,7 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n /** * @Route("/blog/{page}", name="blog_list", requirements={"page": "\d+"}) */ - public function listAction($page) + public function list($page) { // ... } @@ -189,7 +171,7 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n /** * @Route("/blog/{slug}", name="blog_show") */ - public function showAction($slug) + public function show($slug) { // ... } @@ -200,7 +182,7 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n # config/routes.yaml blog_list: path: /blog/{page} - defaults: { _controller: AppBundle:Blog:list } + controller: App\Controller\BlogController::list requirements: page: '\d+' @@ -217,7 +199,7 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n http://symfony.com/schema/routing/routing-1.0.xsd"> - AppBundle:Blog:list + App\Controller\BlogController::list \d+ @@ -229,10 +211,11 @@ To fix this, add a *requirement* that the ``{page}`` wildcard can *only* match n // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; + use App\Controller\BlogController; $collection = new RouteCollection(); $collection->add('blog_list', new Route('/blog/{page}', array( - '_controller' => 'AppBundle:Blog:list', + '_controller' => [BlogController::class, 'list'], ), array( 'page' => '\d+' ))); @@ -279,7 +262,7 @@ So how can you make ``blog_list`` once again match when the user visits /** * @Route("/blog/{page}", name="blog_list", requirements={"page": "\d+"}) */ - public function listAction($page = 1) + public function list($page = 1) { // ... } @@ -290,7 +273,9 @@ So how can you make ``blog_list`` once again match when the user visits # config/routes.yaml blog_list: path: /blog/{page} - defaults: { _controller: AppBundle:Blog:list, page: 1 } + controller: App\Controller\BlogController::list + defaults: + page: 1 requirements: page: '\d+' @@ -307,7 +292,7 @@ So how can you make ``blog_list`` once again match when the user visits http://symfony.com/schema/routing/routing-1.0.xsd"> - AppBundle:Blog:list + App\Controller\BlogController::list 1 \d+ @@ -321,12 +306,13 @@ So how can you make ``blog_list`` once again match when the user visits // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; + use App\Controller\BlogController; $collection = new RouteCollection(); $collection->add('blog_list', new Route( '/blog/{page}', array( - '_controller' => 'AppBundle:Blog:list', + '_controller' => [BlogController::class, 'list'], 'page' => 1, ), array( @@ -372,7 +358,7 @@ With all of this in mind, check out this advanced example: * } * ) */ - public function showAction($_locale, $year, $slug) + public function show($_locale, $year, $slug) { } } @@ -382,7 +368,9 @@ With all of this in mind, check out this advanced example: # config/routes.yaml article_show: path: /articles/{_locale}/{year}/{slug}.{_format} - defaults: { _controller: AppBundle:Article:show, _format: html } + controller: App\Controller\ArticleController::show + defaults: + _format: html requirements: _locale: en|fr _format: html|rss @@ -400,7 +388,7 @@ With all of this in mind, check out this advanced example: - AppBundle:Article:show + App\Controller\ArticleController::show html en|fr html|rss @@ -414,12 +402,13 @@ With all of this in mind, check out this advanced example: // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; + use App\Controller\ArticleController; $collection = new RouteCollection(); $collection->add( 'article_show', new Route('/articles/{_locale}/{year}/{slug}.{_format}', array( - '_controller' => 'AppBundle:Article:show', + '_controller' => [ArticleController::class, 'show'], '_format' => 'html', ), array( '_locale' => 'en|fr', @@ -449,16 +438,7 @@ a slash. URLs matching this route might look like: Ultimately, the request format is used for such things as setting the ``Content-Type`` of the response (e.g. a ``json`` request format translates - into a ``Content-Type`` of ``application/json``). It can also be used in the - controller to render a different template for each value of ``_format``. - The ``_format`` parameter is a very powerful way to render the same content - in different formats. - - In Symfony versions previous to 3.0, it is possible to override the request - format by adding a query parameter named ``_format`` (for example: - ``/foo/bar?_format=json``). Relying on this behavior not only is considered - a bad practice but it will complicate the upgrade of your applications to - Symfony 3. + into a ``Content-Type`` of ``application/json``). .. note:: @@ -500,44 +480,9 @@ that are special: each adds a unique piece of functionality inside your applicat Controller Naming Pattern ------------------------- -If you use YAML, XML or PHP route configuration, then each route must have a -``_controller`` parameter, which dictates which controller should be executed when -that route is matched. This parameter uses a simple string pattern called the -*logical controller name*, which Symfony maps to a specific PHP method and class. -The pattern has three parts, each separated by a colon: - - **bundle**:**controller**:**action** - -For example, a ``_controller`` value of ``AppBundle:Blog:show`` means: - -============= ================== ================ -Bundle Controller Class Method Name -============= ================== ================ -``AppBundle`` ``BlogController`` ``showAction()`` -============= ================== ================ - -The controller might look like this:: - - // src/Controller/BlogController.php - namespace App\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\Controller; - - class BlogController extends Controller - { - public function showAction($slug) - { - // ... - } - } - -Notice that Symfony adds the string ``Controller`` to the class name (``Blog`` -=> ``BlogController``) and ``Action`` to the method name (``show`` => ``showAction()``). - -You could also refer to this controller using its fully-qualified class name -and method: ``App\Controller\BlogController::showAction``. But if you -follow some simple conventions, the logical name is more concise and allows -more flexibility. +The ``controller`` value in your routes has a very simple format ``CONTROLLER_CLASS::METHOD``. +If your controller is registered as a service, you can also use just one colon separator +(e.g. ``service_name:index``). .. tip:: @@ -545,75 +490,16 @@ more flexibility. you do not have to pass the method name, but can just use the fully qualified class name (e.g. ``App\Controller\BlogController``). -.. note:: - - In addition to using the logical name or the fully-qualified class name, - Symfony supports a third way of referring to a controller. This method - uses just one colon separator (e.g. ``service_name:indexAction``) and - refers to the controller as a service (see :doc:`/controller/service`). - -.. index:: - single: Routing; Creating routes - -.. _routing-creating-routes: - -Loading Routes --------------- - -Symfony loads all the routes for your application from a *single* routing configuration -file: ``config/routes.yaml``. But from inside of this file, you can load any -*other* routing files you want. In fact, by default, Symfony loads annotation route -configuration from your AppBundle's ``Controller/`` directory, which is how Symfony -sees our annotation routes: - -.. configuration-block:: - - .. code-block:: yaml - - # config/routes.yaml - app: - resource: "@AppBundle/Controller/" - type: annotation - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // config/routes.php - use Symfony\Component\Routing\RouteCollection; - - $collection = new RouteCollection(); - $collection->addCollection( - // second argument is the type, which is required to enable - // the annotation reader for this resource - $loader->import("@AppBundle/Controller/", "annotation") - ); - - return $collection; - -For more details on loading routes, including how to prefix the paths of loaded routes, -see :doc:`/routing/external_resources`. - .. index:: single: Routing; Generating URLs +.. _routing-generate: + Generating URLs --------------- -The routing system should also be used to generate URLs. In reality, routing -is a bidirectional system: mapping the URL to a controller and -a route back to a URL. +The routing system can also generate URLs. In reality, routing is a bidirectional +system: mapping the URL to a controller and also a route back to a URL. To generate a URL, you need to specify the name of the route (e.g. ``blog_show``) and any wildcards (e.g. ``slug = my-blog-post``) used in the path for that @@ -621,7 +507,7 @@ route. With this information, any URL can easily be generated:: class MainController extends Controller { - public function showAction($slug) + public function show($slug) { // ... @@ -633,16 +519,31 @@ route. With this information, any URL can easily be generated:: } } -.. note:: +If you need to generate a URL from a service, type-hint the :class:`Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface` +service:: - The ``generateUrl()`` method defined in the base - :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class is - just a shortcut for this code:: + // src/Service/SomeService.php - $url = $this->container->get('router')->generate( - 'blog_show', - array('slug' => 'my-blog-post') - ); + use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + + class SomeService + { + private $router; + + public function __construct(UrlGeneratorInterface $router) + { + $this->router = $router; + } + + public function someMethod() + { + $url = $this->router->generate( + 'blog_show', + array('slug' => 'my-blog-post') + ); + // ... + } + } .. index:: single: Routing; Generating URLs in a template @@ -693,12 +594,12 @@ Troubleshooting Here are some common errors you might see while working with routing: - Controller "App\Controller\BlogController::showAction()" requires that you + Controller "App\Controller\BlogController::show()" requires that you provide a value for the "$slug" argument. This happens when your controller method has an argument (e.g. ``$slug``):: - public function showAction($slug) + public function show($slug) { // .. } @@ -729,15 +630,6 @@ one for each supported language; or use any of the bundles created by the community to implement this feature, such as `JMSI18nRoutingBundle`_ and `BeSimpleI18nRoutingBundle`_. -Summary -------- - -Routing is a system for mapping the URL of incoming requests to the controller -function that should be called to process the request. It both allows you -to specify beautiful URLs and keeps the functionality of your application -decoupled from those URLs. Routing is a bidirectional mechanism, meaning that it -should also be used to generate URLs. - Keep Going! -----------