From 026346cbd92691d018f158cf61827607cbaa7739 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 18 Oct 2012 15:53:13 +0200 Subject: [PATCH 1/7] Updated .gitignore to ignore Sphinx files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..0e7262e0af3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +Makefile +_exts +_build +conf.py +make.bat From 9a47dcf5d2eceec61576d79accf8dd9018dda04c Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sun, 4 Nov 2012 10:54:18 +0100 Subject: [PATCH 2/7] Bootstrapped Form component chapter --- components/form/index.rst | 5 +++++ components/index.rst | 1 + components/map.rst.inc | 2 ++ 3 files changed, 8 insertions(+) create mode 100644 components/form/index.rst diff --git a/components/form/index.rst b/components/form/index.rst new file mode 100644 index 00000000000..2526f961a27 --- /dev/null +++ b/components/form/index.rst @@ -0,0 +1,5 @@ +Form +==== + +.. toctree:: + :maxdepth: 2 diff --git a/components/index.rst b/components/index.rst index ea482e29589..e55f9ba0b3b 100644 --- a/components/index.rst +++ b/components/index.rst @@ -13,6 +13,7 @@ The Components event_dispatcher/index filesystem finder + form/index http_foundation/index locale process diff --git a/components/map.rst.inc b/components/map.rst.inc index cd78013e7e7..6083d164877 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -49,6 +49,8 @@ * :doc:`/components/finder` +* :doc:`/components/form/index` + * :doc:`/components/http_foundation/index` * :doc:`/components/http_foundation/introduction` From fabbbae7e7bf0b434469a6bc70c7f5b73b2776e8 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sat, 3 Nov 2012 20:34:51 +0100 Subject: [PATCH 3/7] Introduction chapter for the Form component --- components/form/index.rst | 2 + components/form/introduction.rst | 438 +++++++++++++++++++++++++++++++ components/map.rst.inc | 2 + 3 files changed, 442 insertions(+) create mode 100644 components/form/introduction.rst diff --git a/components/form/index.rst b/components/form/index.rst index 2526f961a27..8283298b120 100644 --- a/components/form/index.rst +++ b/components/form/index.rst @@ -3,3 +3,5 @@ .. toctree:: :maxdepth: 2 + + introduction diff --git a/components/form/introduction.rst b/components/form/introduction.rst new file mode 100644 index 00000000000..1d2e14964d1 --- /dev/null +++ b/components/form/introduction.rst @@ -0,0 +1,438 @@ +.. index:: + single: Forms + single: Components; Form + +The Form Component +================== + + The Form component allows you to easily create, process and reuse HTML + forms. + +TODO: introduction + +Installation +------------ + +You can install the component in many different ways: + +* Use the official Git repository (https://github.com/symfony/Form); +* Install it via Composer (``symfony/form`` on `Packagist`_). + +Configuration +------------- + +.. tip:: + + If you are working with the full-stack Symfony framework, the Form component + is already configured for you. In this case you can skip this section. + +In Symfony2, forms are represented by objects. These objects are constructed +with a *form factory*. Building a form factory is simple:: + + use Symfony\Component\Form\Forms; + + $formFactory = Forms::createFormFactory(); + +This factory can already be used to create basic forms, but it is lacking +support for very important features: + +* **Request Handling:** Support for request handling and file uploads; +* **CSRF Protection:** You should always protect forms against + Cross-Site-Request-Forgery (CSRF) attacks; +* **Templating:** Support for a templating layer allows to reuse HTML fragments + when rendering a form; +* **Translation:** You normally want to translate error messages, field labels + or similar strings in your forms; +* **Validation:** You would like to use a validation library for validating + the form inputs. + +The Symfony2 Form component relies on other libraries to solve these problems. +Most of the time you will use Twig and the Symfony HttpFoundation, Translation +and Validator components, but you can replace any of these with a different +library of your choice. + +The following sections explain how to plug Symfony2's libraries into the form +factory. + +.. tip:: + + See https://github.com/bschussek/standalone-forms + +Request Handling +~~~~~~~~~~~~~~~~ + +.. code-block:: json + + { + "require": { + "symfony/http-foundation": "2.1.*" + } + } + +.. code-block:: php + + use Symfony\Component\Form\Forms; + use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; + + $formFactory = Forms::createFormFactoryBuilder() + ->addExtension(new HttpFoundationExtension()) + ->getFormFactory(); + +TODO: Cross reference the HTTP foundation chapter where the configuration of the +Templating component is explained. + +CSRF Protection +~~~~~~~~~~~~~~~ + +Protection against CSRF attacks is built into the Form component, but you need +to explicitly enable it or replace it with a custom solution. The following +snippet adds CSRF protection to the form factory:: + + use Symfony\Component\Form\Forms; + use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; + use Symfony\Component\HttpFoundation\Session\Session; + + define('CSRF_SECRET', ''); + + $session = new Session(); + + $csrfProvider = new SessionCsrfProvider($session, CSRF_SECRET); + + $formFactory = Forms::createFormFactoryBuilder() + // ... + ->addExtension(new CsrfExtension($csrfProvider)) + ->getFormFactory(); + +To secure your application against CSRF attacks, you need to define a CSRF +secret. Generate a random string with at least 32 characters, insert it in the +above snippet and make sure that nobody except for your web server can access +the secret. That is all you need to enable CSRF protection. + +.. tip:: + + When not using HttpFoundation, load DefaultCsrfProvider instead which relies + on PHP's native session handling:: + + use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; + + $csrfProvider = new DefaultCsrfProvider(CSRF_SECRET); + +Twig Templating +~~~~~~~~~~~~~~~ + +.. code-block:: json + + { + "require": { + "symfony/twig-bridge": "2.1.*" + } + } + +.. code-block:: php + + use Symfony\Component\Form\Forms; + use Symfony\Bridge\Twig\Extension\FormExtension; + use Symfony\Bridge\Twig\Form\TwigRenderer; + use Symfony\Bridge\Twig\Form\TwigRendererEngine; + + define('DEFAULT_FORM_THEME', 'form_div_layout.html.twig'); + + define('VENDOR_DIR', realpath(__DIR__ . '/../vendor')); + define('VENDOR_TWIG_BRIDGE_DIR', VENDOR_DIR . '/symfony/twig-bridge/Symfony/Bridge/Twig'); + define('VIEWS_DIR', realpath(__DIR__ . '/../views')); + + $twig = new Twig_Environment(new Twig_Loader_Filesystem(array( + VIEWS_DIR, + VENDOR_TWIG_BRIDGE_DIR . '/Resources/views/Form', + ))); + $formEngine = new TwigRendererEngine(array(DEFAULT_FORM_THEME)); + $formEngine->setEnvironment($twig); + $twig->addExtension(new FormExtension(new TwigRenderer($formEngine, $csrfProvider))); + + $formFactory = Forms::createFormFactoryBuilder() + // ... + ->getFormFactory(); + +TODO: Cross reference the templating chapter where the configuration of the +Templating component is explained. Also reference the Twig documentation where +more details about Twig and its configuration can be found. + +Translation +~~~~~~~~~~~ + +.. code-block:: json + + { + "require": { + "symfony/translation": "2.1.*", + "symfony/config": "2.1.*" + } + } + +.. code-block:: php + + use Symfony\Component\Form\Forms; + use Symfony\Component\Translation\Translator; + use Symfony\Component\Translation\Loader\XliffFileLoader; + use Symfony\Bridge\Twig\Extension\TranslationExtension; + + $translator = new Translator('en'); + $translator->addLoader('xlf', new XliffFileLoader()); + + $twig->addExtension(new TranslationExtension($translator)); + + $formFactory = Forms::createFormFactoryBuilder() + // ... + ->getFormFactory(); + +TODO: Cross reference the translator docs where the detailed configuration of +the validator is explained. + +Validation +~~~~~~~~~~ + +.. code-block:: json + + { + "require": { + "symfony/validator": "2.1.*" + } + } + +.. code-block:: php + + use Symfony\Component\Form\Forms; + use Symfony\Component\Form\Extension\Validator\ValidatorExtension; + use Symfony\Component\Validator\Validation; + + define('VENDOR_DIR', realpath(__DIR__ . '/../vendor')); + define('VENDOR_FORM_DIR', VENDOR_DIR . '/symfony/form/Symfony/Component/Form'); + define('VENDOR_VALIDATOR_DIR', VENDOR_DIR . '/symfony/validator/Symfony/Component/Validator'); + + $validator = Validation::createValidator(); + + $translator->addResource('xlf', VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); + $translator->addResource('xlf', VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); + + $formFactory = Forms::createFormFactoryBuilder() + // ... + ->addExtension(new ValidatorExtension($validator)) + ->getFormFactory(); + +TODO: Cross reference the validator docs where the detailed configuration of +the validator is explained. + +Accessing the Form Factory +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Either in global variable or by use of DICs. + +Creating a Simple Form +---------------------- + +.. configuration-block:: + + .. code-block:: php-standalone + + $form = $formFactory->createBuilder() + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + + echo $twig->render('new.html.twig', array( + 'form' => $form->createView(), + )); + + .. code-block:: php-symfony + + // src/Acme/TaskBundle/Controller/DefaultController.php + namespace Acme\TaskBundle\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Request; + + class DefaultController extends Controller + { + public function newAction(Request $request) + { + $form = $this->createFormBuilder() + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + + return $this->render('AcmeTaskBundle:Default:new.html.twig', array( + 'form' => $form->createView(), + )); + } + } + +Setting Default Values +~~~~~~~~~~~~~~~~~~~~~~ + +.. configuration-block:: + + .. code-block:: php-standalone + + $defaults = array( + 'dueDate' => new \DateTime('tomorrow'), + ); + + $form = $formFactory->createBuilder('form', $defaults) + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + + .. code-block:: php-symfony + + $defaults = array( + 'dueDate' => new \DateTime('tomorrow'), + ); + + $form = $this->createFormBuilder($defaults) + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + +Rendering the Form +~~~~~~~~~~~~~~~~~~ + +Now that the form has been created, the next step is to render it. This is +done by passing a special form "view" object to your template (notice the +``$form->createView()`` in the controller above) and using a set of form +helper functions: + +.. code-block:: html+jinja + +
+ {{ form_widget(form) }} + + +
+ +.. image:: /images/book/form-simple.png + :align: center + +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. + +Handling Form Submissions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. configuration-block:: + + .. code-block:: php-standalone + + use Symfony\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\RedirectResponse; + + $form = $formFactory->createBuilder() + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + + $request = Request::createFromGlobals(); + + if ($request->isMethod('POST')) { + $form->bind($request); + + if ($form->isValid()) { + $data = $form->getData(); + + // ... perform some action, such as saving the data to the database + + $response = new RedirectResponse('/task/success'); + $response->prepare($request); + + return $response->send(); + } + } + + // ... + + .. code-block:: php-symfony + + // ... + + public function newAction(Request $request) + { + $form = $this->createFormBuilder() + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + + if ($request->isMethod('POST')) { + $form->bind($request); + + if ($form->isValid()) { + $data = $form->getData(); + + // ... perform some action, such as saving the data to the database + + return $this->redirect($this->generateUrl('task_success')); + } + } + + // ... + } + + +.. note:: + + When not using HttpFoundation:: + + if (isset($_POST[$form->getName()])) { + $form->bind($_POST[$form->getName()) + + // ... + } + + but! no support for file uploads then + + +Form Validation +~~~~~~~~~~~~~~~ + +.. configuration-block:: + + .. code-block:: php-standalone + + use Symfony\Component\Validator\Constraints\NotBlank; + use Symfony\Component\Validator\Constraints\Type; + + $form = $formFactory->createBuilder() + ->add('task', 'text', array( + 'constraints' => new NotBlank(), + )) + ->add('dueDate', 'date', array( + 'constraints' => array( + new NotBlank(), + new Type('\DateTime'), + ) + )) + ->getForm(); + + .. code-block:: php-symfony + + use Symfony\Component\Validator\Constraints\NotBlank; + use Symfony\Component\Validator\Constraints\Type; + + $form = $this->createFormBuilder() + ->add('task', 'text', array( + 'constraints' => new NotBlank(), + )) + ->add('dueDate', 'date', array( + 'constraints' => array( + new NotBlank(), + new Type('\DateTime'), + ) + )) + ->getForm(); + + +That's all you need to know for creating a basic form! + +.. _Packagist: https://packagist.org/packages/symfony/form diff --git a/components/map.rst.inc b/components/map.rst.inc index 6083d164877..b06675f6f04 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -51,6 +51,8 @@ * :doc:`/components/form/index` + * :doc:`/components/form/introduction` + * :doc:`/components/http_foundation/index` * :doc:`/components/http_foundation/introduction` From 11b96764455dce1ea5f9397599cb3d40a4e9b528 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 11 Nov 2012 16:46:13 -0600 Subject: [PATCH 4/7] [WIP][#1883] Adding more and more details for the form introduction Still a WIP because: a) Bernhard will certainly need to review it b) I would like to re-read it - I felt that there are still gaps - too many details here, too few details there c) This is part of a larger, on-going project to refactor the forms docs d) We need to bootstrap the validation and translation component docs, and other things need to be refactored to make sense --- book/forms.rst | 2 + components/form/introduction.rst | 252 +++++++++++++++++++++++++------ 2 files changed, 209 insertions(+), 45 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 0a245b9416f..6ed3a9c6c6c 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -263,6 +263,8 @@ possible paths: .. index:: single: Forms; Validation +.. _book-forms-form-validation: + Form Validation --------------- diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 1d2e14964d1..f512f624d8f 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -8,7 +8,11 @@ The Form Component The Form component allows you to easily create, process and reuse HTML forms. -TODO: introduction +The form component is a tool to help you solve the problem of allowing end-users +to interact with the data and modify the data in your application. And thought +traditionally this has been through HTML forms, the component focuses on +processing data to and from your client and application, whether that data +be from a normal form post or from an API. Installation ------------ @@ -24,10 +28,10 @@ Configuration .. tip:: If you are working with the full-stack Symfony framework, the Form component - is already configured for you. In this case you can skip this section. + is already configured for you. In this case, skip to :ref:`component-form-intro-create-simple-form`. -In Symfony2, forms are represented by objects. These objects are constructed -with a *form factory*. Building a form factory is simple:: +In Symfony2, forms are represented by objects and these objects are built +by using a *form factory*. Building a form factory is simple:: use Symfony\Component\Form\Forms; @@ -37,39 +41,38 @@ This factory can already be used to create basic forms, but it is lacking support for very important features: * **Request Handling:** Support for request handling and file uploads; -* **CSRF Protection:** You should always protect forms against - Cross-Site-Request-Forgery (CSRF) attacks; -* **Templating:** Support for a templating layer allows to reuse HTML fragments - when rendering a form; -* **Translation:** You normally want to translate error messages, field labels - or similar strings in your forms; -* **Validation:** You would like to use a validation library for validating - the form inputs. +* **CSRF Protection:** Support for protection against Cross-Site-Request-Forgery + (CSRF) attacks; +* **Templating:** Integration with a templating layer that allows you to reuse + HTML fragments when rendering a form; +* **Translation:** Support for translating error messages, field labels and + other strings; +* **Validation:** Integration with a validation library to generate error + messages for submitted data. The Symfony2 Form component relies on other libraries to solve these problems. -Most of the time you will use Twig and the Symfony HttpFoundation, Translation -and Validator components, but you can replace any of these with a different -library of your choice. +Most of the time you will use Twig and the Symfony :doc:`HttpFoundation`, +Translation and Validator components, but you can replace any of these with +a different library of your choice. -The following sections explain how to plug Symfony2's libraries into the form +The following sections explain how to plug these libraries into the form factory. .. tip:: - See https://github.com/bschussek/standalone-forms + For a working example, see https://github.com/bschussek/standalone-forms Request Handling ~~~~~~~~~~~~~~~~ -.. code-block:: json - - { - "require": { - "symfony/http-foundation": "2.1.*" - } - } +To process form data, you'll need to grab information off of the request (typically +``$_POST`` data) and pass the array of submitted data to :method:`Symfony\\Component\\Form\\Form::bind`. +The Form component optionally integrates with Symfony's :doc:`HttpFoundation` +component to make this even easier. -.. code-block:: php +To integrate the HttpFoundation component, add the +:class:`Symfony\\Component\\Form\\Extension\\HttpFoundation\HttpFoundationExtension` +to your form factory:: use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; @@ -78,8 +81,14 @@ Request Handling ->addExtension(new HttpFoundationExtension()) ->getFormFactory(); -TODO: Cross reference the HTTP foundation chapter where the configuration of the -Templating component is explained. +Now, when you process a form, you can pass the :class:`Symfony\\Component\\HttpFoundation\\Request`` +object to :method:`Symfony\\Component\\Form\\Form::bind` instead of the raw +array of submitted values. + +.. note:: + + For more information about the ``HttpFoundation`` component or how to + install it, see :doc:`/components/http_foundation/introduction`. CSRF Protection ~~~~~~~~~~~~~~~ @@ -93,8 +102,10 @@ snippet adds CSRF protection to the form factory:: use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; use Symfony\Component\HttpFoundation\Session\Session; + // generate a CSRF secret, which you *may* want to set as a constant define('CSRF_SECRET', ''); + // create a Session object from the HttpFoundation component $session = new Session(); $csrfProvider = new SessionCsrfProvider($session, CSRF_SECRET); @@ -106,13 +117,18 @@ snippet adds CSRF protection to the form factory:: To secure your application against CSRF attacks, you need to define a CSRF secret. Generate a random string with at least 32 characters, insert it in the -above snippet and make sure that nobody except for your web server can access -the secret. That is all you need to enable CSRF protection. +above snippet and make sure that nobody except your web server can access +the secret. + +Internally, this extension will automatically add a hidden field to every +form (called ``__token`` by default) whose value is automatically generated +and validated when binding the form. .. tip:: - When not using HttpFoundation, load DefaultCsrfProvider instead which relies - on PHP's native session handling:: + If you're not using the HttpFoundation component, load use + :class:`Symfony\\Component\\Form\\Extension\\Csrf\\CsrfProvider\\DefaultCsrfProvider` + instead, which relies on PHP's native session handling:: use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; @@ -121,6 +137,16 @@ the secret. That is all you need to enable CSRF protection. Twig Templating ~~~~~~~~~~~~~~~ +If you're using the Form component to process HTML forms, you'll need a way +to easily render your form as HTML form fields (complete with field values, +errors, and labels). If you use `Twig`_ as your template engine, the Form +component offers a rich integration. + +To use the integration, you'll need the ``TwigBridge``, which provides integration +between Twig and several Symfony2 components. If you're using Composer, you +could install the latest 2.1 version by adding the following ``require`` +line to your ``composer.json`` file: + .. code-block:: json { @@ -129,17 +155,24 @@ Twig Templating } } -.. code-block:: php +The TwigBridge integration provides you with several :doc:`Twig FunctionssetEnvironment($twig); + // add the FormExtension to Twig $twig->addExtension(new FormExtension(new TwigRenderer($formEngine, $csrfProvider))); + // create your form factory as normal $formFactory = Forms::createFormFactoryBuilder() // ... ->getFormFactory(); -TODO: Cross reference the templating chapter where the configuration of the -Templating component is explained. Also reference the Twig documentation where -more details about Twig and its configuration can be found. +The exact details of your `Twig Configuration`_ will vary, but the goal is +always to add the :class:`Symfony\\Bridge\\Twig\\Extension\\FormExtension` +to Twig, which gives you access to the Twig functions for rendering forms. +To do this, you first need to create a :class:`Symfony\\Bridge\\Twig\\Form\\TwigRendererEngine`, +where you define your :ref:`form themes` +(i.e. resources/files that define form HTML markup). + +For general details on rendering forms, see :doc:`/cookbook/form/form_customization`. + +.. note:: + + If you use the Twig integration, read ":ref:`component-form-intro-install-translation`" + below for details on the needed translation filters. + +.. _component-form-intro-install-translation: Translation ~~~~~~~~~~~ +If you're using the Twig integration with one of the default form theme files +(e.g. ``form_div_layout.html.twig``), there are 2 Twig filters (``trans`` +and ``transChoice``) that are used for translating form labels, errors, option +text and other strings. + +To add these Twig filters, you can either use the built-in +:class:`Symfony\\Bridge\\Twig\\Extension\\TranslationExtension` that integrates +with Symfony's ``Translation`` component, or add the 2 Twig filters yourself, +via your own Twig extension. + +To use the built-in integration, be sure that your project has Symfony's +``Translation`` and :doc:`Config` components +installed. If you're using Composer, you could get the latest 2.1 version +of each of these by adding the following to your ``composer.json`` file: + .. code-block:: json { @@ -170,28 +232,48 @@ Translation } } -.. code-block:: php +Next, add the :class:`Symfony\\Bridge\\Twig\\Extension\\TranslationExtension` +to your ``Twig_Environment`` instance:: use Symfony\Component\Form\Forms; use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Loader\XliffFileLoader; use Symfony\Bridge\Twig\Extension\TranslationExtension; + // create the Translator $translator = new Translator('en'); + // somehow load some translations into it $translator->addLoader('xlf', new XliffFileLoader()); + $translator->addResource( + 'xlf', + __DIR__.'/path/to/translations/messages.en.xlf', + 'en' + ); + // add the TranslationExtension (gives us trans and transChoice filters) $twig->addExtension(new TranslationExtension($translator)); $formFactory = Forms::createFormFactoryBuilder() // ... ->getFormFactory(); -TODO: Cross reference the translator docs where the detailed configuration of -the validator is explained. +Depending on how your translations are being loaded, you can now add string +keys, such as field labels, and their translations to your translation files. + +For more details on translations, see :doc:`/book/translation`. Validation ~~~~~~~~~~ +The Form component comes with tight (but optional) integration with Symfony's +Validator component. If you're using a different solution for validation, +no problem! Simply take the bound data of your form (which is an array or +object) and pass it through your own validation system. + +To use the integration with Symfony's Validator component, first make sure +it's installed in your application. If you're using Composer and want to +install the latest 2.1 version, add this to your ``composer.json``: + .. code-block:: json { @@ -200,7 +282,13 @@ Validation } } -.. code-block:: php +If you're not familiar with Symfony's Validator component, read more about +it: :doc:`/book/validation`. The Form component comes with a +:class:`Symfony\\Component\\Form\\Extension\\Validator\\ValidatorExtension` +class, which automatically applies validation to your data on bind. These +errors are then mapped to the correct field and rendered. + +Your integration with the Validation component will look something like this:: use Symfony\Component\Form\Forms; use Symfony\Component\Form\Extension\Validator\ValidatorExtension; @@ -210,8 +298,10 @@ Validation define('VENDOR_FORM_DIR', VENDOR_DIR . '/symfony/form/Symfony/Component/Form'); define('VENDOR_VALIDATOR_DIR', VENDOR_DIR . '/symfony/validator/Symfony/Component/Validator'); + // create the validator - details will vary $validator = Validation::createValidator(); + // there are built-in translations for the core error messages $translator->addResource('xlf', VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); $translator->addResource('xlf', VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); @@ -220,17 +310,43 @@ Validation ->addExtension(new ValidatorExtension($validator)) ->getFormFactory(); -TODO: Cross reference the validator docs where the detailed configuration of -the validator is explained. +To learn more, skip down to the :ref:`component-form-intro-validation` section. Accessing the Form Factory ~~~~~~~~~~~~~~~~~~~~~~~~~~ -Either in global variable or by use of DICs. +Your application only needs one form factory, and that one factory object +should be used to create any and all form objects in your application. This +means that you should create it in some central, bootstrap part of your application +and then access it whenever you need to build a form. + +Exactly how you gain access to your one form factory is up to you. If you're +using a :term`Service Container`, then you should add the form factory to +your container and grab it out whenever you need to. If your application +uses global or static variables (not usually a good idea), then you can store +the object on some static class or do something similar. + +Regardless of how you architect your application, just remember that you +should only have one form factory and that you'll need to be able to access +it throughout your application. + +.. _component-form-intro-create-simple-form: Creating a Simple Form ---------------------- +.. tip:: + + If you're using the Symfony2 framework, then the form factory is available + automatically as a service called ``form.factory``. Also, the default + base controller class has a :method:`Symfony\\Bundle\\FrameworkBundle\\Controller::createFormBuilder` + method, which is a shortcut to fetch the form factory and call ``createBuilder`` + on it. + +Creating a form is done via a :class:`Symfony\\Component\\Form\\FormBuilder` +object, where you build and configure different fields. The form builder +is created from the form factory. + .. configuration-block:: .. code-block:: php-standalone @@ -256,6 +372,8 @@ Creating a Simple Form { public function newAction(Request $request) { + // createFormBuilder is a shortcut to get the "form factory" + // and then call "createBuilder" on it $form = $this->createFormBuilder() ->add('task', 'text') ->add('dueDate', 'date') @@ -267,9 +385,21 @@ Creating a Simple Form } } +As you can see, creating a form is like writing a recipe: you call ``add`` +for each new field you want to create. The first argument to ``add`` is the +name of your field, and the second is the field "type". The Form component +comes with a lot of :ref:`built-in types`. + +Now that you've built your form, learn how to :ref:`render` +it and :ref:`process the form submission`. + Setting Default Values ~~~~~~~~~~~~~~~~~~~~~~ +If you need you form to load with some default values (or you're building +an "edit" form), simply pass in the default data when creating your form +builder: + .. configuration-block:: .. code-block:: php-standalone @@ -294,6 +424,14 @@ Setting Default Values ->add('dueDate', 'date') ->getForm(); +.. tip:: + + In this example, the default data is an array. Later, when you use the + :ref:`data_class` option to bind data directly + to objects, your default data will be an instance of that object. + +.. _component-form-intro-rendering-form: + Rendering the Form ~~~~~~~~~~~~~~~~~~ @@ -319,9 +457,14 @@ 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. +.. _component-form-intro-handling-submission: + Handling Form Submissions ~~~~~~~~~~~~~~~~~~~~~~~~~ +To handle form submissions, use the :method:`Symfony\\Component\\Form\\Form::bind` +method: + .. configuration-block:: .. code-block:: php-standalone @@ -379,10 +522,17 @@ Handling Form Submissions // ... } +This defines a common form "workflow", which looks like this: + +1) Build your form; +2) If POST, process the form by calling ``bind``; +3a) If the form is valid, perform some action and redirect; +3b) If the form is invalid, re-render the form (which will now contain errors) .. note:: - When not using HttpFoundation:: + If you're not using HttpFoundation, just pass the POST'ed data directly + to ``bind``: if (isset($_POST[$form->getName()])) { $form->bind($_POST[$form->getName()) @@ -390,12 +540,18 @@ Handling Form Submissions // ... } - but! no support for file uploads then + If you're uploading files, you'll need to do a little bit more work by + merging the ``$_POST`` array with the ``$_FILES`` array before passing + it into ``bind``. +.. _component-form-intro-validation: Form Validation ~~~~~~~~~~~~~~~ +The easiest way to add validation to your form is via the ``constraints`` +option when building each field: + .. configuration-block:: .. code-block:: php-standalone @@ -432,7 +588,13 @@ Form Validation )) ->getForm(); +When the form is bound, these validation constraints will be applied automatically +and the errors will display next to the fields on error. + +.. note:: -That's all you need to know for creating a basic form! + For a list of all of the built-in validation constraints, see + :doc:`/reference/constraints`. .. _Packagist: https://packagist.org/packages/symfony/form +.. _Twig: http://twig.sensiolabs.org From 37105521af99e73d5a6426f2f45004f5cce6d6dd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 22 Nov 2012 13:42:32 -0500 Subject: [PATCH 5/7] [Form] Making updates per @bschussek and fixing some markup issues --- components/form/introduction.rst | 49 +++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/components/form/introduction.rst b/components/form/introduction.rst index f512f624d8f..616e01dd12d 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -9,7 +9,7 @@ The Form Component forms. The form component is a tool to help you solve the problem of allowing end-users -to interact with the data and modify the data in your application. And thought +to interact with the data and modify the data in your application. And though traditionally this has been through HTML forms, the component focuses on processing data to and from your client and application, whether that data be from a normal form post or from an API. @@ -155,7 +155,7 @@ line to your ``composer.json`` file: } } -The TwigBridge integration provides you with several :doc:`Twig Functions` that help you render each the HTML widget, label and error for each field (as well as a few other things). To configure the integration, you'll need to bootstrap or access Twig and add the :class:`Symfony\\Bridge\\Twig\\Extension\\FormExtension`:: @@ -267,8 +267,8 @@ Validation The Form component comes with tight (but optional) integration with Symfony's Validator component. If you're using a different solution for validation, -no problem! Simply take the bound data of your form (which is an array or -object) and pass it through your own validation system. +no problem! Simply take the submitted/bound data of your form (which is an +array or object) and pass it through your own validation system. To use the integration with Symfony's Validator component, first make sure it's installed in your application. If you're using Composer and want to @@ -302,8 +302,18 @@ Your integration with the Validation component will look something like this:: $validator = Validation::createValidator(); // there are built-in translations for the core error messages - $translator->addResource('xlf', VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); - $translator->addResource('xlf', VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); + $translator->addResource( + 'xlf', + VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', + 'en', + 'validators' + ); + $translator->addResource( + 'xlf', + VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', + 'en', + 'validators' + ); $formFactory = Forms::createFormFactoryBuilder() // ... @@ -320,6 +330,12 @@ should be used to create any and all form objects in your application. This means that you should create it in some central, bootstrap part of your application and then access it whenever you need to build a form. +.. note:: + + In this document, the form factory is always a locally variable called + ``$formFactory``. The point here is that you will probably need to create + this object in some more "global" way so you can access it from anywhere. + Exactly how you gain access to your one form factory is up to you. If you're using a :term`Service Container`, then you should add the form factory to your container and grab it out whenever you need to. If your application @@ -373,7 +389,7 @@ is created from the form factory. public function newAction(Request $request) { // createFormBuilder is a shortcut to get the "form factory" - // and then call "createBuilder" on it + // and then call "createBuilder()" on it $form = $this->createFormBuilder() ->add('task', 'text') ->add('dueDate', 'date') @@ -388,7 +404,7 @@ is created from the form factory. As you can see, creating a form is like writing a recipe: you call ``add`` for each new field you want to create. The first argument to ``add`` is the name of your field, and the second is the field "type". The Form component -comes with a lot of :ref:`built-in types`. +comes with a lot of :doc:`built-in types`. Now that you've built your form, learn how to :ref:`render` it and :ref:`process the form submission`. @@ -507,6 +523,7 @@ method: ->add('dueDate', 'date') ->getForm(); + // only process the form if the request is a POST request if ($request->isMethod('POST')) { $form->bind($request); @@ -522,17 +539,20 @@ method: // ... } -This defines a common form "workflow", which looks like this: +This defines a common form "workflow", which contains 3 different possibilities: + +1) On the initial GET request (i.e. when the user "surfs" to your page), + build your form and render it; + +If the request is a POST, process the submitted data (via ``bind``). Then: -1) Build your form; -2) If POST, process the form by calling ``bind``; -3a) If the form is valid, perform some action and redirect; -3b) If the form is invalid, re-render the form (which will now contain errors) +2) if the form is invalid, re-render the form (which will now contain errors) +3) if the form is valid, perform some action and redirect; .. note:: If you're not using HttpFoundation, just pass the POST'ed data directly - to ``bind``: + to ``bind``:: if (isset($_POST[$form->getName()])) { $form->bind($_POST[$form->getName()) @@ -598,3 +618,4 @@ and the errors will display next to the fields on error. .. _Packagist: https://packagist.org/packages/symfony/form .. _Twig: http://twig.sensiolabs.org +.. _`Twig Configuration`: http://twig.sensiolabs.org/doc/intro.html \ No newline at end of file From c6428b4b115d98e1cc66a003bab85e8d2c229038 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 17 Dec 2012 21:34:24 -0600 Subject: [PATCH 6/7] Removing .gitignore file - putting the sphinx build files right in this directory is just one option --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 0e7262e0af3..00000000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -Makefile -_exts -_build -conf.py -make.bat From 7f2f8c6e4b2d533792cbf1fe462e4cb616385329 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 17 Dec 2012 21:39:43 -0600 Subject: [PATCH 7/7] [Form] Removing constants, just to avoid confusion about them being somehow important internally --- components/form/introduction.rst | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 616e01dd12d..28efd4ce06f 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -102,13 +102,13 @@ snippet adds CSRF protection to the form factory:: use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; use Symfony\Component\HttpFoundation\Session\Session; - // generate a CSRF secret, which you *may* want to set as a constant - define('CSRF_SECRET', ''); + // generate a CSRF secret from somewhere + $csrfSecret = ''; // create a Session object from the HttpFoundation component $session = new Session(); - $csrfProvider = new SessionCsrfProvider($session, CSRF_SECRET); + $csrfProvider = new SessionCsrfProvider($session, $csrfSecret); $formFactory = Forms::createFormFactoryBuilder() // ... @@ -132,7 +132,7 @@ and validated when binding the form. use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; - $csrfProvider = new DefaultCsrfProvider(CSRF_SECRET); + $csrfProvider = new DefaultCsrfProvider($csrfSecret); Twig Templating ~~~~~~~~~~~~~~~ @@ -167,19 +167,19 @@ to bootstrap or access Twig and add the :class:`Symfony\\Bridge\\Twig\\Extension // the Twig file that holds all the default markup for rendering forms // this file comes with TwigBridge - define('DEFAULT_FORM_THEME', 'form_div_layout.html.twig'); + $defaultFormTheme = 'form_div_layout.html.twig'; - define('VENDOR_DIR', realpath(__DIR__ . '/../vendor')); + $vendorDir = realpath(__DIR__ . '/../vendor'); // the path to TwigBridge so Twig can locate the form_div_layout.html.twig file - define('VENDOR_TWIG_BRIDGE_DIR', VENDOR_DIR . '/symfony/twig-bridge/Symfony/Bridge/Twig'); + $vendorTwigBridgeDir = $vendorDir . '/symfony/twig-bridge/Symfony/Bridge/Twig'; // the path to your other templates - define('VIEWS_DIR', realpath(__DIR__ . '/../views')); + $viewsDir = realpath(__DIR__ . '/../views'); $twig = new Twig_Environment(new Twig_Loader_Filesystem(array( - VIEWS_DIR, - VENDOR_TWIG_BRIDGE_DIR . '/Resources/views/Form', + $viewsDir, + $vendorTwigBridgeDir . '/Resources/views/Form', ))); - $formEngine = new TwigRendererEngine(array(DEFAULT_FORM_THEME)); + $formEngine = new TwigRendererEngine(array($defaultFormTheme)); $formEngine->setEnvironment($twig); // add the FormExtension to Twig $twig->addExtension(new FormExtension(new TwigRenderer($formEngine, $csrfProvider))); @@ -294,9 +294,9 @@ Your integration with the Validation component will look something like this:: use Symfony\Component\Form\Extension\Validator\ValidatorExtension; use Symfony\Component\Validator\Validation; - define('VENDOR_DIR', realpath(__DIR__ . '/../vendor')); - define('VENDOR_FORM_DIR', VENDOR_DIR . '/symfony/form/Symfony/Component/Form'); - define('VENDOR_VALIDATOR_DIR', VENDOR_DIR . '/symfony/validator/Symfony/Component/Validator'); + $vendorDir = realpath(__DIR__ . '/../vendor'); + $vendorFormDir = $vendorDir . '/symfony/form/Symfony/Component/Form'; + $vendorValidatorDir = $vendorDir . '/symfony/validator/Symfony/Component/Validator'; // create the validator - details will vary $validator = Validation::createValidator(); @@ -304,13 +304,13 @@ Your integration with the Validation component will look something like this:: // there are built-in translations for the core error messages $translator->addResource( 'xlf', - VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', + $vendorFormDir . '/Resources/translations/validators.en.xlf', 'en', 'validators' ); $translator->addResource( 'xlf', - VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', + $vendorValidatorDir . '/Resources/translations/validators.en.xlf', 'en', 'validators' );