diff --git a/book/controller.rst b/book/controller.rst index 5cc592a0c87..7308e4f5ae4 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -33,9 +33,9 @@ common examples: * *Controller A* prepares a ``Response`` object representing the content for the homepage of the site. -* *Controller B* reads the ``slug`` parameter from the request to load a +* *Controller B* reads the ``{slug}`` placeholder from the request to load a blog entry from the database and creates a ``Response`` object displaying - that blog. If the ``slug`` can't be found in the database, it creates and + that blog. If the ``{slug}`` can't be found in the database, it creates and returns a ``Response`` object with a 404 status code. * *Controller C* handles the form submission of a contact form. It reads @@ -54,31 +54,45 @@ Every request handled by a Symfony project goes through the same simple lifecycl The framework takes care of all the repetitive stuff: you just need to write your custom code in the controller function: -#. Each request is handled by a single front controller file (e.g. ``app.php`` - or ``app_dev.php``) that bootstraps the application; +#. Each request executes a single front controller file (e.g. ``app.php`` on production + or ``app_dev.php`` on development) that bootstraps the application; -#. The ``Router`` reads information from the request (e.g. the URI), finds - a route that matches that information, and reads the ``_controller`` parameter - from the route; +#. The front controller's only job is to initialize Symfony's engine (called the + ``Kernel``) and pass it a ``Request`` object to handle; -#. The controller from the matched route is executed and the code inside the - controller creates and returns a ``Response`` object; +#. The Symfony core asks the router to inspect the request; -#. The HTTP headers and content of the ``Response`` object are sent back to - the client. +#. The router matches the incoming URL to a specific route and returns + information about the route, including the controller that should be + executed; -Creating a page is as easy as creating a controller (#3) and making a route that -maps a URL to that controller (#2). +#. The correct controller from the matched route is executed and the code + inside the controller creates and returns the appropriate ``Response`` + object; + +#. The HTTP headers and content of the ``Response`` object are sent back + to the client. + +Creating a page is as easy as creating a controller (#5) and making a route +that maps a URL to that controller (#4). + +.. image:: /images/http-xkcd-request.png + :align: center .. note:: - Though similarly named, a "front controller" is different from the - "controllers" talked about in this chapter. A front controller - is a short PHP file that lives in your web directory and through which - all requests are directed. A typical application will have a production - front controller (e.g. ``app.php``) and a development front controller - (e.g. ``app_dev.php``). You'll likely never need to edit, view or worry - about the front controllers in your application. + Though similarly named, a "front controller" is different from the PHP + functions called "controllers" talked about in this chapter. A front + controller is a short PHP file that lives in your ``web/`` directory + through which all requests are directed. A typical application will + have a production front controller (e.g. ``app.php``) and a development + front controller (e.g. ``app_dev.php``). You'll likely never need to + edit, view or worry about the front controllers in your application. + The "controller class" is a convenient way to group several "controllers", + also called actions, together in one class (e.g. ``updateAction()``, + ``deleteAction()``, etc). So, a controller is a method inside a controller + class. They hold your code which creates and returns the appropriate + ``Response`` object. .. index:: single: Controller; Simple example @@ -87,10 +101,8 @@ A Simple Controller ------------------- While a controller can be any PHP callable (a function, method on an object, -or a ``Closure``), a controller is usually a method inside a controller class. -Controllers are also called *actions*. - -.. code-block:: php +or a ``Closure``), a controller is usually a method inside a controller +class:: // src/AppBundle/Controller/HelloController.php namespace AppBundle\Controller; @@ -105,33 +117,29 @@ Controllers are also called *actions*. } } -.. tip:: - - Note that the *controller* is the ``indexAction`` method, which lives - inside a *controller class* (``HelloController``). Don't be confused - by the naming: a *controller class* is simply a convenient way to group - several controllers/actions together. Typically, the controller class - will house several controllers/actions (e.g. ``updateAction``, ``deleteAction``, - etc). +The controller is the ``indexAction()`` method, which lives inside a +controller class ``HelloController``. This controller is pretty straightforward: * *line 2*: Symfony takes advantage of PHP's namespace functionality to namespace the entire controller class. -* *line 4*: Symfony again takes advantage of PHP's namespace functionality: the ``use`` keyword imports the - ``Response`` class, which the controller must return. +* *line 4*: Symfony again takes advantage of PHP's namespace functionality: + the ``use`` keyword imports the ``Response`` class, which the controller + must return. * *line 6*: The class name is the concatenation of a name for the controller class (i.e. ``Hello``) and the word ``Controller``. This is a convention that provides consistency to controllers and allows them to be referenced - only by the first part of the name (i.e. ``Hello``) in the routing configuration. + only by the first part of the name (i.e. ``Hello``) in the routing + configuration. * *line 8*: Each action in a controller class is suffixed with ``Action`` - and is referenced in the routing configuration by the action's name (``index``). + and is referenced in the routing configuration by the action's name (e.g. ``index``). In the next section, you'll create a route that maps a URI to this action. You'll learn how the route's placeholders (``{name}``) become arguments - to the action method (``$name``). + to the controller method (``$name``). * *line 10*: The controller creates and returns a ``Response`` object. @@ -213,14 +221,11 @@ Simple, right? .. sidebar:: The AppBundle:Hello:index controller syntax - If you use the YML or XML formats, you'll refer to the controller using - a special shortcut syntax: ``AppBundle:Hello:index``. For more details - on the controller format, see :ref:`controller-string-syntax`. - -.. seealso:: - - You can learn much more about the routing system in the - :doc:`Routing chapter `. + If you use the YAML or XML formats, you'll refer to the controller + using a special shortcut syntax called the *logical controller name* + which, for example, looks like ``AppBundle:Hello:index``. For more + details on the controller format, read + :ref:`controller-string-syntax` subtitle of the Routing chapter. .. index:: single: Controller; Controller arguments @@ -231,8 +236,9 @@ Route Parameters as Controller Arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You already know that the route points to the -``HelloController::indexAction()`` method that lives inside AppBundle. What's -more interesting is the argument that is passed to that method:: +``HelloController::indexAction()`` controller method that lives inside AppBundle. +What's more interesting is the argument that is passed to that controller +method:: // src/AppBundle/Controller/HelloController.php // ... @@ -247,11 +253,14 @@ more interesting is the argument that is passed to that method:: } The controller has a single argument, ``$name``, which corresponds to the -``{name}`` parameter from the matched route (``ryan`` if you go to ``/hello/ryan``). -When executing your controller, Symfony matches each argument with a parameter -from the route. So the value for ``{name}`` is passed to ``$name``. +``{name}`` placeholder from the matched route (e.g. ``ryan`` if you go to +``/hello/ryan``). When executing the controller, Symfony matches each argument +with a placeholder from the route. So the value for ``{name}`` is passed +to ``$name``. Just make sure they the name of the placeholder is the +same as the name of the argument variable. -Take the following more-interesting example: +Take the following more-interesting example, where the controller has two +arguments:: .. configuration-block:: @@ -307,86 +316,53 @@ Take the following more-interesting example: return $collection; -Now, the controller can have two arguments:: - - public function indexAction($firstName, $lastName) - { - // ... - } - -Mapping route parameters to controller arguments is easy and flexible. Keep -the following guidelines in mind while you develop. +Mapping route parameters to controller arguments is easy and flexible. +Keep the following guidelines in mind while you develop. -* **The order of the controller arguments does not matter** +#. **The order of the controller arguments does not matter** - Symfony matches the parameter **names** from the route to the variable - **names** of the controller. The arguments of the controller could be totally - reordered and still work perfectly:: + Symfony matches the parameter **names** from the route to the variable + **names** of the controller. The arguments of the controller could be + totally reordered and still work perfectly:: - public function indexAction($lastName, $firstName) - { - // ... - } + public function indexAction($lastName, $firstName) + { + // ... + } -* **Each required controller argument must match up with a routing parameter** +#. **Each required controller argument must match up with a routing parameter** - The following would throw a ``RuntimeException`` because there is no ``foo`` - parameter defined in the route:: + The following would throw a ``RuntimeException`` because there is no + ``foo`` parameter defined in the route:: - public function indexAction($firstName, $lastName, $foo) - { - // ... - } + public function indexAction($firstName, $lastName, $foo) + { + // ... + } - Making the argument optional, however, is perfectly ok. The following - example would not throw an exception:: + Making the argument optional, however, is perfectly ok. The following + example would not throw an exception:: - public function indexAction($firstName, $lastName, $foo = 'bar') - { - // ... - } + public function indexAction($firstName, $lastName, $foo = 'bar') + { + // ... + } -* **Not all routing parameters need to be arguments on your controller** +#. **Not all routing parameters need to be arguments on your controller** - If, for example, the ``lastName`` weren't important for your controller, - you could omit it entirely:: + If, for example, the ``lastName`` weren't important for your controller, + you could omit it entirely:: - public function indexAction($firstName) - { - // ... - } + public function indexAction($firstName) + { + // ... + } .. tip:: - Every route also has a special ``_route`` parameter, which is equal to - the name of the route that was matched (e.g. ``hello``). Though not usually - useful, this is also available as a controller argument. You can also - pass other variables from your route to your controller arguments. See - :doc:`/cookbook/routing/extra_information`. - -.. _book-controller-request-argument: - -The ``Request`` as a Controller Argument -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -What if you need to read query parameters, grab a request header or get access -to an uploaded file? All of that information is stored in Symfony's ``Request`` -object. To get it in your controller, just add it as an argument and -**type-hint it with the Request class**:: - - use Symfony\Component\HttpFoundation\Request; - - public function indexAction($firstName, $lastName, Request $request) - { - $page = $request->query->get('page', 1); - - // ... - } - -.. seealso:: + You can also pass other variables from your route to your controller + arguments. See :doc:`/cookbook/routing/extra_information`. - Want to know more about getting information from the request? See - :ref:`Access Request Information `. .. index:: single: Controller; Base controller class @@ -394,11 +370,17 @@ object. To get it in your controller, just add it as an argument and The Base Controller Class ------------------------- -For convenience, Symfony comes with an optional base ``Controller`` class. -If you extend it, you'll get access to a number of helper methods and all -of your service objects via the container (see :ref:`controller-accessing-services`). - -Add the ``use`` statement atop the ``Controller`` class and then modify the +For convenience, Symfony comes with an optional base +:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class. +If you extend it, this won't change anything about how your controller +works, but you'll get access to a number of **helper methods** and the +**service container** (see :ref:`controller-accessing-services`): an +array-like object that gives you access to every useful object in the +system. These useful objects are called **services**, and Symfony ships +with a service object that can render Twig templates, another that can +log messages and many more. + +Add the ``use`` statement atop the ``Controller`` class and then modify ``HelloController`` to extend it:: // src/AppBundle/Controller/HelloController.php @@ -411,23 +393,31 @@ Add the ``use`` statement atop the ``Controller`` class and then modify the // ... } -This doesn't actually change anything about how your controller works: it -just gives you access to helper methods that the base controller class makes -available. These are just shortcuts to using core Symfony functionality that's -available to you with or without the use of the base ``Controller`` class. -A great way to see the core functionality in action is to look in the -`Controller class`_. +Helper methods are just shortcuts to using core Symfony functionality +that's available to you with or without the use of the base +``Controller`` class. A great way to see the core functionality in +action is to look in the +:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class. .. seealso:: - If you're curious about how a controller would work that did *not* extend - this base class, check out :doc:`Controllers as Services `. - This is optional, but can give you more control over the exact objects/dependencies - that are injected into your controller. + If you're curious about how a controller would work that did *not* + extend this base ``Controller`` class, check out cookbook article + :doc:`Controllers as Services `. + This is optional, but can give you more control over the exact + objects/dependencies that are injected into your controller. .. index:: single: Controller; Redirecting +Generating URLs +~~~~~~~~~~~~~~~ + +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::generateUrl` +method is just a helper method that generates the URL for a given route. + +.. _book-redirecting-users-browser: + Redirecting ~~~~~~~~~~~ @@ -441,21 +431,27 @@ If you want to redirect the user to another page, use the ``redirectToRoute()`` // return $this->redirect($this->generateUrl('homepage')); } -Or, if you want to redirect externally, just use ``redirect()`` and pass it the URL:: +.. versionadded:: 2.6 + The ``redirectToRoute()`` method was introduced in Symfony 2.6. Previously (and still now), you + could use ``redirect()`` and ``generateUrl()`` together for this (see the example above). + +By default, the ``redirectToRoute()`` method performs a 302 (temporary) redirect. To +perform a 301 (permanent) redirect, modify the third argument:: public function indexAction() { - return $this->redirect('http://symfony.com/doc'); + return $this->redirectToRoute('homepage', array(), 301); } -By default, the ``redirectToRoute()`` method performs a 302 (temporary) redirect. To -perform a 301 (permanent) redirect, modify the third argument:: +To redirect to an *external* site, use ``redirect()`` and pass it the external URL:: public function indexAction() { - return $this->redirectToRoute('homepage', array(), 301); + return $this->redirect('http://symfony.com/doc'); } +For more information, see the :doc:`Routing chapter `. + .. tip:: The ``redirectToRoute()`` method is simply a shortcut that creates a @@ -484,24 +480,29 @@ object for you:: // renders app/Resources/views/hello/index.html.twig return $this->render('hello/index.html.twig', array('name' => $name)); -You can also put templates in deeper sub-directories. Just try to avoid creating -unnecessarily deep structures:: +Templates can also live in deeper sub-directories. Just try to avoid +creating unnecessarily deep structures:: // renders app/Resources/views/hello/greetings/index.html.twig return $this->render('hello/greetings/index.html.twig', array( 'name' => $name )); +Templates are a generic way to render content in *any* format. And while in +most cases you'll use templates to render HTML content, a template can just +as easily generate JavaScript, CSS, XML or any other format you can dream of. +To learn how to render different templating formats read the :ref:`template-formats` +section of the Creating and Using Templates chapter. + The Symfony templating engine is explained in great detail in the -:doc:`Templating ` chapter. +:doc:`Creating and Using Templates chapter `. -.. sidebar:: Referencing Templates that Live inside the Bundle +.. sidebar:: Templating Naming Pattern - You can also put templates in the ``Resources/views`` directory of a - bundle and reference them with a - ``BundleName:DirectoryName:FileName`` syntax. For example, - ``AppBundle:Hello:index.html.twig`` would refer to the template located in - ``src/AppBundle/Resources/views/Hello/index.html.twig``. See :ref:`template-referencing-in-bundle`. + You can also put templates in the ``Resources/views`` directory of a bundle and + reference them with a special shortcut syntax like ``@AppBundle/Hello/index.html.twig`` + or ``@AppBundle/layout.html.twig``. These would live in at ``Resources/views/Hello/index.html.twig`` + and ``Resources/views/layout.html.twig`` inside the bundle respectively. .. index:: single: Controller; Accessing services @@ -517,7 +518,9 @@ any other "work" you can think of. When you install a new bundle, it probably brings in even *more* services. When extending the base controller class, you can access any Symfony service -via the ``get()`` method. Here are several common services you might need:: +via the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::get` +method of the ``Controller`` class. Here are several common services you might +need:: $templating = $this->get('templating'); @@ -543,7 +546,7 @@ 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 class, do the following:: +If you're extending the base ``Controller`` class, do the following:: public function indexAction() { @@ -556,8 +559,9 @@ If you're extending the base controller class, do the following:: return $this->render(...); } -The ``createNotFoundException()`` method is just a shortcut to create a -special :class:`Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException` +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::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 - @@ -568,16 +572,35 @@ Symfony will automatically return a 500 HTTP response code. 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 ``app_dev.php`` - -see :ref:`page-creation-environments`). +error page is shown to the developer (i.e. when you're using the ``app_dev.php`` +front controller - see :ref:`page-creation-environments`). -You'll want to customize the error page your user sees. To do that, see the -":doc:`/cookbook/controller/error_pages`" cookbook recipe. +You'll want to customize the error page your user sees. To do that, see +the ":doc:`/cookbook/controller/error_pages`" cookbook recipe. .. index:: single: Controller; The session single: Session +.. _book-controller-request-argument: + +The Request object as a Controller Argument +------------------------------------------- + +What if you need to read query parameters, grab a request header or get access +to an uploaded file? All of that information is stored in Symfony's ``Request`` +object. To get it in your controller, just add it as an argument and +**type-hint it with the ``Request`` class**:: + + use Symfony\Component\HttpFoundation\Request; + + public function indexAction($firstName, $lastName, Request $request) + { + $page = $request->query->get('page', 1); + + // ... + } + Managing the Session -------------------- @@ -586,8 +609,11 @@ about the user (be it a real person using a browser, a bot, or a web service) between requests. By default, Symfony stores the attributes in a cookie by using the native PHP sessions. -Storing and retrieving information from the session can be easily achieved -from any controller:: +To retrieve the session, call +:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::getSession` +method on the ``Request`` object. This method returns a +:class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface` with easy +methods for storing and fetching things from the session:: use Symfony\Component\HttpFoundation\Request; @@ -605,8 +631,7 @@ from any controller:: $filters = $session->get('filters', array()); } -These attributes will remain in the session for the remainder of that user's -session. +Stored attributes remain in the session for the remainder of that user's session. .. index:: single: Session; Flash messages @@ -685,13 +710,52 @@ read any flash messages from the session: .. index:: single: Controller; Response object -The Response Object -------------------- +The Request and Response Object +------------------------------- + +As mentioned :ref:`earlier `, the framework will +pass the ``Request`` object to any controller argument taht is type-hinted with +the ``Request`` class:: + + use Symfony\Component\HttpFoundation\Request; + + public function indexAction(Request $request) + { + $request->isXmlHttpRequest(); // is it an Ajax request? + + $request->getPreferredLanguage(array('en', 'fr')); + + // retrieve GET and POST variables respectively + $request->query->get('page'); + $request->request->get('page'); -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:: + // retrieve SERVER variables + $request->server->get('HTTP_HOST'); + + // retrieves an instance of UploadedFile identified by foo + $request->files->get('foo'); + + // retrieve a COOKIE value + $request->cookies->get('PHPSESSID'); + + // retrieve an HTTP request header, with normalized, lowercase keys + $request->headers->get('host'); + $request->headers->get('content_type'); + } + +The ``Request`` class has several public properties and methods that return any +information you need about the request. + +Like the ``Request``, the ``Response`` object has also a public ``headers`` property. +This is a :class:`Symfony\\Component\\HttpFoundation\\ResponseHeaderBag` that has +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:: use Symfony\Component\HttpFoundation\Response; @@ -702,11 +766,6 @@ content that's sent back to the client:: $response = new Response(json_encode(array('name' => $name))); $response->headers->set('Content-Type', 'application/json'); -The ``headers`` property is a :class:`Symfony\\Component\\HttpFoundation\\HeaderBag` -object and has some nice methods for getting and setting the headers. The -header names are normalized so that using ``Content-Type`` is equivalent to -``content-type`` or even ``content_type``. - There are also special classes to make certain kinds of responses easier: * For JSON, there is :class:`Symfony\\Component\\HttpFoundation\\JsonResponse`. @@ -715,53 +774,22 @@ There are also special classes to 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`. +* For streamed responses, there is + :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse`. See :ref:`streaming-response`. .. seealso:: - Don't worry! There is a lot more information about the Response object - in the component documentation. See :ref:`component-http-foundation-response`. - -.. index:: - single: Controller; Request object - -The Request Object ------------------- - -Besides the values of the routing placeholders, the controller also has access -to the ``Request`` object. The framework injects the ``Request`` object in the -controller if a variable is type-hinted with -:class:`Symfony\\Component\\HttpFoundation\\Request`:: - - use Symfony\Component\HttpFoundation\Request; - - public function indexAction(Request $request) - { - $request->isXmlHttpRequest(); // is it an Ajax request? - - $request->getPreferredLanguage(array('en', 'fr')); - - $request->query->get('page'); // get a $_GET parameter - - $request->request->get('page'); // get a $_POST parameter - } - -Like the ``Response`` object, the request headers are stored in a ``HeaderBag`` -object and are easily accessible. - -.. seealso:: - - Don't worry! There is a lot more information about the Request object - in the component documentation. See :ref:`component-http-foundation-request`. + Now that you know the basics you can continue your research on Symfony + ``Request`` and ``Response`` object in the + :ref:`HttpFoundation component documentation `. Creating Static Pages --------------------- You can create a static page without even creating a controller (only a route -and template are needed). - -See :doc:`/cookbook/templating/render_without_controller`. +and template are needed). See cookbook article +:doc:`/cookbook/templating/render_without_controller`. .. index:: single: Controller; Forwarding @@ -769,10 +797,10 @@ See :doc:`/cookbook/templating/render_without_controller`. Forwarding to Another Controller -------------------------------- -Though not very common, you can also forward to another controller internally -with the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward` -method. Instead of redirecting the user's browser, it makes an internal sub-request, -and calls the controller. The ``forward()`` method returns the ``Response`` +Though not very common, you can also forward to another controller +internally with the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward` +method. Instead of redirecting the user's browser, this makes an "internal" sub-request +and calls the defined controller. The ``forward()`` method returns the ``Response`` object that's returned from *that* controller:: public function indexAction($name) @@ -787,13 +815,8 @@ object that's returned from *that* controller:: return $response; } -Notice that the ``forward()`` method uses a special string representation -of the controller (see :ref:`controller-string-syntax`). In this case, the -target controller function will be ``SomethingController::fancyAction()`` -inside the AppBundle. The array passed to the method becomes the arguments on -the resulting controller. This same idea is used when embedding controllers -into templates (see :ref:`templating-embedding-controller`). The target -controller method would look something like this:: +The array passed to the method becomes the arguments for the resulting controller. +The target controller method might look something like this:: public function fancyAction($name, $color) { @@ -801,10 +824,7 @@ controller method would look something like this:: } Just like when creating a controller for a route, the order of the arguments of -``fancyAction`` doesn't matter. Symfony matches the index key names (e.g. -``name``) with the method argument names (e.g. ``$name``). If you change the -order of the arguments, Symfony will still pass the correct value to each -variable. +``fancyAction()`` doesn't matter: the matching is done by name. .. _checking-the-validity-of-a-csrf-token: diff --git a/book/doctrine.rst b/book/doctrine.rst index 24248043376..2fa22c88a3e 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -111,8 +111,8 @@ information. By convention, this information is usually configured in an of your project, like inside your Apache configuration, for example. For more information, see :doc:`/cookbook/configuration/external_parameters`. -Now that Doctrine knows about your database, you can have it create the database -for you: +Now that Doctrine can connect to your database, the following command +can automatically generate an empty ``test_project`` database for you: .. code-block:: bash @@ -131,10 +131,6 @@ for you: $ php bin/console doctrine:database:drop --force $ php bin/console doctrine:database:create - There's no way to configure these defaults inside Doctrine, as it tries to be - as agnostic as possible in terms of environment configuration. One way to solve - this problem is to configure server-level defaults. - Setting UTF8 defaults for MySQL is as simple as adding a few lines to your configuration file (typically ``my.cnf``): @@ -145,6 +141,55 @@ for you: collation-server = utf8mb4_general_ci # Replaces utf8_general_ci character-set-server = utf8mb4 # Replaces utf8 + You can also change the defaults for Doctrine so that the generated SQL + uses the correct character set. + + .. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + doctrine: + charset: utf8mb4 + dbal: + default_table_options: + charset: utf8mb4 + collate: utf8mb4_unicode_ci + + .. code-block:: xml + + + + + + + + utf8mb4 + utf8mb4_unicode_ci + + + + + .. code-block:: php + + // app/config/config.php + $configuration->loadFromExtension('doctrine', array( + 'dbal' => array( + 'charset' => 'utf8mb4', + 'default_table_options' => array( + 'charset' => 'utf8mb4' + 'collate' => 'utf8mb4_unicode_ci' + ) + ), + )); + We recommend against MySQL's ``utf8`` character set, since it does not support 4-byte unicode characters, and strings containing them will be truncated. This is fixed by the `newer utf8mb4 character set`_. @@ -209,9 +254,9 @@ inside the ``Entity`` directory of your AppBundle:: class Product { - protected $name; - protected $price; - protected $description; + private $name; + private $price; + private $description; } The class - often called an "entity", meaning *a basic class that holds data* - @@ -238,19 +283,21 @@ Add Mapping Information ~~~~~~~~~~~~~~~~~~~~~~~ Doctrine allows you to work with databases in a much more interesting way -than just fetching rows of a column-based table into an array. Instead, Doctrine -allows you to persist entire *objects* to the database and fetch entire objects -out of the database. This works by mapping a PHP class to a database table, -and the properties of that PHP class to columns on the table: +than just fetching rows of scalar data into an array. Instead, Doctrine +allows you to fetch entire *objects* out of the database, and to persist +entire objects to the database. For Doctrine to be able to do this, you +must *map* your database tables to specific PHP classes, and the columns +on those tables must be mapped to specific properties on their corresponding +PHP classes. .. image:: /images/book/doctrine_image_1.png :align: center -For Doctrine to be able to do this, you just have to create "metadata", or -configuration that tells Doctrine exactly how the ``Product`` class and its -properties should be *mapped* to the database. This metadata can be specified -in a number of different formats including YAML, XML or directly inside the -``Product`` class via annotations: +You'll provide this mapping information in the form of "metadata", a collection +of rules that tells Doctrine exactly how the ``Product`` class and its +properties should be *mapped* to a specific database table. This metadata +can be specified in a number of different formats, including YAML, XML or +directly inside the ``Product`` class via DocBlock annotations: .. configuration-block:: @@ -272,22 +319,22 @@ in a number of different formats including YAML, XML or directly inside the * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ - protected $id; + private $id; /** * @ORM\Column(type="string", length=100) */ - protected $name; + private $name; /** * @ORM\Column(type="decimal", scale=2) */ - protected $price; + private $price; /** * @ORM\Column(type="text") */ - protected $description; + private $description; } .. code-block:: yaml @@ -355,14 +402,15 @@ see the :ref:`book-doctrine-field-types` section. .. caution:: - Be careful that your class name and properties aren't mapped to a protected - SQL keyword (such as ``group`` or ``user``). For example, if your entity - class name is ``Group``, then, by default, your table name will be ``group``, - which will cause an SQL error in some engines. See Doctrine's - `Reserved SQL keywords documentation`_ on how to properly escape these - names. Alternatively, if you're free to choose your database schema, - simply map to a different table name or column name. See Doctrine's - `Creating Classes for the Database`_ and `Property Mapping`_ documentation. + Be careful if the names of your entity classes (or their properties) + are also reserved SQL keywords like ``GROUP`` or ``USER``. For example, + if your entity's class name is ``Group``, then, by default, the corresponding + table name would be ``group``. This will cause an SQL error in some database + engines. See Doctrine's `Reserved SQL keywords documentation`_ for details + on how to properly escape these names. Alternatively, if you're free + to choose your database schema, simply map to a different table name + or column name. See Doctrine's `Creating Classes for the Database`_ + and `Property Mapping`_ documentation. .. note:: @@ -386,9 +434,10 @@ Generating Getters and Setters Even though Doctrine now knows how to persist a ``Product`` object to the database, the class itself isn't really useful yet. Since ``Product`` is just -a regular PHP class, you need to create getter and setter methods (e.g. ``getName()``, -``setName()``) in order to access its properties (since the properties are -``protected``). Fortunately, Doctrine can do this for you by running: +a regular PHP class with ``private`` properties, you need to create ``public`` +getter and setter methods (e.g. ``getName()``, ``setName($name)``) in order +to access its properties in the rest of your application's code. Fortunately, +the following command can generate these boilerplate methods automatically: .. code-block:: bash @@ -402,16 +451,16 @@ doesn't replace your existing methods). .. caution:: Keep in mind that Doctrine's entity generator produces simple getters/setters. - You should check generated entities and adjust getter/setter logic to your own - needs. + You should review the generated methods and add any logic, if necessary, + to suit the needs of your application. .. sidebar:: More about ``doctrine:generate:entities`` With the ``doctrine:generate:entities`` command you can: - * generate getters and setters; + * generate getter and setter methods in entity classes; - * generate repository classes configured with the + * generate repository classes on behalf of entities configured with the ``@ORM\Entity(repositoryClass="...")`` annotation; * generate the appropriate constructor for 1:n and n:m relations. @@ -422,11 +471,10 @@ doesn't replace your existing methods). removed. You can also use the ``--no-backup`` option to prevent generating these backup files. - Note that you don't *need* to use this command. Doctrine doesn't rely - on code generation. Like with normal PHP classes, you just need to make - sure that your protected/private properties have getter and setter methods. - Since this is a common thing to do when using Doctrine, this command - was created. + Note that you don't *need* to use this command. You could also write the + necessary getters and setters by hand. This option simply exists to save + you time, since creating these methods is often a common task during + development. You can also generate all known entities (i.e. any PHP class with Doctrine mapping information) of a bundle or an entire namespace: @@ -439,13 +487,6 @@ mapping information) of a bundle or an entire namespace: # generates all entities of bundles in the Acme namespace $ php bin/console doctrine:generate:entities Acme -.. note:: - - Doctrine doesn't care whether your properties are ``protected`` or ``private``, - or whether you have a getter or setter function for a property. - The getters and setters are generated here only because you'll need them - to interact with your PHP object. - .. _book-doctrine-creating-the-database-tables-schema: Creating the Database Tables/Schema @@ -465,17 +506,21 @@ in your application. To do this, run: Actually, this command is incredibly powerful. It compares what your database *should* look like (based on the mapping information of - your entities) with how it *actually* looks, and generates the SQL statements - needed to *update* the database to where it should be. In other words, if you add - a new property with mapping metadata to ``Product`` and run this task - again, it will generate the "alter table" statement needed to add that - new column to the existing ``product`` table. + your entities) with how it *actually* looks, and executes the SQL statements + needed to *update* the database schema to where it should be. In other + words, if you add a new property with mapping metadata to ``Product`` + and run this task, it will execute the "ALTER TABLE" statement needed + to add that new column to the existing ``product`` table. An even better way to take advantage of this functionality is via `migrations`_, which allow you to generate these SQL statements and store them in migration classes that can be run systematically on your production - server in order to track and migrate your database schema safely and - reliably. + server in order to update and track changes to your database schema safely + and reliably. + + Whether or not you take advantage of migrations, the ``doctrine:schema:update`` + command should only be used during development. It should not be used in + a production environment. Your database now has a fully-functional ``product`` table with columns that match the metadata you've specified. @@ -483,10 +528,10 @@ match the metadata you've specified. Persisting Objects to the Database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Now that you have a mapped ``Product`` entity and corresponding ``product`` -table, you're ready to persist data to the database. From inside a controller, -this is pretty easy. Add the following method to the ``DefaultController`` -of the bundle:: +Now that you have mapped the ``Product`` entity to its corresponding ``product`` +table, you're ready to persist ``Product`` objects to the database. From inside +a controller, this is pretty easy. Add the following method to the +``DefaultController`` of the bundle:: // src/AppBundle/Controller/DefaultController.php @@ -499,16 +544,19 @@ of the bundle:: public function createAction() { $product = new Product(); - $product->setName('A Foo Bar'); - $product->setPrice('19.99'); - $product->setDescription('Lorem ipsum dolor'); + $product->setName('Keyboard'); + $product->setPrice(19.99); + $product->setDescription('Ergonomic and stylish!'); $em = $this->getDoctrine()->getManager(); + // tells Doctrine you want to (eventually) save the Product (no queries yet) $em->persist($product); + + // actually executes the queries (i.e. the INSERT query) $em->flush(); - return new Response('Created product id '.$product->getId()); + return new Response('Saved new product with id '.$product->getId()); } .. note:: @@ -528,20 +576,20 @@ of the bundle:: Take a look at the previous example in more detail: * **lines 10-13** In this section, you instantiate and work with the ``$product`` - object like any other, normal PHP object. + object like any other normal PHP object. * **line 15** This line fetches Doctrine's *entity manager* object, which is - responsible for handling the process of persisting and fetching objects - to and from the database. + responsible for the process of persisting objects to, and fetching objects + from, the database. -* **line 17** The ``persist()`` method tells Doctrine to "manage" the ``$product`` - object. This does not actually cause a query to be made to the database (yet). +* **line 17** The ``persist($product)`` call tells Doctrine to "manage" the + ``$product`` object. This does **not** cause a query to be made to the database. * **line 18** When the ``flush()`` method is called, Doctrine looks through all of the objects that it's managing to see if they need to be persisted - to the database. In this example, the ``$product`` object has not been - persisted yet, so the entity manager executes an ``INSERT`` query and a - row is created in the ``product`` table. + to the database. In this example, the ``$product`` object's data doesn't + exist in the database, so the entity manager executes an ``INSERT`` query, + creating a new row in the ``product`` table. .. note:: @@ -552,9 +600,9 @@ Take a look at the previous example in more detail: ``Product`` objects and then subsequently call ``flush()``, Doctrine will execute 100 ``INSERT`` queries using a single prepared statement object. -When creating or updating objects, the workflow is always the same. In the -next section, you'll see how Doctrine is smart enough to automatically issue -an ``UPDATE`` query if the record already exists in the database. +Whether creating or updating objects, the workflow is always the same. In +the next section, you'll see how Doctrine is smart enough to automatically +issue an ``UPDATE`` query if the entity already exists in the database. .. tip:: @@ -569,15 +617,15 @@ Fetching an object back out of the database is even easier. For example, suppose you've configured a route to display a specific ``Product`` based on its ``id`` value:: - public function showAction($id) + public function showAction($productId) { $product = $this->getDoctrine() ->getRepository('AppBundle:Product') - ->find($id); + ->find($productId); if (!$product) { throw $this->createNotFoundException( - 'No product found for id '.$id + 'No product found for id '.$productId ); } @@ -605,21 +653,21 @@ repository object for an entity class via:: As long as your entity lives under the ``Entity`` namespace of your bundle, this will work. -Once you have your repository, you have access to all sorts of helpful methods:: +Once you have a repository object, you can access all sorts of helpful methods:: + + // query for a single product by its primary key (usually "id") + $product = $repository->find($productId); - // query by the primary key (usually "id") - $product = $repository->find($id); + // dynamic method names to find a single product based on a column value + $product = $repository->findOneById($productId); + $product = $repository->findOneByName('Keyboard'); - // dynamic method names to find based on a column value - $product = $repository->findOneById($id); - $product = $repository->findOneByName('foo'); + // dynamic method names to find a group of products based on a column value + $products = $repository->findByPrice(19.99); // find *all* products $products = $repository->findAll(); - // find a group of products based on an arbitrary column value - $products = $repository->findByPrice(19.99); - .. note:: Of course, you can also issue complex queries, which you'll learn more @@ -628,14 +676,14 @@ Once you have your repository, you have access to all sorts of helpful methods:: You can also take advantage of the useful ``findBy`` and ``findOneBy`` methods to easily fetch objects based on multiple conditions:: - // query for one product matching by name and price + // query for a single product matching the given name and price $product = $repository->findOneBy( - array('name' => 'foo', 'price' => 19.99) + array('name' => 'Keyboard', 'price' => 19.99) ); - // query for all products matching the name, ordered by price + // query for multiple products matching the given name, ordered by price $products = $repository->findBy( - array('name' => 'foo'), + array('name' => 'Keyboard'), array('price' => 'ASC') ); @@ -661,14 +709,14 @@ Updating an Object Once you've fetched an object from Doctrine, updating it is easy. Suppose you have a route that maps a product id to an update action in a controller:: - public function updateAction($id) + public function updateAction($productId) { $em = $this->getDoctrine()->getManager(); - $product = $em->getRepository('AppBundle:Product')->find($id); + $product = $em->getRepository('AppBundle:Product')->find($productId); if (!$product) { throw $this->createNotFoundException( - 'No product found for id '.$id + 'No product found for id '.$productId ); } @@ -710,9 +758,8 @@ Querying for Objects You've already seen how the repository object allows you to run basic queries without any work:: - $repository->find($id); - - $repository->findOneByName('Foo'); + $product = $repository->find($productId); + $product = $repository->findOneByName('Keyboard'); Of course, Doctrine also allows you to write more complex queries using the Doctrine Query Language (DQL). DQL is similar to SQL except that you should @@ -738,8 +785,6 @@ Doctrine's native SQL-like language called DQL to make a query for this:: )->setParameter('price', '19.99'); $products = $query->getResult(); - // to get just one result: - // $product = $query->setMaxResults(1)->getOneOrNullResult(); If you're comfortable with SQL, then DQL should feel very natural. The biggest difference is that you need to think in terms of "objects" instead of rows @@ -935,7 +980,7 @@ To relate the ``Category`` and ``Product`` entities, start by creating a /** * @ORM\OneToMany(targetEntity="Product", mappedBy="category") */ - protected $products; + private $products; public function __construct() { @@ -1018,7 +1063,7 @@ object, you'll want to add a ``$category`` property to the ``Product`` class: * @ORM\ManyToOne(targetEntity="Category", inversedBy="products") * @ORM\JoinColumn(name="category_id", referencedColumnName="id") */ - protected $category; + private $category; } .. code-block:: yaml @@ -1116,12 +1161,13 @@ Now you can see this new code in action! Imagine you're inside a controller:: public function createProductAction() { $category = new Category(); - $category->setName('Main Products'); + $category->setName('Computer Peripherals'); $product = new Product(); - $product->setName('Foo'); + $product->setName('Keyboard'); $product->setPrice(19.99); - $product->setDescription('Lorem ipsum dolor'); + $product->setDescription('Ergonomic and stylish!'); + // relate this product to the category $product->setCategory($category); @@ -1131,8 +1177,8 @@ Now you can see this new code in action! Imagine you're inside a controller:: $em->flush(); return new Response( - 'Created product id: '.$product->getId() - .' and category id: '.$category->getId() + 'Saved new product with id: '.$product->getId() + .' and new category with id: '.$category->getId() ); } } @@ -1147,13 +1193,13 @@ Fetching Related Objects When you need to fetch associated objects, your workflow looks just like it did before. First, fetch a ``$product`` object and then access its related -``Category``:: +``Category`` object:: - public function showAction($id) + public function showAction($productId) { $product = $this->getDoctrine() ->getRepository('AppBundle:Product') - ->find($id); + ->find($productId); $categoryName = $product->getCategory()->getName(); @@ -1176,11 +1222,11 @@ the category (i.e. it's "lazily loaded"). You can also query in the other direction:: - public function showProductsAction($id) + public function showProductsAction($categoryId) { $category = $this->getDoctrine() ->getRepository('AppBundle:Category') - ->find($id); + ->find($categoryId); $products = $category->getProducts(); @@ -1201,7 +1247,7 @@ to the given ``Category`` object via their ``category_id`` value. $product = $this->getDoctrine() ->getRepository('AppBundle:Product') - ->find($id); + ->find($productId); $category = $product->getCategory(); @@ -1239,14 +1285,14 @@ can avoid the second query by issuing a join in the original query. Add the following method to the ``ProductRepository`` class:: // src/AppBundle/Entity/ProductRepository.php - public function findOneByIdJoinedToCategory($id) + public function findOneByIdJoinedToCategory($productId) { $query = $this->getEntityManager() ->createQuery( 'SELECT p, c FROM AppBundle:Product p JOIN p.category c WHERE p.id = :id' - )->setParameter('id', $id); + )->setParameter('id', $productId); try { return $query->getSingleResult(); @@ -1258,11 +1304,11 @@ following method to the ``ProductRepository`` class:: Now, you can use this method in your controller to query for a ``Product`` object and its related ``Category`` with just one query:: - public function showAction($id) + public function showAction($productId) { $product = $this->getDoctrine() ->getRepository('AppBundle:Product') - ->findOneByIdJoinedToCategory($id); + ->findOneByIdJoinedToCategory($productId); $category = $product->getCategory(); diff --git a/book/installation.rst b/book/installation.rst index 5c659511566..ae0e01a5c39 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -204,11 +204,7 @@ your `Apache`_ or `Nginx`_ web server as explained in :doc:`/cookbook/configuration/web_server_configuration`. When you are finished working on your Symfony application, you can stop the -server with the ``server:stop`` command: - -.. code-block:: bash - - $ php bin/console server:stop +server by pressing `Ctrl+C` from terminal. Checking Symfony Application Configuration and Setup ---------------------------------------------------- diff --git a/book/templating.rst b/book/templating.rst index 65e54ccae24..be8167e5a89 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -382,7 +382,7 @@ Template Naming and Locations By default, templates can live in two different locations: ``app/Resources/views/`` - The applications ``views`` directory can contain application-wide base templates + The application's ``views`` directory can contain application-wide base templates (i.e. your application's layouts and templates of the application bundle) as well as templates that override third party bundle templates (see :ref:`overriding-bundle-templates`). @@ -616,7 +616,7 @@ articles:: } } -The ``recentList`` template is perfectly straightforward: +The ``recent_list`` template is perfectly straightforward: .. configuration-block:: @@ -975,7 +975,7 @@ route: In this case, you need to specify both the route name (``article_show``) and a value for the ``{slug}`` parameter. Using this route, revisit the -``recentList`` template from the previous section and link to the articles +``recent_list`` template from the previous section and link to the articles correctly: .. configuration-block:: @@ -1053,7 +1053,7 @@ being used and generating the correct paths accordingly. Additionally, if you use the ``asset`` function, Symfony can automatically append a query string to your asset, in order to guarantee that updated static -assets won't be cached when deployed. For example, ``/images/logo.png`` might +assets won't be loaded from cache after being deployed. For example, ``/images/logo.png`` might look like ``/images/logo.png?v2``. For more information, see the :ref:`reference-framework-assets-version` configuration option. diff --git a/book/translation.rst b/book/translation.rst index c34e1e136c1..e21f5b7084d 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -927,5 +927,5 @@ steps: .. _`i18n`: https://en.wikipedia.org/wiki/Internationalization_and_localization .. _`ISO 3166-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes .. _`ISO 639-1`: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes -.. _`Translatable Extension`: https://github.com/l3pp4rd/DoctrineExtensions +.. _`Translatable Extension`: http://atlantic18.github.io/DoctrineExtensions/doc/translatable.html .. _`Translatable Behavior`: https://github.com/KnpLabs/DoctrineBehaviors diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index c021ffc1175..6f6c6ccc5eb 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -147,25 +147,28 @@ exist:: // the query string is '?foo=bar' $request->query->get('foo'); - // returns bar + // returns 'bar' $request->query->get('bar'); // returns null - $request->query->get('bar', 'bar'); - // returns 'bar' + $request->query->get('bar', 'baz'); + // returns 'baz' When PHP imports the request query, it handles request parameters like ``foo[bar]=bar`` in a special way as it creates an array. So you can get the ``foo`` parameter and you will get back an array with a ``bar`` element:: - // the query string is '?foo[bar]=bar' + // the query string is '?foo[bar]=baz' $request->query->get('foo'); - // returns array('bar' => 'bar') + // returns array('bar' => 'baz') $request->query->get('foo[bar]'); - // returns null + // returns null + + $request->query->get('foo')['bar']; + // returns 'baz' .. _component-foundation-attributes: @@ -500,7 +503,8 @@ if it should:: BinaryFileResponse::trustXSendfileTypeHeader(); -You can still set the ``Content-Type`` of the sent file, or change its ``Content-Disposition``:: +With the ``BinaryFileResponse``, you can still set the ``Content-Type`` of the sent file, +or change its ``Content-Disposition``:: $response->headers->set('Content-Type', 'text/plain'); $response->setContentDisposition( diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index f30b8543b56..3d162b7d328 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -17,6 +17,11 @@ You can install the component in 2 different ways: * :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/var-dumper). +.. note:: + + If using it inside a Symfony application, make sure that the + DebugBundle is enabled in your ``app/AppKernel.php`` file. + .. _components-var-dumper-dump: The dump() Function diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index 841e93ffb1a..0a776be2a70 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -50,7 +50,7 @@ can use double quotes, for these characters it is more convenient to use single quotes, which avoids having to escape any backslash ``\``: * ``:``, ``{``, ``}``, ``[``, ``]``, ``,``, ``&``, ``*``, ``#``, ``?``, ``|``, - ``-``, ``<``, ``>``, ``=``, ``!``, ``%``, ``@``, ``\``` + ``-``, ``<``, ``>``, ``=``, ``!``, ``%``, ``@``, ````` The double-quoted style provides a way to express arbitrary strings, by using ``\`` to escape characters and sequences. For instance, it is very useful diff --git a/contributing/code/bc.rst b/contributing/code/bc.rst index c3e80e29684..a51c825c45f 100644 --- a/contributing/code/bc.rst +++ b/contributing/code/bc.rst @@ -236,7 +236,6 @@ Change return type No Add private method Yes Remove private method Yes Change name Yes -Reduce visibility Yes Add argument without a default value Yes Add argument with a default value Yes Remove argument Yes diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index 6ed509e4d63..0bcf49c97d1 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -272,49 +272,10 @@ pull request message, like in: [Yaml] fixed something [Form] [Validator] [FrameworkBundle] added something -The pull request description must include the following checklist at the top -to ensure that contributions may be reviewed without needless feedback -loops and that your contributions can be included into Symfony as quickly as -possible: - -.. code-block:: text - - | Q | A - | ------------- | --- - | Bug fix? | [yes|no] - | New feature? | [yes|no] - | BC breaks? | [yes|no] - | Deprecations? | [yes|no] - | Tests pass? | [yes|no] - | Fixed tickets | [comma separated list of tickets fixed by the PR] - | License | MIT - | Doc PR | [The reference to the documentation PR if any] - -An example submission could now look as follows: - -.. code-block:: text - - | Q | A - | ------------- | --- - | Bug fix? | no - | New feature? | no - | BC breaks? | no - | Deprecations? | no - | Tests pass? | yes - | Fixed tickets | #12, #43 - | License | MIT - | Doc PR | symfony/symfony-docs#123 - -The whole table must be included (do **not** remove lines that you think are -not relevant). For simple typos, minor changes in the PHPDocs, or changes in -translation files, use the shorter version of the check-list: - -.. code-block:: text - - | Q | A - | ------------- | --- - | Fixed tickets | [comma separated list of tickets fixed by the PR] - | License | MIT +The default pull request description contains a table which you must fill in +with the appropriate answers. This ensures that contributions may be reviewed +without needless feedback loops and that your contributions can be included into +Symfony as quickly as possible. Some answers to the questions trigger some more requirements: diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index 9e9262077dd..82757be6b5b 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -91,7 +91,19 @@ Below is the schedule for the first few versions that use this release model: * **Blue** represents the Stabilization phase * **Green** represents the Maintenance period -This results in very predictable dates and maintenance periods: +.. tip:: + + If you want to learn more about the timeline of any given Symfony version, + use the online `timeline calculator`_. + +.. tip:: + + Whenever an important event related to Symfony versions happens (a version + reaches end of maintenance or a new patch version is released for + instance), you can automatically receive an email notification if you + subscribed on the `roadmap notification`_ page. + +.. _version-history: ======= ============== ======= ======================== =========== Version Feature Freeze Release End of Maintenance End of Life @@ -123,19 +135,6 @@ Version Feature Freeze Release End of Maintenance End of Life .. [2] Symfony 2.8 is the last version of the Symfony 2.x branch. .. [3] Symfony 3.0 is the first version to use the new release process based on five minor releases. -.. tip:: - - If you want to learn more about the timeline of any given Symfony version, - use the online `timeline calculator`_. You can also get all data as a JSON - string via a URL like `https://symfony.com/roadmap.json?version=2.x`. - -.. tip:: - - Whenever an important event related to Symfony versions happens (a version - reaches end of maintenance or a new patch version is released for - instance), you can automatically receive an email notification if you - subscribed on the `roadmap notification`_ page. - Backwards Compatibility ----------------------- diff --git a/cookbook/assetic/php.rst b/cookbook/assetic/php.rst index 124ba0dc5ca..2cc6b6bf4f7 100644 --- a/cookbook/assetic/php.rst +++ b/cookbook/assetic/php.rst @@ -42,7 +42,7 @@ Organizing your Web Asset Files ------------------------------- This example will include a setup using the Bootstrap CSS framework, jQuery, FontAwesome -and some regular CSS and and JavaScript application files (called ``main.css`` and +and some regular CSS and JavaScript application files (called ``main.css`` and ``main.js``). The recommended directory structure for this set-up looks like this: .. code-block:: text diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index c57869810b7..a1351b62604 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -141,9 +141,7 @@ listen to the same ``kernel.exception`` event:: namespace AppBundle\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; - use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; class ExceptionSubscriber implements EventSubscriberInterface { diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index d54379e48b0..e6c3ed08ee2 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/cookbook/form/create_custom_field_type.rst @@ -109,7 +109,7 @@ link for details), create a ``gender_widget`` block to handle this: .. code-block:: html+twig - {# app/Resources/views/Form/fields.html.twig #} + {# app/Resources/views/form/fields.html.twig #} {% block gender_widget %} {% spaceless %} {% if expanded %} @@ -130,7 +130,7 @@ link for details), create a ``gender_widget`` block to handle this: .. code-block:: html+php - +
    block($form, 'widget_container_attributes') ?>> diff --git a/cookbook/form/create_form_type_extension.rst b/cookbook/form/create_form_type_extension.rst index a9bef48b3c9..7ad80384c0b 100644 --- a/cookbook/form/create_form_type_extension.rst +++ b/cookbook/form/create_form_type_extension.rst @@ -323,7 +323,7 @@ Generic Form Type Extensions You can modify several form types at once by specifying their common parent (:doc:`/reference/forms/types`). For example, several form types natively -available in Symfony inherit from the ``TextType`` form type (such as ``email``, +available in Symfony inherit from the ``TextType`` form type (such as ``EmailType``, ``SearchType``, ``UrlType``, etc.). A form type extension applying to ``TextType`` (i.e. whose ``getExtendedType`` method returns ``TextType::class``) would apply to all of these form types. diff --git a/cookbook/form/unit_testing.rst b/cookbook/form/unit_testing.rst index 675ffe85a98..1fe851f293b 100644 --- a/cookbook/form/unit_testing.rst +++ b/cookbook/form/unit_testing.rst @@ -128,6 +128,8 @@ make sure the ``FormRegistry`` uses the created instance:: { // mock any dependencies $this->entityManager = $this->getMock('Doctrine\Common\Persistence\ObjectManager'); + + parent::setUp(); } protected function getExtensions() diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index 48c1a9dda15..1dde8a7acfd 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -222,7 +222,7 @@ how to change the sticky locale: .. code-block:: php - // src/AppBundle/EventListener/SwitchUserListener.pnp + // src/AppBundle/EventListener/SwitchUserListener.php namespace AppBundle\EventListener; use Symfony\Component\Security\Http\Event\SwitchUserEvent; diff --git a/reference/constraints/Image.rst b/reference/constraints/Image.rst index 0720326c0a3..fb792878276 100644 --- a/reference/constraints/Image.rst +++ b/reference/constraints/Image.rst @@ -99,7 +99,7 @@ that it is between a certain size, add the following: .. code-block:: yaml # src/AppBundle/Resources/config/validation.yml - AppBundle\Entity\Author + AppBundle\Entity\Author: properties: headshot: - Image: diff --git a/reference/constraints/Isbn.rst b/reference/constraints/Isbn.rst index 51151fd945a..1e82fa4c36d 100644 --- a/reference/constraints/Isbn.rst +++ b/reference/constraints/Isbn.rst @@ -38,8 +38,8 @@ on an object that will contain an ISBN. { /** * @Assert\Isbn( - * type = isbn10, - * message: This value is not valid. + * type = "isbn10", + * message = "This value is not valid." * ) */ protected $isbn; @@ -89,7 +89,7 @@ on an object that will contain an ISBN. public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('isbn', new Assert\Isbn(array( - 'type' => isbn10, + 'type' => 'isbn10', 'message' => 'This value is not valid.' ))); }