diff --git a/book/doctrine.rst b/book/doctrine.rst index 7cde77256c8..87e894c1f45 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -534,10 +534,10 @@ Take a look at the previous example in more detail: responsible for handling the process of persisting and fetching objects to and from the database. -* **line 16** The ``persist()`` method tells Doctrine to "manage" the ``$product`` +* **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** When the ``flush()`` method is called, Doctrine looks through +* **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 diff --git a/book/routing.rst b/book/routing.rst index 76989494359..ed0b4bf809c 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -1473,25 +1473,14 @@ route. With this information, any URL can easily be generated:: .. note:: - In controllers that don't extend Symfony's base - :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller`, - you can use the ``router`` service's - :method:`Symfony\\Component\\Routing\\Router::generate` method:: + The ``generateUrl()`` method defined in the base + :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class is + just a shortcut for this code:: - use Symfony\Component\DependencyInjection\ContainerAware; - - class MainController extends ContainerAware - { - public function showAction($slug) - { - // ... - - $url = $this->container->get('router')->generate( - 'blog_show', - array('slug' => 'my-blog-post') - ); - } - } + $url = $this->container->get('router')->generate( + 'blog_show', + array('slug' => 'my-blog-post') + ); In an upcoming section, you'll learn how to generate URLs from inside templates. diff --git a/book/security.rst b/book/security.rst index 5953e702654..094bb487bfe 100644 --- a/book/security.rst +++ b/book/security.rst @@ -884,7 +884,7 @@ Access Control in Templates ........................... If you want to check if the current user has a role inside a template, use -the built-in helper function: +the built-in ``is_granted()`` helper function: .. configuration-block:: @@ -900,20 +900,18 @@ the built-in helper function: Delete -If you use this function and you are *not* behind a firewall, an exception will -be thrown. Again, it's almost always a good idea to have a main firewall that -covers all URLs (as shown before in this chapter). - -.. caution:: +.. note:: - Be careful with this in your base layout or on your error pages! Because of - some internal Symfony details, to avoid broken error pages in the ``prod`` - environment, wrap calls in these templates with a check for ``app.user``: + In Symfony versions previous to 2.8, using the ``is_granted()`` function + in a page that wasn't behind a firewall resulted in an exception. That's why + you also needed to check first for the existence of the user: .. code-block:: html+twig {% if app.user and is_granted('ROLE_ADMIN') %} + Starting from Symfony 2.8, the ``app.user and ...`` check is no longer needed. + Securing other Services ....................... diff --git a/book/service_container.rst b/book/service_container.rst index 66eb38641be..2cb08ae4078 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -276,10 +276,10 @@ The service container is built using a single configuration resource be imported from inside this file in one way or another. This gives you absolute flexibility over the services in your application. -External service configuration can be imported in two different ways. The first -method, commonly used to import container configuration from the bundles you've -created - is via the ``imports`` directive. The second method, although slightly more -complex offers more flexibility and is commonly used to import third-party bundle +External service configuration can be imported in two different ways. The first +method, commonly used to import container configuration from the bundles you've +created - is via the ``imports`` directive. The second method, although slightly more +complex offers more flexibility and is commonly used to import third-party bundle configuration. Read on to learn more about both methods. .. index:: @@ -1104,13 +1104,15 @@ to be used for a specific purpose. Take the following example: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + + - - + + + .. code-block:: php diff --git a/book/templating.rst b/book/templating.rst index 0a4dc85dd17..4c1de72f752 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -1098,7 +1098,7 @@ if you are using Twig (or the third argument if you are using PHP) to ``true``: .. code-block:: html+jinja - Symfony! + Symfony! .. code-block:: html+php diff --git a/components/console/helpers/debug_formatter.rst b/components/console/helpers/debug_formatter.rst index 885c89ab69c..b5af8bb9782 100644 --- a/components/console/helpers/debug_formatter.rst +++ b/components/console/helpers/debug_formatter.rst @@ -117,7 +117,7 @@ Stopping a Program ------------------ When a program is stopped, you can use -:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::run` to +:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::stop` to notify this to the users:: // ... diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst index b4f21eb964c..2c37d379a8e 100644 --- a/components/console/helpers/table.rst +++ b/components/console/helpers/table.rst @@ -143,3 +143,94 @@ Here is a full list of things you can customize: $table->setStyle('colorful'); This method can also be used to override a built-in style. + +Spanning Multiple Columns and Rows +---------------------------------- + +.. versionadded:: 2.7 + Spanning multiple columns and rows was introduced in Symfony 2.7. + +To make a table cell that spans multiple columns you can use a :class:`Symfony\\Component\\Console\\Helper\\TableCell`:: + + use Symfony\Component\Console\Helper\Table; + use Symfony\Component\Console\Helper\TableSeparator; + use Symfony\Component\Console\Helper\TableCell; + + $table = new Table($output); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + new TableSeparator(), + array(new TableCell('This value spans 3 columns.', array('colspan' => 3))), + )) + ; + $table->render(); + +This results in: + +.. code-block:: text + + +---------------+---------------+-----------------+ + | ISBN | Title | Author | + +---------------+---------------+-----------------+ + | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + +---------------+---------------+-----------------+ + | This value spans 3 columns. | + +---------------+---------------+-----------------+ + +.. tip:: + + You can create a multiple-line page title using a header cell that spans + the enire table width:: + + $table->setHeaders(array( + array(new TableCell('Main table title', array('colspan' => 3))), + array('ISBN', 'Title', 'Author'), + )) + // ... + + This generates: + + .. code-block:: text + + +-------+-------+--------+ + | Main table title | + +-------+-------+--------+ + | ISBN | Title | Author | + +-------+-------+--------+ + | ... | + +-------+-------+--------+ + +In a similar way you can span multiple rows:: + + use Symfony\Component\Console\Helper\Table; + use Symfony\Component\Console\Helper\TableCell; + + $table = new Table($output); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array( + '978-0521567817', + 'De Monarchia', + new TableCell("Dante Alighieri\nspans multiple rows", array('rowspan' => 2)), + ), + array('978-0804169127', 'Divine Comedy'), + )) + ; + $table->render(); + +This outputs: + +.. code-block:: text + + +----------------+---------------+---------------------+ + | ISBN | Title | Author | + +----------------+---------------+---------------------+ + | 978-0521567817 | De Monarchia | Dante Alighieri | + | 978-0804169127 | Divine Comedy | spans multiple rows | + +----------------+---------------+---------------------+ + +You can use the ``colspan`` and ``rowspan`` options at the same time which allows +you to create any table layout you may wish. diff --git a/contributing/code/security.rst b/contributing/code/security.rst index a9260491486..baa1baf711f 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -103,6 +103,8 @@ Security Advisories This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: +* November 23, 2015: `CVE-2015-8125: Potential Remote Timing Attack Vulnerability in Security Remember-Me Service `_ (2.3.35, 2.6.12 and 2.7.7) +* November 23, 2015: `CVE-2015-8124: Session Fixation in the "Remember Me" Login Feature `_ (2.3.35, 2.6.12 and 2.7.7) * May 26, 2015: `CVE-2015-4050: ESI unauthorized access `_ (Symfony 2.3.29, 2.5.12 and 2.6.8) * April 1, 2015: `CVE-2015-2309: Unsafe methods in the Request class `_ (Symfony 2.3.27, 2.5.11 and 2.6.6) * April 1, 2015: `CVE-2015-2308: Esi Code Injection `_ (Symfony 2.3.27, 2.5.11 and 2.6.6) diff --git a/cookbook/configuration/override_dir_structure.rst b/cookbook/configuration/override_dir_structure.rst index 105b225fd62..d387a7931fa 100644 --- a/cookbook/configuration/override_dir_structure.rst +++ b/cookbook/configuration/override_dir_structure.rst @@ -176,8 +176,7 @@ The change in the ``composer.json`` will look like this: ... } -In ``app/autoload.php``, you need to modify the path leading to the -``vendor/autoload.php`` file:: +Then, update the path to the ``autoload.php`` file in ``app/autoload.php``:: // app/autoload.php // ... diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index f169aa64cfb..e516976128a 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -96,7 +96,7 @@ To override the 404 error template for HTML pages, create a new

Page not found

{# example security usage, see below #} - {% if app.user and is_granted('IS_AUTHENTICATED_FULLY') %} + {% if is_granted('IS_AUTHENTICATED_FULLY') %} {# ... #} {% endif %} @@ -124,24 +124,6 @@ store the HTTP status code and message respectively. for the standard HTML exception page or ``exception.json.twig`` for the JSON exception page. -Avoiding Exceptions when Using Security Functions in Error Templates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -One of the common pitfalls when designing custom error pages is to use the -``is_granted()`` function in the error template (or in any parent template -inherited by the error template). If you do that, you'll see an exception thrown -by Symfony. - -The cause of this problem is that routing is done before security. If a 404 error -occurs, the security layer isn't loaded and thus, the ``is_granted()`` function -is undefined. The solution is to add the following check before using this function: - -.. code-block:: twig - - {% if app.user and is_granted('...') %} - {# ... #} - {% endif %} - .. _testing-error-pages: Testing Error Pages during Development diff --git a/cookbook/doctrine/event_listeners_subscribers.rst b/cookbook/doctrine/event_listeners_subscribers.rst index 93cbf3b4c1d..64c8d79ab9a 100644 --- a/cookbook/doctrine/event_listeners_subscribers.rst +++ b/cookbook/doctrine/event_listeners_subscribers.rst @@ -135,12 +135,14 @@ a ``postPersist`` method, which will be called when the event is dispatched:: public function postPersist(LifecycleEventArgs $args) { $entity = $args->getEntity(); - $entityManager = $args->getEntityManager(); - // perhaps you only want to act on some "Product" entity - if ($entity instanceof Product) { - // ... do something with the Product + // only act on some "Product" entity + if (!$entity instanceof Product) { + return; } + + $entityManager = $args->getEntityManager(); + // ... do something with the Product } } @@ -197,10 +199,10 @@ interface and have an event method for each event it subscribes to:: public function index(LifecycleEventArgs $args) { $entity = $args->getEntity(); - $entityManager = $args->getEntityManager(); // perhaps you only want to act on some "Product" entity if ($entity instanceof Product) { + $entityManager = $args->getEntityManager(); // ... do something with the Product } } diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index db442c85740..4772ae871c2 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -411,25 +411,26 @@ it with :ref:`dic-tags-form-type`. array('security.token_storage') ); -If you wish to create it from within a controller or any other service that has -access to the form factory, you then use:: +If you wish to create it from within a service that has access to the form factory, +you then use:: - use Symfony\Component\DependencyInjection\ContainerAware; + $form = $formFactory->create('friend_message'); - class FriendMessageController extends ContainerAware +In a controller that extends the :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` +class, you can simply call:: + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + class FriendMessageController extends Controller { public function newAction(Request $request) { - $form = $this->get('form.factory')->create('friend_message'); + $form = $this->createForm('friend_message'); // ... } } -If you extend the ``Symfony\Bundle\FrameworkBundle\Controller\Controller`` class, you can simply call:: - - $form = $this->createForm('friend_message'); - You can also easily embed the form type into another form:: // inside some other "form type" class diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index 82a30190cc8..c936dd8b08e 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -12,10 +12,6 @@ In this entry, you'll build a traditional login form. Of course, when the user logs in, you can load your users from anywhere - like the database. See :ref:`security-user-providers` for details. -This chapter assumes that you've followed the beginning of the -:doc:`security chapter ` and have ``http_basic`` authentication -working properly. - First, enable form login under your firewall: .. configuration-block:: @@ -29,7 +25,6 @@ First, enable form login under your firewall: firewalls: default: anonymous: ~ - http_basic: ~ form_login: login_path: /login check_path: /login_check @@ -47,7 +42,6 @@ First, enable form login under your firewall: - @@ -60,7 +54,6 @@ First, enable form login under your firewall: 'firewalls' => array( 'default' => array( 'anonymous' => null, - 'http_basic' => null, 'form_login' => array( 'login_path' => '/login', 'check_path' => '/login_check', diff --git a/create_framework/dependency-injection.rst b/create_framework/dependency-injection.rst index 86e6b6d78cf..39eaae4702e 100644 --- a/create_framework/dependency-injection.rst +++ b/create_framework/dependency-injection.rst @@ -132,7 +132,7 @@ them. Objects will be created on-demand when you access them from the container or when the container needs them to create other objects. For instance, to create the router listener, we tell Symfony that its class -name is ``Symfony\Component\HttpKernel\EventListener\RouterListener``, and +name is ``Symfony\Component\HttpKernel\EventListener\RouterListener`` and that its constructor takes a matcher object (``new Reference('matcher')``). As you can see, each object is referenced by a name, a string that uniquely identifies each object. The name allows us to get an object and to reference diff --git a/create_framework/event-dispatcher.rst b/create_framework/event-dispatcher.rst index ac9a465fd8e..d2b062e415b 100644 --- a/create_framework/event-dispatcher.rst +++ b/create_framework/event-dispatcher.rst @@ -154,7 +154,7 @@ event (``response``); the event name must be the same as the one used in the ``dispatch()`` call. In the listener, we add the Google Analytics code only if the response is not -a redirection, if the requested format is HTML, and if the response content +a redirection, if the requested format is HTML and if the response content type is HTML (these conditions demonstrate the ease of manipulating the Request and Response data from your code). diff --git a/create_framework/front-controller.rst b/create_framework/front-controller.rst index dcff8303f37..3ef47eb618b 100644 --- a/create_framework/front-controller.rst +++ b/create_framework/front-controller.rst @@ -57,7 +57,7 @@ And for the "Goodbye" page:: We have indeed moved most of the shared code into a central place, but it does not feel like a good abstraction, does it? We still have the ``send()`` method -for all pages, our pages do not look like templates, and we are still not able +for all pages, our pages do not look like templates and we are still not able to test this code properly. Moreover, adding a new page means that we need to create a new PHP script, @@ -159,7 +159,7 @@ web root directory: Now, configure your web server root directory to point to ``web/`` and all other files won't be accessible from the client anymore. -To test your changes in a browser (``http://localhost:4321/?name=Fabien``), run +To test your changes in a browser (``http://localhost:4321/hello/?name=Fabien``), run the PHP built-in server: .. code-block:: bash diff --git a/create_framework/http-foundation.rst b/create_framework/http-foundation.rst index 261821879b1..fcaa6c005b2 100644 --- a/create_framework/http-foundation.rst +++ b/create_framework/http-foundation.rst @@ -299,7 +299,7 @@ the wheel. I've almost forgot to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and applications using it (like `Symfony`_, `Drupal 8`_, `phpBB 4`_, `ezPublish -5`_, `Laravel`_, `Silex`_, and `more`_). +5`_, `Laravel`_, `Silex`_ and `more`_). .. _`Twig`: http://twig.sensiolabs.org/ .. _`HTTP specification`: http://tools.ietf.org/wg/httpbis/ diff --git a/create_framework/http-kernel-httpkernel-class.rst b/create_framework/http-kernel-httpkernel-class.rst index eb3b8cf7741..ab0663dcbce 100644 --- a/create_framework/http-kernel-httpkernel-class.rst +++ b/create_framework/http-kernel-httpkernel-class.rst @@ -11,7 +11,7 @@ There should be an easier way, right? Enter the ``HttpKernel`` class. Instead of solving the same problem over and over again and instead of reinventing the wheel each time, the ``HttpKernel`` -class is a generic, extensible, and flexible implementation of +class is a generic, extensible and flexible implementation of ``HttpKernelInterface``. This class is very similar to the framework class we have written so far: it diff --git a/create_framework/routing.rst b/create_framework/routing.rst index 8a59ce83c8a..2d994b4d8f0 100644 --- a/create_framework/routing.rst +++ b/create_framework/routing.rst @@ -82,7 +82,7 @@ of default values for route attributes (``array('name' => 'World')``). :doc:`Routing component documentation ` to learn more about its many features like URL generation, attribute requirements, HTTP method enforcements, loaders for YAML or XML files, - dumpers to PHP or Apache rewrite rules for enhanced performance, and much + dumpers to PHP or Apache rewrite rules for enhanced performance and much more. Based on the information stored in the ``RouteCollection`` instance, a diff --git a/create_framework/separation-of-concerns.rst b/create_framework/separation-of-concerns.rst index d7bb049970c..8eba278c6da 100644 --- a/create_framework/separation-of-concerns.rst +++ b/create_framework/separation-of-concerns.rst @@ -8,7 +8,7 @@ class. It would bring us better *reusability* and easier testing to name just a few benefits. If you have a closer look at the code, ``front.php`` has one input, the -Request, and one output, the Response. Our framework class will follow this +Request and one output, the Response. Our framework class will follow this simple principle: the logic is about creating the Response associated with a Request. diff --git a/create_framework/templating.rst b/create_framework/templating.rst index a6d55350f78..5fa4b4bc468 100644 --- a/create_framework/templating.rst +++ b/create_framework/templating.rst @@ -50,7 +50,7 @@ rendered:: As ``render_template`` is used as an argument to the PHP ``call_user_func()`` function, we can replace it with any valid PHP `callbacks`_. This allows us to -use a function, an anonymous function, or a method of a class as a +use a function, an anonymous function or a method of a class as a controller... your choice. As a convention, for each route, the associated controller is configured via diff --git a/images/book/doctrine_web_debug_toolbar.png b/images/book/doctrine_web_debug_toolbar.png index b7c5a690d72..dd5c3fffbb1 100644 Binary files a/images/book/doctrine_web_debug_toolbar.png and b/images/book/doctrine_web_debug_toolbar.png differ diff --git a/images/book/security_anonymous_wdt.png b/images/book/security_anonymous_wdt.png index d780c894d5a..4803ed1b108 100644 Binary files a/images/book/security_anonymous_wdt.png and b/images/book/security_anonymous_wdt.png differ diff --git a/images/book/symfony_loggedin_wdt.png b/images/book/symfony_loggedin_wdt.png index 7280d9978bb..6605c35edf7 100644 Binary files a/images/book/symfony_loggedin_wdt.png and b/images/book/symfony_loggedin_wdt.png differ diff --git a/images/cookbook/controller/error_pages/exceptions-in-dev-environment.png b/images/cookbook/controller/error_pages/exceptions-in-dev-environment.png index dffd1460b9b..225dcdfa0dc 100644 Binary files a/images/cookbook/controller/error_pages/exceptions-in-dev-environment.png and b/images/cookbook/controller/error_pages/exceptions-in-dev-environment.png differ diff --git a/images/quick_tour/hello_fabien.png b/images/quick_tour/hello_fabien.png deleted file mode 100644 index 8329899b090..00000000000 Binary files a/images/quick_tour/hello_fabien.png and /dev/null differ diff --git a/images/quick_tour/profiler.png b/images/quick_tour/profiler.png index 795f5deb05f..3b55b75f3af 100644 Binary files a/images/quick_tour/profiler.png and b/images/quick_tour/profiler.png differ diff --git a/images/quick_tour/web_debug_toolbar.png b/images/quick_tour/web_debug_toolbar.png index 1e2b38d06cb..86249e862f3 100644 Binary files a/images/quick_tour/web_debug_toolbar.png and b/images/quick_tour/web_debug_toolbar.png differ diff --git a/images/quick_tour/welcome.png b/images/quick_tour/welcome.png index 7eb0395eb43..738105f715d 100644 Binary files a/images/quick_tour/welcome.png and b/images/quick_tour/welcome.png differ diff --git a/images/reference/form/choice-example1.png b/images/reference/form/choice-example1.png new file mode 100644 index 00000000000..00e47d0bb27 Binary files /dev/null and b/images/reference/form/choice-example1.png differ diff --git a/images/reference/form/choice-example2.png b/images/reference/form/choice-example2.png new file mode 100644 index 00000000000..147d82bcfca Binary files /dev/null and b/images/reference/form/choice-example2.png differ diff --git a/images/reference/form/choice-example3.png b/images/reference/form/choice-example3.png new file mode 100644 index 00000000000..232f8519fee Binary files /dev/null and b/images/reference/form/choice-example3.png differ diff --git a/images/reference/form/choice-example4.png b/images/reference/form/choice-example4.png new file mode 100644 index 00000000000..7f6071d3532 Binary files /dev/null and b/images/reference/form/choice-example4.png differ diff --git a/images/reference/form/choice-example5.png b/images/reference/form/choice-example5.png new file mode 100644 index 00000000000..188eeeec234 Binary files /dev/null and b/images/reference/form/choice-example5.png differ diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 381d016c1a2..f533fd53dcf 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -219,7 +219,7 @@ If you're using an IDE like TextMate or Mac Vim, then Symfony can turn all of the file paths in an exception message into a link, which will open that file in your IDE. -Symfony contains preconfigured urls for some popular IDEs, you can set them +Symfony contains preconfigured URLs for some popular IDEs, you can set them using the following keys: * ``textmate`` @@ -230,7 +230,7 @@ using the following keys: .. versionadded:: 2.3.14 The ``emacs`` and ``sublime`` editors were introduced in Symfony 2.3.14. -You can also specify a custom url string. If you do this, all percentage +You can also specify a custom URL string. If you do this, all percentage signs (``%``) must be doubled to escape that character. For example, if you use PHPstorm on the Mac OS platform, you will do something like: @@ -269,8 +269,11 @@ you use PHPstorm on the Mac OS platform, you will do something like: Of course, since every developer uses a different IDE, it's better to set this on a system level. This can be done by setting the ``xdebug.file_link_format`` -in the ``php.ini`` configuration to the url string. If this configuration -value is set, then the ``ide`` option will be ignored. +in the ``php.ini`` configuration to the URL string. + +If you don't use Xdebug, another way is to set this URL string in the +``SYMFONY__TEMPLATING__HELPER__CODE__FILE_LINK_FORMAT`` environment variable. +If any of these configurations values are set, the ``ide`` option will be ignored. .. _reference-framework-test: diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 69079e4168d..346e7ef7a08 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -1,24 +1,30 @@ .. index:: single: Forms; Fields; choice -choice Field Type -================= +choice Field Type (select drop-downs, radio buttons & checkboxes) +================================================================= A multi-purpose field used to allow the user to "choose" one or more options. It can be rendered as a ``select`` tag, radio buttons, or checkboxes. -To use this field, you must specify *either* the ``choice_list`` or ``choices`` -option. +To use this field, you must specify *either* ``choices`` or ``choice_loader`` option. +-------------+------------------------------------------------------------------------------+ | Rendered as | can be various tags (see below) | +-------------+------------------------------------------------------------------------------+ | Options | - `choices`_ | -| | - `choice_list`_ | +| | - `choices_as_values`_ | +| | - `choice_loader`_ | +| | - `choice_label`_ | +| | - `choice_attr`_ | | | - `placeholder`_ | | | - `expanded`_ | | | - `multiple`_ | | | - `preferred_choices`_ | +| | - `group_by`_ | +| | - `choice_value`_ | +| | - `choice_name`_ | +| | - `choice_list`_ (deprecated) | +-------------+------------------------------------------------------------------------------+ | Overridden | - `compound`_ | | options | - `empty_data`_ | @@ -44,37 +50,107 @@ Example Usage ------------- The easiest way to use this field is to specify the choices directly via -the ``choices`` option. The key of the array becomes the value that's actually -set on your underlying object (e.g. ``m``), while the value is what the -user sees on the form (e.g. ``Male``). +the ``choices`` option:: -.. code-block:: php - - $builder->add('gender', 'choice', array( - 'choices' => array('m' => 'Male', 'f' => 'Female'), - 'required' => false, + $builder->add('isAttending', 'choice', array( + 'choices' => array( + 'Maybe' => null, + 'Yes' => true, + 'No' => false, + ), + // *this line is important* + 'choices_as_values' => true, )); -By setting ``multiple`` to true, you can allow the user to choose multiple -values. The widget will be rendered as a multiple ``select`` tag or a series -of checkboxes depending on the ``expanded`` option:: +This will create a ``select`` drop-down like this: - $builder->add('availability', 'choice', array( - 'choices' => array( - 'morning' => 'Morning', - 'afternoon' => 'Afternoon', - 'evening' => 'Evening', - ), - 'multiple' => true, - )); +.. image:: /images/reference/form/choice-example1.png + :align: center + +If the user selects ``No``, the form will return ``false`` for this field. Similarly, +if the starting data for this field is ``true``, then ``Yes`` will be auto-selected. +In other words, the **value** of each item is the value you want to get/set in PHP +code, while the **key** is what will be shown to the user. + +.. caution:: -You can also use the ``choice_list`` option, which takes an object that -can specify the choices for your widget. + The ``choices_as_values`` *must* be set to ``true`` in all cases. This activates + the "new" choice type API, which was introduced in Symfony 2.7. If you omit this + option (or set it to ``false``), you'll activate the old API, which is deprecated + and will be removed in 3.0. To read about the old API, read an older version of + the docs. + +Advanced Example (with Objects!) +-------------------------------- + +This field has a *lot* of options and most control how the field is displayed. In +this example, the underlying data is some ``Category`` object that has a ``getName()`` +method:: + + $builder->add('category', 'choice', [ + 'choices' => [ + new Category('Cat1'), + new Category('Cat2'), + new Category('Cat3'), + new Category('Cat4'), + ], + 'choices_as_values' => true, + 'choice_label' => function($category, $key, $index) { + /** @var Category $category */ + return strtoupper($category->getName()); + }, + 'choice_attr' => function($category, $key, $index) { + return ['class' => 'category_'.strtolower($category->getName())]; + }, + 'group_by' => function($category, $key, $index) { + // randomly assign things into 2 groups + return rand(0, 1) == 1 ? 'Group A' : 'Group B' + }, + 'preferred_choices' => function($category, $key, $index) { + return $category->getName() == 'Cat2' || $category->getName() == 'Cat3'; + }, + ]); + +You can also customize the `choice_name`_ and `choice_value`_ of each choice if +you need further HTML customization. .. _forms-reference-choice-tags: .. include:: /reference/forms/types/options/select_how_rendered.rst.inc +Customizing each Option's Text (Label) +-------------------------------------- + +Normally, the array key of each item in the ``choices`` option is used as the +text that's shown to the user. But that can be completely customized via the +`choice_label`_ option. Check it out for more details. + +.. _form-choices-simple-grouping: + +Grouping Options +---------------- + +You can easily "group" options in a select by passing a multi-dimensional choices array:: + + $builder->add('stockStatus', 'choice', [ + 'choices' => [ + 'Main Statuses' => [ + 'Yes' => 'stock_yes', + 'No' => 'stock_no', + ], + 'Out of Stock Statuses' => [ + 'Backordered' => 'stock_backordered', + 'Discontinued' => 'stock_discontinued', + ] + ], + 'choices_as_values' => true, + ); + +.. image:: /images/reference/form/choice-example4.png + :align: center + +To get fancier, use the `group_by`_ option. + Field Options ------------- @@ -85,19 +161,88 @@ choices This is the most basic way to specify the choices that should be used by this field. The ``choices`` option is an array, where the array key -is the item value and the array value is the item's label:: +is the item's label and the array value is the item's value:: + + $builder->add('inStock', 'choice', array( + 'choices' => array('In Stock' => true, 'Out of Stock' => false), + // always include this + 'choices_as_values' => true, + )); + +choices_as_values +~~~~~~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: false + +.. versionadded:: 2.7 + + The ``choices_as_values`` option was introduced in Symfony 2.7. + +The ``choices_as_values`` option was added to keep backward compatibility with the +*old* way of handling the ``choices`` option. When set to ``false`` (or omitted), +the choice keys are used as the underlying value and the choice values are shown +to the user. + +* Before 2.7 (and deprecated now):: $builder->add('gender', 'choice', array( - 'choices' => array('m' => 'Male', 'f' => 'Female'), + // Shows "Male" to the user, returns "m" when selected + 'choices' => array('m' => 'Male', 'f' => 'Female'), + // before 2.7, this option didn't actually exist, but the + // behavior was equivalent to setting this to false in 2.7. + 'choices_as_values' => false, )); -.. tip:: +* Since 2.7:: + + $builder->add('gender', 'choice', array( + // Shows "Male" to the user, returns "m" when selected + 'choices' => array('Male' => 'm', 'Female' => 'f'), + 'choices_as_values' => true, + )); + +In Symfony 3.0, the ``choices_as_values`` option doesn't exist, but the ``choice`` +type behaves as if it were set to true: + +* Default for 3.0:: + + $builder->add('gender', 'choice', array( + 'choices' => array('Male' => 'm', 'Female' => 'f'), + )); + +choice_loader +~~~~~~~~~~~~~ + +.. versionadded:: 2.7 + + The ``choice_loader`` option was added in Symfony 2.7. + +**type**: :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface` + +The ``choice_loader`` can be used to only partially load the choices in cases where +a fully-loaded list is not necessary. This is only needed in advanced cases and +would replace the ``choices`` option. + +.. _reference-form-choice-label: + +.. include:: /reference/forms/types/options/choice_label.rst.inc + +.. include:: /reference/forms/types/options/choice_attr.rst.inc + +.. include:: /reference/forms/types/options/placeholder.rst.inc + +.. include:: /reference/forms/types/options/expanded.rst.inc + +.. include:: /reference/forms/types/options/multiple.rst.inc + +.. include:: /reference/forms/types/options/preferred_choices.rst.inc + +.. include:: /reference/forms/types/options/group_by.rst.inc + +.. include:: /reference/forms/types/options/choice_value.rst.inc + +.. include:: /reference/forms/types/options/choice_name.rst.inc - When the values to choose from are not integers or strings (but e.g. - floats or booleans), you should use the `choice_list`_ option instead. - With this option you are able to keep the original data format which - is important to ensure that the user input is validated properly and - useless database updates caused by a data type mismatch are avoided. choice_list ~~~~~~~~~~~ @@ -105,7 +250,7 @@ choice_list .. caution:: The ``choice_list`` option of ChoiceType was deprecated in Symfony 2.7. - You should use ``choices`` or ``choice_loader`` now. + You should use `choices`_ or `choice_loader`_ now. **type**: :class:`Symfony\\Component\\Form\\Extension\\Core\\ChoiceList\\ChoiceListInterface` @@ -141,14 +286,6 @@ But don't be confused! If ``Full`` is selected (value ``0`` in HTML), ``1`` will be returned in your form. If ``Almost empty`` is selected (value ``2`` in HTML), ``0.1`` will be returned. -.. include:: /reference/forms/types/options/placeholder.rst.inc - -.. include:: /reference/forms/types/options/expanded.rst.inc - -.. include:: /reference/forms/types/options/multiple.rst.inc - -.. include:: /reference/forms/types/options/preferred_choices.rst.inc - Overridden Options ------------------ diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index 58faabc5ea2..da48a29b788 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -12,15 +12,13 @@ objects from the database. +-------------+------------------------------------------------------------------+ | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------+ -| Options | - `choice_label`_ | -| | - `class`_ | -| | - `data_class`_ | -| | - `em`_ | -| | - `group_by`_ | +| Options | - `class`_ | +| | - `choice_label`_ | | | - `query_builder`_ | +| | - `em`_ | +-------------+------------------------------------------------------------------+ -| Overridden | - `choice_list`_ | -| options | - `choices`_ | +| Overridden | - `choices`_ | +| options | | +-------------+------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type: | | options | | @@ -28,6 +26,7 @@ objects from the database. | | - `expanded`_ | | | - `multiple`_ | | | - `preferred_choices`_ | +| | - `group_by`_ | | | | | | from the :doc:`form ` type: | | | | @@ -103,6 +102,15 @@ then you can supply the ``choices`` option directly:: Field Options ------------- +class +~~~~~ + +**type**: ``string`` **required** + +The class of your entity (e.g. ``AcmeStoreBundle:Category``). This can be +a fully-qualified class name (e.g. ``Acme\StoreBundle\Entity\Category``) +or the short alias name (as shown prior). + choice_label ~~~~~~~~~~~~ @@ -110,16 +118,33 @@ choice_label The ``choice_label`` option was introduced in Symfony 2.7. Prior to Symfony 2.7, it was called ``property`` (which has the same functionality). -**type**: ``string`` +**type**: ``string`` or ``callable`` + +This is the property that should be used for displaying the entities as text in +the HTML element:: + + $builder->add('category', 'entity', array( + 'class' => 'AppBundle:Category', + 'choice_label' => 'displayName', + )); + +If left blank, the entity object will be cast to a string and so must have a ``__toString()`` +method. You can also pass a callback function for more control:: -This is the property that should be used for displaying the entities -as text in the HTML element. If left blank, the entity object will be -cast into a string and so must have a ``__toString()`` method. + $builder->add('category', 'entity', array( + 'class' => 'AppBundle:Category', + 'choice_label' => function ($category) { + return $category->getDisplayName(); + } + )); + +The method is called for each entity in the list and passed to the function. For +more detais, see the main :ref:`choice_label ` documentation. .. note:: - The ``choice_label`` option is the property path used to display the option. - So you can use anything supported by the + When passing a string, the ``choice_label`` option is a property path. So you + can use anything supported by the :doc:`PropertyAccessor component ` For example, if the translations property is actually an associative @@ -130,36 +155,6 @@ cast into a string and so must have a ``__toString()`` method. 'choice_label' => 'translations[en].name', )); -class -~~~~~ - -**type**: ``string`` **required** - -The class of your entity (e.g. ``AcmeStoreBundle:Category``). This can be -a fully-qualified class name (e.g. ``Acme\StoreBundle\Entity\Category``) -or the short alias name (as shown prior). - -.. include:: /reference/forms/types/options/data_class.rst.inc - -em -~~ - -**type**: ``string`` | ``Doctrine\Common\Persistence\ObjectManager`` **default**: the default entity manager - -If specified, the specified entity manager will be used to load the choices -instead of the default entity manager. - -group_by -~~~~~~~~ - -**type**: ``string`` - -This is a property path (e.g. ``author.name``) used to organize the -available choices in groups. It only works when rendered as a select tag -and does so by adding ``optgroup`` elements around options. Choices that -do not return a value for this property path are rendered directly under -the select tag, without a surrounding optgroup. - query_builder ~~~~~~~~~~~~~ @@ -171,18 +166,17 @@ either be a ``QueryBuilder`` object or a Closure. If using a Closure, it should take a single argument, which is the ``EntityRepository`` of the entity and return an instance of ``QueryBuilder``. -Overridden Options ------------------- +em +~~ + +**type**: ``string`` | ``Doctrine\Common\Persistence\ObjectManager`` **default**: the default entity manager -choice_list -~~~~~~~~~~~ +If specified, this entity manager will be used to load the choices +instead of the ``default`` entity manager. -**default**: :class:`Symfony\\Bridge\\Doctrine\\Form\\ChoiceList\\EntityChoiceList` -The purpose of the ``entity`` type is to create and configure this ``EntityChoiceList`` -for you, by using all of the above options. If you need to override this -option, you may just consider using the :doc:`/reference/forms/types/choice` -directly. +Overridden Options +------------------ choices ~~~~~~~ @@ -213,6 +207,8 @@ type: is a complete example in the cookbook article :doc:`/cookbook/form/form_collections`. +.. include:: /reference/forms/types/options/group_by.rst.inc + .. include:: /reference/forms/types/options/preferred_choices.rst.inc .. note:: diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index 4008b621247..860ff593d45 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -128,6 +128,8 @@ The actual default value of this option depends on other field options: .. include:: /reference/forms/types/options/post_max_size_message.rst.inc +.. _reference-form-option-property-path: + .. include:: /reference/forms/types/options/property_path.rst.inc .. include:: /reference/forms/types/options/read_only.rst.inc diff --git a/reference/forms/types/options/choice_attr.rst.inc b/reference/forms/types/options/choice_attr.rst.inc new file mode 100644 index 00000000000..4b4d8f187ac --- /dev/null +++ b/reference/forms/types/options/choice_attr.rst.inc @@ -0,0 +1,26 @@ +choice_attr +~~~~~~~~~~~ + +.. versionadded:: 2.7 + The ``choice_attr`` option was introduced in Symfony 2.7. + +**type**: ``array``, ``callable`` or ``string`` **default**: ``array()`` + +Use this to add additional HTML attributes to each choice. This can be an array +of attributes (if they are the same for each choice), a callable or a property path +(just like `choice_label`_). + +If an array, the keys of the ``choices`` array must be used as keys:: + + $builder->add('attending', 'choice', array( + 'choices' => array( + 'Yes' => true, + 'No' => false, + 'Maybe' => null, + ), + 'choices_as_values' => true, + 'choice_attr' => function($val, $key, $index) { + // adds a class like attending_yes, attending_no, etc + return ['class' => 'attending_'.strtolower($key)]; + }, + )); diff --git a/reference/forms/types/options/choice_label.rst.inc b/reference/forms/types/options/choice_label.rst.inc new file mode 100644 index 00000000000..d2ebb176906 --- /dev/null +++ b/reference/forms/types/options/choice_label.rst.inc @@ -0,0 +1,50 @@ +choice_label +~~~~~~~~~~~~ + +.. versionadded:: 2.7 + The ``choice_label`` option was introduced in Symfony 2.7. + +**type**: ``callable`` or ``string`` **default**: ``null`` + +Normally, the array key of each item in the ``choices`` option is used as the +text that's shown to the user. The ``choice_label`` option allows you to take +more control:: + + $builder->add('attending', 'choice', array( + 'choices' => array( + 'yes' => true, + 'no' => false, + 'maybe' => null, + ), + 'choices_as_values' => true, + 'choice_label' => function ($value, $key, $index) { + if ($value == true) { + return 'Definitely!'; + } + return strtoupper($key); + + // or if you want to translate some key + //return 'form.choice.'.$key; + }, + )); + +This method is called for *each* choice, passing you the choice ``$value`` and the +``$key`` from the choices array (``$index`` is related to `choice_value`_). This +will give you: + +.. image:: /images/reference/form/choice-example2.png + :align: center + +If your choice values are objects, then ``choice_label`` can also be a +:ref:`property path `. Imagine you have some +``Status`` class with a ``getDisplayName()`` method:: + + $builder->add('attending', 'choice', array( + 'choices' => array( + new Status(Status::YES), + new Status(Status::NO), + new Status::(Status::MAYBE), + ), + 'choices_as_values' => true, + 'choice_label' => 'displayName', + )); diff --git a/reference/forms/types/options/choice_name.rst.inc b/reference/forms/types/options/choice_name.rst.inc new file mode 100644 index 00000000000..45a228489a3 --- /dev/null +++ b/reference/forms/types/options/choice_name.rst.inc @@ -0,0 +1,14 @@ +choice_name +~~~~~~~~~~~ + +.. versionadded:: 2.7 + The ``choice_name`` option was introduced in Symfony 2.7. + +**type**: ``callable`` or ``string`` **default**: ``null`` + +Controls the internal field name of the choice. You normally don't care about this, +but in some advanced cases, you might. For example, this "name" becomes the index +of the choice views in the template. + +This can be a callable or a property path. See `choice_label`_ for similar usage. +If ``null`` is used, an incrementing integer is used as the name. diff --git a/reference/forms/types/options/choice_value.rst.inc b/reference/forms/types/options/choice_value.rst.inc new file mode 100644 index 00000000000..fda5a4e7542 --- /dev/null +++ b/reference/forms/types/options/choice_value.rst.inc @@ -0,0 +1,23 @@ +choice_value +~~~~~~~~~~~~ + +.. versionadded:: 2.7 + The ``choice_value`` option was introduced in Symfony 2.7. + +**type**: ``callable`` or ``string`` **default**: ``null`` + +Returns the string "value" for each choice. This is used in the ``value`` attribute +in HTML and submitted in the POST/PUT requests. You don't normally need to worry +about this, but it might be handy when processing an API request (since you can +configure the value that will be sent in the API request). + +This can be a callable or a property path. See `choice_label`_ for similar usage. +If ``null`` is used, an incrementing integer is used as the name. + +.. caution:: + + In Symfony 2.7, there was a small backwards-compatibility break with how the + `value` attribute of options is generated. This is not a problem unless you + rely on the option values in JavaScript. See `issue #14825`_ for details. + +.. _`issue #14825`: https://github.com/symfony/symfony/pull/14825 diff --git a/reference/forms/types/options/group_by.rst.inc b/reference/forms/types/options/group_by.rst.inc new file mode 100644 index 00000000000..9147a0a09bf --- /dev/null +++ b/reference/forms/types/options/group_by.rst.inc @@ -0,0 +1,43 @@ +group_by +~~~~~~~~ + +.. versionadded:: 2.7 + The ``group_by`` option was introduced in Symfony 2.7. + +**type**: ``array``, ``callable`` or ``string`` **default**: ``null`` + +You can easily "group" options in a select simply by passing a multi-dimensional +array to ``choices``. See the :ref:`Grouping Options ` +section about that. + +The ``group_by`` option is an alternative way to group choices, which gives you +a bit more flexibility. + +Take the following example:: + + $builder->add('publishAt', 'choice', array( + 'choices' => array( + 'now' => new \DateTime('now'), + 'tomorrow' => new \DateTime('+1 day'), + '1 week' => new \DateTime('+1 week'), + '1 month' => new \DateTime('+1 month') + ), + 'choices_as_values' => true, + 'group_by' => function($val, $key, $index) { + if ($val <= new \DateTime('+3 days')) { + return 'Soon'; + } else { + return 'Later'; + } + }, + )); + +This groups the dates that are within 3 days into "Soon" and everything else into +a "Later" group: + +.. image:: /images/reference/form/choice-example5.png + :align: center + +If you return ``null``, the option won't be grouped. You can also pass a string +"property path" that will be called to get the group. See the `choice_label`_ for +details about using a property path. diff --git a/reference/forms/types/options/preferred_choices.rst.inc b/reference/forms/types/options/preferred_choices.rst.inc index f2289ee537b..e9b0f54ea8f 100644 --- a/reference/forms/types/options/preferred_choices.rst.inc +++ b/reference/forms/types/options/preferred_choices.rst.inc @@ -1,31 +1,64 @@ preferred_choices ~~~~~~~~~~~~~~~~~ -**type**: ``array`` **default**: ``array()`` +**type**: ``array``, ``callable`` or ``string`` **default**: ``array()`` -If this option is specified, then a sub-set of all of the options will be -moved to the top of the select menu. The following would move the "Baz" -option to the top, with a visual separator between it and the rest of the -options:: +This option allows you to move certain choices to the top of your list with a visual +separator between them and the rest of the options. If you have a form of languages, +you can list the most popular on top, like Bork Bork and Pirate:: - $builder->add('foo_choices', 'choice', array( - 'choices' => array('foo' => 'Foo', 'bar' => 'Bar', 'baz' => 'Baz'), - 'preferred_choices' => array('baz'), + $builder->add('language', 'choice', array( + 'choices' => array( + 'English' => 'en', + 'Spanish' => 'es', + 'Bork' => 'muppets', + 'Pirate' => 'arr' + ), + 'choices_as_values' => true, + 'preferred_choices' => array('muppets', 'arr') )); -Note that preferred choices are only meaningful when rendering as a ``select`` -element (i.e. ``expanded`` is false). The preferred choices and normal choices -are separated visually by a set of dotted lines (i.e. ``-------------------``). -This can be customized when rendering the field: +.. versionadded:: 2.7 + Setting a callable or propery path was introduced in Symfony 2.7. + +This options can also be a callback function to give you more flexibility. This might +be especially useful if your values are objects:: + + $builder->add('publishAt', 'choice', array( + 'choices' => array( + 'now' => new \DateTime('now'), + 'tomorrow' => new \DateTime('+1 day'), + '1 week' => new \DateTime('+1 week'), + '1 month' => new \DateTime('+1 month') + ), + 'choices_as_values' => true, + 'preferred_choices' => function ($val, $key) { + // prefer options within 3 days + return $val <= new \DateTime('+3 days'); + }, + )); + +This will "prefer" the "now" and "tomorrow" choices only: + +.. image:: /images/reference/form/choice-example3.png + :align: center + +Finally, if your values are objects, you can also specific a property path string +on the object that will return true or false. + +The preferred choices are only meaningful when rendering a ``select`` element +(i.e. ``expanded`` false). The preferred choices and normal choices are separated +visually by a set of dotted lines (i.e. ``-------------------``). This can be customized +when rendering the field: .. configuration-block:: .. code-block:: twig - {{ form_widget(form.foo_choices, { 'separator': '=====' }) }} + {{ form_widget(form.publishAt, { 'separator': '=====' }) }} .. code-block:: php - widget($form['foo_choices'], array( + widget($form['publishAt'], array( 'separator' => '=====' )) ?>