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/forms/types/choice.rst b/reference/forms/types/choice.rst index 07f80d3cf3c..20d8f2af519 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,108 @@ 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($val, $key, $index) { + /** @var Category $val */ + return strtoupper($val->getName()); + }, + 'choice_attr' => function($val, $key, $index) { + return ['class' => 'category_'.strtolower($val->getName())]; + }, + 'group_by' => function($val, $key, $index) { + // randomly assign things into 2 groups + return rand(0, 1) == 1 ? 'Group A' : 'Group B' + }, + 'preferred_choices' => function($val, $key, $index) { + return $val->getName() == 'Cat2' || $val->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 +162,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 options 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 +251,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 +287,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 5aabfdd5f23..01ce0f7862d 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 e9686c4a77e..66ba30ca19c 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 57cfea43830..900ee17cd40 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:: jinja - {{ form_widget(form.foo_choices, { 'separator': '=====' }) }} + {{ form_widget(form.publishAt, { 'separator': '=====' }) }} .. code-block:: php - widget($form['foo_choices'], array( + widget($form['publishAt'], array( 'separator' => '=====' )) ?>