From 89d56ea04cfbd67ffe2bed783905b79dadef0449 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 29 Oct 2017 14:07:12 -0400 Subject: [PATCH 1/7] Updating routing for Flex --- routing.rst | 266 +++++++++++++++------------------------------------- 1 file changed, 78 insertions(+), 188 deletions(-) diff --git a/routing.rst b/routing.rst index 843cd507808..93b8400c01b 100644 --- a/routing.rst +++ b/routing.rst @@ -4,33 +4,23 @@ 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 woud 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 ----------------- +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 +39,7 @@ The route is simple: * * @Route("/blog", name="blog_list") */ - public function listAction() + public function list() { // ... } @@ -59,7 +49,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 +63,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 +79,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 +92,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 +119,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. ``routing.xml``) and Symfony will automatically use it. .. _routing-requirements: @@ -181,7 +161,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 +169,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 +180,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 +197,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 +209,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 +260,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 +271,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 +290,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 +304,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 +356,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 +366,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 +386,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 +400,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 +436,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 +478,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 +488,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 +505,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 +517,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 +592,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 +628,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! ----------- From 8b923e61cbff684a16286c378c1fbb32612466ea Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 29 Oct 2017 14:12:41 -0400 Subject: [PATCH 2/7] WIP controller changes --- controller.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controller.rst b/controller.rst index b44b8273672..bca047a4141 100644 --- a/controller.rst +++ b/controller.rst @@ -35,8 +35,8 @@ This renders a page that prints a lucky (random) 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. +create the response. It might read information from the request, load data from a +database (or API), 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. From 218e2d4301096768f187f664f5e37f7d38ce8e35 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 29 Oct 2017 14:31:41 -0400 Subject: [PATCH 3/7] WIP controller chapter --- controller.rst | 66 ++++++++++++++++++----------------------------- page_creation.rst | 2 ++ 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/controller.rst b/controller.rst index bca047a4141..142eb4ea16c 100644 --- a/controller.rst +++ b/controller.rst @@ -66,7 +66,7 @@ class:: /** * @Route("/lucky/number/{max}") */ - public function numberAction($max) + public function number($max) { $number = mt_rand(0, $max); @@ -76,7 +76,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 +89,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 +103,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,12 +120,12 @@ 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 +Add the ``use`` statement atop your controller class and then modify ``LuckyController`` to extend it:: // src/Controller/LuckyController.php @@ -146,11 +145,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 @@ -169,11 +169,17 @@ 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); @@ -189,23 +195,9 @@ 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 some URL provided byend-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,15 +213,7 @@ 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:: diff --git a/page_creation.rst b/page_creation.rst index 3f198eda84e..e34ccde4825 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 ----------------- From 44f2ff83ffb44834489e9fe371f7a9864efd2905 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Nov 2017 22:32:47 -0400 Subject: [PATCH 4/7] more work on getting started docs --- controller.rst | 51 ++++++++++------------------------------------- page_creation.rst | 20 ++++++++++++++----- 2 files changed, 26 insertions(+), 45 deletions(-) diff --git a/controller.rst b/controller.rst index 142eb4ea16c..6c132bd4884 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 data from a -database (or API), 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,7 +34,7 @@ class:: class LuckyController { /** - * @Route("/lucky/number/{max}") + * @Route("/lucky/number/{max}", name="app_lucky_number") */ public function number($max) { @@ -126,14 +96,17 @@ To make life nicer, Symfony comes with two optional base You can extend either to get access to some `helper methods`_. Add the ``use`` statement atop your controller class and then modify -``LuckyController`` to extend it:: +``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 { // ... } @@ -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 ~~~~~~~~~~~ @@ -184,18 +157,16 @@ and ``redirect()`` methods:: 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 byend-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`_. .. index:: diff --git a/page_creation.rst b/page_creation.rst index e34ccde4825..71e7f9e9f02 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -166,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 @@ -203,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 { // ... } @@ -268,7 +278,7 @@ project: ``src/`` All your PHP code lives here. -99% of the time, you'll be working in ``src/`` (PHP files) or ``config/`` (everything +Most 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. So what about the other directories in the project? From 10563a9335452e9544e811eed102ad18761b0725 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 5 Nov 2017 16:51:46 -0500 Subject: [PATCH 5/7] Proofing the controller chapters --- controller.rst | 159 +++++++++----------------- reference/configuration/framework.rst | 2 + 2 files changed, 58 insertions(+), 103 deletions(-) diff --git a/controller.rst b/controller.rst index 6c132bd4884..f5e448ccbc6 100644 --- a/controller.rst +++ b/controller.rst @@ -191,9 +191,10 @@ Templating and Twig are explained more in the 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 @@ -289,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:: @@ -337,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: @@ -403,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: ~ - - .. code-block:: xml +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 `. - - - +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; @@ -466,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`. @@ -558,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:: @@ -578,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:: @@ -617,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; @@ -631,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() @@ -663,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/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 ~~~~~~~ From 7f0163a18a5f966d33d59289e78a346c986479b2 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 30 Nov 2017 22:43:07 -0500 Subject: [PATCH 6/7] tweaks --- controller.rst | 2 +- page_creation.rst | 7 +++++-- routing.rst | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/controller.rst b/controller.rst index f5e448ccbc6..102ca934e3f 100644 --- a/controller.rst +++ b/controller.rst @@ -118,7 +118,7 @@ and many others that you'll learn about next. .. tip:: - What's the difference between ``Controller`` or ``AbstractController``. Not much: + 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 diff --git a/page_creation.rst b/page_creation.rst index 71e7f9e9f02..225731d015b 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -278,8 +278,8 @@ project: ``src/`` All your PHP code lives here. -Most 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? @@ -299,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/routing.rst b/routing.rst index 93b8400c01b..e12ea0767be 100644 --- a/routing.rst +++ b/routing.rst @@ -8,7 +8,7 @@ Beautiful URLs are a must for any serious web application. This means leaving be 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 woud 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. @@ -126,7 +126,7 @@ use them later to :ref:`generate URLs `. 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! Just create a - new routing file (e.g. ``routing.xml``) and Symfony will automatically use it. + new routing file (e.g. ``routes.xml``) and Symfony will automatically use it. .. _routing-requirements: From 0cbbc283235ca06470d23b7ed8ad3d992c3894fc Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 30 Nov 2017 22:48:04 -0500 Subject: [PATCH 7/7] fixing build errors --- page_creation.rst | 10 +++++----- routing.rst | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/page_creation.rst b/page_creation.rst index 225731d015b..34251dad928 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -168,11 +168,11 @@ To get a list of *all* of the routes in your system, use the ``debug:router`` co You should see your *one* route so far: - ------------------ -------- -------- ------ --------------- - Name Method Scheme Host Path - ------------------ -------- -------- ------ --------------- - app_lucky_number ANY ANY ANY /lucky/number - ------------------ -------- -------- ------ --------------- +================== ======== ======== ====== =============== + Name Method Scheme Host Path +================== ======== ======== ====== =============== + app_lucky_number ANY ANY ANY /lucky/number +================== ======== ======== ====== =============== You'll learn about many more commands as you continue! diff --git a/routing.rst b/routing.rst index e12ea0767be..732c847c5be 100644 --- a/routing.rst +++ b/routing.rst @@ -15,6 +15,8 @@ the change is simple. .. index:: single: Routing; Basics +.. _routing-creating-routes: + Creating Routes ---------------