diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 6d70561e914..91910a5582b 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -15,9 +15,7 @@ Building Forms The Form component allows you to build forms right inside your controller code. Honestly, unless you need to reuse the form somewhere else, that's totally fine. But for organize and reuse, we recommend that you define each -form in its own PHP class: - -.. code-block:: php +form in its own PHP class:: namespace AppBundle\Form; @@ -51,9 +49,7 @@ form in its own PHP class: } } -To use the class, use ``createForm`` and instantiate the new class: - -.. code-block:: php +To use the class, use ``createForm`` and instantiate the new class:: use AppBundle\Form\PostType; // ... @@ -108,9 +104,7 @@ directly in your form class, this would effectively limit the scope of that form This form *may* have been designed for creating posts, but if you wanted to reuse it for editing posts, the button label would be wrong. Instead, -some developers configure form buttons in the controller: - -.. code-block:: php +some developers configure form buttons in the controller:: namespace AppBundle\Controller\Admin; diff --git a/book/doctrine.rst b/book/doctrine.rst index 871695ff21d..5ec35ce9338 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -32,15 +32,6 @@ The easiest way to understand how Doctrine works is to see it in action. In this section, you'll configure your database, create a ``Product`` object, persist it to the database and fetch it back out. -.. sidebar:: Code along with the Example - - If you want to follow along with the example in this chapter, create - an ``AcmeStoreBundle`` via: - - .. code-block:: bash - - $ php app/console generate:bundle --namespace=Acme/StoreBundle - Configuring the Database ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -202,17 +193,15 @@ Creating an Entity Class Suppose you're building an application where products need to be displayed. Without even thinking about Doctrine or databases, you already know that you need a ``Product`` object to represent those products. Create this class -inside the ``Entity`` directory of your ``AcmeStoreBundle``:: +inside the ``Entity`` directory of your ``AppBundle``:: - // src/Acme/StoreBundle/Entity/Product.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/Product.php + namespace AppBundle\Entity; class Product { protected $name; - protected $price; - protected $description; } @@ -258,8 +247,8 @@ in a number of different formats including YAML, XML or directly inside the .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Product.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/Product.php + namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; @@ -294,8 +283,8 @@ in a number of different formats including YAML, XML or directly inside the .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml - Acme\StoreBundle\Entity\Product: + # src/AppBundle/Resources/config/doctrine/Product.orm.yml + AppBundle\Entity\Product: type: entity table: product id: @@ -314,14 +303,14 @@ in a number of different formats including YAML, XML or directly inside the .. code-block:: xml - + - + @@ -394,7 +383,7 @@ a regular PHP class, you need to create getter and setter methods (e.g. ``getNam .. code-block:: bash - $ php app/console doctrine:generate:entities Acme/StoreBundle/Entity/Product + $ php app/console doctrine:generate:entities AppBundle/Entity/Product This command makes sure that all of the getters and setters are generated for the ``Product`` class. This is a safe command - you can run it over and @@ -435,7 +424,9 @@ mapping information) of a bundle or an entire namespace: .. code-block:: bash - $ php app/console doctrine:generate:entities AcmeStoreBundle + # generates all entities in the AppBundle + $ php app/console doctrine:generate:entities AppBundle + # generates all entities of bundles in the Acme namespace $ php app/console doctrine:generate:entities Acme .. note:: @@ -485,17 +476,16 @@ Persisting Objects to the Database Now that you have a mapped ``Product`` entity and corresponding ``product`` table, you're ready to persist data to the database. From inside a controller, this is pretty easy. Add the following method to the ``DefaultController`` -of the bundle: +of the bundle:: -.. code-block:: php - :linenos: - // src/Acme/StoreBundle/Controller/DefaultController.php + // src/AppBundle/Controller/DefaultController.php // ... - use Acme\StoreBundle\Entity\Product; + use AppBundle\Entity\Product; use Symfony\Component\HttpFoundation\Response; + // ... public function createAction() { $product = new Product(); @@ -504,6 +494,7 @@ of the bundle: $product->setDescription('Lorem ipsum dolor'); $em = $this->getDoctrine()->getManager(); + $em->persist($product); $em->flush(); @@ -526,17 +517,17 @@ of the bundle: Take a look at the previous example in more detail: -* **lines 9-12** In this section, you instantiate and work with the ``$product`` +* **lines 10-13** In this section, you instantiate and work with the ``$product`` object like any other, normal PHP object. -* **line 14** This line fetches Doctrine's *entity manager* object, which is +* **line 15** This line fetches Doctrine's *entity manager* object, which is responsible for handling the process of persisting and fetching objects to and from the database. -* **line 15** The ``persist()`` method tells Doctrine to "manage" the ``$product`` +* **line 16** The ``persist()`` method tells Doctrine to "manage" the ``$product`` object. This does not actually cause a query to be made to the database (yet). -* **line 16** When the ``flush()`` method is called, Doctrine looks through +* **line 17** When the ``flush()`` method is called, Doctrine looks through all of the objects that it's managing to see if they need to be persisted to the database. In this example, the ``$product`` object has not been persisted yet, so the entity manager executes an ``INSERT`` query and a @@ -544,12 +535,12 @@ Take a look at the previous example in more detail: .. note:: - In fact, since Doctrine is aware of all your managed entities, when you call - the ``flush()`` method, it calculates an overall changeset and executes - the queries in the correct order. It utilizes cached prepared statement to - slightly improve the performance. For example, if you persist a total of 100 - ``Product`` objects and then subsequently call ``flush()``, Doctrine will - execute 100 ``INSERT`` queries using a single prepared statement object. + In fact, since Doctrine is aware of all your managed entities, when you call + the ``flush()`` method, it calculates an overall changeset and executes + the queries in the correct order. It utilizes cached prepared statement to + slightly improve the performance. For example, if you persist a total of 100 + ``Product`` objects and then subsequently call ``flush()``, Doctrine will + execute 100 ``INSERT`` queries using a single prepared statement object. When creating or updating objects, the workflow is always the same. In the next section, you'll see how Doctrine is smart enough to automatically issue @@ -571,7 +562,7 @@ on its ``id`` value:: public function showAction($id) { $product = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->find($id); if (!$product) { @@ -595,12 +586,12 @@ job is to help you fetch entities of a certain class. You can access the repository object for an entity class via:: $repository = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product'); + ->getRepository('AppBundle:Product'); .. note:: - The ``AcmeStoreBundle:Product`` string is a shortcut you can use anywhere - in Doctrine instead of the full class name of the entity (i.e. ``Acme\StoreBundle\Entity\Product``). + The ``AppBundle:Product`` string is a shortcut you can use anywhere + in Doctrine instead of the full class name of the entity (i.e. ``AppBundle\Entity\Product``). As long as your entity lives under the ``Entity`` namespace of your bundle, this will work. @@ -660,7 +651,7 @@ you have a route that maps a product id to an update action in a controller:: public function updateAction($id) { $em = $this->getDoctrine()->getManager(); - $product = $em->getRepository('AcmeStoreBundle:Product')->find($id); + $product = $em->getRepository('AppBundle:Product')->find($id); if (!$product) { throw $this->createNotFoundException( @@ -726,7 +717,7 @@ cost more than ``19.99``, ordered from cheapest to most expensive. You can use Doctrine's ``QueryBuilder`` for this:: $repository = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product'); + ->getRepository('AppBundle:Product'); $query = $repository->createQueryBuilder('p') ->where('p.price > :price') @@ -764,7 +755,7 @@ directly using DQL:: $em = $this->getDoctrine()->getManager(); $query = $em->createQuery( 'SELECT p - FROM AcmeStoreBundle:Product p + FROM AppBundle:Product p WHERE p.price > :price ORDER BY p.price ASC' )->setParameter('price', '19.99'); @@ -773,7 +764,7 @@ directly using DQL:: If you're comfortable with SQL, then DQL should feel very natural. The biggest difference is that you need to think in terms of "objects" instead of rows -in a database. For this reason, you select *from* the ``AcmeStoreBundle:Product`` +in a database. For this reason, you select *from* the ``AppBundle:Product`` *object* and then alias it as ``p`` (as you see, this is equal to what you already did in the previous section). @@ -796,13 +787,13 @@ To do this, add the name of the repository class to your mapping definition: .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Product.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/Product.php + namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** - * @ORM\Entity(repositoryClass="Acme\StoreBundle\Entity\ProductRepository") + * @ORM\Entity(repositoryClass="AppBundle\Entity\ProductRepository") */ class Product { @@ -811,15 +802,15 @@ To do this, add the name of the repository class to your mapping definition: .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml - Acme\StoreBundle\Entity\Product: + # src/AppBundle/Resources/config/doctrine/Product.orm.yml + AppBundle\Entity\Product: type: entity - repositoryClass: Acme\StoreBundle\Entity\ProductRepository + repositoryClass: AppBundle\Entity\ProductRepository # ... .. code-block:: xml - + + name="AppBundle\Entity\Product" + repository-class="AppBundle\Entity\ProductRepository"> @@ -839,7 +830,7 @@ used earlier to generate the missing getter and setter methods: .. code-block:: bash - $ php app/console doctrine:generate:entities Acme + $ php app/console doctrine:generate:entities AppBundle Next, add a new method - ``findAllOrderedByName()`` - to the newly generated repository class. This method will query for all of the ``Product`` entities, @@ -847,8 +838,8 @@ ordered alphabetically. .. code-block:: php - // src/Acme/StoreBundle/Entity/ProductRepository.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/ProductRepository.php + namespace AppBundle\Entity; use Doctrine\ORM\EntityRepository; @@ -858,7 +849,7 @@ ordered alphabetically. { return $this->getEntityManager() ->createQuery( - 'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC' + 'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC' ) ->getResult(); } @@ -872,7 +863,7 @@ ordered alphabetically. You can use this new method just like the default finder methods of the repository:: $em = $this->getDoctrine()->getManager(); - $products = $em->getRepository('AcmeStoreBundle:Product') + $products = $em->getRepository('AppBundle:Product') ->findAllOrderedByName(); .. note:: @@ -893,7 +884,7 @@ you can let Doctrine create the class for you. .. code-block:: bash - $ php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Category" --fields="name:string(255)" + $ php app/console doctrine:generate:entity --entity="AppBundle:Category" --fields="name:string(255)" This task generates the ``Category`` entity for you, with an ``id`` field, a ``name`` field and the associated getter and setter functions. @@ -908,7 +899,7 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Category.php + // src/AppBundle/Entity/Category.php // ... use Doctrine\Common\Collections\ArrayCollection; @@ -930,8 +921,8 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Category.orm.yml - Acme\StoreBundle\Entity\Category: + # src/AppBundle/Resources/config/doctrine/Category.orm.yml + AppBundle\Entity\Category: type: entity # ... oneToMany: @@ -942,14 +933,14 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: xml - + - + + - + getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->find($id); $categoryName = $product->getCategory()->getName(); @@ -1162,7 +1153,7 @@ You can also query in the other direction:: public function showProductAction($id) { $category = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Category') + ->getRepository('AppBundle:Category') ->find($id); $products = $category->getProducts(); @@ -1183,12 +1174,12 @@ to the given ``Category`` object via their ``category_id`` value. example:: $product = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->find($id); $category = $product->getCategory(); - // prints "Proxies\AcmeStoreBundleEntityCategoryProxy" + // prints "Proxies\AppBundleEntityCategoryProxy" echo get_class($category); This proxy object extends the true ``Category`` object, and looks and @@ -1220,12 +1211,12 @@ Of course, if you know up front that you'll need to access both objects, you can avoid the second query by issuing a join in the original query. Add the following method to the ``ProductRepository`` class:: - // src/Acme/StoreBundle/Entity/ProductRepository.php + // src/AppBundle/Entity/ProductRepository.php public function findOneByIdJoinedToCategory($id) { $query = $this->getEntityManager() ->createQuery( - 'SELECT p, c FROM AcmeStoreBundle:Product p + 'SELECT p, c FROM AppBundle:Product p JOIN p.category c WHERE p.id = :id' )->setParameter('id', $id); @@ -1243,7 +1234,7 @@ object and its related ``Category`` with just one query:: public function showAction($id) { $product = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->findOneByIdJoinedToCategory($id); $category = $product->getCategory(); @@ -1304,7 +1295,7 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Product.php + // src/AppBundle/Entity/Product.php /** * @ORM\PrePersist @@ -1316,8 +1307,8 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml - Acme\StoreBundle\Entity\Product: + # src/AppBundle/Resources/config/doctrine/Product.orm.yml + AppBundle\Entity\Product: type: entity # ... lifecycleCallbacks: @@ -1325,14 +1316,14 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: xml - + - + diff --git a/book/forms.rst b/book/forms.rst index 27135c4f817..dab0a3137f3 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -12,8 +12,9 @@ learning the most important features of the form library along the way. .. note:: The Symfony Form component is a standalone library that can be used outside - of Symfony projects. For more information, see the `Symfony Form component`_ - on GitHub. + of Symfony projects. For more information, see the + :doc:`Form component documentation ` on + GitHub. .. index:: single: Forms; Create a simple form @@ -26,13 +27,12 @@ display "tasks". Because your users will need to edit and create tasks, you're going to need to build a form. But before you begin, first focus on the generic ``Task`` class that represents and stores the data for a single task:: - // src/Acme/TaskBundle/Entity/Task.php - namespace Acme\TaskBundle\Entity; + // src/AppBundle/Entity/Task.php + namespace AppBundle\Entity; class Task { protected $task; - protected $dueDate; public function getTask() @@ -56,16 +56,6 @@ going to need to build a form. But before you begin, first focus on the generic } } -.. note:: - - If you're coding along with this example, create the ``AcmeTaskBundle`` - first by running the following command (and accepting all of the default - options): - - .. code-block:: bash - - $ php app/console generate:bundle --namespace=Acme/TaskBundle - This class is a "plain-old-PHP-object" because, so far, it has nothing to do with Symfony or any other library. It's quite simply a normal PHP object that directly solves a problem inside *your* application (i.e. the need to @@ -84,11 +74,11 @@ render the actual HTML form. In Symfony, this is done by building a form object and then rendering it in a template. For now, this can all be done from inside a controller:: - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Acme\TaskBundle\Entity\Task; + use AppBundle\Entity\Task; use Symfony\Component\HttpFoundation\Request; class DefaultController extends Controller @@ -106,7 +96,7 @@ from inside a controller:: ->add('save', 'submit', array('label' => 'Create Task')) ->getForm(); - return $this->render('AcmeTaskBundle:Default:new.html.twig', array( + return $this->render('Default/new.html.twig', array( 'form' => $form->createView(), )); } @@ -154,15 +144,17 @@ helper functions: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} - - {{ form(form) }} + {# app/Resources/views/Default/new.html.twig #} + {{ form_start(form) }} + {{ form_widget(form) }} + {{ form_end(form) }} .. code-block:: html+php - - - form($form) ?> + + start($form) ?> + widget($form) ?> + end($form) ?> .. image:: /images/book/form-simple.png :align: center @@ -173,12 +165,24 @@ helper functions: 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(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 ``
`` 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. +That's it! Just three lines are needed to render the complete form: + +* ``form_start(form)`` - Renders the start tag of the form, including the + correct enctype attribute when using file uploads; + +* ``form_widget(form)`` - Renders all of the fields, which includes the field + element itself, a label and any validation error messages for the field; + +* ``form_end()`` - Renders the end tag of the form and any fields that have not + yet been rendered, in case you rendered each field yourself. This is useful + for rendering hidden fields and taking advantage of the automatic + :ref:`CSRF Protection `. + +.. seealso:: + + 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"). @@ -339,8 +343,8 @@ object. .. code-block:: yaml - # Acme/TaskBundle/Resources/config/validation.yml - Acme\TaskBundle\Entity\Task: + # AppBundle/Resources/config/validation.yml + AppBundle\Entity\Task: properties: task: - NotBlank: ~ @@ -350,7 +354,7 @@ object. .. code-block:: php-annotations - // Acme/TaskBundle/Entity/Task.php + // AppBundle/Entity/Task.php use Symfony\Component\Validator\Constraints as Assert; class Task @@ -369,14 +373,14 @@ object. .. code-block:: xml - + - + @@ -389,7 +393,7 @@ object. .. code-block:: php - // Acme/TaskBundle/Entity/Task.php + // AppBundle/Entity/Task.php use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Type; @@ -435,14 +439,12 @@ corresponding errors printed out with the form. .. code-block:: html+jinja - {# src/Acme/DemoBundle/Resources/views/Default/new.html.twig #} - + {# app/Resources/views/Default/new.html.twig #} {{ form(form, {'attr': {'novalidate': 'novalidate'}}) }} .. code-block:: html+php - - + form($form, array( 'attr' => array('novalidate' => 'novalidate'), )) ?> @@ -525,7 +527,7 @@ to an array callback:: { $resolver->setDefaults(array( 'validation_groups' => array( - 'Acme\AcmeBundle\Entity\Client', + 'AppBundle\Entity\Client', 'determineValidationGroups', ), )); @@ -748,7 +750,7 @@ 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 #} + {# app/Resources/views/Default/new.html.twig #} {{ form_start(form) }} {{ form_errors(form) }} @@ -758,7 +760,7 @@ of code. Of course, you'll usually need much more flexibility when rendering: .. code-block:: html+php - + start($form) ?> errors($form) ?> @@ -766,24 +768,19 @@ of code. Of course, you'll usually need much more flexibility when rendering: row($form['dueDate']) ?> end($form) ?> -Take a look at each part: - -* ``form_start(form)`` - Renders the start tag of the form. +You already know the ``form_start()`` and ``form_end()`` functions, but what do +the other functions do? * ``form_errors(form)`` - Renders any errors global to the whole form (field-specific errors are displayed next to each field); * ``form_row(form.dueDate)`` - Renders the label, any errors, and the HTML form widget for the given field (e.g. ``dueDate``) inside, by default, a - ``div`` element; - -* ``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 `. + ``div`` element. The majority of the work is done by the ``form_row`` helper, which renders -the label, errors and HTML form widget of each field inside a ``div`` tag -by default. In the :ref:`form-theming` section, you'll learn how the ``form_row`` +the label, errors and HTML form widget of each field inside a ``div`` tag by +default. In the :ref:`form-theming` section, you'll learn how the ``form_row`` output can be customized on many different levels. .. tip:: @@ -970,19 +967,12 @@ to the ``form()`` or the ``form_start()`` helper: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} - {{ form(form, {'action': path('target_route'), 'method': 'GET'}) }} - + {# app/Resources/views/Default/new.html.twig #} {{ 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', @@ -1010,8 +1000,8 @@ However, a better practice is to build the form in a separate, standalone PHP class, which can then be reused anywhere in your application. Create a new class that will house the logic for building the task form:: - // src/Acme/TaskBundle/Form/Type/TaskType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/Type/TaskType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -1036,10 +1026,10 @@ This new class contains all the directions needed to create the task form (note that the ``getName()`` method should return a unique identifier for this form "type"). It can be used to quickly build a form object in the controller:: - // src/Acme/TaskBundle/Controller/DefaultController.php + // src/AppBundle/Controller/DefaultController.php // add this new use statement at the top of the class - use Acme\TaskBundle\Form\Type\TaskType; + use AppBundle\Form\Type\TaskType; public function newAction() { @@ -1058,7 +1048,7 @@ the choice is ultimately up to you. .. sidebar:: Setting the ``data_class`` Every form needs to know the name of the class that holds the underlying - data (e.g. ``Acme\TaskBundle\Entity\Task``). Usually, this is just guessed + data (e.g. ``AppBundle\Entity\Task``). Usually, this is just guessed based off of the object passed to the second argument to ``createForm`` (i.e. ``$task``). Later, when you begin embedding forms, this will no longer be sufficient. So, while not always necessary, it's generally a @@ -1070,7 +1060,7 @@ the choice is ultimately up to you. public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', + 'data_class' => 'AppBundle\Entity\Task', )); } @@ -1121,16 +1111,16 @@ easy to use in your application. .. code-block:: yaml - # src/Acme/TaskBundle/Resources/config/services.yml + # src/AppBundle/Resources/config/services.yml services: acme_demo.form.type.task: - class: Acme\TaskBundle\Form\Type\TaskType + class: AppBundle\Form\Type\TaskType tags: - { name: form.type, alias: task } .. code-block:: xml - + + class="AppBundle\Form\Type\TaskType"> @@ -1148,11 +1138,11 @@ easy to use in your application. .. code-block:: php - // src/Acme/TaskBundle/Resources/config/services.php + // src/AppBundle/Resources/config/services.php $container ->register( 'acme_demo.form.type.task', - 'Acme\TaskBundle\Form\Type\TaskType' + 'AppBundle\Form\Type\TaskType' ) ->addTag('form.type', array( 'alias' => 'task', @@ -1161,7 +1151,7 @@ easy to use in your application. That's it! Now you can use your form type directly in a controller:: - // src/Acme/TaskBundle/Controller/DefaultController.php + // src/AppBundle/Controller/DefaultController.php // ... public function newAction() @@ -1174,7 +1164,7 @@ That's it! Now you can use your form type directly in a controller:: or even use from within the form type of another form:: - // src/Acme/TaskBundle/Form/Type/ListType.php + // src/AppBundle/Form/Type/ListType.php // ... class ListType extends AbstractType @@ -1242,8 +1232,8 @@ Embedding a Single Object Suppose that each ``Task`` belongs to a simple ``Category`` object. Start, of course, by creating the ``Category`` object:: - // src/Acme/TaskBundle/Entity/Category.php - namespace Acme\TaskBundle\Entity; + // src/AppBundle/Entity/Category.php + namespace AppBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -1264,7 +1254,7 @@ Next, add a new ``category`` property to the ``Task`` class:: // ... /** - * @Assert\Type(type="Acme\TaskBundle\Entity\Category") + * @Assert\Type(type="AppBundle\Entity\Category") * @Assert\Valid() */ protected $category; @@ -1291,8 +1281,8 @@ Next, add a new ``category`` property to the ``Task`` class:: Now that your application has been updated to reflect the new requirements, create a form class so that a ``Category`` object can be modified by the user:: - // src/Acme/TaskBundle/Form/Type/CategoryType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/Type/CategoryType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -1308,7 +1298,7 @@ create a form class so that a ``Category`` object can be modified by the user:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Category', + 'data_class' => 'AppBundle\Entity\Category', )); } @@ -1412,7 +1402,7 @@ do this, create a new template file that will store the new markup: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #} + {# app/Resources/views/Form/fields.html.twig #} {% block form_row %} {% spaceless %}
@@ -1425,7 +1415,7 @@ do this, create a new template file that will store the new markup: .. code-block:: html+php - +
label($form, $label) ?> errors($form) ?> @@ -1441,19 +1431,19 @@ renders the form: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} - {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %} + {# app/Resources/views/Default/new.html.twig #} + {% form_theme form 'Form/fields.html.twig' %} - {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' 'AcmeTaskBundle:Form:fields2.html.twig' %} + {% form_theme form 'Form/fields.html.twig' 'Form/fields2.html.twig' %} - + {# ... render the form #} .. code-block:: html+php - - setTheme($form, array('AcmeTaskBundle:Form')) ?> + + setTheme($form, array('Form')) ?> - setTheme($form, array('AcmeTaskBundle:Form', 'AcmeTaskBundle:Form')) ?> + setTheme($form, array('Form', 'Form2')) ?> @@ -1474,14 +1464,6 @@ 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. -.. code-block:: html+jinja - - {# 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', 'AcmeTaskBundle:Form:fields2.html.twig'] %} - For a more extensive discussion, see :doc:`/cookbook/form/form_customization`. .. index:: @@ -1496,9 +1478,10 @@ In Symfony, every part of a form that is rendered - HTML form elements, errors, labels, etc. - is defined in a base theme, which is a collection of blocks in Twig and a collection of template files in PHP. -In Twig, every block needed is defined in a single template file (`form_div_layout.html.twig`_) -that lives inside the `Twig Bridge`_. Inside this file, you can see every block -needed to render a form and every default field type. +In Twig, every block needed is defined in a single template file (e.g. +`form_div_layout.html.twig`_) that lives inside the `Twig Bridge`_. Inside this +file, you can see every block needed to render a form and every default field +type. In PHP, the fragments are individual template files. By default they are located in the `Resources/views/Form` directory of the framework bundle (`view on GitHub`_). @@ -1529,7 +1512,7 @@ are 4 possible *parts* of a form that can be rendered: .. note:: - There are actually 2 other *parts* - ``rows`` and ``rest`` - + 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 @@ -1588,7 +1571,7 @@ file: twig: form: resources: - - 'AcmeTaskBundle:Form:fields.html.twig' + - 'Form/fields.html.twig' # ... .. code-block:: xml @@ -1603,7 +1586,7 @@ file: - AcmeTaskBundle:Form:fields.html.twig + Form/fields.html.twig @@ -1615,7 +1598,7 @@ file: $container->loadFromExtension('twig', array( 'form' => array( 'resources' => array( - 'AcmeTaskBundle:Form:fields.html.twig', + 'Form/fields.html.twig', ), ), // ... @@ -1631,7 +1614,7 @@ to define form output. .. code-block:: html+jinja - {% extends '::base.html.twig' %} + {% extends 'base.html.twig' %} {# import "_self" as the form theme #} {% form_theme form _self %} @@ -1661,7 +1644,7 @@ to define form output. PHP ... -To automatically include the customized templates from the ``Acme/TaskBundle/Resources/views/Form`` +To automatically include the customized templates from the ``app/Resources/views/Form`` directory created earlier in *all* templates, modify your application configuration file: @@ -1674,7 +1657,7 @@ file: templating: form: resources: - - 'AcmeTaskBundle:Form' + - 'Form' # ... .. code-block:: xml @@ -1690,7 +1673,7 @@ file: - AcmeTaskBundle:Form + Form @@ -1704,15 +1687,15 @@ file: 'templating' => array( 'form' => array( 'resources' => array( - 'AcmeTaskBundle:Form', + 'Form', ), ), ) // ... )); -Any fragments inside the ``Acme/TaskBundle/Resources/views/Form`` directory -are now used globally to define form output. +Any fragments inside the ``app/Resources/views/Form`` directory are now used +globally to define form output. .. index:: single: Forms; CSRF protection @@ -1752,7 +1735,7 @@ The CSRF token can be customized on a form-by-form basis. For example:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', + 'data_class' => 'AppBundle\Entity\Task', 'csrf_protection' => true, 'csrf_field_name' => '_token', // a unique key to help generate the secret token diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index cd8d04d0de0..eaadacff92f 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -14,7 +14,7 @@ applications, while staying out of your way. Symfony is built on the best ideas from many technologies: the tools and concepts you're about to learn represent the efforts of thousands of people, over many years. In other words, you're not just learning "Symfony", you're learning the fundamentals of the -web, development best practices, and how to use many amazing new PHP libraries, +web, development best practices and how to use many amazing new PHP libraries, inside or independently of Symfony. So, get ready. True to the Symfony philosophy, this chapter begins by explaining the fundamental @@ -33,9 +33,9 @@ takes place: :align: center And while the actual language used is a bit more formal, it's still dead-simple. -HTTP is the term used to describe this simple text-based language. And no -matter how you develop on the web, the goal of your server is *always* to -understand simple text requests, and return simple text responses. +HTTP is the term used to describe this simple text-based language. No matter +how you develop on the web, the goal of your server is *always* to understand +simple text requests, and return simple text responses. Symfony is built from the ground up around that reality. Whether you realize it or not, HTTP is something you use everyday. With Symfony, you'll learn @@ -48,7 +48,7 @@ Step1: The Client Sends a Request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Every conversation on the web starts with a *request*. The request is a text -message created by a client (e.g. a browser, an iPhone app, etc) in a +message created by a client (e.g. a browser, a smartphone app, etc) in a special format known as HTTP. The client sends that request to a server, and then waits for the response. @@ -98,7 +98,7 @@ delete a specific blog entry, for example: There are actually nine HTTP methods defined by the HTTP specification, but many of them are not widely used or supported. In reality, many modern - browsers don't support the ``PUT`` and ``DELETE`` methods. + browsers don't even support the ``PUT`` and ``DELETE`` methods. In addition to the first line, an HTTP request invariably contains other lines of information called request headers. The headers can supply a wide @@ -161,7 +161,7 @@ communication on the web. And as important and powerful as this process is, it's inescapably simple. The most important fact is this: regardless of the language you use, the -type of application you build (web, mobile, JSON API), or the development +type of application you build (web, mobile, JSON API) or the development philosophy you follow, the end goal of an application is **always** to understand each request and create and return the appropriate response. @@ -277,6 +277,7 @@ an HTTP response message. This allows your application to use an object-oriented interface to construct the response that needs to be returned to the client:: use Symfony\Component\HttpFoundation\Response; + $response = new Response(); $response->setContent('

Hello world!

'); @@ -366,12 +367,13 @@ on that value. This can get ugly quickly:: // index.php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; + $request = Request::createFromGlobals(); $path = $request->getPathInfo(); // the URI path being requested if (in_array($path, array('', '/'))) { $response = new Response('Welcome to the homepage.'); - } elseif ($path == '/contact') { + } elseif ('/contact' === $path) { $response = new Response('Contact us'); } else { $response = new Response('Page not found.', 404); @@ -427,7 +429,7 @@ by adding an entry for ``/contact`` to your routing configuration file: # app/config/routing.yml contact: path: /contact - defaults: { _controller: AcmeDemoBundle:Main:contact } + defaults: { _controller: AppBundle:Main:contact } .. code-block:: xml @@ -439,7 +441,7 @@ by adding an entry for ``/contact`` to your routing configuration file: http://symfony.com/schema/routing/routing-1.0.xsd"> - AcmeDemoBundle:Main:contact + AppBundle:Main:contact @@ -451,24 +453,18 @@ by adding an entry for ``/contact`` to your routing configuration file: $collection = new RouteCollection(); $collection->add('contact', new Route('/contact', array( - '_controller' => 'AcmeDemoBundle:Main:contact', + '_controller' => 'AppBundle:Main:contact', ))); return $collection; -.. note:: - - This example uses :doc:`YAML ` to define the routing - configuration. Routing configuration can also be written in other formats - such as XML or PHP. - When someone visits the ``/contact`` page, this route is matched, and the specified controller is executed. As you'll learn in the :doc:`routing chapter `, the ``AcmeDemoBundle:Main:contact`` string is a short syntax that points to a specific PHP method ``contactAction`` inside a class called ``MainController``:: - // src/Acme/DemoBundle/Controller/MainController.php - namespace Acme\DemoBundle\Controller; + // src/AppBundle/Controller/MainController.php + namespace AppBundle\Controller; use Symfony\Component\HttpFoundation\Response; @@ -491,8 +487,8 @@ email messages. .. _symfony2-build-your-app-not-your-tools: -Symfony: Build your App, not your Tools. ----------------------------------------- +Symfony: Build your App, not your Tools +--------------------------------------- You now know that the goal of any app is to interpret each incoming request and create an appropriate response. As an application grows, it becomes more @@ -528,24 +524,21 @@ regardless of how your project is developed. To name a few: about how that request should be handled (e.g. execute the ``contactAction()`` method); -* `Form`_ - A full-featured and flexible framework for creating forms and - handling form submissions; +* :doc:`Form ` - A full-featured and flexible + framework for creating forms and handling form submissions; * `Validator`_ - A system for creating rules about data and then validating whether or not user-submitted data follows those rules; -* :doc:`ClassLoader ` - An autoloading library that allows - PHP classes to be used without needing to manually ``require`` the files - containing those classes; - * :doc:`Templating ` - A toolkit for rendering templates, handling template inheritance (i.e. a template is decorated with a layout) and performing other common template tasks; -* `Security`_ - A powerful library for handling all types of security inside - an application; +* :doc:`Security ` - A powerful library for + handling all types of security inside an application; -* `Translation`_ - A framework for translating strings in your application. +* :doc:`Translation ` - A framework for + translating strings in your application. Each and every one of these components is decoupled and can be used in *any* PHP project, regardless of whether or not you use the Symfony framework. @@ -582,8 +575,5 @@ sensible defaults. For more advanced users, the sky is the limit. .. _`List of HTTP status codes`: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes .. _`List of HTTP header fields`: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields .. _`List of common media types`: http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types -.. _`Form`: https://github.com/symfony/Form .. _`Validator`: https://github.com/symfony/Validator -.. _`Security`: https://github.com/symfony/Security -.. _`Translation`: https://github.com/symfony/Translation .. _`Swift Mailer`: http://swiftmailer.org/