diff --git a/book/controller.rst b/book/controller.rst index 1eac0bd0640..b981c243b12 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -325,7 +325,7 @@ working with forms, for example:: { $form = $this->createForm(...); - $form->bind($request); + $form->handleRequest($request); // ... } @@ -663,7 +663,8 @@ For example, imagine you're processing a form submit:: { $form = $this->createForm(...); - $form->bind($this->getRequest()); + $form->handleRequest($this->getRequest()); + if ($form->isValid()) { // do some sort of processing diff --git a/book/forms.rst b/book/forms.rst index 272eae5e86e..cc80e9643ac 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -101,6 +101,7 @@ from inside a controller:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') + ->add('save', 'submit') ->getForm(); return $this->render('AcmeTaskBundle:Default:new.html.twig', array( @@ -125,6 +126,11 @@ In this example, you've added two fields to your form - ``task`` and ``dueDate`` corresponding to the ``task`` and ``dueDate`` properties of the ``Task`` class. You've also assigned each a "type" (e.g. ``text``, ``date``), which, among other things, determines which HTML form tag(s) is rendered for that field. +Finally, you added a submit button for submitting the form to the server. + +.. versionadded:: 2.3 + Support for submit buttons was added in Symfony 2.3. Before that, you had + to add buttons to the form's HTML manually. Symfony2 comes with many built-in types that will be discussed shortly (see :ref:`book-forms-type-reference`). @@ -145,35 +151,30 @@ helper functions: .. code-block:: html+jinja {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} -
- {{ form_widget(form) }} - -
+ {{ form(form) }} .. code-block:: html+php -
enctype($form) ?> > - widget($form) ?> - -
+ form($form) ?> .. image:: /images/book/form-simple.png :align: center .. note:: - This example assumes that you've created a route called ``task_new`` - that points to the ``AcmeTaskBundle:Default:new`` controller that - was created earlier. + This example assumes that you submit the form in a "POST" request and to + the same URL that it was displayed in. You will learn later how to + change the request method and the target URL of the form. -That's it! By printing ``form_widget(form)``, each field in the form is -rendered, along with a label and error message (if there is one). As easy -as this is, it's not very flexible (yet). Usually, you'll want to render each -form field individually so you can control how the form looks. You'll learn how -to do that in the ":ref:`form-rendering-template`" section. +That's it! By printing ``form(form)``, each field in the form is rendered, along +with a label and error message (if there is one). The ``form`` function also +surrounds everything in the necessary HTML ``form`` tag. As easy as this is, +it's not very flexible (yet). Usually, you'll want to render each form field +individually so you can control how the form looks. You'll learn how to do +that in the ":ref:`form-rendering-template`" section. Before moving on, notice how the rendered ``task`` input field has the value of the ``task`` property from the ``$task`` object (i.e. "Write a blog post"). @@ -190,18 +191,17 @@ it into a format that's suitable for being rendered in an HTML form. (e.g. ``isPublished()`` or ``hasReminder()``) instead of a getter (e.g. ``getPublished()`` or ``getReminder()``). - .. versionadded:: 2.1 - Support for "hasser" methods was added in Symfony 2.1. - .. index:: - single: Forms; Handling form submission + single: Forms; Handling form submissions + +.. _book-form-handling-form-submissions: Handling Form Submissions ~~~~~~~~~~~~~~~~~~~~~~~~~ The second job of a form is to translate user-submitted data back to the properties of an object. To make this happen, the submitted data from the -user must be bound to the form. Add the following functionality to your +user must be written into the form. Add the following functionality to your controller:: // ... @@ -215,55 +215,94 @@ controller:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') + ->add('save', 'submit') ->getForm(); - if ($request->isMethod('POST')) { - $form->bind($request); + $form->handleRequest($request); - if ($form->isValid()) { - // perform some action, such as saving the task to the database + if ($form->isValid()) { + // perform some action, such as saving the task to the database - return $this->redirect($this->generateUrl('task_success')); - } + return $this->redirect($this->generateUrl('task_success')); } // ... } -.. versionadded:: 2.1 - The ``bind`` method was made more flexible in Symfony 2.1. It now accepts - the raw client data (same as before) or a Symfony Request object. This - is preferred over the deprecated ``bindRequest`` method. +.. versionadded:: 2.3 + The :method:`Symfony\Component\Form\FormInterface::handleRequest` method was + added in Symfony 2.3. Previously, the ``$request`` was passed to the + ``submit`` method - a strategy which is deprecated and will be removed + in Symfony 3.0. For details on that method, see :ref:`cookbook-form-submit-request`. -Now, when submitting the form, the controller binds the submitted data to the -form, which translates that data back to the ``task`` and ``dueDate`` properties -of the ``$task`` object. This all happens via the ``bind()`` method. +This controller follows a common pattern for handling forms, and has three +possible paths: -.. note:: +#. When initially loading the page in a browser, the form is simply created and + rendered. :method:`Symfony\Component\Form\FormInterface::handleRequest` + recognizes that the form was not submitted and does nothing. + :method:`Symfony\Component\Form\FormInterface::isValid` returns ``false`` + if the form was not submitted. - As soon as ``bind()`` is called, the submitted data is transferred - to the underlying object immediately. This happens regardless of whether - or not the underlying data is actually valid. +#. When the user submits the form, :method:`Symfony\Component\Form\FormInterface::handleRequest` + recognizes this and immediately writes the submitted data back into the + ``task`` and ``dueDate`` properties of the ``$task`` object. Then this object + is validated. If it is invalid (validation is covered in the next section), + :method:`Symfony\Component\Form\FormInterface::isValid` returns ``false`` + again, so the form is rendered together with all validation errors; -This controller follows a common pattern for handling forms, and has three -possible paths: + .. note:: -#. When initially loading the page in a browser, the request method is ``GET`` - and the form is simply created and rendered; + You can use the method :method:`Symfony\Component\Form\FormInterface::isSubmitted` + to check whether a form was submitted, regardless of whether or not the + submitted data is actually valid. -#. When the user submits the form (i.e. the method is ``POST``) with invalid - data (validation is covered in the next section), the form is bound and - then rendered, this time displaying all validation errors; +#. When the user submits the form with valid data, the submitted data is again + written into the form, but this time :method:`Symfony\Component\Form\FormInterface::isValid` + returns ``true``. Now you have the opportunity to perform some actions using + the ``$task`` object (e.g. persisting it to the database) before redirecting + the user to some other page (e.g. a "thank you" or "success" page). -#. When the user submits the form with valid data, the form is bound and - you have the opportunity to perform some actions using the ``$task`` - object (e.g. persisting it to the database) before redirecting the user - to some other page (e.g. a "thank you" or "success" page). + .. note:: -.. note:: + Redirecting a user after a successful form submission prevents the user + from being able to hit "refresh" and re-post the data. + +.. index:: + single: Forms; Multiple Submit Buttons + +.. _book-form-submitting-multiple-buttons: + +Submitting Forms with Multiple Buttons +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + Support for buttons in forms was added in Symfony 2.3. - Redirecting a user after a successful form submission prevents the user - from being able to hit "refresh" and re-post the data. +When your form contains more than one submit button, you will want to check +which of the buttons was clicked to adapt the program flow in your controller. +Let's add a second button with the caption "Save and add" to our form:: + + $form = $this->createFormBuilder($task) + ->add('task', 'text') + ->add('dueDate', 'date') + ->add('save', 'submit') + ->add('saveAndAdd', 'submit') + ->getForm(); + +In your controller, use the button's +:method:`Symfony\\Component\\Form\\ClickableInterface::isClicked` method for +querying if the "Save and add" button was clicked:: + + if ($form->isValid()) { + // ... perform some action, such as saving the task to the database + + $nextAction = $form->get('saveAndAdd')->isClicked() + ? 'task_new' + : 'task_success'; + + return $this->redirect($this->generateUrl($nextAction)); + } .. index:: single: Forms; Validation @@ -408,16 +447,46 @@ method:: In both of these cases, *only* the ``registration`` validation group will be used to validate the underlying object. -Groups based on Submitted Data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. index:: + single: Forms; Disabling validation -.. versionadded:: 2.1 - The ability to specify a callback or Closure in ``validation_groups`` - is new to version 2.1 +Disabling Validation +~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + The ability to set ``validation_groups`` to false was added in Symfony 2.3, + although setting it to an empty array achieved the same result in previous + versions. + +Sometimes it is useful to suppress the validation of a form altogether. For +these cases, you can skip the call to :method:`Symfony\\Component\\Form\\FormInterface::isValid` +in your controller. If this is not possible, you can alternatively set the +``validation_groups`` option to ``false`` or an empty array:: + + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'validation_groups' => false, + )); + } + +Note that when you do that, the form will still run basic integrity checks, +for example whether an uploaded file was too large or whether non-existing +fields were submitted. If you want to suppress validation completely, remove +the :method:`Symfony\\Component\\Form\\FormInterface::isValid` call from your +controller. + +.. index:: + single: Forms; Validation groups based on submitted data + +Groups based on the Submitted Data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you need some advanced logic to determine the validation groups (e.g. based on submitted data), you can set the ``validation_groups`` option -to an array callback, or a ``Closure``:: +to an array callback:: use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -429,9 +498,9 @@ to an array callback, or a ``Closure``:: } This will call the static method ``determineValidationGroups()`` on the -``Client`` class after the form is bound, but before validation is executed. +``Client`` class after the form is submitted, but before validation is executed. The Form object is passed as an argument to that method (see next example). -You can also define whole logic inline by using a Closure:: +You can also define whole logic inline by using a ``Closure``:: use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -450,6 +519,44 @@ You can also define whole logic inline by using a Closure:: )); } +.. index:: + single: Forms; Validation groups based on clicked button + +Groups based on the Clicked Button +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + Support for buttons in forms was added in Symfony 2.3. + +When your form contains multiple submit buttons, you can change the validation +group depending on which button is used to submit the form. For example, +consider a form in a wizard that lets you advance to the next step or go back +to the previous step. Let's assume also that when returning to the previous +step, the data of the form should be saved, but not validated. + +First, we need to add the two buttons to the form:: + + $form = $this->createFormBuilder($task) + // ... + ->add('nextStep', 'submit') + ->add('previousStep', 'submit') + ->getForm(); + +Then, we configure the button for returning to the previous step to run +specific validation groups. In this example, we want it to suppress validation, +so we set its ``validation_groups`` options to false:: + + $form = $this->createFormBuilder($task) + // ... + ->add('previousStep', 'submit', array( + 'validation_groups' => false, + )) + ->getForm(); + +Now the form will skip your validation constraints. It will still validate +basic integrity constraints, such as checking whether an uploaded file was too +large or whether you tried to submit text in a number field. + .. index:: single: Forms; Built-in field types @@ -605,35 +712,30 @@ of code. Of course, you'll usually need much more flexibility when rendering: .. code-block:: html+jinja {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} -
+ {{ form_start(form) }} {{ form_errors(form) }} {{ form_row(form.task) }} {{ form_row(form.dueDate) }} - {{ form_rest(form) }} - -
+ {{ form_end(form) }} .. code-block:: html+php -
enctype($form) ?>> + start($form) ?> errors($form) ?> row($form['task']) ?> row($form['dueDate']) ?> - rest($form) ?> - -
+ end($form) ?> Take a look at each part: -* ``form_enctype(form)`` - If at least one field is a file upload field, this - renders the obligatory ``enctype="multipart/form-data"``; +* ``form_start(form)`` - Renders the start tag of the form. * ``form_errors(form)`` - Renders any errors global to the whole form (field-specific errors are displayed next to each field); @@ -642,10 +744,8 @@ Take a look at each part: form widget for the given field (e.g. ``dueDate``) inside, by default, a ``div`` element; -* ``form_rest(form)`` - Renders any fields that have not yet been rendered. - It's usually a good idea to place a call to this helper at the bottom of - each form (in case you forgot to output a field or don't want to bother - manually rendering hidden fields). This helper is also useful for taking +* ``form_end()`` - Renders the end tag of the form and any fields that have not + yet been rendered. This is useful for rendering hidden fields and taking advantage of the automatic :ref:`CSRF Protection`. The majority of the work is done by the ``form_row`` helper, which renders @@ -683,39 +783,46 @@ used the ``form_row`` helper: .. code-block:: html+jinja - {{ form_errors(form) }} + {{ form_start(form) }} + {{ form_errors(form) }} -
- {{ form_label(form.task) }} - {{ form_errors(form.task) }} - {{ form_widget(form.task) }} -
+
+ {{ form_label(form.task) }} + {{ form_errors(form.task) }} + {{ form_widget(form.task) }} +
-
- {{ form_label(form.dueDate) }} - {{ form_errors(form.dueDate) }} - {{ form_widget(form.dueDate) }} -
+
+ {{ form_label(form.dueDate) }} + {{ form_errors(form.dueDate) }} + {{ form_widget(form.dueDate) }} +
+ + - {{ form_rest(form) }} + {{ form_end(form) }} .. code-block:: html+php - errors($form) ?> + start($form) ?> -
- label($form['task']) ?> - errors($form['task']) ?> - widget($form['task']) ?> -
+ errors($form) ?> -
- label($form['dueDate']) ?> - errors($form['dueDate']) ?> - widget($form['dueDate']) ?> -
+
+ label($form['task']) ?> + errors($form['task']) ?> + widget($form['task']) ?> +
+ +
+ label($form['dueDate']) ?> + errors($form['dueDate']) ?> + widget($form['dueDate']) ?> +
- rest($form) ?> + + + end($form) ?> If the auto-generated label for a field isn't quite right, you can explicitly specify it: @@ -740,7 +847,7 @@ field: .. code-block:: html+jinja - {{ form_widget(form.task, { 'attr': {'class': 'task_field'} }) }} + {{ form_widget(form.task, {'attr': {'class': 'task_field'}}) }} .. code-block:: html+php @@ -783,6 +890,75 @@ available in the :doc:`reference manual`. Read this to know everything about the helpers available and the options that can be used with each. +.. index:: + single: Forms; Changing the action and method + +.. _book-forms-changing-action-and-method: + +Changing the Action and Method of a Form +---------------------------------------- + +So far, the ``form_start()`` helper has been used to render the form's start +tag and we assumed that each form is submitted to the same URL in a POST request. +Sometimes you want to change these parameters. You can do so in a few different +ways. If you build your form in the controller, you can use ``setAction()`` and +``setMethod()``:: + + $form = $this->createFormBuilder($task) + ->setAction($this->generateUrl('target_route')) + ->setMethod('GET') + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + +.. note:: + + This example assumes that you've created a route called ``target_route`` + that points to the controller that processes the form. + +In :ref:`book-form-creating-form-classes` you will learn how to move the +form building code into separate classes. When using an external form class +in the controller, you can pass the action and method as form options:: + + $form = $this->createForm(new TaskType(), $task, array( + 'action' => $this->generateUrl('target_route'), + 'method' => 'GET', + )); + +Finally, you can override the action and method in the template by passing them +to the ``form()`` or the ``form_start()`` helper: + +.. configuration-block:: + + .. code-block:: html+jinja + + {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} + {{ form(form, {'action': path('target_route'), 'method': 'GET'}) }} + + {{ form_start(form, {'action': path('target_route'), 'method': 'GET'}) }} + + .. code-block:: html+php + + + form($form, array( + 'action' => $view['router']->generate('target_route'), + 'method' => 'GET', + )) ?> + + start($form, array( + 'action' => $view['router']->generate('target_route'), + 'method' => 'GET', + )) ?> + +.. note:: + + If the form's method is not GET or POST, but PUT, PATCH or DELETE, Symfony2 + will insert a hidden field with the name "_method" that stores this method. + The form will be submitted in a normal POST request, but Symfony2's router + is capable of detecting the "_method" parameter and will interpret the + request as PUT, PATCH or DELETE request. Read the cookbook chapter + ":doc:`/cookbook/routing/method_parameters`" for more information. + .. index:: single: Forms; Creating form classes @@ -912,7 +1088,7 @@ you can fetch it from the form:: For more information, see the :doc:`Doctrine ORM chapter`. -The key thing to understand is that when the form is bound, the submitted +The key thing to understand is that when the form is submitted, the submitted data is transferred to the underlying object immediately. If you want to persist that data, you simply need to persist the object itself (which already contains the submitted data). @@ -1045,7 +1221,6 @@ as the original ``Task`` fields: {{ form_row(form.category.name) }} - {{ form_rest(form) }} {# ... #} .. code-block:: html+php @@ -1057,7 +1232,6 @@ as the original ``Task`` fields: row($form['category']['name']) ?> - rest($form) ?> When the user submits the form, the submitted data for the ``Category`` fields @@ -1143,7 +1317,7 @@ renders the form: {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' 'AcmeTaskBundle:Form:fields2.html.twig' %} -
+ {{ form(form) }} .. code-block:: html+php @@ -1152,7 +1326,7 @@ renders the form: setTheme($form, array('AcmeTaskBundle:Form', 'AcmeTaskBundle:Form')) ?> - + form($form) ?> The ``form_theme`` tag (in Twig) "imports" the fragments defined in the given template and uses them when rendering the form. In other words, when the @@ -1171,18 +1345,13 @@ To customize any portion of a form, you just need to override the appropriate fragment. Knowing exactly which block or file to override is the subject of the next section. -.. versionadded:: 2.1 - An alternate Twig syntax for ``form_theme`` has been introduced in 2.1. It accepts - any valid Twig expression (the most noticeable difference is using an array when - using multiple themes). - - .. code-block:: html+jinja +.. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} + {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} - {% form_theme form with 'AcmeTaskBundle:Form:fields.html.twig' %} + {% form_theme form with 'AcmeTaskBundle:Form:fields.html.twig' %} - {% form_theme form with ['AcmeTaskBundle:Form:fields.html.twig', 'AcmeTaskBundle:Form:fields2.html.twig'] %} + {% form_theme form with ['AcmeTaskBundle:Form:fields.html.twig', 'AcmeTaskBundle:Form:fields2.html.twig'] %} For a more extensive discussion, see :doc:`/cookbook/form/form_customization`. @@ -1231,7 +1400,7 @@ are 4 possible *parts* of a form that can be rendered: .. note:: - There are actually 3 other *parts* - ``rows``, ``rest``, and ``enctype`` - + There are actually 2 other *parts* - ``rows`` and ``rest`` - but you should rarely if ever need to worry about overriding them. By knowing the field type (e.g. ``textarea``) and which part you want to @@ -1425,7 +1594,7 @@ ensures that the user - not some other entity - is submitting the given data. Symfony automatically validates the presence and accuracy of this token. The ``_token`` field is a hidden field and will be automatically rendered -if you include the ``form_rest()`` function in your template, which ensures +if you include the ``form_end()`` function in your template, which ensures that all un-rendered fields are output. The CSRF token can be customized on a form-by-form basis. For example:: @@ -1486,12 +1655,12 @@ an array of the submitted data. This is actually really easy:: ->add('message', 'textarea') ->getForm(); - if ($request->isMethod('POST')) { - $form->bind($request); + $form->handleRequest($request); - // data is an array with "name", "email", and "message" keys - $data = $form->getData(); - } + if ($form->isValid()) { + // data is an array with "name", "email", and "message" keys + $data = $form->getData(); + } // ... render the form } @@ -1526,25 +1695,20 @@ Adding Validation The only missing piece is validation. Usually, when you call ``$form->isValid()``, the object is validated by reading the constraints that you applied to that -class. If your form is binding to an object (i.e. you're using the ``data_class`` +class. If your form is mapped to an object (i.e. you're using the ``data_class`` option or passing an object to your form), this is almost always the approach you want to use. See :doc:`/book/validation` for more details. .. _form-option-constraints: -But if you're not binding to an object and are instead retrieving a simple -array of your submitted data, how can you add constraints to the data of your -form? +But if the form is not mapped to an object and you instead want to retrieve a +simple array of your submitted data, how can you add constraints to the data of +your form? The answer is to setup the constraints yourself, and attach them to the individual fields. The overall approach is covered a bit more in the :ref:`validation chapter`, but here's a short example: -.. versionadded:: 2.1 - The ``constraints`` option, which accepts a single constraint or an array - of constraints (before 2.1, the option was called ``validation_constraint``, - and only accepted a single constraint) is new to Symfony 2.1. - .. code-block:: php use Symfony\Component\Validator\Constraints\Length; diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index ff684736571..b655c100439 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -433,7 +433,7 @@ content: { "require": { - "symfony/symfony": "2.2.*" + "symfony/symfony": "2.3.*" }, "autoload": { "files": ["model.php","controllers.php"] diff --git a/book/internals.rst b/book/internals.rst index d61951b86fb..f9edc56a6fb 100644 --- a/book/internals.rst +++ b/book/internals.rst @@ -367,9 +367,6 @@ The ``FrameworkBundle`` registers several listeners: ``kernel.terminate`` Event .......................... -.. versionadded:: 2.1 - The ``kernel.terminate`` event is new since Symfony 2.1. - The purpose of this event is to perform "heavier" tasks after the response was already served to the client. diff --git a/book/page_creation.rst b/book/page_creation.rst index f90ae6d4f48..7ba449cc615 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -586,7 +586,6 @@ method of the ``AppKernel`` class:: new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), - new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(), ); if (in_array($this->getEnvironment(), array('dev', 'test'))) { @@ -813,9 +812,6 @@ options of each feature. Default Configuration Dump ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``config:dump-reference`` command was added in Symfony 2.1 - You can dump the default configuration for a bundle in yaml to the console using the ``config:dump-reference`` command. Here is an example of dumping the default FrameworkBundle configuration: diff --git a/book/routing.rst b/book/routing.rst index 09ebf2d94d8..12d96cb68f1 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -788,7 +788,7 @@ a slash. URLs matching this route might look like: .. note:: Sometimes you want to make certain parts of your routes globally configurable. - Symfony2.1 provides you with a way to do this by leveraging service container + Symfony provides you with a way to do this by leveraging service container parameters. Read more about this in ":doc:`/cookbook/routing/service_container_parameters`. Special Routing Parameters diff --git a/book/security.rst b/book/security.rst index cb583031a90..ee45430393e 100644 --- a/book/security.rst +++ b/book/security.rst @@ -410,11 +410,6 @@ submission (i.e. ``/login_check``): URL as the firewall will automatically catch and process any form submitted to this URL. -.. versionadded:: 2.1 - As of Symfony 2.1, you *must* have routes configured for your ``login_path``, - ``check_path`` ``logout`` keys. These keys can be route names (as shown - in this example) or URLs that have routes configured for them. - Notice that the name of the ``login`` route matches the``login_path`` config value, as that's where the security system will redirect users that need to login. @@ -770,7 +765,7 @@ access control should be used on this request. The following ``access_control`` options are used for matching: * ``path`` -* ``ip`` +* ``ip`` or ``ips`` * ``host`` * ``methods`` @@ -877,6 +872,11 @@ prevent any direct access to these resources from a web browser (by guessing the ESI URL pattern), the ESI route **must** be secured to be only visible from the trusted reverse proxy cache. +.. versionadded:: 2.3 + Version 2.3 allows multiple IP addresses in a single rule with the ``ips: [a, b]`` + construct. Prior to 2.3, users should create one rule per IP address to match and + use the ``ip`` key instead of ``ips``. + Here is an example of how you might secure all ESI routes that start with a given prefix, ``/esi``, from outside access: @@ -888,20 +888,20 @@ given prefix, ``/esi``, from outside access: security: # ... access_control: - - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 } + - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - { path: ^/esi, roles: ROLE_NO_ACCESS } .. code-block:: xml - + .. code-block:: php 'access_control' => array( - array('path' => '^/esi', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ip' => '127.0.0.1'), + array('path' => '^/esi', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ips' => '127.0.0.1, ::1'), array('path' => '^/esi', 'role' => 'ROLE_NO_ACCESS'), ), @@ -909,7 +909,7 @@ Here is how it works when the path is ``/esi/something`` coming from the ``10.0.0.1`` IP: * The first access control rule is ignored as the ``path`` matches but the - ``ip`` does not; + ``ip`` does not match either of the IPs listed; * The second access control rule is enabled (the only restriction being the ``path`` and it matches): as the user cannot have the ``ROLE_NO_ACCESS`` @@ -917,7 +917,8 @@ Here is how it works when the path is ``/esi/something`` coming from the be anything that does not match an existing role, it just serves as a trick to always deny access). -Now, if the same request comes from ``127.0.0.1``: +Now, if the same request comes from ``127.0.0.1`` or ``::1`` (the IPv6 loopback +address): * Now, the first access control rule is enabled as both the ``path`` and the ``ip`` match: access is allowed as the user always has the @@ -1161,12 +1162,6 @@ custom user class is that it implements the :class:`Symfony\\Component\\Security interface. This means that your concept of a "user" can be anything, as long as it implements this interface. -.. versionadded:: 2.1 - In Symfony 2.1, the ``equals`` method was removed from ``UserInterface``. - If you need to override the default implementation of comparison logic, - implement the new :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface` - interface. - .. note:: The user object will be serialized and saved in the session during requests, diff --git a/book/service_container.rst b/book/service_container.rst index 1de05025ee0..5c99cc632a5 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -227,10 +227,6 @@ The end result is exactly the same as before - the difference is only in to look for parameters with those names. When the container is built, it looks up the value of each parameter and uses it in the service definition. -.. versionadded:: 2.1 - Escaping the ``@`` character in YAML parameter values is new in Symfony 2.1.9 - and Symfony 2.2.1. - .. note:: If you want to use a string that starts with an ``@`` sign as a parameter diff --git a/book/templating.rst b/book/templating.rst index 04e420e3bc3..25058d3b70e 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -665,9 +665,6 @@ Controllers are fast to execute and promote good code organization and reuse. Asynchronous Content with hinclude.js ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - hinclude.js support was added in Symfony 2.1 - Controllers can be embedded asynchronously using the hinclude.js_ javascript library. As the embedded content comes from another page (or controller for that matter), Symfony2 uses the standard ``render`` helper to configure ``hinclude`` tags: @@ -1409,9 +1406,6 @@ is ``true``. By default this means that the variables will be dumped in the Syntax Checking --------------- -.. versionadded:: 2.1 - The ``twig:lint`` command was added in Symfony 2.1 - You can check for syntax errors in Twig templates using the ``twig:lint`` console command: diff --git a/book/testing.rst b/book/testing.rst index 204a74bcbd9..d042b7cefb2 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -401,6 +401,10 @@ The Client supports many operations that can be done in a real browser:: Accessing Internal Objects ~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. versionadded:: 2.3 + The ``getInternalRequest()`` and ``getInternalResponse()`` method were + added in Symfony 2.3. + If you use the client to test your application, you might want to access the client's internal objects:: @@ -409,8 +413,18 @@ client's internal objects:: You can also get the objects related to the latest request:: + // the HttpKernel request instance $request = $client->getRequest(); + + // the BrowserKit request instance + $request = $client->getInternalRequest(); + + // the HttpKernel response instance $response = $client->getResponse(); + + // the BrowserKit response instance + $response = $client->getInternalResponse(); + $crawler = $client->getCrawler(); If your requests are not insulated, you can also access the ``Container`` and diff --git a/book/translation.rst b/book/translation.rst index 16ea6db9eb7..c58f36fd5b7 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -554,11 +554,6 @@ by defining a ``default_locale`` for the framework: 'default_locale' => 'en', )); -.. versionadded:: 2.1 - The ``default_locale`` parameter was defined under the session key - originally, however, as of 2.1 this has been moved. This is because the - locale is now set on the request instead of the session. - .. _book-translation-locale-url: The Locale and the URL @@ -825,9 +820,6 @@ texts* and complex expressions: Note that this only influences the current template, not any "included" templates (in order to avoid side effects). -.. versionadded:: 2.1 - The ``trans_default_domain`` tag is new in Symfony2.1 - PHP Templates ~~~~~~~~~~~~~ diff --git a/book/validation.rst b/book/validation.rst index f1c676eca9a..71700c0cb8f 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -205,8 +205,8 @@ Validation and Forms The ``validator`` service can be used at any time to validate any object. In reality, however, you'll usually work with the ``validator`` indirectly when working with forms. Symfony's form library uses the ``validator`` service -internally to validate the underlying object after values have been submitted -and bound. The constraint violations on the object are converted into ``FieldError`` +internally to validate the underlying object after values have been submitted. +The constraint violations on the object are converted into ``FieldError`` objects that can easily be displayed with your form. The typical form submission workflow looks like the following from inside a controller:: @@ -220,14 +220,12 @@ workflow looks like the following from inside a controller:: $author = new Author(); $form = $this->createForm(new AuthorType(), $author); - if ($request->isMethod('POST')) { - $form->bind($request); + $form->handleRequest($request); - if ($form->isValid()) { - // the validation passed, do something with the $author object + if ($form->isValid()) { + // the validation passed, do something with the $author object - return $this->redirect($this->generateUrl(...)); - } + return $this->redirect($this->generateUrl(...)); } return $this->render('BlogBundle:Author:form.html.twig', array( diff --git a/bundles/map.rst.inc b/bundles/map.rst.inc index 44424cc6a1d..92d742ce70c 100644 --- a/bundles/map.rst.inc +++ b/bundles/map.rst.inc @@ -1,10 +1,5 @@ * :doc:`SensioFrameworkExtraBundle ` * :doc:`SensioGeneratorBundle ` -* `JMSSecurityExtraBundle`_ -* `JMSDiExtraBundle`_ * :doc:`DoctrineFixturesBundle ` * :doc:`DoctrineMigrationsBundle ` * :doc:`DoctrineMongoDBBundle ` - -.. _`JMSSecurityExtraBundle`: http://jmsyst.com/bundles/JMSSecurityExtraBundle/1.2 -.. _`JMSDiExtraBundle`: http://jmsyst.com/bundles/JMSDiExtraBundle/1.1 diff --git a/components/class_loader.rst b/components/class_loader.rst index 1e7064b1894..f4bb2a2c095 100644 --- a/components/class_loader.rst +++ b/components/class_loader.rst @@ -33,9 +33,6 @@ You can install the component in many different ways: Usage ----- -.. versionadded:: 2.1 - The ``useIncludePath`` method was added in Symfony 2.1. - Registering the :class:`Symfony\\Component\\ClassLoader\\UniversalClassLoader` autoloader is straightforward:: diff --git a/components/config/definition.rst b/components/config/definition.rst index 2b9726a0de7..e85f5feccb3 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -100,7 +100,7 @@ node definition. Node type are available for: * scalar * boolean * array -* enum (new in 2.1) +* enum * integer (new in 2.2) * float (new in 2.2) * variable (no validation) diff --git a/components/console/events.rst b/components/console/events.rst new file mode 100644 index 00000000000..661e2586038 --- /dev/null +++ b/components/console/events.rst @@ -0,0 +1,118 @@ +.. index:: + single: Console; Events + +Using Events +============ + +.. versionadded:: 2.3 + Console events were added in Symfony 2.3. + +The Application class of the Console component allows you to optionally hook +into the lifecycle of a console application via events. Instead of reinventing +the wheel, it uses the Symfony EventDispatcher component to do the work:: + + use Symfony\Component\Console\Application; + use Symfony\Component\EventDispatcher\EventDispatcher; + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->run(); + +The ``ConsoleEvents::COMMAND`` Event +------------------------------------ + +**Typical Purposes**: Doing something before any command is run (like logging +which command is going to be executed), or displaying something about the event +to be executed. + +Just before executing any command, the ``ConsoleEvents::COMMAND`` event is +dispatched. Listeners receive a +:class:`Symfony\\Component\\Console\\Event\\ConsoleCommandEvent` event:: + + use Symfony\Component\Console\Event\ConsoleCommandEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) { + // get the input instance + $input = $event->getInput(); + + // get the output instance + $output = $event->getOutput(); + + // get the command to be executed + $command = $event->getCommand(); + + // write something about the command + $output->writeln(sprintf('Before running command %s', $command->getName())); + + // get the application + $application = $command->getApplication(); + }); + +The ``ConsoleEvents::TERMINATE`` event +-------------------------------------- + +**Typical Purposes**: To perform some cleanup actions after the command has +been executed. + +After the command has been executed, the ``ConsoleEvents::TERMINATE`` event is +dispatched. It can be used to do any actions that need to be executed for all +commands or to cleanup what you initiated in a ``ConsoleEvents::COMMAND`` +listener (like sending logs, closing a database connection, sending emails, +...). A listener might also change the exit code. + +Listeners receive a +:class:`Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent` event:: + + use Symfony\Component\Console\Event\ConsoleTerminateEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) { + // get the output + $output = $event->getOutput(); + + // get the command that has been executed + $command = $event->getCommand(); + + // display something + $output->writeln(sprintf('After running command %s', $command->getName())); + + // change the exit code + $event->setExitCode(128); + }); + +.. tip:: + + This event is also dispatched when an exception is thrown by the command. + It is then dispatched just before the ``ConsoleEvents::EXCEPTION`` event. + The exit code received in this case is the exception code. + +The ``ConsoleEvents::EXCEPTION`` event +-------------------------------------- + +**Typical Purposes**: Handle exceptions thrown during the execution of a +command. + +Whenever an exception is thrown by a command, the ``ConsoleEvents::EXCEPTION`` +event is dispatched. A listener can wrap or change the exception or do +anything useful before the exception is thrown by the application. + +Listeners receive a +:class:`Symfony\\Component\\Console\\Event\\ConsoleForExceptionEvent` event:: + + use Symfony\Component\Console\Event\ConsoleForExceptionEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::EXCEPTION, function (ConsoleForExceptionEvent $event) { + $output = $event->getOutput(); + + $command = $event->getCommand(); + + $output->writeln(sprintf('Oops, exception thrown while running command %s', $command->getName())); + + // get the current exit code (the exception code or the exit code set by a ConsoleEvents::TERMINATE event) + $exitCode = $event->getExitCode(); + + // change the exception to another one + $event->setException(new \LogicException('Caught exception', $exitCode, $event->getException())); + }); diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index 927d8a80ebd..2825037039d 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -53,6 +53,24 @@ The user will be asked "Please enter the name of the bundle". She can type some name which will be returned by the ``ask`` method. If she leaves it empty, the default value (``AcmeDemoBundle`` here) is returned. +Autocompletion +~~~~~~~~~~~~~~ + +.. versionadded:: 2.2 + Autocompletion for questions was added in Symfony 2.2. + +You can also specify an array of potential answers for a given question. These +will be autocompleted as the user types:: + + $dialog = $this->getHelperSet()->get('dialog'); + $bundleNames = array('AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); + $name = $dialog->ask( + $output, + 'Please enter the name of a bundle', + 'FooBundle', + $bundleNames + ); + Hiding the User's Response ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,18 +194,48 @@ from a predefined list:: $colors, 0 ); - $output->writeln('You have just selected: ' . $colors[$color]); + $output->writeln('You have just selected: '.$colors[$color]); // ... do something with the color The option which should be selected by default is provided with the fourth -parameter. The default is ``null``, which means that no option is the default one. +argument. The default is ``null``, which means that no option is the default one. If the user enters an invalid string, an error message is shown and the user is asked to provide the answer another time, until she enters a valid string or the maximum attempts is reached (which you can define in the fifth -parameter). The default value for the attempts is ``false``, which means infinite -attempts. You can define your own error message in the sixth parameter. +argument). The default value for the attempts is ``false``, which means infinite +attempts. You can define your own error message in the sixth argument. + +.. versionadded:: 2.3 + Multiselect support was added in Symfony 2.3. + +Multiple Choices +................ + +Sometimes, multiple answers can be given. The DialogHelper provides this +feature using comma separated values. This is disabled by default, to enable +this set the seventh argument to ``true``:: + + // ... + + $selected = $dialog->select( + $output, + 'Please select your favorite color (default to red)', + $colors, + 0, + false, + 'Value "%s" is invalid', + true // enable multiselect + ); + + $selectedColors = array_map(function($c) use ($colors) { + return $colors[$c]; + }, $selected) + + $output->writeln('You have just selected: ' . implode(', ', $selectedColors)); + +Now, when the user enters ``1,2``, the result will be: ``You have just selected: blue, yellow``. Testing a Command which expects input ------------------------------------- diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index adb2a4bbc8a..c922e732e64 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -10,6 +10,7 @@ The Console Helpers dialoghelper formatterhelper progresshelper + tablehelper The Console Components comes with some useful helpers. These helpers contain function to ease some common tasks. diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index cbc819ad832..60b32c03975 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,3 +1,4 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` * :doc:`/components/console/helpers/progresshelper` +* :doc:`/components/console/helpers/tablehelper` diff --git a/components/console/helpers/progresshelper.rst b/components/console/helpers/progresshelper.rst index 73b008f91d6..cadf41d947d 100644 --- a/components/console/helpers/progresshelper.rst +++ b/components/console/helpers/progresshelper.rst @@ -7,6 +7,9 @@ Progress Helper .. versionadded:: 2.2 The ``progress`` helper was added in Symfony 2.2. +.. versionadded:: 2.3 + The ``setCurrent`` method was added in Symfony 2.3. + When executing longer-running commands, it may be helpful to show progress information, which updates as your command runs: @@ -28,6 +31,12 @@ pass it a total number of units, and advance the progress as your command execut $progress->finish(); +.. tip:: + + You can also set the current progress by calling the + :method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::setCurrent` + method. + The appearance of the progress output can be customized as well, with a number of different levels of verbosity. Each of these displays different possible items - like percentage completion, a moving progress bar, or current/total diff --git a/components/console/helpers/tablehelper.rst b/components/console/helpers/tablehelper.rst new file mode 100644 index 00000000000..1145dd7b603 --- /dev/null +++ b/components/console/helpers/tablehelper.rst @@ -0,0 +1,55 @@ +.. index:: + single: Console Helpers; Table Helper + +Table Helper +============ + +.. versionadded:: 2.3 + The ``table`` helper was added in Symfony 2.3. + +When building a console application it may be useful to display tabular data: + +.. image:: /images/components/console/table.png + +To display table, use the :class:`Symfony\\Component\\Console\\Helper\\TableHelper`, +set headers, rows and render:: + + $table = $app->getHelperSet()->get('table'); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + )) + ; + $table->render($output); + +The table layout can be customized as well. There are two ways to customize +table rendering: using named layouts or by customizing rendering options. + +Customize Table Layout using Named Layouts +------------------------------------------ + +The Table helper ships with two preconfigured table layouts: + +* ``TableHelper::LAYOUT_DEFAULT`` + +* ``TableHelper::LAYOUT_BORDERLESS`` + +Layout can be set using :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setLayout` method. + +Customize Table Layout using Rendering Options +---------------------------------------------- + +You can also control table rendering by setting custom rendering option values: + +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setPaddingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setHorizontalBorderChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVerticalBorderChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setCrossingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setCellHeaderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setCellRowFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setBorderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setPadType` diff --git a/components/console/index.rst b/components/console/index.rst index 4d0a12e4d70..c814942d018 100644 --- a/components/console/index.rst +++ b/components/console/index.rst @@ -7,5 +7,5 @@ Console introduction usage single_command_tool - + events helpers/index diff --git a/components/console/introduction.rst b/components/console/introduction.rst index d332b326835..776bf749c41 100755 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -163,16 +163,22 @@ You can also set these colors and options inside the tagname:: Verbosity Levels ~~~~~~~~~~~~~~~~ -The console has 3 levels of verbosity. These are defined in the +.. versionadded:: 2.3 + The ``VERBOSITY_VERY_VERBOSE`` and ``VERBOSITY_DEBUG`` constants were introduced + in version 2.3 + +The console has 5 levels of verbosity. These are defined in the :class:`Symfony\\Component\\Console\\Output\\OutputInterface`: -================================== =============================== -Option Value -================================== =============================== -OutputInterface::VERBOSITY_QUIET Do not output any messages -OutputInterface::VERBOSITY_NORMAL The default verbosity level -OutputInterface::VERBOSITY_VERBOSE Increased verbosity of messages -================================== =============================== +======================================= ================================== +Option Value +======================================= ================================== +OutputInterface::VERBOSITY_QUIET Do not output any messages +OutputInterface::VERBOSITY_NORMAL The default verbosity level +OutputInterface::VERBOSITY_VERBOSE Increased verbosity of messages +OutputInterface::VERBOSITY_VERY_VERBOSE Informative non essential messages +OutputInterface::VERBOSITY_DEBUG Debug messages +======================================= ================================== You can specify the quiet verbosity level with the ``--quiet`` or ``-q`` option. The ``--verbose`` or ``-v`` option is used when you want an increased @@ -181,12 +187,12 @@ level of verbosity. .. tip:: The full exception stacktrace is printed if the ``VERBOSITY_VERBOSE`` - level is used. + level or above is used. It is possible to print a message in a command for only a specific verbosity level. For example:: - if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) { + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $output->writeln(...); } diff --git a/components/console/usage.rst b/components/console/usage.rst index ac7f0b8a2c1..1a73c276ab5 100755 --- a/components/console/usage.rst +++ b/components/console/usage.rst @@ -77,6 +77,14 @@ with: $ php app/console list --verbose $ php app/console list -v +The verbose flag can optionally take a value between 1 (default) and 3 to +output even more verbose messages: + + $ php app/console list --verbose=2 + $ php app/console list -vv + $ php app/console list --verbose=3 + $ php app/console list -vvv + If you set the optional arguments to give your application a name and version:: $application = new Application('Acme Console Application', '1.2'); diff --git a/components/debug.rst b/components/debug.rst new file mode 100644 index 00000000000..a718ba6ee74 --- /dev/null +++ b/components/debug.rst @@ -0,0 +1,75 @@ +.. index:: + single: Debug + single: Components; Debug + +The Debug Component +=================== + + The Debug Component provides tools to ease debugging PHP code. + +.. versionadded:: 2.3 + The Debug Component is new to Symfony 2.3. Previously, the classes were + located in the ``HttpKernel`` component. + +Installation +------------ + +You can install the component in many different ways: + +* Use the official Git repository (https://github.com/symfony/Debug); +* :doc:`Install it via Composer ` (``symfony/debug`` on `Packagist`_). + +Usage +----- + +The Debug component provides several tools to help you debug PHP code. +Enabling them all is as easy as it can get:: + + use Symfony\Component\Debug\Debug; + + Debug::enable(); + +The :method:`Symfony\\Component\\Debug\\Debug::enable` method registers an +error handler and an exception handler. If the :doc:`ClassLoader component +` is available, a special class loader is also +registered. + +Read the following sections for more information about the different available +tools. + +.. caution:: + + You should never enable the debug tools in a production environment as + they might disclose sensitive information to the user. + +Enabling the Error Handler +-------------------------- + +The :class:`Symfony\\Component\\Debug\\ErrorHandler` class catches PHP errors +and converts them to exceptions (of class :phpclass:`ErrorException` or +:class:`Symfony\\Component\\Debug\\Exception\\FatalErrorException` for PHP +fatal errors):: + + use Symfony\Component\Debug\ErrorHandler; + + ErrorHandler::register(); + +Enabling the Exception Handler +------------------------------ + +The :class:`Symfony\\Component\\Debug\\ExceptionHandler` class catches +uncaught PHP exceptions and converts them to a nice PHP response. It is useful +in debug mode to replace the default PHP/XDebug output with something prettier +and more useful:: + + use Symfony\Component\Debug\ExceptionHandler; + + ExceptionHandler::register(); + +.. note:: + + If the :doc:`HttpFoundation component ` is + available, the handler uses a Symfony Response object; if not, it falls + back to a regular PHP response. + +.. _Packagist: https://packagist.org/packages/symfony/debug diff --git a/components/dependency_injection/index.rst b/components/dependency_injection/index.rst index e1d6b0eab8d..b3960eb8e00 100644 --- a/components/dependency_injection/index.rst +++ b/components/dependency_injection/index.rst @@ -14,5 +14,6 @@ configurators parentservices advanced + lazy_services workflow diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst new file mode 100644 index 00000000000..88ad0c51f59 --- /dev/null +++ b/components/dependency_injection/lazy_services.rst @@ -0,0 +1,92 @@ +.. index:: + single: Dependency Injection; Lazy Services + +Lazy Services +============= + +.. versionadded:: 2.3 + Lazy services were added in Symfony 2.3. + +Why Lazy Services? +------------------ + +In some cases, you may want to inject a service that is a bit heavy to instantiate, +but is not always used inside your object. For example, imagine you have +a ``NewsletterManager`` and you inject a ``mailer`` service into it. Only +a few methods on your ``NewsletterManager`` actually use the ``mailer``, +but even when you don't need it, a ``mailer`` service is always instantiated +in order to construct your ``NewsletterManager``. + +Configuring lazy services is one answer to this. With a lazy service, a "proxy" +of the ``mailer`` service is actually injected. It looks and acts just like +the ``mailer``, except that the ``mailer`` isn't actually instantiated until +you interact with the proxy in some way. + +Installation +------------ + +In order to use the lazy service instantiation, you will first need to install +the `ProxyManager bridge`_: + +.. code-block:: bash + + $ php composer.phar require symfony/proxy-manager-bridge:2.3.* + +.. note:: + + If you're using the full-stack framework, this package is not included + and needs to be added to ``composer.json`` and installed (which is what + the above command does). + +Configuration +------------- + +You can mark the service as ``lazy`` by manipulating its definition: + +.. configuration-block:: + + .. code-block:: yaml + + services: + foo: + class: Acme\Foo + lazy: true + + .. code-block:: xml + + + + .. code-block:: php + + $definition = new Definition('Acme\Foo'); + $definition->setLazy(true); + $container->setDefinition('foo', $definition); + +You can then require the service from the container:: + + $service = $container->get('foo'); + +At this point the retrieved ``$service`` should be a virtual `proxy`_ with +the same signature of the class representing the service. You can also inject +the service just like normal into other services. The object that's actually +injected will be the proxy. + +.. note:: + + If you don't install the `ProxyManager bridge`_, the container will just + skip over the ``lazy`` flag and simply instantiate the service as it would + normally do. + +The proxy gets initialized and the actual service is instantiated as soon +as you interact in any way with this object. + +Additional Resources +-------------------- + +You can read more about how proxies are instantiated, generated and initialized +in the `documentation of ProxyManager`_. + + +.. _`ProxyManager bridge`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bridge/ProxyManager +.. _`proxy`: http://en.wikipedia.org/wiki/Proxy_pattern +.. _`documentation of ProxyManager`: https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md \ No newline at end of file diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index e4457f8125a..cb5efdbe31d 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -200,6 +200,13 @@ and :phpclass:`DOMNode` objects: $html .= $domElement->ownerDocument->saveHTML($domElement); } + Or you can get the HTML of the first node using + :method:`Symfony\\Component\\DomCrawler\\Crawler::html`:: + + $html = $crawler->html(); + + The ``html`` method is new in Symfony 2.3. + Form and Link support ~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/event_dispatcher/container_aware_dispatcher.rst b/components/event_dispatcher/container_aware_dispatcher.rst index 0e2551da0d5..9417b15315e 100644 --- a/components/event_dispatcher/container_aware_dispatcher.rst +++ b/components/event_dispatcher/container_aware_dispatcher.rst @@ -4,9 +4,6 @@ The Container Aware Event Dispatcher ==================================== -.. versionadded:: 2.1 - This feature was moved into the EventDispatcher component in Symfony 2.1. - Introduction ------------ diff --git a/components/event_dispatcher/generic_event.rst b/components/event_dispatcher/generic_event.rst index 654c2e51236..9ac16d430b1 100644 --- a/components/event_dispatcher/generic_event.rst +++ b/components/event_dispatcher/generic_event.rst @@ -4,9 +4,6 @@ The Generic Event Object ======================== -.. versionadded:: 2.1 - The ``GenericEvent`` event class was added in Symfony 2.1 - The base :class:`Symfony\\Component\\EventDispatcher\\Event` class provided by the ``Event Dispatcher`` component is deliberately sparse to allow the creation of API specific event objects by inheritance using OOP. This allow for elegant and diff --git a/components/event_dispatcher/introduction.rst b/components/event_dispatcher/introduction.rst index 5cd4fe3af21..32553118aa8 100644 --- a/components/event_dispatcher/introduction.rst +++ b/components/event_dispatcher/introduction.rst @@ -440,9 +440,6 @@ which returns a boolean value:: EventDispatcher aware Events and Listeners ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``Event`` object contains a reference to the invoking dispatcher since Symfony 2.1 - The ``EventDispatcher`` always injects a reference to itself in the passed event object. This means that all listeners have direct access to the ``EventDispatcher`` object that notified the listener via the passed ``Event`` @@ -535,9 +532,6 @@ can be the way to go, especially for optional dependencies. Dispatcher Shortcuts ~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - ``EventDispatcher::dispatch()`` method returns the event since Symfony 2.1. - The :method:`EventDispatcher::dispatch` method always returns an :class:`Symfony\\Component\\EventDispatcher\\Event` object. This allows for various shortcuts. For example if one does not need @@ -575,9 +569,6 @@ and so on... Event Name Introspection ~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - Added event name to the ``Event`` object since Symfony 2.1 - Since the ``EventDispatcher`` already knows the name of the event when dispatching it, the event name is also injected into the :class:`Symfony\\Component\\EventDispatcher\\Event` objects, making it available diff --git a/components/filesystem.rst b/components/filesystem.rst index 70f085dd19a..aae513f8804 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -6,10 +6,6 @@ The Filesystem Component The Filesystem components provides basic utilities for the filesystem. -.. versionadded:: 2.1 - The Filesystem Component is new to Symfony 2.1. Previously, the ``Filesystem`` - class was located in the ``HttpKernel`` component. - Installation ------------ @@ -235,6 +231,24 @@ isAbsolutePath // return false $fs->isAbsolutePath('../dir'); +.. versionadded:: 2.3 + ``dumpFile`` is new in Symfony 2.3 + +dumpFile +~~~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::dumpFile` allows you to +dump contents to a file. It does this in an atomic manner: it writes a temporary +file first and then moves it to the new file location when it's finished. +This means that the user will always see either the complete old file or +complete new file (but never a partially-written file):: + + $fs->dumpFile('file.txt', 'Hello World'); + +The ``file.txt`` file contains ``Hello World`` now. + +A desired file mode can be passed as the third argument. + Error Handling -------------- diff --git a/components/finder.rst b/components/finder.rst index ab1cda43e82..d6016aaaee1 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -96,6 +96,14 @@ Exclude directories from matching with the $finder->in(__DIR__)->exclude('ruby'); +.. versionadded:: 2.3 + The :method:`Symfony\\Component\\Finder\\Finder::ignoreUnreadableDirs`` + method was added in Symfony 2.3. + +It's also possible to ignore directories that you don't have permission to read:: + + $finder->ignoreUnreadableDirs()->in(__DIR__); + As the Finder uses PHP iterators, you can pass any URL with a supported `protocol`_:: @@ -182,9 +190,6 @@ The ``notName()`` method excludes files matching a pattern:: File Contents ~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``contains()`` and ``notContains()`` methods were added in version 2.1 - Restrict files by contents with the :method:`Symfony\\Component\\Finder\\Finder::contains` method:: @@ -243,9 +248,6 @@ Restrict by a size range by chaining calls:: The comparison operator can be any of the following: ``>``, ``>=``, ``<``, ``<=``, ``==``, ``!=``. -.. versionadded:: 2.1 - The operator ``!=`` was added in version 2.1. - The target value may use magnitudes of kilobytes (``k``, ``ki``), megabytes (``m``, ``mi``), or gigabytes (``g``, ``gi``). Those suffixed with an ``i`` use the appropriate ``2**n`` version in accordance with the `IEC standard`_. @@ -296,9 +298,6 @@ instance. The file is excluded from the result set if the Closure returns Reading contents of returned files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - Method ``getContents()`` have been introduced in version 2.1. - The contents of returned files can be read with :method:`Symfony\\Component\\Finder\\SplFileInfo::getContents`:: diff --git a/components/http_foundation/index.rst b/components/http_foundation/index.rst index 9937c960776..348f8e50ca4 100644 --- a/components/http_foundation/index.rst +++ b/components/http_foundation/index.rst @@ -8,4 +8,5 @@ HTTP Foundation sessions session_configuration session_testing + session_php_bridge trusting_proxies diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 125fed672df..25aed5e4c49 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -387,9 +387,6 @@ To redirect the client to another URL, you can use the Streaming a Response ~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - Support for streamed responses was added in Symfony 2.1. - The :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse` class allows you to stream the Response back to the client. The response content is represented by a PHP callable instead of a string:: @@ -421,9 +418,6 @@ represented by a PHP callable instead of a string:: Serving Files ~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``makeDisposition`` method was added in Symfony 2.1. - When sending a file, you must add a ``Content-Disposition`` header to your response. While creating this header for basic file downloads is easy, using non-ASCII filenames is more involving. The @@ -480,10 +474,6 @@ right content and headers. A JSON response might look like this:: ))); $response->headers->set('Content-Type', 'application/json'); -.. versionadded:: 2.1 - The :class:`Symfony\\Component\\HttpFoundation\\JsonResponse` - class was added in Symfony 2.1. - There is also a helpful :class:`Symfony\\Component\\HttpFoundation\\JsonResponse` class, which can make this even easier:: @@ -503,7 +493,10 @@ to ``application/json``. as the outer-most array to ``JsonResponse`` and not an indexed array so that the final result is an object (e.g. ``{"object": "not inside an array"}``) instead of an array (e.g. ``[{"object": "inside an array"}]``). Read - the `OWASP guidelines`_ for more information. + the `OWASP guidelines`_ for more information. + + Only methods that respond to GET requests are vulnerable to XSSI 'JSON Hijacking'. + Methods responding to POST requests only remain unaffected. JSONP Callback ~~~~~~~~~~~~~~ diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 187f5d52c03..49c875354fe 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -221,7 +221,7 @@ PHP 5.4 compatibility ~~~~~~~~~~~~~~~~~~~~~ Since PHP 5.4.0, :phpclass:`SessionHandler` and :phpclass:`SessionHandlerInterface` -are available. Symfony 2.1 provides forward compatibility for the :phpclass:`SessionHandlerInterface` +are available. Symfony provides forward compatibility for the :phpclass:`SessionHandlerInterface` so it can be used under PHP 5.3. This greatly improves inter-operability with other libraries. diff --git a/components/http_foundation/session_php_bridge.rst b/components/http_foundation/session_php_bridge.rst new file mode 100644 index 00000000000..5b55417d983 --- /dev/null +++ b/components/http_foundation/session_php_bridge.rst @@ -0,0 +1,49 @@ +.. index:: + single: HTTP + single: HttpFoundation, Sessions + +Integrating with Legacy Sessions +================================ + +Sometimes it may be necessary to integrate Symfony into a legacy application +where you do not initially have the level of control you require. + +As stated elsewhere, Symfony Sessions are designed to replace the use of +PHP's native ``session_*()`` functions and use of the ``$_SESSION`` +superglobal. Additionally, it is mandatory for Symfony to start the session. + +However when there really are circumstances where this is not possible, you +can use a special storage bridge +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage` +which is designed to allow Symfony to work with a session started outside of +the Symfony Session framework. You are warned that things can interrupt this +use-case unless you are careful: for example the legacy application erases +``$_SESSION``. + +A typical use of this might look like this:: + + use Symfony\Component\HttpFoundation\Session\Session; + use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; + + // legacy application configures session + ini_set('session.save_handler', 'files'); + ini_set('session.save_path', '/tmp'); + session_start(); + + // Get Symfony to interface with this existing session + $session = new Session(new PhpBridgeSessionStorage()); + + // symfony will now interface with the existing PHP session + $session->start(); + +This will allow you to start using the Symfony Session API and allow migration +of your application to Symfony sessions. + +.. note:: + + Symfony sessions store data like attributes in special 'Bags' which use a + key in the ``$_SESSION`` superglobal. This means that a Symfony session + cannot access arbitrary keys in ``$_SESSION`` that may be set by the legacy + application, although all the ``$_SESSION`` contents will be saved when + the session is saved. + diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst index dbd855f4bf0..bce15b95589 100644 --- a/components/http_foundation/sessions.rst +++ b/components/http_foundation/sessions.rst @@ -9,10 +9,6 @@ The Symfony2 HttpFoundation Component has a very powerful and flexible session subsystem which is designed to provide session management through a simple object-oriented interface using a variety of session storage drivers. -.. versionadded:: 2.1 - The :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface` interface, - as well as a number of other changes, are new as of Symfony 2.1. - Sessions are used via the simple :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` implementation of :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface` interface. diff --git a/components/http_foundation/trusting_proxies.rst b/components/http_foundation/trusting_proxies.rst index b3023ddada8..1606e496156 100644 --- a/components/http_foundation/trusting_proxies.rst +++ b/components/http_foundation/trusting_proxies.rst @@ -12,13 +12,20 @@ stored in a ``X-Forwarded-Host`` header. Since HTTP headers can be spoofed, Symfony2 does *not* trust these proxy headers by default. If you are behind a proxy, you should manually whitelist -your proxy:: +your proxy. + +.. versionadded:: 2.3 + CIDR notation support was introduced, so you can whitelist whole + subnets (e.g. ``10.0.0.0/8``, ``fc00::/7``). + +.. code-block:: php use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); - // only trust proxy headers coming from this IP address - $request->setTrustedProxies(array('192.0.0.1')); + + // only trust proxy headers coming from this IP addresses + $request->setTrustedProxies(array('192.0.0.1', '10.0.0.0/8')); Configuring Header Names ------------------------ diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 0be23e83600..e8557f46060 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -451,9 +451,6 @@ method, which sends the headers and prints the ``Response`` content. 8) The ``kernel.terminate`` event ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``kernel.terminate`` event is new to Symfony 2.1. - **Typical Purposes**: To perform some "heavy" action after the response has been streamed to the user diff --git a/components/index.rst b/components/index.rst index 090152afa61..c7ab9d0e113 100644 --- a/components/index.rst +++ b/components/index.rst @@ -9,6 +9,7 @@ The Components config/index console/index css_selector + debug dom_crawler dependency_injection/index event_dispatcher/index @@ -16,7 +17,7 @@ The Components finder http_foundation/index http_kernel/index - locale + intl options_resolver process property_access/index diff --git a/components/intl.rst b/components/intl.rst new file mode 100644 index 00000000000..b19a3d20080 --- /dev/null +++ b/components/intl.rst @@ -0,0 +1,413 @@ +.. index:: + single: Intl + single: Components; Intl + +The Intl Component +================== + + A PHP replacement layer for the C `intl extension`_ that also provides + access to the localization data of the `ICU library`_. + +.. versionadded:: 2.3 + + The Intl component was added in Symfony 2.3. In earlier versions of Symfony, + you should use the Locale component instead. + +.. caution:: + + The replacement layer is limited to the locale "en". If you want to use + other locales, you should `install the intl extension`_ instead. + +Installation +------------ + +You can install the component in two different ways: + +* Using the official Git repository (https://github.com/symfony/Intl); +* :doc:`Install it via Composer` (``symfony/intl`` on `Packagist`_). + +If you install the component via Composer, the following classes and functions +of the intl extension will be automatically provided if the intl extension is +not loaded: + +* :phpclass:`Collator` +* :phpclass:`IntlDateFormatter` +* :phpclass:`Locale` +* :phpclass:`NumberFormatter` +* :phpfunction:`intl_error_name` +* :phpfunction:`intl_is_failure` +* :phpfunction:`intl_get_error_code` +* :phpfunction:`intl_get_error_message` + +When the intl extension is not available, the following classes are used to +replace the intl classes: + +* :class:`Symfony\\Component\\Intl\\Collator\\Collator` +* :class:`Symfony\\Component\\Intl\\DateFormatter\\IntlDateFormatter` +* :class:`Symfony\\Component\\Intl\\Locale\\Locale` +* :class:`Symfony\\Component\\Intl\\NumberFormatter\\NumberFormatter` +* :class:`Symfony\\Component\\Intl\\Globals\\IntlGlobals` + +Composer automatically exposes these classes in the global namespace. + +If you don't use Composer but the +:doc:`Symfony ClassLoader component`, you need to +expose them manually by adding the following lines to your autoload code:: + + if (!function_exists('intl_is_failure')) { + require '/path/to/Icu/Resources/stubs/functions.php'; + + $loader->registerPrefixFallback('/path/to/Icu/Resources/stubs'); + } + +.. sidebar:: ICU and Deployment Problems + + The intl extension internally uses the `ICU library`_ to obtain localization + data such as number formats in different languages, country names and more. + To make this data accessible to userland PHP libraries, Symfony2 ships a copy + in the `ICU component`_. + + Depending on the ICU version compiled with your intl extension, a matching + version of that component needs to be installed. It sounds complicated, + but usually Composer does this for you automatically: + + * 1.0.*: when the intl extension is not available + * 1.1.*: when intl is compiled with ICU 3.8 or higher + * 1.2.*: when intl is compiled with ICU 4.4 or higher + + These versions are important when you deploy your application to a **server with + a lower ICU version** than your development machines, because deployment will + fail if + + * the development machines are compiled with ICU 4.4 or higher, but the + server is compiled with a lower ICU version than 4.4; + * the intl extension is available on the development machines but not on + the server. + + For example, consider that your development machines ship ICU 4.8 and the server + ICU 4.2. When you run ``php composer.phar update`` on the development machine, version + 1.2.* of the ICU component will be installed. But after deploying the + application, ``php composer.phar install`` will fail with the following error: + + .. code-block:: bash + + $ php composer.phar install + Loading composer repositories with package information + Installing dependencies from lock file + Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - symfony/icu 1.2.x requires lib-icu >=4.4 -> the requested linked + library icu has the wrong version installed or is missing from your + system, make sure to have the extension providing it. + + The error tells you that the requested version of the ICU component, version + 1.2, is not compatible with PHP's ICU version 4.2. + + One solution to this problem is to run ``php composer.phar update`` instead of + ``php composer.phar install``. It is highly recommended **not** to do this. The + ``update`` command will install the latest versions of each Composer dependency + to your production server and potentially break the application. + + A better solution is to fix your composer.json to the version required by the + production server. First, determine the ICU version on the server: + + .. code-block:: bash + + $ php -i | grep ICU + ICU version => 4.2.1 + + Then fix the ICU component in your composer.json file to a matching version: + + .. code-block:: json + + "require: { + "symfony/icu": "1.1.*" + } + + Set the version to + + * "1.0.*" if the server does not have the intl extension installed; + * "1.1.*" if the server is compiled with ICU 4.2 or lower. + + Finally, run ``php composer.phar update symfony/icu`` on your development machine, test + extensively and deploy again. The installation of the dependencies will now + succeed. + +Writing and Reading Resource Bundles +------------------------------------ + +The :phpclass:`ResourceBundle` class is not currently supported by this component. +Instead, it includes a set of readers and writers for reading and writing +arrays (or array-like objects) from/to resource bundle files. The following +classes are supported: + +* `TextBundleWriter`_ +* `PhpBundleWriter`_ +* `BinaryBundleReader`_ +* `PhpBundleReader`_ +* `BufferedBundleReader`_ +* `StructuredBundleReader`_ + +Continue reading if you are interested in how to use these classes. Otherwise +skip this section and jump to `Accessing ICU Data`_. + +TextBundleWriter +~~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\Intl\\ResourceBundle\\Writer\\TextBundleWriter` +writes an array or an array-like object to a plain-text resource bundle. The +resulting .txt file can be converted to a binary .res file with the +:class:`Symfony\\Component\\Intl\\ResourceBundle\\Compiler\\BundleCompiler` +class:: + + use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter; + use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompiler; + + $writer = new TextBundleWriter(); + $writer->write('/path/to/bundle', 'en', array( + 'Data' => array( + 'entry1', + 'entry2', + // ... + ), + )); + + $compiler = new BundleCompiler(); + $compiler->compile('/path/to/bundle', '/path/to/binary/bundle'); + +The command "genrb" must be available for the +:class:`Symfony\\Component\\Intl\\ResourceBundle\\Compiler\\BundleCompiler` to +work. If the command is located in a non-standard location, you can pass its +path to the +:class:`Symfony\\Component\\Intl\\ResourceBundle\\Compiler\\BundleCompiler` +constructor. + +PhpBundleWriter +~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\Intl\\ResourceBundle\\Writer\\PhpBundleWriter` +writes an array or an array-like object to a .php resource bundle:: + + use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter; + + $writer = new PhpBundleWriter(); + $writer->write('/path/to/bundle', 'en', array( + 'Data' => array( + 'entry1', + 'entry2', + // ... + ), + )); + +BinaryBundleReader +~~~~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\BinaryBundleReader` +reads binary resource bundle files and returns an array or an array-like object. +This class currently only works with the `intl extension`_ installed:: + + use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + + $reader = new BinaryBundleReader(); + $data = $reader->read('/path/to/bundle', 'en'); + + echo $data['Data']['entry1']; + +PhpBundleReader +~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\PhpBundleReader` +reads resource bundles from .php files and returns an array or an array-like +object:: + + use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader; + + $reader = new PhpBundleReader(); + $data = $reader->read('/path/to/bundle', 'en'); + + echo $data['Data']['entry1']; + +BufferedBundleReader +~~~~~~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\BufferedBundleReader` +wraps another reader, but keeps the last N reads in a buffer, where N is a +buffer size passed to the constructor:: + + use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + use Symfony\Component\Intl\ResourceBundle\Reader\BufferedBundleReader; + + $reader = new BufferedBundleReader(new BinaryBundleReader(), 10); + + // actually reads the file + $data = $reader->read('/path/to/bundle', 'en'); + + // returns data from the buffer + $data = $reader->read('/path/to/bundle', 'en'); + + // actually reads the file + $data = $reader->read('/path/to/bundle', 'fr'); + +StructuredBundleReader +~~~~~~~~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReader` +wraps another reader and offers a +:method:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReaderInterface::readEntry` +method for reading an entry of the resource bundle without having to worry +whether array keys are set or not. If a path cannot be resolved, ``null`` is +returned:: + + use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader; + + $reader = new StructuredBundleReader(new BinaryBundleReader()); + + $data = $reader->read('/path/to/bundle', 'en'); + + // Produces an error if the key "Data" does not exist + echo $data['Data']['entry1']; + + // Returns null if the key "Data" does not exist + echo $reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1')); + +Additionally, the +:method:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReaderInterface::readEntry` +method resolves fallback locales. For example, the fallback locale of "en_GB" is +"en". For single-valued entries (strings, numbers etc.), the entry will be read +from the fallback locale if it cannot be found in the more specific locale. For +multi-valued entries (arrays), the values of the more specific and the fallback +locale will be merged. In order to suppress this behavior, the last parameter +``$fallback`` can be set to ``false``:: + + echo $reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1'), false); + +Accessing ICU Data +------------------ + +The ICU data is located in several "resource bundles". You can access a PHP +wrapper of these bundles through the static +:class:`Symfony\\Component\\Intl\\Intl` class. At the moment, the following +data is supported: + +* `Language and Script Names`_ +* `Country Names`_ +* `Locales`_ +* `Currencies`_ + +Language and Script Names +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The translations of language and script names can be found in the language +bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $languages = Intl::getLanguageBundle()->getLanguageNames(); + // => array('ab' => 'Abkhazian', ...) + + $language = Intl::getLanguageBundle()->getLanguageName('de'); + // => 'German' + + $language = Intl::getLanguageBundle()->getLanguageName('de', 'AT'); + // => 'Austrian German' + + $scripts = Intl::getLanguageBundle()->getScriptNames(); + // => array('Arab' => 'Arabic', ...) + + $script = Intl::getLanguageBundle()->getScriptName('Hans'); + // => 'Simplified' + +All methods accept the translation locale as the last, optional parameter, +which defaults to the current default locale:: + + $languages = Intl::getLanguageBundle()->getLanguageNames('de'); + // => array('ab' => 'Abchasisch', ...) + +Country Names +~~~~~~~~~~~~~ + +The translations of country names can be found in the region bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $countries = Intl::getRegionBundle()->getCountryNames(); + // => array('AF' => 'Afghanistan', ...) + + $country = Intl::getRegionBundle()->getCountryName('GB'); + // => 'United Kingdom' + +All methods accept the translation locale as the last, optional parameter, +which defaults to the current default locale:: + + $countries = Intl::getRegionBundle()->getCountryNames('de'); + // => array('AF' => 'Afghanistan', ...) + +Locales +~~~~~~~ + +The translations of locale names can be found in the locale bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $locales = Intl::getLocaleBundle()->getLocaleNames(); + // => array('af' => 'Afrikaans', ...) + + $locale = Intl::getLocaleBundle()->getLocaleName('zh_Hans_MO'); + // => 'Chinese (Simplified, Macau SAR China)' + +All methods accept the translation locale as the last, optional parameter, +which defaults to the current default locale:: + + $locales = Intl::getLocaleBundle()->getLocaleNames('de'); + // => array('af' => 'Afrikaans', ...) + +Currencies +~~~~~~~~~~ + +The translations of currency names and other currency-related information can +be found in the currency bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $currencies = Intl::getCurrencyBundle()->getCurrencyNames(); + // => array('AFN' => 'Afghan Afghani', ...) + + $currency = Intl::getCurrencyBundle()->getCurrencyName('INR'); + // => 'Indian Rupee' + + $symbol = Intl::getCurrencyBundle()->getCurrencySymbol('INR'); + // => '₹' + + $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits('INR'); + // => 2 + + $roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement('INR'); + // => 0 + +All methods (except for +:method:`Symfony\\Component\\Intl\\ResourceBundle\\CurrencyBundleInterface::getFractionDigits` +and +:method:`Symfony\\Component\\Intl\\ResourceBundle\\CurrencyBundleInterface::getRoundingIncrement`) +accept the translation locale as the last, optional parameter, which defaults +to the current default locale:: + + $currencies = Intl::getCurrencyBundle()->getCurrencyNames('de'); + // => array('AFN' => 'Afghanische Afghani', ...) + +That's all you need to know for now. Have fun coding! + +.. _Packagist: https://packagist.org/packages/symfony/intl +.. _ICU component: https://packagist.org/packages/symfony/icu +.. _intl extension: http://www.php.net/manual/en/book.intl.php +.. _install the intl extension: http://www.php.net/manual/en/intl.setup.php +.. _ICU library: http://site.icu-project.org/ diff --git a/components/locale.rst b/components/locale.rst deleted file mode 100644 index 526a97221c8..00000000000 --- a/components/locale.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. index:: - single: Locale - single: Components; Locale - -The Locale Component -==================== - - Locale component provides fallback code to handle cases when the ``intl`` extension is missing. - Additionally it extends the implementation of a native :phpclass:`Locale` class with several handy methods. - -Replacement for the following functions and classes is provided: - -* :phpfunction:`intl_is_failure` -* :phpfunction:`intl_get_error_code` -* :phpfunction:`intl_get_error_message` -* :phpclass:`Collator` -* :phpclass:`IntlDateFormatter` -* :phpclass:`Locale` -* :phpclass:`NumberFormatter` - -.. note:: - - Stub implementation only supports the ``en`` locale. - -Installation ------------- - -You can install the component in many different ways: - -* Use the official Git repository (https://github.com/symfony/Locale); -* :doc:`Install it via Composer` (``symfony/locale`` on `Packagist`_). - -Usage ------ - -Taking advantage of the fallback code includes requiring function stubs and adding class stubs to the autoloader. - -When using the ClassLoader component following code is sufficient to supplement missing ``intl`` extension: - -.. code-block:: php - - if (!function_exists('intl_get_error_code')) { - require __DIR__.'/path/to/src/Symfony/Component/Locale/Resources/stubs/functions.php'; - - $loader->registerPrefixFallbacks( - array(__DIR__.'/path/to/src/Symfony/Component/Locale/Resources/stubs') - ); - } - -:class:`Symfony\\Component\\Locale\\Locale` class enriches native :phpclass:`Locale` class with additional features: - -.. code-block:: php - - use Symfony\Component\Locale\Locale; - - // Get the country names for a locale or get all country codes - $countries = Locale::getDisplayCountries('pl'); - $countryCodes = Locale::getCountries(); - - // Get the language names for a locale or get all language codes - $languages = Locale::getDisplayLanguages('fr'); - $languageCodes = Locale::getLanguages(); - - // Get the locale names for a given code or get all locale codes - $locales = Locale::getDisplayLocales('en'); - $localeCodes = Locale::getLocales(); - - // Get ICU versions - $icuVersion = Locale::getIntlIcuVersion(); - $icuDataVersion = Locale::getIcuDataVersion(); - -.. _Packagist: https://packagist.org/packages/symfony/locale diff --git a/components/map.rst.inc b/components/map.rst.inc index c14df50277c..d360c65a706 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -16,12 +16,17 @@ * :doc:`/components/console/introduction` * :doc:`/components/console/usage` * :doc:`/components/console/single_command_tool` + * :doc:`/components/console/events` * :doc:`/components/console/helpers/index` * **CSS Selector** * :doc:`/components/css_selector` +* **Debug** + + * :doc:`/components/debug` + * :doc:`/components/dependency_injection/index` * :doc:`/components/dependency_injection/introduction` @@ -34,6 +39,7 @@ * :doc:`/components/dependency_injection/configurators` * :doc:`/components/dependency_injection/parentservices` * :doc:`/components/dependency_injection/advanced` + * :doc:`/components/dependency_injection/lazy_services` * :doc:`/components/dependency_injection/workflow` * **DOM Crawler** @@ -60,15 +66,16 @@ * :doc:`/components/http_foundation/sessions` * :doc:`/components/http_foundation/session_configuration` * :doc:`/components/http_foundation/session_testing` + * :doc:`/components/http_foundation/session_php_bridge` * :doc:`/components/http_foundation/trusting_proxies` * :doc:`/components/http_kernel/index` * :doc:`/components/http_kernel/introduction` -* **Locale** +* **Intl** - * :doc:`/components/locale` + * :doc:`/components/intl` * **Options Resolver** diff --git a/components/process.rst b/components/process.rst index dffec746df5..5f0c6243a78 100644 --- a/components/process.rst +++ b/components/process.rst @@ -63,15 +63,12 @@ anonymous function to the echo 'OUT > '.$buffer; } }); - -.. versionadded:: 2.1 - The non-blocking feature was added in 2.1. Running Processes Asynchronously -------------------------------- You can also start the subprocess and then let it run asynchronously, retrieving -output and the status in your main process whenever you need it. Use the +output and the status in your main process whenever you need it. Use the :method:`Symfony\\Component\\Process\\Process::start` method to start an asynchronous process, the :method:`Symfony\\Component\\Process\\Process::isRunning` method to check if the process is done and the @@ -79,21 +76,21 @@ to check if the process is done and the $process = new Process('ls -lsa'); $process->start(); - + while ($process->isRunning()) { // waiting for process to finish } echo $process->getOutput(); - + You can also wait for a process to end if you started it asynchronously and are done doing other stuff:: $process = new Process('ls -lsa'); $process->start(); - + // ... do other things - + $process->wait(function ($type, $buffer) { if (Process:ERR === $type) { echo 'ERR > '.$buffer; @@ -102,19 +99,31 @@ are done doing other stuff:: } }); +.. note:: + + The :method:`Symfony\\Component\\Process\\Process::wait` method is blocking, + which means that your code will halt at this line until the external + process is completed. + Stopping a Process ------------------ +.. versionadded:: 2.3 + The ``signal`` parameter of the ``stop`` method was added in Symfony 2.3. + Any asynchronous process can be stopped at any time with the :method:`Symfony\\Component\\Process\\Process::stop` method. This method takes -a timeout as its argument. Once the timeout is reached, the process is terminated. +two arguments : a timeout and a signal. Once the timeout is reached, the signal +is sent to the running process. The default signal sent to a process is ``SIGKILL``. +Please read the :ref:`signal documentation below` +to find out more about signal handling in the Process component:: $process = new Process('ls -lsa'); $process->start(); // ... do other things - $process->stop(3); + $process->stop(3, SIGINT); Executing PHP Code in Isolation ------------------------------- @@ -130,9 +139,6 @@ instead:: ); $process->run(); -.. versionadded:: 2.1 - The ``ProcessBuilder`` class was added in Symfony 2.1. - To make your code work better on all platforms, you might want to use the :class:`Symfony\\Component\\Process\\ProcessBuilder` class instead:: @@ -141,6 +147,34 @@ To make your code work better on all platforms, you might want to use the $builder = new ProcessBuilder(array('ls', '-lsa')); $builder->getProcess()->run(); +.. versionadded:: 2.3 + The :method:`ProcessBuilder::setPrefix` + method was added in Symfony 2.3. + +In case you are building a binary driver, you can use the +:method:`Symfony\\Component\\Process\\Process::setPrefix` method to prefix all +the generated process commands. + +The following example will generate two process commands for a tar binary +adapter:: + + use Symfony\Component\Process\ProcessBuilder; + + $builder = new ProcessBuilder(); + $builder->setPrefix('/usr/bin/tar'); + + // '/usr/bin/tar' '--list' '--file=archive.tar.gz' + echo $builder + ->setArguments(array('--list', '--file=archive.tar.gz')) + ->getProcess() + ->getCommandLine(); + + // '/usr/bin/tar' '-xzf' 'archive.tar.gz' + echo $builder + ->setArguments(array('-xzf', 'archive.tar.gz')) + ->getProcess() + ->getCommandLine(); + Process Timeout --------------- @@ -171,4 +205,61 @@ check regularly:: usleep(200000); } +.. _reference-process-signal: + +Process Signals +--------------- + +.. versionadded:: 2.3 + The ``signal`` method was added in Symfony 2.3. + +When running a program asynchronously, you can send it posix signals with the +:method:`Symfony\\Component\\Process\\Process::signal` method:: + + use Symfony\Component\Process\Process; + + $process = new Process('find / -name "rabbit"'); + $process->start(); + + // will send a SIGKILL to the process + $process->signal(SIGKILL); + +.. caution:: + + Due to some limitations in PHP, if you're using signals with the Process + component, you may have to prefix your commands with `exec`_. Please read + `Symfony Issue#5759`_ and `PHP Bug#39992`_ to understand why this is happening. + + POSIX signals are not available on Windows platforms, please refer to the + `PHP documentation`_ for available signals. + +Process Pid +----------- + +.. versionadded:: 2.3 + The ``getPid`` method was added in Symfony 2.3. + +You can access the `pid`_ of a running process with the +:method:`Symfony\\Component\\Process\\Process::getPid` method. + +.. code-block:: php + + use Symfony\Component\Process\Process; + + $process = new Process('/usr/bin/php worker.php'); + $process->start(); + + $pid = $process->getPid(); + +.. caution:: + + Due to some limitations in PHP, if you want to get the pid of a symfony Process, + you may have to prefix your commands with `exec`_. Please read + `Symfony Issue#5759`_ to understand why this is happening. + +.. _`Symfony Issue#5759`: https://github.com/symfony/symfony/issues/5759 +.. _`PHP Bug#39992`: https://bugs.php.net/bug.php?id=39992 +.. _`exec`: http://en.wikipedia.org/wiki/Exec_(operating_system) +.. _`pid`: http://en.wikipedia.org/wiki/Process_identifier +.. _`PHP Documentation`: http://php.net/manual/en/pcntl.constants.php .. _Packagist: https://packagist.org/packages/symfony/process diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index f4d4de5cb23..130d7d0f7f4 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -24,14 +24,18 @@ Usage ----- The entry point of this component is the -:method:`PropertyAccess::getPropertyAccessor` +:method:`PropertyAccess::createPropertyAccessor` factory. This factory will create a new instance of the :class:`Symfony\\Component\\PropertyAccess\\PropertyAccessor` class with the default configuration:: use Symfony\Component\PropertyAccess\PropertyAccess; - $accessor = PropertyAccess::getPropertyAccessor(); + $accessor = PropertyAccess::createPropertyAccessor(); + +.. versionadded:: 2.3 + Before Symfony 2.3, the :method:`Symfony\\Component\\PropertyAccess\\PropertyAccess::createPropertyAccessor` + was called ``getPropertyAccessor()``. Reading from Arrays ------------------- @@ -153,10 +157,10 @@ getters, this means that you can do something like this:: This will produce: ``He is an author`` -Magic Methods -~~~~~~~~~~~~~ +Magic ``__get()`` Method +~~~~~~~~~~~~~~~~~~~~~~~~ -At last, ``getValue`` can use the magic ``__get`` method too:: +The ``getValue`` method can also use the magic ``__get`` method:: // ... class Person @@ -175,6 +179,49 @@ At last, ``getValue`` can use the magic ``__get`` method too:: echo $accessor->getValue($person, 'Wouter'); // array(...) +Magic ``__call()`` Method +~~~~~~~~~~~~~~~~~~~~~~~~~ + +At last, ``getValue`` can use the magic ``__call`` method, but you need to +enable this feature by using :class:`Symfony\\Component\\PropertyAccess\\PropertyAccessorBuilder`:: + + // ... + class Person + { + private $children = array( + 'wouter' => array(...), + ); + + public function __call($name, $args) + { + $property = lcfirst(substr($name, 3)); + if ('get' === substr($name, 0, 3)) { + return isset($this->children[$property]) ? $this->children[$property] : null; + } elseif ('set' === substr($name, 0, 3)) { + $value = 1 == count($args) ? $args[0] : null; + $this->children[$property] = $value; + } + } + } + + $person = new Person(); + + // Enable magic __call + $accessor = PropertyAccess::getPropertyAccessorBuilder() + ->enableMagicCall() + ->getPropertyAccessor(); + + echo $accessor->getValue($person, 'wouter'); // array(...) + +.. versionadded:: 2.3 + The use of magic ``__call()`` method was added in Symfony 2.3. + +.. caution:: + + The ``__call`` feature is disabled by default, you can enable it by calling + :method:`PropertyAccessorBuilder::enableMagicCallEnabled` + see `Enable other Features`_. + Writing to Arrays ----------------- @@ -219,7 +266,7 @@ can use setters, the magic ``__set`` or properties to set values:: } $person = new Person(); - + $accessor->setValue($person, 'firstName', 'Wouter'); $accessor->setValue($person, 'lastName', 'de Jong'); $accessor->setValue($person, 'children', array(new Person())); @@ -228,6 +275,38 @@ can use setters, the magic ``__set`` or properties to set values:: echo $person->getLastName(); // 'de Jong' echo $person->children; // array(Person()); +You can also use ``__call`` to set values but you need to enable the feature, +see `Enable other Features`_. + + // ... + class Person + { + private $children = array(); + + public function __call($name, $args) + { + $property = lcfirst(substr($name, 3)); + if ('get' === substr($name, 0, 3)) { + return isset($this->children[$property]) ? $this->children[$property] : null; + } elseif ('set' === substr($name, 0, 3)) { + $value = 1 == count($args) ? $args[0] : null; + $this->children[$property] = $value; + } + } + + } + + $person = new Person(); + + // Enable magic __call + $accessor = PropertyAccess::getPropertyAccessorBuilder() + ->enableMagicCall() + ->getPropertyAccessor(); + + $accessor->setValue($person, 'wouter', array(...)); + + echo $person->getWouter() // array(...) + Mixing Objects and Arrays ------------------------- @@ -261,4 +340,37 @@ You can also mix objects and arrays:: echo 'Hello '.$accessor->getValue($person, 'children[0].firstName'); // 'Wouter' // equal to $person->getChildren()[0]->firstName +Enable other Features +~~~~~~~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\PropertyAccess\\PropertyAccessor` can be +configured to enable extra features. To do that you could use the +:class:`Symfony\\Component\\PropertyAccess\\PropertyAccessorBuilder`:: + + // ... + $accessorBuilder = PropertyAccess::getPropertyAccessorBuilder(); + + // Enable magic __call + $accessorBuilder->enableMagicCall(); + + // Disable magic __call + $accessorBuilder->disableMagicCall(); + + // Check if magic __call handling is enabled + $accessorBuilder->isMagicCallEnabled() // true or false + + // At the end get the configured property accessor + $accessor = $accessorBuilder->getPropertyAccessor(); + + // Or all in one + $accessor = PropertyAccess::getPropertyAccessorBuilder() + ->enableMagicCall() + ->getPropertyAccessor(); + +Or you can pass parameters directly to the constructor (not the recommended way):: + + // ... + $accessor = new PropertyAccessor(true) // this enable handling of magic __call + + .. _Packagist: https://packagist.org/packages/symfony/property-access diff --git a/components/serializer.rst b/components/serializer.rst index 91b0b0e9734..877bc396639 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -11,6 +11,9 @@ The Serializer Component In order to do so, the Serializer Component follows the following simple schema. +.. _component-serializer-encoders: +.. _component-serializer-normalizers: + .. image:: /images/components/serializer/serializer_workflow.png As you can see in the picture above, an array is used as a man in @@ -47,8 +50,8 @@ which Encoders and Normalizer are going to be available:: $serializer = new Serializer($normalizers, $encoders); -Serializing an object -~~~~~~~~~~~~~~~~~~~~~ +Serializing an Object +--------------------- For the sake of this example, assume the following class already exists in your project:: @@ -100,8 +103,31 @@ The first parameter of the :method:`Symfony\\Component\\Serializer\\Serializer:: is the object to be serialized and the second is used to choose the proper encoder, in this case :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`. +Ignoring Attributes when Serializing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + The :method:`GetSetMethodNormalizer::setIgnoredAttributes` + method was added in Symfony 2.3. + +As an option, there's a way to ignore attributes from the origin object when +serializing. To remove those attributes use the +:method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setIgnoredAttributes` +method on the normalizer definition:: + + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Encoder\JsonEncoder; + use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + + $normalizer = new GetSetMethodNormalizer(); + $normalizer->setIgnoredAttributes(array('age')); + $encoder = new JsonEncoder(); + + $serializer = new Serializer(array($normalizer), array($encoder)); + $serializer->serialize($person, 'json'); // Output: {"name":"foo"} + Deserializing an Object -~~~~~~~~~~~~~~~~~~~~~~~ +----------------------- Let's see now how to do the exactly the opposite. This time, the information of the `People` class would be encoded in XML format:: @@ -122,6 +148,39 @@ needs three parameters: 2. The name of the class this information will be decoded to 3. The encoder used to convert that information into an array +Using Camelized Method Names for Underscored Attributes +------------------------------------------------------- + +.. versionadded:: 2.3 + The :method:`GetSetMethodNormalizer::setCamelizedAttributes` + method was added in Symfony 2.3. + +Sometimes property names from the serialized content are underscored (e.g. +``first_name``). Normally, these attributes will use get/set methods like +``getFirst_name``, when ``getFirstName`` method is what you really want. To +change that behavior use the +:method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setCamelizedAttributes` +method on the normalizer definition:: + + $encoder = new JsonEncoder(); + $normalizer = new GetSetMethodNormalizer(); + $normalizer->setCamelizedAttributes(array('first_name')); + + $serializer = new Serializer(array($normalizer), array($encoder)); + + $json = <<deserialize($json, 'Acme\Person', 'json'); + +As a final result, the deserializer uses the ``first_name`` attribute as if +it were ``firstName`` and uses the ``getFirstName`` and ``setFirstName`` methods. + JMSSerializer ------------- diff --git a/components/yaml/introduction.rst b/components/yaml/introduction.rst index a40888c576c..176852b5822 100644 --- a/components/yaml/introduction.rst +++ b/components/yaml/introduction.rst @@ -140,20 +140,6 @@ error if something goes wrong by adding the filename to the message. must validate the input first. Passing a filename is deprecated in Symfony 2.2, and will be removed in Symfony 3.0. -Executing PHP Inside YAML Files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 2.1 - The ``Yaml::enablePhpParsing()`` method is new to Symfony 2.1. Prior to 2.1, - PHP was *always* executed when calling the ``parse()`` function. - -By default, if you include PHP inside a YAML file, it will not be parsed. -If you do want PHP to be parsed, you must call ``Yaml::enablePhpParsing()`` -before parsing the file to activate this mode. If you only want to allow -PHP code for a single YAML file, be sure to disable PHP parsing after parsing -the single file by calling ``Yaml::$enablePhpParsing = false;`` (``$enablePhpParsing`` -is a public property). - Writing YAML Files ~~~~~~~~~~~~~~~~~~ diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 337e802b475..319b6289504 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -134,6 +134,12 @@ The following classes and files have specific emplacements: | Unit and Functional Tests | ``Tests/`` | +------------------------------+-----------------------------+ +.. note:: + + When building a reusable bundle, model classes should be placed in the + ``Model`` namespace. See :doc:`/cookbook/doctrine/mapping_model_classes` for + how to handle the mapping with a compiler pass. + Classes ------- diff --git a/cookbook/bundles/extension.rst b/cookbook/bundles/extension.rst index 21f9d564615..3ea0fca18bb 100644 --- a/cookbook/bundles/extension.rst +++ b/cookbook/bundles/extension.rst @@ -509,9 +509,6 @@ For more details, see :doc:`/cookbook/bundles/prepend_extension`. Default Configuration Dump ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``config:dump-reference`` command was added in Symfony 2.1 - The ``config:dump-reference`` command allows a bundle's default configuration to be output to the console in yaml. diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index 57118b22b72..1f24c612b92 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -48,9 +48,8 @@ that refers to the bundle. 2.1 Remove bundle routing ~~~~~~~~~~~~~~~~~~~~~~~~~ -The routing for the AcmeDemoBundle can be found in -``app/config/routing_dev.yml``. The routes are ``_welcome``, ``_demo_secured`` -and ``_demo``. Remove all three of these entries. +The routing for the AcmeDemoBundle can be found in ``app/config/routing_dev.yml``. +Remove the ``_acme_demo`` entry at the bottom of this file. 2.2 Remove bundle configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -103,4 +102,4 @@ rely on the bundle you are about to remove. .. tip:: If a third party bundle relies on another bundle, you can find that bundle - mentioned in the ``composer.json`` file included in the bundle directory. \ No newline at end of file + mentioned in the ``composer.json`` file included in the bundle directory. diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index 3cedfb91438..f5c5c5a91ed 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -177,10 +177,9 @@ environment by using this code and changing the environment string. not the application should run in "debug mode". Regardless of the environment, a Symfony2 application can be run with debug mode set to ``true`` or ``false``. This affects many things in the application, such as whether - or not errors should be displayed or if cache files are dynamically rebuilt - on each request. Though not a requirement, debug mode is generally set - to ``true`` for the ``dev`` and ``test`` environments and ``false`` for - the ``prod`` environment. + or not the cache files are dynamically rebuilt on each request. Though not + a requirement, debug mode is generally set to ``true`` for the ``dev`` and + ``test`` environments and ``false`` for the ``prod`` environment. Internally, the value of the debug mode becomes the ``kernel.debug`` parameter used inside the :doc:`service container `. @@ -206,12 +205,15 @@ environment by using this code and changing the environment string. $container->loadFromExtension('doctrine', array( 'dbal' => array( 'logging' => '%kernel.debug%', - // ... ), // ... )); + As of Symfony 2.3, showing errors or not no longer depends on the debug + mode. You'll need to enable that in your front controller by calling + :method:`Symfony\\Component\\Debug\\Debug::enable`. + .. index:: single: Environments; Creating a new environment diff --git a/cookbook/configuration/front_controllers_and_kernel.rst b/cookbook/configuration/front_controllers_and_kernel.rst index 2066898592f..bdf3443bec9 100644 --- a/cookbook/configuration/front_controllers_and_kernel.rst +++ b/cookbook/configuration/front_controllers_and_kernel.rst @@ -44,7 +44,9 @@ to `decorate`_ the kernel with additional features. Examples include: * Configuring the autoloader or adding additional autoloading mechanisms; * Adding HTTP level caching by wrapping the kernel with an instance of - :ref:`AppCache`. + :ref:`AppCache`; +* Enabling (or skipping) the :doc:`ClassCache ` +* Enabling the :doc:`Debug Component `. The front controller can be chosen by requesting URLs like: diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 3bbbff1ad42..0b02f690f08 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -14,13 +14,6 @@ Symfony2 has a built-in solution for database session storage called To use it, you just need to change some parameters in ``config.yml`` (or the configuration format of your choice): -.. versionadded:: 2.1 - In Symfony2.1 the class and namespace are slightly modified. You can now - find the session storage classes in the `Session\\Storage` namespace: - ``Symfony\Component\HttpFoundation\Session\Storage``. Also - note that in Symfony2.1 you should configure ``handler_id`` not ``storage_id`` like in Symfony2.0. - Below, you'll notice that ``%session.storage.options%`` is not used anymore. - .. configuration-block:: .. code-block:: yaml @@ -196,16 +189,16 @@ For MSSQL, the statement might look like the following: .. code-block:: sql CREATE TABLE [dbo].[session]( - [session_id] [nvarchar](255) NOT NULL, - [session_value] [ntext] NOT NULL, + [session_id] [nvarchar](255) NOT NULL, + [session_value] [ntext] NOT NULL, [session_time] [int] NOT NULL, - PRIMARY KEY CLUSTERED( - [session_id] ASC - ) WITH ( - PAD_INDEX = OFF, - STATISTICS_NORECOMPUTE = OFF, - IGNORE_DUP_KEY = OFF, - ALLOW_ROW_LOCKS = ON, - ALLOW_PAGE_LOCKS = ON - ) ON [PRIMARY] + PRIMARY KEY CLUSTERED( + [session_id] ASC + ) WITH ( + PAD_INDEX = OFF, + STATISTICS_NORECOMPUTE = OFF, + IGNORE_DUP_KEY = OFF, + ALLOW_ROW_LOCKS = ON, + ALLOW_PAGE_LOCKS = ON + ) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] diff --git a/cookbook/console/sending_emails.rst b/cookbook/console/sending_emails.rst index 5bacf76955b..f6e7f867049 100644 --- a/cookbook/console/sending_emails.rst +++ b/cookbook/console/sending_emails.rst @@ -20,9 +20,6 @@ and per Command. Configuring the Request Context globally ---------------------------------------- -.. versionadded:: 2.1 - The ``host`` and ``scheme`` parameters are available since Symfony 2.1 - .. versionadded: 2.2 The ``base_url`` parameter is available since Symfony 2.2 diff --git a/cookbook/doctrine/file_uploads.rst b/cookbook/doctrine/file_uploads.rst index 34778c3c0aa..a18d47ddcf9 100644 --- a/cookbook/doctrine/file_uploads.rst +++ b/cookbook/doctrine/file_uploads.rst @@ -222,61 +222,34 @@ The following controller shows you how to handle the entire process:: // ... use Acme\DemoBundle\Entity\Document; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; + use Symfony\Component\HttpFoundation\Request; // ... /** * @Template() */ - public function uploadAction() + public function uploadAction(Request $request) { $document = new Document(); $form = $this->createFormBuilder($document) ->add('name') ->add('file') - ->getForm() - ; + ->getForm(); - if ($this->getRequest()->isMethod('POST')) { - $form->bind($this->getRequest()); - if ($form->isValid()) { - $em = $this->getDoctrine()->getManager(); + $form->handleRequest($request); - $em->persist($document); - $em->flush(); + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); - return $this->redirect($this->generateUrl(...)); - } + $em->persist($document); + $em->flush(); + + return $this->redirect($this->generateUrl(...)); } return array('form' => $form->createView()); } -.. note:: - - When writing the template, don't forget to set the ``enctype`` attribute: - - .. configuration-block:: - - .. code-block:: html+jinja - -

Upload File

- - - {{ form_widget(form) }} - - - - - .. code-block:: html+php - -

Upload File

- -
enctype($form) ?>> - widget($form) ?> - - -
- The previous controller will automatically persist the ``Document`` entity with the submitted name, but it will do nothing about the file and the ``path`` property will be blank. diff --git a/cookbook/doctrine/index.rst b/cookbook/doctrine/index.rst index 170a5f2d718..7f19ef7d4fe 100644 --- a/cookbook/doctrine/index.rst +++ b/cookbook/doctrine/index.rst @@ -12,4 +12,5 @@ Doctrine multiple_entity_managers custom_dql_functions resolve_target_entity + mapping_model_classes registration_form diff --git a/cookbook/doctrine/mapping_model_classes.rst b/cookbook/doctrine/mapping_model_classes.rst new file mode 100644 index 00000000000..48529cc2e26 --- /dev/null +++ b/cookbook/doctrine/mapping_model_classes.rst @@ -0,0 +1,64 @@ +.. index:: + single: Doctrine; Mapping Model classes + +How to provide model classes for several Doctrine implementations +================================================================= + +When building a bundle that could be used not only with Doctrine ORM but +also the CouchDB ODM, MongoDB ODM or PHPCR ODM, you should still only +write one model class. The Doctrine bundles provide a compiler pass to +register the mappings for your model classes. + +.. note:: + + For non-reusable bundles, the easiest option is to put your model classes + in the default locations: ``Entity`` for the Doctrine ORM or ``Document`` + for one of the ODMs. For reusable bundles, rather than duplicate model classes + just to get the auto mapping, use the compiler pass. + +.. versionadded:: 2.3 + The base mapping compiler pass was added in Symfony 2.3. The doctrine bundles + support it from DoctrineBundle >= 1.2.1, MongoDBBundle >= 3.0.0 + +In your bundle class, write the following code to register the compiler pass:: + + use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass; + use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass; + + class FOSUserBundle extends Bundle + { + public function build(ContainerBuilder $container) + { + parent::build($container); + // ... + + $modelDir = realpath(__DIR__.'/Resources/config/doctrine/model'); + $mappings = array( + $modelDir => 'FOS\UserBundle\Model', + ); + + $ormCompilerClass = 'Doctrine\Bundle\DoctrineBundle\DependencyInjection' + . '\Compiler\DoctrineOrmMappingsPass'; + if (class_exists($ormCompilerClass)) { + $container->addCompilerPass( + DoctrineOrmMappingsPass::createXmlMappingDriver( + $mappings, 'fos_user.backend_type_orm' + )); + } + + $mongoCompilerClass = 'Doctrine\Bundle\MongoDBBundle\DependencyInjection' + . '\Compiler\DoctrineMongoDBMappingsPass'; + if (class_exists($mongoCompilerClass)) { + $container->addCompilerPass( + DoctrineMongoDBMappingsPass::createXmlMappingDriver( + $mappings, 'fos_user.backend_type_mongodb' + )); + } + + // TODO: couch + } + } + +The compiler pass provides factory methods for all drivers provided by the +bundle: Annotations, XML, Yaml, PHP and StaticPHP for Doctrine ORM, the ODM +bundles sometimes do not have all of those drivers. diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index 9e735ee877a..3398137a46b 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -232,10 +232,10 @@ controller for displaying the registration form:: { public function registerAction() { - $form = $this->createForm( - new RegistrationType(), - new Registration() - ); + $registration = new Registration(); + $form = $this->createForm(new RegistrationType(), $registration, array( + 'action' => $this->generateUrl('account_create'), + )); return $this->render( 'AcmeAccountBundle:Account:register.html.twig', @@ -249,22 +249,19 @@ and its template: .. code-block:: html+jinja {# src/Acme/AccountBundle/Resources/views/Account/register.html.twig #} -
- {{ form_widget(form) }} - - -
+ {{ form(form) }} -Finally, create the controller which handles the form submission. This performs -the validation and saves the data into the database:: +Finally, create the controller (and the corresponding route ``account_create``) +which handles the form submission. This performs the validation and saves +the data into the database:: - public function createAction() + public function createAction(Request $request) { $em = $this->getDoctrine()->getEntityManager(); $form = $this->createForm(new RegistrationType(), new Registration()); - $form->bind($this->getRequest()); + $form->handleRequest($request); if ($form->isValid()) { $registration = $form->getData(); diff --git a/cookbook/doctrine/resolve_target_entity.rst b/cookbook/doctrine/resolve_target_entity.rst index 9fbe7b7176c..6d7439582ec 100644 --- a/cookbook/doctrine/resolve_target_entity.rst +++ b/cookbook/doctrine/resolve_target_entity.rst @@ -5,10 +5,6 @@ How to Define Relationships with Abstract Classes and Interfaces ================================================================ -.. versionadded:: 2.1 - The ResolveTargetEntityListener is new to Doctrine 2.2, which was first - packaged with Symfony 2.1. - One of the goals of bundles is to create discreet bundles of functionality that do not have many (if any) dependencies, allowing you to use that functionality in other applications without including unnecessary items. diff --git a/cookbook/form/data_transformers.rst b/cookbook/form/data_transformers.rst index fa72d1df22a..aaccbffa095 100644 --- a/cookbook/form/data_transformers.rst +++ b/cookbook/form/data_transformers.rst @@ -157,7 +157,7 @@ when creating your form. Later, you'll learn how you could create a custom Cool, you're done! Your user will be able to enter an issue number into the text field and it will be transformed back into an Issue object. This means -that, after a successful bind, the Form framework will pass a real Issue +that, after a successful submission, the Form framework will pass a real Issue object to ``Task::setIssue()`` instead of the issue number. If the issue isn't found, a form error will be created for that field and @@ -177,11 +177,6 @@ its error message can be controlled with the ``invalid_message`` field option. Model and View Transformers ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.1 - The names and method of the transformers were changed in Symfony 2.1. - ``prependNormTransformer`` became ``addModelTransformer`` and ``appendClientTransformer`` - became ``addViewTransformer``. - In the above example, the transformer was used as a "model" transformer. In fact, there are two different type of transformers and three different types of underlying data. @@ -201,7 +196,7 @@ used directly. 3) **View Data** - This is the format that's used to fill in the form fields themselves. It's also the format in which the user will submit the data. When -you call ``Form::bind($data)``, the ``$data`` is in the "view" data format. +you call ``Form::submit($data)``, the ``$data`` is in the "view" data format. The 2 different types of transformers help convert to and from each of these types of data: diff --git a/cookbook/form/direct_submit.rst b/cookbook/form/direct_submit.rst new file mode 100644 index 00000000000..2d68aeae774 --- /dev/null +++ b/cookbook/form/direct_submit.rst @@ -0,0 +1,120 @@ +.. index:: + single: Form; Form::submit() + +How to use the submit() Function to handle Form Submissions +=========================================================== + +.. versionadded:: + Before Symfony 2.3, the ``submit`` method was known as ``bind``. + +In Symfony 2.3, a new :method:`Symfony\Component\Form\FormInterface::handleRequest` +method was added, which makes handling form submissions easier than ever:: + + use Symfony\Component\HttpFoundation\Request; + // ... + + public function newAction(Request $request) + { + $form = $this->createFormBuilder() + // ... + ->getForm(); + + $form->handleRequest($request); + + if ($form->isValid()) { + // perform some action... + + return $this->redirect($this->generateUrl('task_success')); + } + + return $this->render('AcmeTaskBundle:Default:new.html.twig', array( + 'form' => $form->createView(), + )); + } + +.. tip:: + + To see more about this method, read :ref:`book-form-handling-form-submissions`. + +Calling Form::submit() manually +------------------------------- + +In some cases, you want better control over when exactly your form is submitted +and what data is passed to it. Instead of using the +:method:`Symfony\Component\Form\FormInterface::handleRequest` +method, pass the submitted data directly to +:method:`Symfony\Component\Form\FormInterface::submit`:: + + use Symfony\Component\HttpFoundation\Request; + // ... + + public function newAction(Request $request) + { + $form = $this->createFormBuilder() + // ... + ->getForm(); + + if ($request->isMethod('POST')) { + $form->submit($request->request->get($form->getName())); + + if ($form->isValid()) { + // perform some action... + + return $this->redirect($this->generateUrl('task_success')); + } + } + + return $this->render('AcmeTaskBundle:Default:new.html.twig', array( + 'form' => $form->createView(), + )); + } + +.. tip:: + + Forms consisting of nested fields expect an array in + :method:`Symfony\Component\Form\FormInterface::submit`. You can also submit + individual fields by calling :method:`Symfony\Component\Form\FormInterface::submit` + directly on the field:: + + $form->get('firstName')->submit('Fabien'); + +.. _cookbook-form-submit-request: + +Passing a Request to Form::submit() (deprecated) +------------------------------------------------ + +.. versionadded:: + Before Symfony 2.3, the ``submit`` method was known as ``bind``. + +Before Symfony 2.3, the :method:`Symfony\Component\Form\FormInterface::submit` +method accepted a :class:`Symfony\\Component\\HttpFoundation\\Request` object as +a convenient shortcut to the previous example:: + + use Symfony\Component\HttpFoundation\Request; + // ... + + public function newAction(Request $request) + { + $form = $this->createFormBuilder() + // ... + ->getForm(); + + if ($request->isMethod('POST')) { + $form->submit($request); + + if ($form->isValid()) { + // perform some action... + + return $this->redirect($this->generateUrl('task_success')); + } + } + + return $this->render('AcmeTaskBundle:Default:new.html.twig', array( + 'form' => $form->createView(), + )); + } + +Passing the :class:`Symfony\\Component\HttpFoundation\\Request` directly to +:method:`Symfony\\Component\\Form\\FormInterface::submit`` still works, but is +deprecated and will be removed in Symfony 3.0. You should use the method +:method:`Symfony\Component\Form\FormInterface::handleRequest` instead. diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index aede2db7105..28772dfe394 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -444,14 +444,18 @@ On a form, we can usually listen to the following events: * ``PRE_SET_DATA`` * ``POST_SET_DATA`` -* ``PRE_BIND`` -* ``BIND`` -* ``POST_BIND`` +* ``PRE_SUBMIT`` +* ``SUBMIT`` +* ``POST_SUBMIT`` -When listening to ``BIND`` and ``POST_BIND``, it's already "too late" to make -changes to the form. Fortunately, ``PRE_BIND`` is perfect for this. There +.. versionadded:: 2.3 + The events ``PRE_SUBMIT``, ``SUBMIT`` and ``POST_SUBMIT`` were added in + Symfony 2.3. Before, they were named ``PRE_BIND``, ``BIND`` and ``POST_BIND``. + +When listening to ``SUBMIT`` and ``POST_SUBMIT``, it's already "too late" to make +changes to the form. Fortunately, ``PRE_SUBMIT`` is perfect for this. There is, however, a big difference in what ``$event->getData()`` returns for each -of these events. Specifically, in ``PRE_BIND``, ``$event->getData()`` returns +of these events. Specifically, in ``PRE_SUBMIT``, ``$event->getData()`` returns the raw data submitted by the user. This can be used to get the ``SportMeetup`` id and retrieve it from the database, @@ -494,7 +498,7 @@ The subscriber would now look like:: public static function getSubscribedEvents() { return array( - FormEvents::PRE_BIND => 'preBind', + FormEvents::PRE_SUBMIT => 'preSubmit', FormEvents::PRE_SET_DATA => 'preSetData', ); } @@ -506,7 +510,7 @@ The subscriber would now look like:: { $meetup = $event->getData()->getMeetup(); - // Before binding the form, the "meetup" will be null + // Before SUBMITing the form, the "meetup" will be null if (null === $meetup) { return; } @@ -517,7 +521,7 @@ The subscriber would now look like:: $this->customizeForm($form, $positions); } - public function preBind(FormEvent $event) + public function preSubmit(FormEvent $event) { $data = $event->getData(); $id = $data['event']; @@ -616,6 +620,6 @@ set for every possible kind of sport that our users are registering for. One piece that may still be missing is the client-side updating of your form after the sport is selected. This should be handled by making an AJAX call -back to your application. In that controller, you can bind your form, but -instead of processing it, simply use the bound form to render the updated +back to your application. In that controller, you can submit your form, but +instead of processing it, simply use the submitted form to render the updated fields. The response from the AJAX call can then be used to update the view. diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index f8365de53c0..a0947e1521f 100755 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -172,12 +172,10 @@ In your controller, you'll now initialize a new instance of ``TaskType``:: $form = $this->createForm(new TaskType(), $task); - // process the form on POST - if ($request->isMethod('POST')) { - $form->bind($request); - if ($form->isValid()) { - // ... maybe do some form processing, like saving the Task and Tag objects - } + $form->handleRequest($request); + + if ($form->isValid()) { + // ... maybe do some form processing, like saving the Task and Tag objects } return $this->render('AcmeTaskBundle:Task:new.html.twig', array( @@ -200,7 +198,7 @@ zero tags when first created). {# ... #} -
+ {{ form_start(form) }} {# render the task's only field: description #} {{ form_row(form.description) }} @@ -211,10 +209,9 @@ zero tags when first created).
  • {{ form_row(tag.name) }}
  • {% endfor %} + {{ form_end(form) }} - {{ form_rest(form) }} - {# ... #} -
    + {# ... #} .. code-block:: html+php @@ -222,16 +219,17 @@ zero tags when first created). -
    + start($form) ?> + + row($form['description']) ?> +

    Tags

    • row($tag['name']) ?>
    - - rest($form) ?> -
    + end($form) ?> @@ -381,9 +379,6 @@ HTML contains the tag ``text`` input element with a name of ``task[tags][__name_ and id of ``task_tags___name___name``. The ``__name__`` is a little "placeholder", which you'll replace with a unique, incrementing number (e.g. ``task[tags][3][name]``). -.. versionadded:: 2.1 - The placeholder was changed from ``$$name$$`` to ``__name__`` in Symfony 2.1 - The actual code needed to make this all work can vary quite a bit, but here's one example: @@ -688,40 +683,38 @@ the relationship between the removed ``Tag`` and ``Task`` object. $editForm = $this->createForm(new TaskType(), $task); - if ($request->isMethod('POST')) { - $editForm->bind($this->getRequest()); + $editForm->handleRequest($request); - if ($editForm->isValid()) { + if ($editForm->isValid()) { - // filter $originalTags to contain tags no longer present - foreach ($task->getTags() as $tag) { - foreach ($originalTags as $key => $toDel) { - if ($toDel->getId() === $tag->getId()) { - unset($originalTags[$key]); - } + // filter $originalTags to contain tags no longer present + foreach ($task->getTags() as $tag) { + foreach ($originalTags as $key => $toDel) { + if ($toDel->getId() === $tag->getId()) { + unset($originalTags[$key]); } } + } - // remove the relationship between the tag and the Task - foreach ($originalTags as $tag) { - // remove the Task from the Tag - $tag->getTasks()->removeElement($task); + // remove the relationship between the tag and the Task + foreach ($originalTags as $tag) { + // remove the Task from the Tag + $tag->getTasks()->removeElement($task); - // if it were a ManyToOne relationship, remove the relationship like this - // $tag->setTask(null); + // if it were a ManyToOne relationship, remove the relationship like this + // $tag->setTask(null); - $em->persist($tag); + $em->persist($tag); - // if you wanted to delete the Tag entirely, you can also do that - // $em->remove($tag); - } + // if you wanted to delete the Tag entirely, you can also do that + // $em->remove($tag); + } - $em->persist($task); - $em->flush(); + $em->persist($task); + $em->flush(); - // redirect back to some edit page - return $this->redirect($this->generateUrl('task_edit', array('id' => $id))); - } + // redirect back to some edit page + return $this->redirect($this->generateUrl('task_edit', array('id' => $id))); } // render some form template diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index def162d82ea..ca01f56d11c 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -718,13 +718,9 @@ and customize the ``form_errors`` fragment. {% block form_errors %} {% spaceless %} {% if errors|length > 0 %} -
      +
        {% for error in errors %} -
      • {{ - error.messagePluralization is null - ? error.messageTemplate|trans(error.messageParameters, 'validators') - : error.messageTemplate|transchoice(error.messagePluralization, error.messageParameters, 'validators') - }}
      • +
      • {{ error.message }}
      • {% endfor %}
      {% endif %} @@ -735,28 +731,13 @@ and customize the ``form_errors`` fragment. -
        +
          -
        • getMessagePluralization()) { - echo $view['translator']->trans( - $error->getMessageTemplate(), - $error->getMessageParameters(), - 'validators' - ); - } else { - echo $view['translator']->transChoice( - $error->getMessageTemplate(), - $error->getMessagePluralization(), - $error->getMessageParameters(), - 'validators' - ); - }?>
        • +
        • getMessage() ?>
        - .. tip:: See :ref:`cookbook-form-theming-methods` for how to apply this customization. diff --git a/cookbook/form/index.rst b/cookbook/form/index.rst index 9086e7d6b3e..d148a76ac55 100644 --- a/cookbook/form/index.rst +++ b/cookbook/form/index.rst @@ -10,6 +10,7 @@ Form form_collections create_custom_field_type create_form_type_extension - use_virtuals_forms + inherit_data_option unit_testing use_empty_data + direct_submit diff --git a/cookbook/form/use_virtuals_forms.rst b/cookbook/form/inherit_data_option.rst similarity index 61% rename from cookbook/form/use_virtuals_forms.rst rename to cookbook/form/inherit_data_option.rst index 1c1d1b96bb5..20c421a6cf2 100644 --- a/cookbook/form/use_virtuals_forms.rst +++ b/cookbook/form/inherit_data_option.rst @@ -1,13 +1,15 @@ .. index:: - single: Form; Virtual forms + single: Form; The "inherit_data" option -How to use the Virtual Form Field Option -======================================== +How to Reduce Code Duplication with "inherit_data" +================================================== -The ``virtual`` form field option can be very useful when you have some -duplicated fields in different entities. +.. versionadded:: 2.3 + This ``inherit_data`` option was known as ``virtual`` before Symfony 2.3. -For example, imagine you have two entities, a ``Company`` and a ``Customer``:: +The ``inherit_data`` form field option can be very useful when you have some +duplicated fields in different entities. For example, imagine you have two +entities, a ``Company`` and a ``Customer``:: // src/Acme/HelloBundle/Entity/Company.php namespace Acme\HelloBundle\Entity; @@ -39,13 +41,10 @@ For example, imagine you have two entities, a ``Company`` and a ``Customer``:: private $country; } -Like you can see, each entity shares a few of the same fields: ``address``, +As you can see, each entity shares a few of the same fields: ``address``, ``zipcode``, ``city``, ``country``. -Now, you want to build two forms: one for a ``Company`` and the second for -a ``Customer``. - -Start by creating a very simple ``CompanyType`` and ``CustomerType``:: +Let's build two forms for these entities, ``CompanyType`` and ``CustomerType``:: // src/Acme/HelloBundle/Form/Type/CompanyType.php namespace Acme\HelloBundle\Form\Type; @@ -84,8 +83,9 @@ Start by creating a very simple ``CompanyType`` and ``CustomerType``:: } } -Now, to deal with the four duplicated fields. Here is a (simple) -location form type:: +Instead of including the duplicated fields ``address``, ``zipcode``, ``city`` +and ``country``in both of these forms, we will create a third form for that. +We will call this form simply ``LocationType``:: // src/Acme/HelloBundle/Form/Type/LocationType.php namespace Acme\HelloBundle\Form\Type; @@ -108,7 +108,7 @@ location form type:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'virtual' => true + 'inherit_data' => true )); } @@ -118,20 +118,25 @@ location form type:: } } -You don't *actually* have a location field in each of your entities, so you -can't directly link ``LocationType`` to ``CompanyType`` or ``CustomerType``. -But you absolutely want to have a dedicated form type to deal with location (remember, DRY!). +The location form has an interesting option set, namely ``inherit_data``. This +option lets the form inherit its data from its parent form. If embedded in +the company form, the fields of the location form will access the properties of +the ``Company`` instance. If embedded in the customer form, the fields will +access the properties of the ``Customer`` instance instead. Easy, eh? -The ``virtual`` form field option is the solution. +.. note:: -You can set the option ``'virtual' => true`` in the ``setDefaultOptions()`` method -of ``LocationType`` and directly start using it in the two original form types. + Instead of setting the ``inherit_data`` option inside ``LocationType``, you + can also (just like with any option) pass it in the third argument of + ``$builder->add()``. -Look at the result:: +Let's make this work by adding the location form to our two original forms:: - // CompanyType + // src/Acme/HelloBundle/Form/Type/CompanyType.php public function buildForm(FormBuilderInterface $builder, array $options) { + // ... + $builder->add('foo', new LocationType(), array( 'data_class' => 'Acme\HelloBundle\Entity\Company' )); @@ -139,25 +144,15 @@ Look at the result:: .. code-block:: php - // CustomerType + // src/Acme/HelloBundle/Form/Type/CustomerType.php public function buildForm(FormBuilderInterface $builder, array $options) { + // ... + $builder->add('bar', new LocationType(), array( 'data_class' => 'Acme\HelloBundle\Entity\Customer' )); } -With the virtual option set to false (default behavior), the Form Component -expects each underlying object to have a ``foo`` (or ``bar``) property that -is either some object or array which contains the four location fields. -Of course, you don't have this object/array in your entities and you don't want it! - -With the virtual option set to true, the Form component skips the ``foo`` (or ``bar``) -property, and instead "gets" and "sets" the 4 location fields directly -on the underlying object! - -.. note:: - - Instead of setting the ``virtual`` option inside ``LocationType``, you - can (just like with any options) also pass it in as an array option to - the third argument of ``$builder->add()``. +That's it! You have extracted duplicated field definitions to a separate +location form that you can reuse wherever you need it. diff --git a/cookbook/form/unit_testing.rst b/cookbook/form/unit_testing.rst index 56703f02915..8522f39efa9 100644 --- a/cookbook/form/unit_testing.rst +++ b/cookbook/form/unit_testing.rst @@ -17,14 +17,13 @@ is done in a real application. It is simple to bootstrap and you can trust the Symfony components enough to use them as a testing base. There is already a class that you can benefit from for simple FormTypes -testing: :class:`Symfony\\Component\\Form\\Tests\\Extension\\Core\\Type\\TypeTestCase`. -It is used to test the core types and you can use it to test your types too. +testing: :class:`Symfony\\Component\\Form\\Test\\TypeTestCase`. It is used to +test the core types and you can use it to test your types too. -.. note:: - - Depending on the way you installed your Symfony or Symfony Form Component - the tests may not be downloaded. Use the --prefer-source option with - composer if this is the case. +.. versionadded:: 2.3 + The ``TypeTestCase`` has moved to the ``Symfony\Component\Form\Test`` + namespace in 2.3. Previously, the class was located in + ``Symfony\Component\Form\Tests\Core\Extension\Type``. The Basics ---------- @@ -36,11 +35,11 @@ The simplest ``TypeTestCase`` implementation looks like the following:: use Acme\TestBundle\Form\Type\TestedType; use Acme\TestBundle\Model\TestObject; - use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; + use Symfony\Component\Form\Test\TypeTestCase; class TestedTypeTest extends TypeTestCase { - public function testBindValidData() + public function testSubmitValidData() { $formData = array( 'test' => 'test', @@ -53,7 +52,8 @@ The simplest ``TypeTestCase`` implementation looks like the following:: $object = new TestObject(); $object->fromArray($formData); - $form->bind($formData); + // submit the data to the form directly + $form->submit($formData); $this->assertTrue($form->isSynchronized()); $this->assertEquals($object, $form->getData()); @@ -80,7 +80,7 @@ This test checks that none of your data transformers used by the form failed. The :method:`Symfony\\Component\\Form\\FormInterface::isSynchronized`` method is only set to ``false`` if a data transformer throws an exception:: - $form->bind($formData); + $form->submit($formData); $this->assertTrue($form->isSynchronized()); .. note:: @@ -89,7 +89,7 @@ method is only set to ``false`` if a data transformer throws an exception:: active in the test case and it relies on validation configuration. Instead, unit test your custom constraints directly. -Next, verify the binding and mapping of the form. The test below +Next, verify the submission and mapping of the form. The test below checks if all the fields are correctly specified:: $this->assertEquals($object, $form->getData()); @@ -124,11 +124,11 @@ before creating the parent form:: use Acme\TestBundle\Form\Type\TestedType; use Acme\TestBundle\Model\TestObject; - use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; + use Symfony\Component\Form\Test\TypeTestCase; class TestedTypeTest extends TypeTestCase { - public function testBindValidData() + public function testSubmitValidData() { $this->factory->addType(new TestChildType()); @@ -160,7 +160,7 @@ on other extensions. You need add those extensions to the factory object:: use Acme\TestBundle\Form\Type\TestedType; use Acme\TestBundle\Model\TestObject; - use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; + use Symfony\Component\Form\Test\TypeTestCase; class TestedTypeTest extends TypeTestCase { @@ -201,7 +201,7 @@ a good opportunity to use them:: use Acme\TestBundle\Form\Type\TestedType; use Acme\TestBundle\Model\TestObject; - use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; + use Symfony\Component\Form\Test\TypeTestCase; class TestedTypeTest extends TypeTestCase { diff --git a/cookbook/form/use_empty_data.rst b/cookbook/form/use_empty_data.rst index 2824d7a0e9b..0b09ac3cf62 100644 --- a/cookbook/form/use_empty_data.rst +++ b/cookbook/form/use_empty_data.rst @@ -5,7 +5,7 @@ How to configure Empty Data for a Form Class ============================================ The ``empty_data`` option allows you to specify an empty data set for your -form class. This empty data set would be used if you bind your form, but +form class. This empty data set would be used if you submit your form, but haven't called ``setData()`` on your form or passed in data when you created you form. For example:: diff --git a/cookbook/index.rst b/cookbook/index.rst index a7d3a45e3d3..1622d75cdc4 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -12,6 +12,7 @@ The Cookbook form/index validation/index configuration/index + serializer service_container/index session/index bundles/index @@ -25,6 +26,7 @@ The Cookbook debugging event_dispatcher/index request/index + session/index profiler/index web_services/index symfony1 diff --git a/cookbook/logging/channels_handlers.rst b/cookbook/logging/channels_handlers.rst index 08161091004..18e99350d5d 100644 --- a/cookbook/logging/channels_handlers.rst +++ b/cookbook/logging/channels_handlers.rst @@ -4,10 +4,6 @@ How to log Messages to different Files ====================================== -.. versionadded:: 2.1 - The ability to specify channels for a specific handler was added to - the MonologBundle for Symfony 2.1. - The Symfony Standard Edition contains a bunch of channels for logging: ``doctrine``, ``event``, ``security`` and ``request``. Each channel corresponds to a logger service (``monolog.logger.XXX``) in the container and is injected to the diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 0bfc7b1c567..4bd561d7dac 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -59,6 +59,7 @@ * :doc:`/cookbook/doctrine/multiple_entity_managers` * :doc:`/cookbook/doctrine/custom_dql_functions` * :doc:`/cookbook/doctrine/resolve_target_entity` + * :doc:`/cookbook/doctrine/mapping_model_classes` * :doc:`/cookbook/doctrine/registration_form` * :doc:`/cookbook/email/index` @@ -84,9 +85,10 @@ * :doc:`/cookbook/form/form_collections` * :doc:`/cookbook/form/create_custom_field_type` * :doc:`/cookbook/form/create_form_type_extension` - * :doc:`/cookbook/form/use_virtuals_forms` + * :doc:`/cookbook/form/inherit_data_option` * :doc:`/cookbook/form/unit_testing` * :doc:`/cookbook/form/use_empty_data` + * :doc:`/cookbook/form/direct_submit` * (validation) :doc:`/cookbook/validation/custom_constraint` * (doctrine) :doc:`/cookbook/doctrine/file_uploads` @@ -104,6 +106,10 @@ * :doc:`/cookbook/request/mime_type` +* :doc:`/cookbook/session/index` + + * :doc:`/cookbook/session/php_bridge` + * :doc:`/cookbook/routing/index` * :doc:`/cookbook/routing/scheme` @@ -127,6 +133,10 @@ * :doc:`/cookbook/security/custom_authentication_provider` * :doc:`/cookbook/security/target_path` +* **Serializer** + + * :doc:`/cookbook/serializer` + * :doc:`/cookbook/service_container/index` * :doc:`/cookbook/service_container/event_listener` diff --git a/cookbook/routing/custom_route_loader.rst b/cookbook/routing/custom_route_loader.rst index 42ab3ad17ed..a3317411f82 100644 --- a/cookbook/routing/custom_route_loader.rst +++ b/cookbook/routing/custom_route_loader.rst @@ -35,10 +35,12 @@ and therefore have two important methods: :method:`Symfony\\Component\\Config\\Loader\\LoaderInterface::supports` and :method:`Symfony\\Component\\Config\\Loader\\LoaderInterface::load`. -Take these lines from ``routing.yml``: +Take these lines from the ``routing.yml`` in the AcmeDemoBundle of the Standard +Edition: .. code-block:: yaml + # src/Acme/DemoBundle/Resources/config/routing.yml _demo: resource: "@AcmeDemoBundle/Controller/DemoController.php" type: annotation diff --git a/cookbook/routing/method_parameters.rst b/cookbook/routing/method_parameters.rst index 5446b051f68..ecf152ecc47 100644 --- a/cookbook/routing/method_parameters.rst +++ b/cookbook/routing/method_parameters.rst @@ -76,36 +76,17 @@ Faking the Method with _method .. note:: - The ``_method`` functionality shown here is disabled by default in Symfony 2.2. - To enable it, you must call :method:`Request::enableHttpMethodParameterOverride ` - before you handle the request (e.g. in your front controller). + The ``_method`` functionality shown here is disabled by default in Symfony 2.2 + and enabled by default in Symfony 2.3. To control it in Symfony 2.2, you + must call :method:`Request::enableHttpMethodParameterOverride ` + before you handle the request (e.g. in your front controller). In Symfony + 2.3, use the :ref:`configuration-framework-http_method_override` option. Unfortunately, life isn't quite this simple, since most browsers do not support sending PUT and DELETE requests. Fortunately Symfony2 provides you with a simple way of working around this limitation. By including a ``_method`` parameter in the query string or parameters of an HTTP request, Symfony2 will -use this as the method when matching routes. This can be done easily in forms -with a hidden field. Suppose you have a form for editing a blog post: - -.. code-block:: html+jinja - -
        - - {{ form_widget(form) }} - -
        - -The submitted request will now match the ``blog_update`` route and the ``updateAction`` -will be used to process the form. - -Likewise the delete form could be changed to look like this: - -.. code-block:: html+jinja - -
        - - {{ form_widget(delete_form) }} - -
        - -It will then match the ``blog_delete`` route. +use this as the method when matching routes. Forms automatically include a +hidden field for this parameter if their submission method is not GET or POST. +See :ref:`the related chapter in the forms documentation` +for more information. diff --git a/cookbook/routing/service_container_parameters.rst b/cookbook/routing/service_container_parameters.rst index 8b6d100e264..15128f4c0b5 100644 --- a/cookbook/routing/service_container_parameters.rst +++ b/cookbook/routing/service_container_parameters.rst @@ -4,9 +4,6 @@ How to use Service Container Parameters in your Routes ====================================================== -.. versionadded:: 2.1 - The ability to use parameters in your routes was added in Symfony 2.1. - Sometimes you may find it useful to make some parts of your routes globally configurable. For instance, if you build an internationalized site, you'll probably start with one or two locales. Surely you'll diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index 3d19c16b7d8..4a80291d3ba 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -102,7 +102,7 @@ Creating an ACL, and adding an ACE { $comment = new Comment(); - // ... setup $form, and bind data + // ... setup $form, and submit data if ($form->isValid()) { $entityManager = $this->getDoctrine()->getManager(); diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index 677e69e8b72..6066b4afe6b 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -427,9 +427,6 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` Now that your services are defined, tell your security context about your factory in your bundle class: -.. versionadded:: 2.1 - Before 2.1, the factory below was added via ``security.yml`` instead. - .. code-block:: php // src/Acme/DemoBundle/AcmeDemoBundle.php diff --git a/cookbook/security/custom_provider.rst b/cookbook/security/custom_provider.rst index 6d1dbfc51b2..bf7a1faeefe 100644 --- a/cookbook/security/custom_provider.rst +++ b/cookbook/security/custom_provider.rst @@ -102,10 +102,6 @@ Let's see this in action:: } } -.. versionadded:: 2.1 - The ``EquatableInterface`` was added in Symfony 2.1. Use the ``equals()`` - method of the ``UserInterface`` in Symfony 2.0. - If you have more information about your users - like a "first name" - then you can add a ``firstName`` field to hold that data. diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 7d8bf138288..be9f36f897b 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -167,12 +167,6 @@ interface forces the class to implement the five following methods: For more details on each of these, see :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface`. -.. versionadded:: 2.1 - In Symfony 2.1, the ``equals`` method was removed from ``UserInterface``. - If you need to override the default implementation of comparison logic, - implement the new :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface` - interface and implement the ``isEqualTo`` method. - .. code-block:: php // src/Acme/UserBundle/Entity/User.php diff --git a/cookbook/security/form_login.rst b/cookbook/security/form_login.rst index 37b9f38fce1..b4e3ba01c28 100644 --- a/cookbook/security/form_login.rst +++ b/cookbook/security/form_login.rst @@ -180,10 +180,6 @@ this by setting ``use_referer`` to true (it defaults to false): ), )); -.. versionadded:: 2.1 - As of 2.1, if the referer is equal to the ``login_path`` option, the - user will be redirected to the ``default_target_path``. - Control the Redirect URL from inside the Form ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cookbook/security/securing_services.rst b/cookbook/security/securing_services.rst index 810b83fc9b6..17b39a8ef97 100644 --- a/cookbook/security/securing_services.rst +++ b/cookbook/security/securing_services.rst @@ -145,8 +145,8 @@ Securing Methods Using Annotations ---------------------------------- You can also secure method calls in any service with annotations by using the -optional `JMSSecurityExtraBundle`_ bundle. This bundle is included in the -Symfony2 Standard Distribution. +optional `JMSSecurityExtraBundle`_ bundle. This bundle is not included in the +Symfony2 Standard Distribution, but you can choose to install it. To enable the annotations functionality, :ref:`tag` the service you want to secure with the ``security.secure_service`` tag diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst new file mode 100644 index 00000000000..d4e9baeab00 --- /dev/null +++ b/cookbook/serializer.rst @@ -0,0 +1,111 @@ +.. index:: + single: Serializer + +How to use the Serializer +========================= + +Serializing and deserializing to and from objects and different formats (e.g. +JSON or XML) is a very complex topic. Symfony comes with a +:doc:`Serializer Component`, which gives you some +tools that you can leverage for your solution. + +In fact, before you start, get familiar with the serializer, normalizers +and encoders by reading the :doc:`Serializer Component`. +You should also check out the `JMSSerializerBundle`_, which expands on the +functionality offered by Symfony's core serializer. + +Activating the Serializer +------------------------- + +.. versionadded:: 2.3 + The Serializer has always existed in Symfony, but prior to Symfony 2.3, + you needed to build the ``serializer`` service yourself. + +The ``serializer`` service is not available by default. To turn it on, activate +it in your configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + # ... + serializer: + enabled: true + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + // ... + 'serializer' => array( + 'enabled' => true + ), + )); + +Adding Normalizers and Encoders +------------------------------- + +Once enabled, the ``serializer`` service will be available in the container +and will be loaded with two :ref:`encoders` +(:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and +:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) +but no :ref:`normalizers`, meaning you'll +need to load your own. + +You can load normalizers and/or encoders by tagging them as +:ref:`serializer.normalizer` and +:ref:`serializer.encoder`. It's also +possible to set the priority of the tag in order to decide the matching order. + +Here an example on how to load the load +the :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + get_set_method_normalizer: + class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer + tags: + - { name: serializer.normalizer } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition( + 'Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer' + )); + $definition->addTag('serializer.normalizer'); + $container->setDefinition('get_set_method_normalizer', $definition); + +.. note:: + + The :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` + is broken by design. As soon as you have a circular object graph, an + infinite loop is created when calling the getters. You're encouraged + to add your own normalizers that fit your use-case. + +.. _JMSSerializerBundle: http://jmsyst.com/bundles/JMSSerializerBundle \ No newline at end of file diff --git a/cookbook/service_container/scopes.rst b/cookbook/service_container/scopes.rst index aee6710636f..e779bc933b9 100644 --- a/cookbook/service_container/scopes.rst +++ b/cookbook/service_container/scopes.rst @@ -21,13 +21,15 @@ scopes: - ``prototype``: A new instance is created each time you request the service. -The FrameworkBundle also defines a third scope: ``request``. This scope is -tied to the request, meaning a new instance is created for each subrequest -and is unavailable outside the request (for instance in the CLI). +The +:class:`Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel` +also defines a third scope: ``request``. This scope is tied to the request, +meaning a new instance is created for each subrequest and is unavailable +outside the request (for instance in the CLI). Scopes add a constraint on the dependencies of a service: a service cannot depend on services from a narrower scope. For example, if you create a generic -``my_foo`` service, but try to inject the ``request`` component, you'll receive +``my_foo`` service, but try to inject the ``request`` service, you will receive a :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` when compiling the container. Read the sidebar below for more details. @@ -69,10 +71,74 @@ when compiling the container. Read the sidebar below for more details. A service can of course depend on a service from a wider scope without any issue. -Setting the Scope in the Definition ------------------------------------ +Using a Service from a narrower Scope +------------------------------------- + +If your service has a dependency on a scoped service (like the ``request``), +you have three ways to deal with it: + +* Use setter injection if the dependency is "synchronized"; this is the + recommended way and the best solution for the ``request`` instance as it is + synchronized with the ``request`` scope (see + :ref:`using-synchronized-service`). + +* Put your service in the same scope as the dependency (or a narrower one). If + you depend on the ``request`` service, this means putting your new service + in the ``request`` scope (see :ref:`changing-service-scope`); + +* Pass the entire container to your service and retrieve your dependency from + the container each time you need it to be sure you have the right instance + -- your service can live in the default ``container`` scope (see + :ref:`passing-container`); + +Each scenario is detailed in the following sections. + +.. _using-synchronized-service: + +Using a synchronized Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + Synchronized services are new in Symfony 2.3. + +Injecting the container or setting your service to a narrower scope have +drawbacks. For synchronized services (like the ``request``), using setter +injection is the best option as it has no drawbacks and everything works +without any special code in your service or in your definition:: + + // src/Acme/HelloBundle/Mail/Mailer.php + namespace Acme\HelloBundle\Mail; + + use Symfony\Component\HttpFoundation\Request; + + class Mailer + { + protected $request; -The scope of a service is set in the definition of the service: + public function setRequest(Request $request = null) + { + $this->request = $request; + } + + public function sendEmail() + { + if (null === $this->request) { + // throw an error? + } + + // ... do something using the request here + } + } + +Whenever the ``request`` scope is entered or left, the service container will +automatically call the ``setRequest()`` method with the current ``request`` +instance. + +You might have noticed that the ``setRequest()`` method accepts ``null`` as a +valid value for the ``request`` argument. That's because when leaving the +``request`` scope, the ``request`` instance can be ``null`` (for the master +request for instance). Of course, you should take care of this possibility in +your code. This should also be taken into account when declaring your service: .. configuration-block:: @@ -82,42 +148,117 @@ The scope of a service is set in the definition of the service: services: greeting_card_manager: class: Acme\HelloBundle\Mail\GreetingCardManager - scope: request + calls: + - [setRequest, ['@?request']] .. code-block:: xml - + + + + .. code-block:: php // src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\ContainerInterface; - $container->setDefinition( + $definition = $container->setDefinition( 'greeting_card_manager', new Definition('Acme\HelloBundle\Mail\GreetingCardManager') - )->setScope('request'); + ) + ->addMethodCall('setRequest', array( + new Reference('request', ContainerInterface::NULL_ON_INVALID_REFERENCE, false) + )); -If you don't specify the scope, it defaults to ``container``, which is what -you want most of the time. Unless your service depends on another service -that's scoped to a narrower scope (most commonly, the ``request`` service), -you probably don't need to set the scope. +.. tip:: -Using a Service from a narrower Scope -------------------------------------- + You can declare your own ``synchronized`` services very easily; here is + the declaration of the ``request`` service for reference: + + .. configuration-block:: + + .. code-block:: yaml -If your service depends on a scoped service, the best solution is to put -it in the same scope (or a narrower one). Usually, this means putting your -new service in the ``request`` scope. + services: + request: + scope: request + synthetic: true + synchronized: true -But this is not always possible (for instance, a twig extension must be in -the ``container`` scope as the Twig environment needs it as a dependency). -In these cases, you should pass the entire container into your service and -retrieve your dependency from the container each time you need it to be sure -you have the right instance:: + .. code-block:: xml + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\ContainerInterface; + + $definition = $container->setDefinition('request') + ->setScope('request') + ->setSynthetic(true) + ->setSynchronized(true); + +.. _changing-service-scope: + +Changing the Scope of your Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Changing the scope of a service should be done in its definition: + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/HelloBundle/Resources/config/services.yml + services: + greeting_card_manager: + class: Acme\HelloBundle\Mail\GreetingCardManager + scope: request + arguments: [@request] + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // src/Acme/HelloBundle/Resources/config/services.php + use Symfony\Component\DependencyInjection\Definition; + + $definition = $container->setDefinition( + 'greeting_card_manager', + new Definition( + 'Acme\HelloBundle\Mail\GreetingCardManager', + array(new Reference('request'), + )) + )->setScope('request'); + +.. _passing-container: + +Passing the Container as a Dependency of your Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting the scope to a narrower one is not always possible (for instance, a +twig extension must be in the ``container`` scope as the Twig environment +needs it as a dependency). In these cases, you can pass the entire container +into your service:: // src/Acme/HelloBundle/Mail/Mailer.php namespace Acme\HelloBundle\Mail; @@ -160,8 +301,7 @@ The service config for this class would look something like this: services: my_mailer: class: "%my_mailer.class%" - arguments: - - "@service_container" + arguments: ["@service_container"] # scope: container can be omitted as it is the default .. code-block:: xml @@ -195,10 +335,11 @@ The service config for this class would look something like this: .. note:: Injecting the whole container into a service is generally not a good - idea (only inject what you need). In some rare cases, it's necessary - when you have a service in the ``container`` scope that needs a service - in the ``request`` scope. + idea (only inject what you need). + +.. tip:: -If you define a controller as a service then you can get the ``Request`` object -without injecting the container by having it passed in as an argument of your -action method. See :ref:`book-controller-request-argument` for details. + If you define a controller as a service then you can get the ``Request`` + object without injecting the container by having it passed in as an + argument of your action method. See + :ref:`book-controller-request-argument` for details. diff --git a/cookbook/session/index.rst b/cookbook/session/index.rst index 687148dcd10..b05831f89ea 100644 --- a/cookbook/session/index.rst +++ b/cookbook/session/index.rst @@ -4,4 +4,5 @@ Sessions .. toctree:: :maxdepth: 2 + php_bridge proxy_examples diff --git a/cookbook/session/php_bridge.rst b/cookbook/session/php_bridge.rst new file mode 100644 index 00000000000..6783bf398e2 --- /dev/null +++ b/cookbook/session/php_bridge.rst @@ -0,0 +1,36 @@ +.. index:: + single: Sessions + +Bridge a legacy application with Symfony Sessions +------------------------------------------------- + +.. versionadded:: 2.3 + The ability to integrate with a legacy PHP session was added in Symfony 2.3. + +If you're integrating the Symfony full-stack Framework into a legacy application +that starts the session with ``session_start()``, you may still be able to +use Symfony's session management by using the PHP Bridge session. + +If the application has sets it's own PHP save handler, you can specify null +for the ``handler_id``: + +.. code-block:: yaml + + framework: + session: + storage_id: session.storage.php_bridge + handler_id: ~ + +Otherwise, if the problem is simply that you cannot avoid the application +starting the session with ``session_start()``, you can still make use of +a Symfony based session save handler by specifying the save handler as in +the example below: + +.. code-block:: yaml + + framework: + session: + storage_id: session.storage.php_bridge + handler_id: session.handler.native_file + +For more details, see :doc:`/components/http_foundation/session_php_bridge`. \ No newline at end of file diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index 02ac6651c38..04311ee17d8 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -79,11 +79,6 @@ The validator class is also simple, and only has one required method: ``validate The first parameter of the ``addViolation`` call is the error message to use for that violation. -.. versionadded:: 2.1 - The ``isValid`` method was renamed to ``validate`` in Symfony 2.1. The - ``setMessage`` method was also deprecated, in favor of calling ``addViolation`` - on the context. - Using the new Validator ----------------------- diff --git a/images/components/console/table.png b/images/components/console/table.png new file mode 100644 index 00000000000..ba1e3ae79b9 Binary files /dev/null and b/images/components/console/table.png differ diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index 30cffa72fb3..9ece99f9acb 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -105,7 +105,6 @@ a single ``Bundle`` class that describes it:: new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), - new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(), ); if (in_array($this->getEnvironment(), array('dev', 'test'))) { diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 7a13463e430..9c8a77a6b80 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -201,11 +201,22 @@ requested URL against some configured paths. By default, these paths file. When you're in the ``dev`` :ref:`environment` - indicated by the app_**dev**.php front controller - the ``app/config/routing_dev.yml`` configuration file is also loaded. In the Standard Edition, the routes to -these "demo" pages are placed in that file: +these "demo" pages are imported from this file: .. code-block:: yaml # app/config/routing_dev.yml + # ... + + # AcmeDemoBundle routes (to be removed) + _acme_demo: + resource: "@AcmeDemoBundle/Resources/config/routing.yml" + +This imports a ``routing.yml`` file that lives inside the AcmeDemoBundle: + +.. code-block:: yaml + + # src/Acme/DemoBundle/Resources/config/routing.yml _welcome: path: / defaults: { _controller: AcmeDemoBundle:Welcome:index } @@ -318,7 +329,8 @@ key: .. code-block:: yaml - # app/config/routing_dev.yml + # src/Acme/DemoBundle/Resources/config/routing.yml + # ... _demo: resource: "@AcmeDemoBundle/Controller/DemoController.php" type: annotation diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index 51a160204be..e1d840c0d1d 100755 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -207,28 +207,6 @@ Going to the ``http://localhost/app_dev.php/demo/secured/hello`` URL will automatically redirect you to the login form because this resource is protected by a ``firewall``. -You can also force the action to require a given role by using the ``@Secure`` -annotation on the controller:: - - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; - use JMS\SecurityExtraBundle\Annotation\Secure; - - /** - * @Route("/hello/admin/{name}", name="_demo_secured_hello_admin") - * @Secure(roles="ROLE_ADMIN") - * @Template() - */ - public function helloAdminAction($name) - { - return array('name' => $name); - } - -Now, log in as ``user`` (who does *not* have the ``ROLE_ADMIN`` role) and -from the secured hello page, click on the "Hello resource secured" link. -Symfony2 should return a 403 HTTP status code, indicating that the user -is "forbidden" from accessing that resource. - .. note:: The Symfony2 security layer is very flexible and comes with many different diff --git a/redirection_map b/redirection_map index 054ad65c416..925a47b6641 100644 --- a/redirection_map +++ b/redirection_map @@ -20,3 +20,4 @@ /components/routing /components/routing/introduction /cookbook/console/generating_urls /cookbook/console/sending_emails /components/yaml /components/yaml/introduction +/cookbook/form/use_virtuals_forms /cookbook/form/inherit_data_option diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index c21d97b95da..f98a46372a5 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -16,9 +16,9 @@ Configuration ------------- * `secret`_ +* `http_method_override`_ * `ide`_ * `test`_ -* `trust_proxy_headers`_ * `trusted_proxies`_ * `form`_ * enabled @@ -36,11 +36,14 @@ Configuration * `gc_probability`_ * `gc_maxlifetime`_ * `save_path`_ +* `serializer`_ + * :ref:`enabled` * `templating`_ * `assets_base_urls`_ * `assets_version`_ * `assets_version_format`_ * `profiler`_ + * `collect`_ * :ref:`enabled` secret @@ -53,6 +56,23 @@ it's used for generating the CSRF tokens, but it could be used in any other context where having a unique string is useful. It becomes the service container parameter named ``kernel.secret``. +.. _configuration-framework-http_method_override: + +http_method_override +~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + The ``http_method_override`` option is new in Symfony 2.3. + +**type**: ``Boolean`` **default**: ``true`` + +This determines whether the ``_method`` request parameter is used as the intended +HTTP method on POST requests. If enabled, the +:method:`Request::enableHttpMethodParameterOverride ` +gets called automatically. It becomes the service container parameter named +``kernel.http_method_override``. For more information, see +:doc:`/cookbook/routing/method_parameters`. + ide ~~~ @@ -102,42 +122,29 @@ trusted_proxies Configures the IP addresses that should be trusted as proxies. For more details, see :doc:`/components/http_foundation/trusting_proxies`. +.. versionadded:: 2.3 + CIDR notation support was introduced, so you can whitelist whole + subnets (e.g. ``10.0.0.0/8``, ``fc00::/7``). + .. configuration-block:: .. code-block:: yaml framework: - trusted_proxies: [192.0.0.1] + trusted_proxies: [192.0.0.1, 10.0.0.0/8] .. code-block:: xml - + .. code-block:: php $container->loadFromExtension('framework', array( - 'trusted_proxies' => array('192.0.0.1'), + 'trusted_proxies' => array('192.0.0.1', '10.0.0.0/8'), )); -trust_proxy_headers -~~~~~~~~~~~~~~~~~~~ - -.. caution:: - - The ``trust_proxy_headers`` option is deprecated and will be removed in - Symfony 2.3. See `trusted_proxies`_ and :doc:`/components/http_foundation/trusting_proxies` - for details on how to properly trust proxy data. - -**type**: ``Boolean`` - -Configures if HTTP headers (like ``HTTP_X_FORWARDED_FOR``, ``X_FORWARDED_PROTO``, and -``X_FORWARDED_HOST``) are trusted as an indication for an SSL connection. By default, it is -set to ``false`` and only SSL_HTTPS connections are indicated as secure. - -You should enable this setting if your application is behind a reverse proxy. - .. _reference-framework-form: form @@ -160,9 +167,6 @@ name which is defined in the ``php.ini`` with the ``session.name`` directive. cookie_lifetime ............... -.. versionadded:: 2.1 - This option was formerly known as ``lifetime`` - **type**: ``integer`` **default**: ``0`` This determines the lifetime of the session - in seconds. By default it will use @@ -171,9 +175,6 @@ This determines the lifetime of the session - in seconds. By default it will use cookie_path ........... -.. versionadded:: 2.1 - This option was formerly known as ``path`` - **type**: ``string`` **default**: ``/`` This determines the path to set in the session cookie. By default it will use ``/``. @@ -181,9 +182,6 @@ This determines the path to set in the session cookie. By default it will use `` cookie_domain ............. -.. versionadded:: 2.1 - This option was formerly known as ``domain`` - **type**: ``string`` **default**: ``''`` This determines the domain to set in the session cookie. By default it's blank, @@ -193,9 +191,6 @@ to the cookie specification. cookie_secure ............. -.. versionadded:: 2.1 - This option was formerly known as ``secure`` - **type**: ``Boolean`` **default**: ``false`` This determines whether cookies should only be sent over secure connections. @@ -203,9 +198,6 @@ This determines whether cookies should only be sent over secure connections. cookie_httponly ............... -.. versionadded:: 2.1 - This option was formerly known as ``httponly`` - **type**: ``Boolean`` **default**: ``false`` This determines whether cookies should only accessible through the HTTP protocol. @@ -216,9 +208,6 @@ through XSS attacks. gc_probability .............. -.. versionadded:: 2.1 - The ``gc_probability`` option is new in version 2.1 - **type**: ``integer`` **default**: ``1`` This defines the probability that the garbage collector (GC) process is started @@ -229,9 +218,6 @@ that the GC process will start on each request. gc_divisor .......... -.. versionadded:: 2.1 - The ``gc_divisor`` option is new in version 2.1 - **type**: ``integer`` **default**: ``100`` See `gc_probability`_. @@ -239,9 +225,6 @@ See `gc_probability`_. gc_maxlifetime .............. -.. versionadded:: 2.1 - The ``gc_maxlifetime`` option is new in version 2.1 - **type**: ``integer`` **default**: ``14400`` This determines the number of seconds after which data will be seen as "garbage" @@ -283,6 +266,22 @@ value to ``null``: ), )); +.. _configuration-framework-serializer: + +serializer +~~~~~~~~~~ + +.. _serializer.enabled: + +enabled +....... + +**type**: ``boolean`` **default**: ``false`` + +Whether to enable the ``serializer`` service or not in the service container. + +For more details, see :doc:`/cookbook/serializer`. + templating ~~~~~~~~~~ @@ -303,15 +302,6 @@ is `protocol-relative`_ (i.e. starts with `//`) it will be added to both collections. URLs starting with ``http://`` will only be added to the ``http`` collection. -.. versionadded:: 2.1 - Unlike most configuration blocks, successive values for ``assets_base_urls`` - will overwrite each other instead of being merged. This behavior was chosen - because developers will typically define base URL's for each environment. - Given that most projects tend to inherit configurations - (e.g. ``config_test.yml`` imports ``config_dev.yml``) and/or share a common - base configuration (i.e. ``config.yml``), merging could yield a set of base - URL's for multiple environments. - .. _ref-framework-assets-version: assets_version @@ -423,8 +413,25 @@ enabled **default**: ``true`` in the ``dev`` and ``test`` environments -The profiler can be disabled by setting this key to ``false``. In reality, -the profiler still exists, but the data collectors are not activated. +The profiler can be disabled by setting this key to ``false``. + +.. versionadded:: 2.3 + + The ``collect`` option is new in Symfony 2.3. Previously, when ``profiler.enabled`` + was false, the profiler *was* actually enabled, but the collectors were + disabled. Now the profiler and collectors can be controller independently. + +collect +....... + +**default**: ``true`` + +This option configures the way the profiler behaves when it is enabled. If set +to ``true``, the profiler collects data for all requests. If you want to only +collect information on-demand, you can set the ``collect`` flag to ``false`` +and activate the data collectors by hand:: + + $profiler->enable(); Full Default Configuration -------------------------- @@ -434,9 +441,8 @@ Full Default Configuration .. code-block:: yaml framework: - charset: ~ secret: ~ - trust_proxy_headers: false + http_method_override: true trusted_proxies: [] ide: ~ test: ~ @@ -461,8 +467,9 @@ Full Default Configuration # profiler configuration profiler: enabled: false + collect: true only_exceptions: false - only_master_requests: false + only_master_requests: false dsn: file:%kernel.cache_dir%/profiler username: password: @@ -489,8 +496,6 @@ Full Default Configuration # session configuration session: - # DEPRECATED! Session starts on demand - auto_start: false storage_id: session.storage.native handler_id: session.handler.native_file name: ~ @@ -504,20 +509,9 @@ Full Default Configuration gc_maxlifetime: ~ save_path: %kernel.cache_dir%/sessions - # DEPRECATED! Please use: cookie_lifetime - lifetime: ~ - - # DEPRECATED! Please use: cookie_path - path: ~ - - # DEPRECATED! Please use: cookie_domain - domain: ~ - - # DEPRECATED! Please use: cookie_secure - secure: ~ - - # DEPRECATED! Please use: cookie_httponly - httponly: ~ + # serializer configuration + serializer: + enabled: false # templating configuration templating: @@ -566,9 +560,4 @@ Full Default Configuration file_cache_dir: %kernel.cache_dir%/annotations debug: %kernel.debug% - -.. versionadded:: 2.1 - The ```framework.session.auto_start`` setting has been removed in Symfony2.1, - it will start on demand now. - .. _`protocol-relative`: http://tools.ietf.org/html/rfc3986#section-4.2 diff --git a/reference/configuration/kernel.rst b/reference/configuration/kernel.rst index 24566753ebb..2a8544aff22 100644 --- a/reference/configuration/kernel.rst +++ b/reference/configuration/kernel.rst @@ -17,10 +17,6 @@ Configuration * `Cache Directory`_ * `Log Directory`_ -.. versionadded:: 2.1 - The :method:`Symfony\\Component\\HttpKernel\\Kernel::getCharset` method is new - in Symfony 2.1 - Charset ~~~~~~~ diff --git a/reference/configuration/monolog.rst b/reference/configuration/monolog.rst index 266b4e56a8f..7b43558e2bb 100644 --- a/reference/configuration/monolog.rst +++ b/reference/configuration/monolog.rst @@ -52,6 +52,7 @@ Monolog Configuration Reference from_email: ~ to_email: ~ subject: ~ + mailer: ~ email_prototype: id: ~ # Required (when the email_prototype is used) method: ~ diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 9d208d605e2..6179c5dff9c 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -147,6 +147,12 @@ Each part will be explained in the next section. # by default, the login form *must* be a POST, not a GET post_only: true remember_me: false + + # by default, a session must exist before submitting an authentication request + # if false, then Request::hasPreviousSession is not called during authentication + # new in Symfony 2.3 + require_previous_session: true + remember_me: token_provider: name key: someS3cretKey diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 5977ce092fb..f6f860b5c22 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -31,14 +31,19 @@ TwigBundle Configuration Reference # set to service or leave blank type: ~ value: ~ - autoescape: ~ - base_template_class: ~ # Example: Twig_Template - cache: "%kernel.cache_dir%/twig" - charset: "%kernel.charset%" - debug: "%kernel.debug%" - strict_variables: ~ - auto_reload: ~ - optimizations: ~ + autoescape: ~ + + # The following were added in Symfony 2.3. + # See http://twig.sensiolabs.org/doc/recipes.html#using-the-template-name-to-set-the-default-escaping-strategy + autoescape_service: ~ # Example: @my_service + autoescape_service_method: ~ # use in combination with autoescape_service option + base_template_class: ~ # Example: Twig_Template + cache: "%kernel.cache_dir%/twig" + charset: "%kernel.charset%" + debug: "%kernel.debug%" + strict_variables: ~ + auto_reload: ~ + optimizations: ~ .. code-block:: xml diff --git a/reference/constraints.rst b/reference/constraints.rst index 1069b0ea3e0..d3d09d6fd58 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -14,17 +14,22 @@ Validation Constraints Reference constraints/Type constraints/Email - constraints/MinLength - constraints/MaxLength constraints/Length constraints/Url constraints/Regex constraints/Ip - constraints/Max - constraints/Min constraints/Range + constraints/EqualTo + constraints/NotEqualTo + constraints/IdenticalTo + constraints/NotIdenticalTo + constraints/LessThan + constraints/LessThanOrEqual + constraints/GreaterThan + constraints/GreaterThanOrEqual + constraints/Date constraints/DateTime constraints/Time @@ -42,6 +47,9 @@ Validation Constraints Reference constraints/CardScheme constraints/Luhn + constraints/Iban + constraints/Isbn + constraints/Issn constraints/Callback constraints/All diff --git a/reference/constraints/Collection.rst b/reference/constraints/Collection.rst index 4e709f1d9c0..a636041a2eb 100644 --- a/reference/constraints/Collection.rst +++ b/reference/constraints/Collection.rst @@ -163,8 +163,9 @@ the above example, the ``allowMissingFields`` option was set to true, meaning that if either of the ``personal_email`` or ``short_bio`` elements were missing from the ``$personalData`` property, no validation error would occur. -.. versionadded:: 2.1 - The ``Required`` and ``Optional`` constraints are new to Symfony 2.1. +.. versionadded:: 2.3 + The ``Required`` and ``Optional`` constraints were moved to the namespace + ``Symfony\Component\Validator\Constraints\`` in Symfony 2.3. Required and Optional Field Constraints ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -191,8 +192,8 @@ field is optional but must be a valid email if supplied, you can do the followin /** * @Assert\Collection( * fields={ - * "personal_email" = @Assert\Collection\Required({@Assert\NotBlank, @Assert\Email}), - * "alternate_email" = @Assert\Collection\Optional({@Assert\Email}), + * "personal_email" = @Assert\Required({@Assert\NotBlank, @Assert\Email}), + * "alternate_email" = @Assert\Optional(@Assert\Email), * } * ) */ @@ -217,8 +218,8 @@ field is optional but must be a valid email if supplied, you can do the followin { $metadata->addPropertyConstraint('profileData', new Assert\Collection(array( 'fields' => array( - 'personal_email' => new Assert\Collection\Required(array(new Assert\NotBlank(), new Assert\Email())), - 'alternate_email' => new Assert\Collection\Optional(array(new Assert\Email())), + 'personal_email' => new Assert\Required(array(new Assert\NotBlank(), new Assert\Email())), + 'alternate_email' => new Assert\Optional(new Assert\Email()), ), ))); } diff --git a/reference/constraints/Count.rst b/reference/constraints/Count.rst index 9c808e9576d..da8a93405da 100644 --- a/reference/constraints/Count.rst +++ b/reference/constraints/Count.rst @@ -4,9 +4,6 @@ Count Validates that a given collection's (i.e. an array or an object that implements Countable) element count is *between* some minimum and maximum value. -.. versionadded:: 2.1 - The Count constraint was added in Symfony 2.1. - +----------------+---------------------------------------------------------------------+ | Applies to | :ref:`property or method` | +----------------+---------------------------------------------------------------------+ diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index f0ea3098655..8df886b3fa8 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -107,9 +107,6 @@ check the validity of the MX record of the host of the given email. checkHost ~~~~~~~~~ -.. versionadded:: 2.1 - The ``checkHost`` option was added in Symfony 2.1 - **type**: ``Boolean`` **default**: ``false`` If true, then the :phpfunction:`checkdnsrr` PHP function will be used to diff --git a/reference/constraints/EqualTo.rst b/reference/constraints/EqualTo.rst new file mode 100644 index 00000000000..f698223e721 --- /dev/null +++ b/reference/constraints/EqualTo.rst @@ -0,0 +1,100 @@ +EqualTo +======= + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is equal to another value, defined in the options. To +force that a value is *not* equal, see :doc:`/reference/constraints/NotEqualTo`. + +.. caution:: + + This constraint compares using ``==``, so ``3`` and ``"3"`` are considered + equal. Use :doc:`/reference/constraints/IdenticalTo` to compare with + ``===``. + ++----------------+-----------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+-----------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+-----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\EqualTo` | ++----------------+-----------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\EqualToValidator` | ++----------------+-----------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is equal to +``20``, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - EqualTo: + value: 20 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\EqualTo( + * value = 20 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\EqualTo(array( + 'value' => 20, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be equal to {{ compared_value }}`` + +This is the message that will be shown if the value is not equal. diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst new file mode 100644 index 00000000000..b3f4f42df79 --- /dev/null +++ b/reference/constraints/GreaterThan.rst @@ -0,0 +1,97 @@ +GreaterThan +=========== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is greater than another value, defined in the options. To +force that a value is greater than or equal to another value, see +:doc:`/reference/constraints/GreaterThanOrEqual`. To force a value is less +than another value, see :doc:`/reference/constraints/LessThan`. + ++----------------+---------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+---------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+---------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThan` | ++----------------+---------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThanValidator` | ++----------------+---------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is greater than +``18``, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - GreaterThan: + value: 18 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\GreaterThan( + * value = 18 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\GreaterThan(array( + 'value' => 18, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be greater than {{ compared_value }}`` + +This is the message that will be shown if the value is not greather than the +comparison value. diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst new file mode 100644 index 00000000000..cb71925f8e4 --- /dev/null +++ b/reference/constraints/GreaterThanOrEqual.rst @@ -0,0 +1,96 @@ +GreaterThanOrEqual +================== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is greater than or equal to another value, defined in +the options. To force that a value is greater than another value, see +:doc:`/reference/constraints/GreaterThan`. + ++----------------+----------------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+----------------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+----------------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqual` | ++----------------+----------------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\GreaterThanOrEqualValidator` | ++----------------+----------------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is greater than +or equal to ``18``, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - GreaterThanOrEqual: + value: 18 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\GreaterThanOrEqual( + * value = 18 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\GreaterThanOrEqual(array( + 'value' => 18, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be greater than or equal to {{ compared_value }}`` + +This is the message that will be shown if the value is not greater than or equal +to the comparison value. diff --git a/reference/constraints/Iban.rst b/reference/constraints/Iban.rst new file mode 100644 index 00000000000..9f52ee766e6 --- /dev/null +++ b/reference/constraints/Iban.rst @@ -0,0 +1,95 @@ +Iban +==== + +.. versionadded:: 2.3 + The Iban constraint was added in Symfony 2.3. + +This constraint is used to ensure that a bank account number has the proper format of +an `International Bank Account Number (IBAN)`_. IBAN is an internationally agreed means +of identifying bank accounts across national borders with a reduced risk of propagating +transcription errors. + ++----------------+-----------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+-----------------------------------------------------------------------+ +| Options | - `message`_ | ++----------------+-----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Iban` | ++----------------+-----------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\IbanValidator` | ++----------------+-----------------------------------------------------------------------+ + +Basic Usage +----------- + +To use the Iban validator, simply apply it to a property on an object that +will contain an International Bank Account Number. + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/SubscriptionBundle/Resources/config/validation.yml + Acme\SubscriptionBundle\Entity\Transaction: + properties: + bankAccountNumber: + - Iban: + message: This is not a valid International Bank Account Number (IBAN). + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php-annotations + + // src/Acme/SubscriptionBundle/Entity/Transaction.php + namespace Acme\SubscriptionBundle\Entity\Transaction; + + use Symfony\Component\Validator\Constraints as Assert; + + class Transaction + { + /** + * @Assert\Iban(message = "This is not a valid International Bank Account Number (IBAN).") + */ + protected $bankAccountNumber; + } + + .. code-block:: php + + // src/Acme/SubscriptionBundle/Entity/Transaction.php + namespace Acme\SubscriptionBundle\Entity\Transaction; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Transaction + { + protected $bankAccountNumber; + + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('bankAccountNumber', new Assert\Iban(array( + 'message' => 'This is not a valid International Bank Account Number (IBAN).', + ))); + } + } + +Available Options +----------------- + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This is not a valid International Bank Account Number (IBAN).`` + +The default message supplied when the value does not pass the Iban check. + +.. _`International Bank Account Number (IBAN)`: http://en.wikipedia.org/wiki/International_Bank_Account_Number diff --git a/reference/constraints/IdenticalTo.rst b/reference/constraints/IdenticalTo.rst new file mode 100644 index 00000000000..9c9dbaba783 --- /dev/null +++ b/reference/constraints/IdenticalTo.rst @@ -0,0 +1,101 @@ +IdenticalTo +=========== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is identical to another value, defined in the options. +To force that a value is *not* identical, see +:doc:`/reference/constraints/NotIdenticalTo`. + +.. caution:: + + This constraint compares using ``===``, so ``3`` and ``"3"`` are *not* + considered equal. Use :doc:`/reference/constraints/EqualTo` to compare + with ``==``. + ++----------------+--------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+--------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+--------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\IdenticalTo` | ++----------------+--------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\IdenticalToValidator`| ++----------------+--------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is equal to +``20`` and an integer, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - IdenticalTo: + value: 20 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\IdenticalTo( + * value = 20 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\IdenticalTo(array( + 'value' => 20, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be identical to {{ compared_value_type }} {{ compared_value }}`` + +This is the message that will be shown if the value is not identical. diff --git a/reference/constraints/Image.rst b/reference/constraints/Image.rst index ca90c6476c7..180397330a3 100644 --- a/reference/constraints/Image.rst +++ b/reference/constraints/Image.rst @@ -156,9 +156,6 @@ mimeTypesMessage **type**: ``string`` **default**: ``This file is not a valid image`` -.. versionadded:: 2.1 - All of the min/max width/height options are new to Symfony 2.1. - minWidth ~~~~~~~~ diff --git a/reference/constraints/Isbn.rst b/reference/constraints/Isbn.rst new file mode 100644 index 00000000000..48aa7ab9744 --- /dev/null +++ b/reference/constraints/Isbn.rst @@ -0,0 +1,136 @@ +Isbn +==== + +.. versionadded:: 2.3 + The Isbn constraint was added in Symfony 2.3. + +This constraint validates that an ISBN (International Standard Book Numbers) +number is either a valid ISBN-10, a valid ISBN-13 or both. + ++----------------+----------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+----------------------------------------------------------------------+ +| Options | - `isbn10`_ | +| | - `isbn13`_ | +| | - `isbn10Message`_ | +| | - `isbn13Message`_ | +| | - `bothIsbnMessage`_ | ++----------------+----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Isbn` | ++----------------+----------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\IsbnValidator` | ++----------------+----------------------------------------------------------------------+ + +Basic Usage +----------- + +To use the ``Isbn`` validator, simply apply it to a property or method +on an object that will contain a ISBN number. + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/BookcaseBunlde/Resources/config/validation.yml + Acme\BookcaseBunlde\Entity\Book: + properties: + isbn: + - Isbn: + isbn10: true + isbn13: true + bothIsbnMessage: This value is neither a valid ISBN-10 nor a valid ISBN-13. + + .. code-block:: php-annotations + + // src/Acme/BookcaseBunlde/Entity/Book.php + use Symfony\Component\Validator\Constraints as Assert; + + class Book + { + /** + * @Assert\Isbn( + * isbn10 = true, + * isbn13 = true, + * bothIsbnMessage = "This value is neither a valid ISBN-10 nor a valid ISBN-13." + * ) + */ + protected $isbn; + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/BookcaseBunlde/Entity/Book.php + namespace Acme\BookcaseBunlde\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Book + { + protected $isbn; + + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('isbn', new Assert\Isbn(array( + 'isbn10' => true, + 'isbn13' => true, + 'bothIsbnMessage' => 'This value is neither a valid ISBN-10 nor a valid ISBN-13.' + ))); + } + } + +Available Options +----------------- + +isbn10 +~~~~~~ + +**type**: ``boolean`` [:ref:`default option`] + +If this required option is set to ``true`` the constraint will check if the +code is a valid ISBN-10 code. + +isbn13 +~~~~~~ + +**type**: ``boolean`` [:ref:`default option`] + +If this required option is set to ``true`` the constraint will check if the +code is a valid ISBN-13 code. + +isbn10Message +~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is not a valid ISBN-10.`` + +The message that will be shown if the `isbn10`_ option is true and the given +value does not pass the ISBN-10 check. + +isbn13Message +~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is not a valid ISBN-13.`` + +The message that will be shown if the `isbn13`_ option is true and the given +value does not pass the ISBN-13 check. + +bothIsbnMessage +~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is neither a valid ISBN-10 nor a valid ISBN-13.`` + +The message that will be shown if both the `isbn10`_ and `isbn13`_ options +are true and the given value does not pass the ISBN-13 nor the ISBN-13 check. diff --git a/reference/constraints/Issn.rst b/reference/constraints/Issn.rst new file mode 100644 index 00000000000..62e7ef64323 --- /dev/null +++ b/reference/constraints/Issn.rst @@ -0,0 +1,101 @@ +Issn +==== + +.. versionadded:: 2.3 + The ISSN validation is new in Symfony 2.3. + +Validates that a value is a valid `ISSN`_. + ++----------------+-----------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+-----------------------------------------------------------------------+ +| Options | - `message`_ | +| | - `caseSensitive`_ | +| | - `requireHyphen`_ | ++----------------+-----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Issn` | ++----------------+-----------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\IssnValidator` | ++----------------+-----------------------------------------------------------------------+ + +Basic Usage +----------- + +.. configuration-block:: + + .. code-block:: yaml + + # src/JournalBundle/Resources/config/validation.yml + Acme\JournalBundle\Entity\Journal: + properties: + issn: + - Issn: ~ + + .. code-block:: php-annotations + + // src/Acme/JournalBundle/Entity/Journal.php + namespace Acme\JournalBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Journal + { + /** + * @Assert\Issn + */ + protected $issn; + } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // src/Acme/JournalBundle/Entity/Journal.php + namespace Acme\JournalBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Journal + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('issn', new Assert\Issn()); + } + } + +Options +------- + +message +~~~~~~~ + +**type**: ``String`` default: ``This value is not a valid ISSN.`` + +The message shown if the given value is not a valid ISSN. + +caseSensitive +~~~~~~~~~~~~~ + +**type**: ``Boolean`` default: ``false`` + +The validator will allow ISSN values to end with a lower case 'x' by default. +When switching this to ``true``, the validator requires an upper case 'X'. + +requireHyphen +~~~~~~~~~~~~~ + +**type**: ``Boolean`` default: ``false`` + +The validator will allow non hyphenated ISSN values by default. When switching +this to ``true``, the validator requires a hyphenated ISSN value. + +.. _`ISSN`: http://en.wikipedia.org/wiki/Issn + diff --git a/reference/constraints/Length.rst b/reference/constraints/Length.rst index cb5a1ce84f1..045638bca9b 100644 --- a/reference/constraints/Length.rst +++ b/reference/constraints/Length.rst @@ -3,9 +3,6 @@ Length Validates that a given string length is *between* some minimum and maximum value. -.. versionadded:: 2.1 - The Length constraint was added in Symfony 2.1. - +----------------+----------------------------------------------------------------------+ | Applies to | :ref:`property or method` | +----------------+----------------------------------------------------------------------+ diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst new file mode 100644 index 00000000000..ba449062d5a --- /dev/null +++ b/reference/constraints/LessThan.rst @@ -0,0 +1,97 @@ +LessThan +======== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is less than another value, defined in the options. To +force that a value is less than or equal to another value, see +:doc:`/reference/constraints/LessThanOrEqual`. To force a value is greater +than another value, see :doc:`/reference/constraints/GreaterThan`. + ++----------------+------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\LessThan` | ++----------------+------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\LessThanValidator` | ++----------------+------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is less than +``80``, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThan: + value: 80 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThan( + * value = 80 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThan(array( + 'value' => 80, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be less than {{ compared_value }}`` + +This is the message that will be shown if the value is not less than the +comparison value. diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst new file mode 100644 index 00000000000..1376d918de4 --- /dev/null +++ b/reference/constraints/LessThanOrEqual.rst @@ -0,0 +1,96 @@ +LessThanOrEqual +=============== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is less than or equal to another value, defined in the +options. To force that a value is less than another value, see +:doc:`/reference/constraints/LessThan`. + ++----------------+-------------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+-------------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+-------------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\LessThanOrEqual` | ++----------------+-------------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\LessThanOrEqualValidator` | ++----------------+-------------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is less than or +equal to ``80``, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - LessThanOrEqual: + value: 80 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\LessThanOrEqual( + * value = 80 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual(array( + 'value' => 80, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be less than or equal to {{ compared_value }}`` + +This is the message that will be shown if the value is not less than or equal +to the comparison value. diff --git a/reference/constraints/Max.rst b/reference/constraints/Max.rst deleted file mode 100644 index 2873badd137..00000000000 --- a/reference/constraints/Max.rst +++ /dev/null @@ -1,111 +0,0 @@ -Max -=== - -.. caution:: - - The Max constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Range` with the ``max`` - option instead. - -Validates that a given number is *less* than some maximum number. - -+----------------+--------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+--------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `invalidMessage`_ | -+----------------+--------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Max` | -+----------------+--------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MaxValidator` | -+----------------+--------------------------------------------------------------------+ - -Basic Usage ------------ - -To verify that the "age" field of a class is not greater than "50", you might -add the following: - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/EventBundle/Resources/config/validation.yml - Acme\EventBundle\Entity\Participant: - properties: - age: - - Max: { limit: 50, message: You must be 50 or under to enter. } - - .. code-block:: php-annotations - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - /** - * @Assert\Max(limit = 50, message = "You must be 50 or under to enter.") - */ - protected $age; - } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('age', new Assert\Max(array( - 'limit' => 50, - 'message' => 'You must be 50 or under to enter.', - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "max" value. Validation will fail if the given -value is **greater** than this max value. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value should be {{ limit }} or less`` - -The message that will be shown if the underlying value is greater than the -`limit`_ option. - -invalidMessage -~~~~~~~~~~~~~~ - -**type**: ``string`` **default**: ``This value should be a valid number`` - -The message that will be shown if the underlying value is not a number (per -the :phpfunction:`is_numeric` PHP function). diff --git a/reference/constraints/MaxLength.rst b/reference/constraints/MaxLength.rst deleted file mode 100644 index 54c6fd4d95d..00000000000 --- a/reference/constraints/MaxLength.rst +++ /dev/null @@ -1,107 +0,0 @@ -MaxLength -========= - -.. caution:: - - The MaxLength constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Length` with the ``max`` - option instead. - -Validates that the length of a string is not larger than the given limit. - -+----------------+-------------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+-------------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `charset`_ | -+----------------+-------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\MaxLength` | -+----------------+-------------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MaxLengthValidator` | -+----------------+-------------------------------------------------------------------------+ - -Basic Usage ------------ - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Blog: - properties: - summary: - - MaxLength: 100 - - .. code-block:: php-annotations - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - /** - * @Assert\MaxLength(100) - */ - protected $summary; - } - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('summary', new Assert\MaxLength(array( - 'limit' => 100, - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "max" value. Validation will fail if the length -of the give string is **greater** than this number. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value is too long. It should have {{ limit }} characters or less`` - -The message that will be shown if the underlying string has a length that -is longer than the `limit`_ option. - -charset -~~~~~~~ - -**type**: ``charset`` **default**: ``UTF-8`` - -If the PHP extension "mbstring" is installed, then the PHP function :phpfunction:`mb_strlen` -will be used to calculate the length of the string. The value of the ``charset`` -option is passed as the second argument to that function. diff --git a/reference/constraints/Min.rst b/reference/constraints/Min.rst deleted file mode 100644 index a42d5fd76c0..00000000000 --- a/reference/constraints/Min.rst +++ /dev/null @@ -1,111 +0,0 @@ -Min -=== - -.. caution:: - - The Min constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Range` with the ``min`` - option instead. - -Validates that a given number is *greater* than some minimum number. - -+----------------+--------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+--------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `invalidMessage`_ | -+----------------+--------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Min` | -+----------------+--------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MinValidator` | -+----------------+--------------------------------------------------------------------+ - -Basic Usage ------------ - -To verify that the "age" field of a class is "18" or greater, you might add -the following: - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/EventBundle/Resources/config/validation.yml - Acme\EventBundle\Entity\Participant: - properties: - age: - - Min: { limit: 18, message: You must be 18 or older to enter. } - - .. code-block:: php-annotations - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - /** - * @Assert\Min(limit = "18", message = "You must be 18 or older to enter.") - */ - protected $age; - } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity\Participant; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('age', new Assert\Min(array( - 'limit' => '18', - 'message' => 'You must be 18 or older to enter.', - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "min" value. Validation will fail if the given -value is **less** than this min value. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value should be {{ limit }} or more`` - -The message that will be shown if the underlying value is less than the `limit`_ -option. - -invalidMessage -~~~~~~~~~~~~~~ - -**type**: ``string`` **default**: ``This value should be a valid number`` - -The message that will be shown if the underlying value is not a number (per -the :phpfunction:`is_numeric` PHP function). diff --git a/reference/constraints/MinLength.rst b/reference/constraints/MinLength.rst deleted file mode 100644 index b9ad5afd075..00000000000 --- a/reference/constraints/MinLength.rst +++ /dev/null @@ -1,112 +0,0 @@ -MinLength -========= - -.. caution:: - - The MinLength constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Length` with the ``min`` - option instead. - -Validates that the length of a string is at least as long as the given limit. - -+----------------+-------------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+-------------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `charset`_ | -+----------------+-------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\MinLength` | -+----------------+-------------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MinLengthValidator` | -+----------------+-------------------------------------------------------------------------+ - -Basic Usage ------------ - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Blog: - properties: - firstName: - - MinLength: { limit: 3, message: "Your name must have at least {{ limit }} characters." } - - .. code-block:: php-annotations - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - /** - * @Assert\MinLength( - * limit=3, - * message="Your name must have at least {{ limit }} characters." - * ) - */ - protected $summary; - } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('summary', new Assert\MinLength(array( - 'limit' => 3, - 'message' => 'Your name must have at least {{ limit }} characters.', - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "min" value. Validation will fail if the length -of the give string is **less** than this number. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value is too short. It should have {{ limit }} characters or more`` - -The message that will be shown if the underlying string has a length that -is shorter than the `limit`_ option. - -charset -~~~~~~~ - -**type**: ``charset`` **default**: ``UTF-8`` - -If the PHP extension "mbstring" is installed, then the PHP function :phpfunction:`mb_strlen` -will be used to calculate the length of the string. The value of the ``charset`` -option is passed as the second argument to that function. diff --git a/reference/constraints/NotEqualTo.rst b/reference/constraints/NotEqualTo.rst new file mode 100644 index 00000000000..bea2bf6eb38 --- /dev/null +++ b/reference/constraints/NotEqualTo.rst @@ -0,0 +1,101 @@ +NotEqualTo +========== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is **not** equal to another value, defined in the +options. To force that a value is equal, see +:doc:`/reference/constraints/EqualTo`. + +.. caution:: + + This constraint compares using ``!=``, so ``3`` and ``"3"`` are considered + equal. Use :doc:`/reference/constraints/NotIdenticalTo` to compare with + ``!==``. + ++----------------+-------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+-------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+-------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\NotEqualTo` | ++----------------+-------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\NotEqualToValidator`| ++----------------+-------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is not equal to +``15``, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - NotEqualTo: + value: 15 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\NotEqualTo( + * value = 15 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\NotEqualTo(array( + 'value' => 15, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should not be equal to {{ compared_value }}`` + +This is the message that will be shown if the value is not equal. diff --git a/reference/constraints/NotIdenticalTo.rst b/reference/constraints/NotIdenticalTo.rst new file mode 100644 index 00000000000..2f659cbdbc5 --- /dev/null +++ b/reference/constraints/NotIdenticalTo.rst @@ -0,0 +1,101 @@ +NotIdenticalTo +=========== + +.. versionadded:: 2.3 + This constraint is new in version 2.3. + +Validates that a value is **not** identical to another value, defined in the +options. To force that a value is identical, see +:doc:`/reference/constraints/IdenticalTo`. + +.. caution:: + + This constraint compares using ``!==``, so ``3`` and ``"3"`` are + considered not equal. Use :doc:`/reference/constraints/NotEqualTo` to compare + with ``!=``. + ++----------------+-----------------------------------------------------------------------------+ +| Applies to | :ref:`property or method` | ++----------------+-----------------------------------------------------------------------------+ +| Options | - `value`_ | +| | - `message`_ | ++----------------+-----------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\NotIdenticalTo` | ++----------------+-----------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\NotIdenticalToValidator`| ++----------------+-----------------------------------------------------------------------------+ + +Basic Usage +----------- + +If you want to ensure that the ``age`` of a ``Person`` class is *not* equal to +``15`` and *not* an integer, you could do the following: + +.. configuration-block:: + + .. code-block:: yaml + + # src/SocialBundle/Resources/config/validation.yml + Acme\SocialBundle\Entity\Person: + properties: + age: + - NotIdenticalTo: + value: 15 + + .. code-block:: php-annotations + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + /** + * @Assert\NotIdenticalTo( + * value = 15 + * ) + */ + protected $age; + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/SocialBundle/Entity/Person.php + namespace Acme\SocialBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Person + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('age', new Assert\NotIdenticalTo(array( + 'value' => 15, + ))); + } + } + +Options +------- + +.. include:: /reference/constraints/_comparison-value-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should not be identical to {{ compared_value_type }} {{ compared_value }}`` + +This is the message that will be shown if the value is not equal. diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst index 891c1ec8331..357cdb105e3 100644 --- a/reference/constraints/Range.rst +++ b/reference/constraints/Range.rst @@ -3,9 +3,6 @@ Range Validates that a given number is *between* some minimum and maximum number. -.. versionadded:: 2.1 - The Range constraint was added in Symfony 2.1. - +----------------+---------------------------------------------------------------------+ | Applies to | :ref:`property or method` | +----------------+---------------------------------------------------------------------+ diff --git a/reference/constraints/UniqueEntity.rst b/reference/constraints/UniqueEntity.rst index 27c0204c7a8..20ad42ff98f 100644 --- a/reference/constraints/UniqueEntity.rst +++ b/reference/constraints/UniqueEntity.rst @@ -143,17 +143,10 @@ repositoryMethod **type**: ``string`` **default**: ``findBy`` -.. versionadded:: 2.1 - The ``repositoryMethod`` option was added in Symfony 2.1. Before, it - always used the ``findBy`` method. - The name of the repository method to use for making the query to determine the uniqueness. If it's left blank, the ``findBy`` method will be used. This method should return a countable result. -.. versionadded:: 2.1 - The ``ignoreNull`` option was added in Symfony 2.1. - ignoreNull ~~~~~~~~~~ diff --git a/reference/constraints/UserPassword.rst b/reference/constraints/UserPassword.rst index 0ad827ac4c7..cc8b5c9ee86 100644 --- a/reference/constraints/UserPassword.rst +++ b/reference/constraints/UserPassword.rst @@ -1,9 +1,6 @@ UserPassword ============ -.. versionadded:: 2.1 - This constraint is new in version 2.1. - .. note:: Since Symfony 2.2, the ``UserPassword*`` classes in the diff --git a/reference/constraints/_comparison-value-option.rst.inc b/reference/constraints/_comparison-value-option.rst.inc new file mode 100644 index 00000000000..9d799c27dc7 --- /dev/null +++ b/reference/constraints/_comparison-value-option.rst.inc @@ -0,0 +1,7 @@ +value +~~~~~ + +**type**: ``mixed`` + +This option is required. It defines the value to compare to. It can be a +string, number or object. diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 0482c4c5447..2c185d11cbf 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -16,8 +16,6 @@ String Constraints ~~~~~~~~~~~~~~~~~~ * :doc:`Email ` -* :doc:`MinLength ` -* :doc:`MaxLength ` * :doc:`Length ` * :doc:`Url ` * :doc:`Regex ` @@ -26,10 +24,20 @@ String Constraints Number Constraints ~~~~~~~~~~~~~~~~~~ -* :doc:`Max ` -* :doc:`Min ` * :doc:`Range ` +Comparison Constraints +~~~~~~~~~~~~~~~~~~~~~~ + +* :doc:`EqualTo ` +* :doc:`NotEqualTo ` +* :doc:`IdenticalTo ` +* :doc:`NotIdenticalTo ` +* :doc:`LessThan ` +* :doc:`LessThanOrEqual ` +* :doc:`GreaterThan ` +* :doc:`GreaterThanOrEqual ` + Date Constraints ~~~~~~~~~~~~~~~~ @@ -54,11 +62,14 @@ File Constraints * :doc:`File ` * :doc:`Image ` -Financial Constraints -~~~~~~~~~~~~~~~~~~~~~ +Financial and other Number Constraints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * :doc:`CardScheme ` * :doc:`Luhn ` +* :doc:`Iban ` +* :doc:`Isbn ` +* :doc:`Issn ` Other Constraints ~~~~~~~~~~~~~~~~~ diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 765aaef9638..52e0b04a5a4 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -61,6 +61,10 @@ may also be tags in other bundles you use that aren't listed here. +-----------------------------------+---------------------------------------------------------------------------+ | `security.remember_me_aware`_ | To allow remember me authentication | +-----------------------------------+---------------------------------------------------------------------------+ +| `serializer.encoder`_ | Register a new encoder in the ``serializer`` service | ++-----------------------------------+---------------------------------------------------------------------------+ +| `serializer.normalizer`_ | Register a new normalizer in the ``serializer`` service | ++-----------------------------------+---------------------------------------------------------------------------+ | `swiftmailer.plugin`_ | Register a custom SwiftMailer Plugin | +-----------------------------------+---------------------------------------------------------------------------+ | `templating.helper`_ | Make your service available in PHP templates | @@ -560,9 +564,6 @@ kernel.event_subscriber **Purpose**: To subscribe to a set of different events/hooks in Symfony -.. versionadded:: 2.1 - The ability to add kernel event subscribers is new to 2.1. - To enable a custom subscriber, add it as a regular service in one of your configuration, and tag it with ``kernel.event_subscriber``: @@ -809,6 +810,30 @@ is used behind the scenes to determine if the user should have access. The For more information, read the cookbook article: :doc:`/cookbook/security/voters`. +.. _reference-dic-tags-serializer-encoder: + +serializer.encoder +------------------ + +**Purpose**: Register a new encoder in the ``serializer`` service + +The class that's tagged should implement the :class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` +and :class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface`. + +For more details, see :doc:`/cookbook/serializer`. + +.. _reference-dic-tags-serializer-normalizer: + +serializer.normalizer +--------------------- + +**Purpose**: Register a new normalizer in the Serializer service + +The class that's tagged should implement the :class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface` +and :class:`Symfony\\Component\\Serializer\\Normalizer\\DenormalizerInterface`. + +For more details, see :doc:`/cookbook/serializer`. + swiftmailer.plugin ------------------ diff --git a/reference/forms/twig_reference.rst b/reference/forms/twig_reference.rst index e34b4c6e463..74ddcca51a8 100644 --- a/reference/forms/twig_reference.rst +++ b/reference/forms/twig_reference.rst @@ -24,6 +24,66 @@ rendering forms. There are several different functions available, and each is responsible for rendering a different part of a form (e.g. labels, errors, widgets, etc). +.. _reference-forms-twig-form: + +form(view, variables) +--------------------- + +Renders the HTML of a complete form. + +.. code-block:: jinja + + {# render the form and change the submission method #} + {{ form(form, {'method': 'GET'}) }} + +You will mostly use this helper for prototyping or if you use custom form +themes. If you need more flexibility in rendering the form, you should use +the other helpers to render individual parts of the form instead: + +.. code-block:: jinja + + {{ form_start(form) }} + {{ form_errors(form) }} + + {{ form_row(form.name) }} + {{ form_row(form.dueDate) }} + + + {{ form_end(form) }} + +.. _reference-forms-twig-start: + +form_start(view, variables) +--------------------------- + +Renders the start tag of a form. This helper takes care of printing the +configured method and target action of the form. It will also include the +correct ``enctype`` property if the form contains upload fields. + +.. code-block:: jinja + + {# render the start tag and change the submission method #} + {{ form_start(form, {'method': 'GET'}) }} + +.. _reference-forms-twig-end: + +form_end(view, variables) +------------------------- + +Renders the end tag of a form. + +.. code-block:: jinja + + {{ form_end(form) }} + +This helper also outputs ``form_rest()`` unless you set ``render_rest`` to +false: + +.. code-block:: jinja + + {# don't render unrendered fields #} + {{ form_end(form, {'render_rest': false}) }} + .. _reference-forms-twig-label: form_label(view, label, variables) @@ -119,6 +179,11 @@ obvious (since it'll render the field for you). form_enctype(view) ------------------ +.. note:: + + This helper was deprecated in Symfony 2.3 and will be removed in Symfony 3.0. + You should use ``form_start()`` instead. + If the form contains at least one file upload field, this will render the required ``enctype="multipart/form-data"`` form attribute. It's always a good idea to include this in your form tag: @@ -230,10 +295,6 @@ object: get('name')->vars['label'] ?> -.. versionadded:: 2.1 - The ``valid``, ``label_attr``, ``compound``, and ``disabled`` variables - are new in Symfony 2.1. - +-----------------+-----------------------------------------------------------------------------------------+ | Variable | Usage | +=================+=========================================================================================+ @@ -277,4 +338,4 @@ object: | | (for example, a ``choice`` field, which is actually a group of checkboxes | +-----------------+-----------------------------------------------------------------------------------------+ -.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/2.1/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig diff --git a/reference/forms/types.rst b/reference/forms/types.rst index 36b471c9e6f..9c46ae00770 100644 --- a/reference/forms/types.rst +++ b/reference/forms/types.rst @@ -9,6 +9,7 @@ Form Types Reference :hidden: types/birthday + types/button types/checkbox types/choice types/collection @@ -30,7 +31,9 @@ Form Types Reference types/percent types/radio types/repeated + types/reset types/search + types/submit types/text types/textarea types/time diff --git a/reference/forms/types/birthday.rst b/reference/forms/types/birthday.rst index 8d6af04af6d..0d05e757a42 100644 --- a/reference/forms/types/birthday.rst +++ b/reference/forms/types/birthday.rst @@ -34,7 +34,7 @@ option defaults to 120 years ago to the current year. | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | -| | - `virtual`_ | +| | - `inherit_data`_ | +----------------------+-------------------------------------------------------------------------------+ | Parent type | :doc:`date` | +----------------------+-------------------------------------------------------------------------------+ @@ -83,4 +83,4 @@ These options inherit from the :doc:`date` type: .. include:: /reference/forms/types/options/mapped.rst.inc -.. include:: /reference/forms/types/options/virtual.rst.inc +.. include:: /reference/forms/types/options/inherit_data.rst.inc diff --git a/reference/forms/types/button.rst b/reference/forms/types/button.rst new file mode 100644 index 00000000000..6e8cce6ed57 --- /dev/null +++ b/reference/forms/types/button.rst @@ -0,0 +1,34 @@ +.. index:: + single: Forms; Fields; button + +button Field Type +================= + +.. versionadded:: 2.3 + The ``button`` type was added in Symfony 2.3 + +A simple, non-responsive button. + ++----------------------+----------------------------------------------------------------------+ +| Rendered as | ``button`` tag | ++----------------------+----------------------------------------------------------------------+ +| Options | - `attr`_ | +| | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+----------------------------------------------------------------------+ +| Parent type | none | ++----------------------+----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ButtonType` | ++----------------------+----------------------------------------------------------------------+ + +Options +------- + +.. include:: /reference/forms/types/options/button_attr.rst.inc + +.. include:: /reference/forms/types/options/button_disabled.rst.inc + +.. include:: /reference/forms/types/options/button_label.rst.inc + +.. include:: /reference/forms/types/options/button_translation_domain.rst.inc diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index ad0d611c29f..0ed374307ab 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -26,7 +26,7 @@ option. | | - `disabled`_ | | | - `error_bubbling`_ | | | - `mapped`_ | -| | - `virtual`_ | +| | - `inherit_data`_ | | | - `by_reference`_ | | | - `empty_data`_ | +-------------+-----------------------------------------------------------------------------+ @@ -123,7 +123,7 @@ These options inherit from the :doc:`field` type: .. include:: /reference/forms/types/options/mapped.rst.inc -.. include:: /reference/forms/types/options/virtual.rst.inc +.. include:: /reference/forms/types/options/inherit_data.rst.inc .. include:: /reference/forms/types/options/by_reference.rst.inc diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 10952ded3c4..a4c6282a68d 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -152,7 +152,7 @@ you need is the JavaScript: .. code-block:: html+jinja -
        + {{ form_start(form) }} {# ... #} {# store the prototype on the data-prototype attribute #} @@ -168,7 +168,7 @@ you need is the JavaScript: Add another email {# ... #} -
        + {{ form_end(form) }}