diff --git a/.gitignore b/.gitignore
index b0b2aafe151..805ea28a8f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,2 @@
/_build
-/bundles/DoctrineFixturesBundle
-/bundles/DoctrineMigrationsBundle
-/bundles/DoctrineMongoDBBundle
-/bundles/SensioFrameworkExtraBundle
-/bundles/SensioGeneratorBundle
-/cmf
/_exts
diff --git a/.travis.yml b/.travis.yml
index 73c2a7043f4..73bca138115 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,8 +3,9 @@ language: python
python:
- "2.7"
+sudo: false
+
install:
- - "bash install.sh"
- "pip install -q -r requirements.txt --use-mirrors"
script: sphinx-build -nW -b html -d _build/doctrees . _build/html
diff --git a/_exts b/_exts
index 03bc1c60172..52f7bd2216c 160000
--- a/_exts
+++ b/_exts
@@ -1 +1 @@
-Subproject commit 03bc1c60172a280619e3476f22b111b4a187895d
+Subproject commit 52f7bd2216cc22ef52494f346c5643bb2a74513f
diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst
new file mode 100644
index 00000000000..b15903be992
--- /dev/null
+++ b/best_practices/business-logic.rst
@@ -0,0 +1,341 @@
+Organizing Your Business Logic
+==============================
+
+In computer software, **business logic** or domain logic is "the part of the
+program that encodes the real-world business rules that determine how data can
+be created, displayed, stored, and changed" (read `full definition`_).
+
+In Symfony applications, business logic is all the custom code you write for
+your app that's not specific to the framework (e.g. routing and controllers).
+Domain classes, Doctrine entities and regular PHP classes that are used as
+services are good examples of business logic.
+
+For most projects, you should store everything inside the AppBundle.
+Inside here, you can create whatever directories you want to organize things:
+
+.. code-block:: text
+
+ symfony2-project/
+ ├─ app/
+ ├─ src/
+ │ └─ AppBundle/
+ │ └─ Utils/
+ │ └─ MyClass.php
+ ├─ vendor/
+ └─ web/
+
+Storing Classes Outside of the Bundle?
+--------------------------------------
+
+But there's no technical reason for putting business logic inside of a bundle.
+If you like, you can create your own namespace inside the ``src/`` directory
+and put things there:
+
+.. code-block:: text
+
+ symfony2-project/
+ ├─ app/
+ ├─ src/
+ │ ├─ Acme/
+ │ │ └─ Utils/
+ │ │ └─ MyClass.php
+ │ └─ AppBundle/
+ ├─ vendor/
+ └─ web/
+
+.. tip::
+
+ The recommended approach of using the ``AppBundle/`` directory is for
+ simplicity. If you're advanced enough to know what needs to live in
+ a bundle and what can live outside of one, then feel free to do that.
+
+Services: Naming and Format
+---------------------------
+
+The blog application needs a utility that can transform a post title (e.g.
+"Hello World") into a slug (e.g. "hello-world"). The slug will be used as
+part of the post URL.
+
+Let's, create a new ``Slugger`` class inside ``src/AppBundle/Utils/`` and
+add the following ``slugify()`` method:
+
+.. code-block:: php
+
+ // src/AppBundle/Utils/Slugger.php
+ namespace AppBundle\Utils;
+
+ class Slugger
+ {
+ public function slugify($string)
+ {
+ return preg_replace(
+ '/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string)))
+ );
+ }
+ }
+
+Next, define a new service for that class.
+
+.. code-block:: yaml
+
+ # app/config/services.yml
+ services:
+ # keep your service names short
+ app.slugger:
+ class: AppBundle\Utils\Slugger
+
+Traditionally, the naming convention for a service involved following the
+class name and location to avoid name collisions. Thus, the service
+*would have been* called ``app.utils.slugger``. But by using short service names,
+your code will be easier to read and use.
+
+.. best-practice::
+
+ The name of your application's services should be as short as possible,
+ but unique enough that you can search your project for the service if
+ you ever need to.
+
+Now you can use the custom slugger in any controller class, such as the
+``AdminController``:
+
+.. code-block:: php
+
+ public function createAction(Request $request)
+ {
+ // ...
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $slug = $this->get('app.slugger')->slugify($post->getTitle());
+ $post->setSlug($slug);
+
+ // ...
+ }
+ }
+
+Service Format: YAML
+--------------------
+
+In the previous section, YAML was used to define the service.
+
+.. best-practice::
+
+ Use the YAML format to define your own services.
+
+This is controversial, and in our experience, YAML and XML usage is evenly
+distributed among developers, with a slight preference towards YAML.
+Both formats have the same performance, so this is ultimately a matter of
+personal taste.
+
+We recommend YAML because it's friendly to newcomers and concise. You can
+of course use whatever format you like.
+
+Service: No Class Parameter
+---------------------------
+
+You may have noticed that the previous service definition doesn't configure
+the class namespace as a parameter:
+
+.. code-block:: yaml
+
+ # app/config/services.yml
+
+ # service definition with class namespace as parameter
+ parameters:
+ slugger.class: AppBundle\Utils\Slugger
+
+ services:
+ app.slugger:
+ class: "%slugger.class%"
+
+This practice is cumbersome and completely unnecessary for your own services:
+
+.. best-practice::
+
+ Don't define parameters for the classes of your services.
+
+This practice was wrongly adopted from third-party bundles. When Symfony
+introduced its service container, some developers used this technique to easily
+allow overriding services. However, overriding a service by just changing its
+class name is a very rare use case because, frequently, the new service has
+different constructor arguments.
+
+Using a Persistence Layer
+-------------------------
+
+Symfony is an HTTP framework that only cares about generating an HTTP response
+for each HTTP request. That's why Symfony doesn't provide a way to talk to
+a persistence layer (e.g. database, external API). You can choose whatever
+library or strategy you want for this.
+
+In practice, many Symfony applications rely on the independent
+`Doctrine project`_ to define their model using entities and repositories.
+Just like with business logic, we recommend storing Doctrine entities in the
+AppBundle.
+
+The three entities defined by our sample blog application are a good example:
+
+.. code-block:: text
+
+ symfony2-project/
+ ├─ ...
+ └─ src/
+ └─ AppBundle/
+ └─ Entity/
+ ├─ Comment.php
+ ├─ Post.php
+ └─ User.php
+
+.. tip::
+
+ If you're more advanced, you can of course store them under your own
+ namespace in ``src/``.
+
+Doctrine Mapping Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Doctrine Entities are plain PHP objects that you store in some "database".
+Doctrine only knows about your entities through the mapping metadata configured
+for your model classes. Doctrine supports four metadata formats: YAML, XML,
+PHP and annotations.
+
+.. best-practice::
+
+ Use annotations to define the mapping information of the Doctrine entities.
+
+Annotations are by far the most convenient and agile way of setting up and
+looking for mapping information:
+
+.. code-block:: php
+
+ namespace AppBundle\Entity;
+
+ use Doctrine\ORM\Mapping as ORM;
+ use Doctrine\Common\Collections\ArrayCollection;
+
+ /**
+ * @ORM\Entity
+ */
+ class Post
+ {
+ const NUM_ITEMS = 10;
+
+ /**
+ * @ORM\Id
+ * @ORM\GeneratedValue
+ * @ORM\Column(type="integer")
+ */
+ private $id;
+
+ /**
+ * @ORM\Column(type="string")
+ */
+ private $title;
+
+ /**
+ * @ORM\Column(type="string")
+ */
+ private $slug;
+
+ /**
+ * @ORM\Column(type="text")
+ */
+ private $content;
+
+ /**
+ * @ORM\Column(type="string")
+ */
+ private $authorEmail;
+
+ /**
+ * @ORM\Column(type="datetime")
+ */
+ private $publishedAt;
+
+ /**
+ * @ORM\OneToMany(
+ * targetEntity="Comment",
+ * mappedBy="post",
+ * orphanRemoval=true
+ * )
+ * @ORM\OrderBy({"publishedAt" = "ASC"})
+ */
+ private $comments;
+
+ public function __construct()
+ {
+ $this->publishedAt = new \DateTime();
+ $this->comments = new ArrayCollection();
+ }
+
+ // getters and setters ...
+ }
+
+All formats have the same performance, so this is once again ultimately a
+matter of taste.
+
+Data Fixtures
+~~~~~~~~~~~~~
+
+As fixtures support is not enabled by default in Symfony, you should execute
+the following command to install the Doctrine fixtures bundle:
+
+.. code-block:: bash
+
+ $ composer require "doctrine/doctrine-fixtures-bundle"
+
+Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
+``test`` environments:
+
+.. code-block:: php
+
+ use Symfony\Component\HttpKernel\Kernel;
+
+ class AppKernel extends Kernel
+ {
+ public function registerBundles()
+ {
+ $bundles = array(
+ // ...
+ );
+
+ if (in_array($this->getEnvironment(), array('dev', 'test'))) {
+ // ...
+ $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
+ }
+
+ return $bundles;
+ }
+
+ // ...
+ }
+
+We recommend creating just *one* `fixture class`_ for simplicity, though
+you're welcome to have more if that class gets quite large.
+
+Assuming you have at least one fixtures class and that the database access
+is configured properly, you can load your fixtures by executing the following
+command:
+
+.. code-block:: bash
+
+ $ php app/console doctrine:fixtures:load
+
+ Careful, database will be purged. Do you want to continue Y/N ? Y
+ > purging database
+ > loading AppBundle\DataFixtures\ORM\LoadFixtures
+
+Coding Standards
+----------------
+
+The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that
+were defined by the PHP community. You can learn more about
+:doc:`the Symfony Coding standards ` and even
+use the `PHP-CS-Fixer`_, which is a command-line utility that can fix the
+coding standards of an entire codebase in a matter of seconds.
+
+.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic
+.. _`Doctrine project`: http://www.doctrine-project.org/
+.. _`fixture class`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
+.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
+.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
+.. _`PHP-CS-Fixer`: https://github.com/FriendsOfPHP/PHP-CS-Fixer
diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst
new file mode 100644
index 00000000000..df9779b121e
--- /dev/null
+++ b/best_practices/configuration.rst
@@ -0,0 +1,182 @@
+Configuration
+=============
+
+Configuration usually involves different application parts (such as infrastructure
+and security credentials) and different environments (development, production).
+That's why Symfony recommends that you split the application configuration into
+three parts.
+
+.. _config-parameters.yml:
+
+Infrastructure-Related Configuration
+------------------------------------
+
+.. best-practice::
+
+ Define the infrastructure-related configuration options in the
+ ``app/config/parameters.yml`` file.
+
+The default ``parameters.yml`` file follows this recommendation and defines the
+options related to the database and mail server infrastructure:
+
+.. code-block:: yaml
+
+ # app/config/parameters.yml
+ parameters:
+ database_driver: pdo_mysql
+ database_host: 127.0.0.1
+ database_port: ~
+ database_name: symfony
+ database_user: root
+ database_password: ~
+
+ mailer_transport: smtp
+ mailer_host: 127.0.0.1
+ mailer_user: ~
+ mailer_password: ~
+
+ # ...
+
+These options aren't defined inside the ``app/config/config.yml`` file because
+they have nothing to do with the application's behavior. In other words, your
+application doesn't care about the location of your database or the credentials
+to access to it, as long as the database is correctly configured.
+
+Canonical Parameters
+~~~~~~~~~~~~~~~~~~~~
+
+.. best-practice::
+
+ Define all your application's parameters in the
+ ``app/config/parameters.yml.dist`` file.
+
+Since version 2.3, Symfony includes a configuration file called ``parameters.yml.dist``,
+which stores the canonical list of configuration parameters for the application.
+
+Whenever a new configuration parameter is defined for the application, you
+should also add it to this file and submit the changes to your version control
+system. Then, whenever a developer updates the project or deploys it to a server,
+Symfony will check if there is any difference between the canonical
+``parameters.yml.dist`` file and your local ``parameters.yml`` file. If there
+is a difference, Symfony will ask you to provide a value for the new parameter
+and it will add it to your local ``parameters.yml`` file.
+
+Application-Related Configuration
+---------------------------------
+
+.. best-practice::
+
+ Define the application behavior related configuration options in the
+ ``app/config/config.yml`` file.
+
+The ``config.yml`` file contains the options used by the application to modify
+its behavior, such as the sender of email notifications, or the enabled
+`feature toggles`_. Defining these values in ``parameters.yml`` file would
+add an extra layer of configuration that's not needed because you don't need
+or want these configuration values to change on each server.
+
+The configuration options defined in the ``config.yml`` file usually vary from
+one :doc:`environment ` to another. That's
+why Symfony already includes ``app/config/config_dev.yml`` and ``app/config/config_prod.yml``
+files so that you can override specific values for each environment.
+
+Constants vs Configuration Options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One of the most common errors when defining application configuration is to
+create new options for values that never change, such as the number of items for
+paginated results.
+
+.. best-practice::
+
+ Use constants to define configuration options that rarely change.
+
+The traditional approach for defining configuration options has caused many
+Symfony apps to include an option like the following, which would be used
+to control the number of posts to display on the blog homepage:
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ parameters:
+ homepage.num_items: 10
+
+If you ask yourself when the last time was that you changed the value of
+*any* option like this, odds are that you *never* have. Creating a configuration
+option for a value that you are never going to configure just isn't necessary.
+Our recommendation is to define these values as constants in your application.
+You could, for example, define a ``NUM_ITEMS`` constant in the ``Post`` entity:
+
+.. code-block:: php
+
+ // src/AppBundle/Entity/Post.php
+ namespace AppBundle\Entity;
+
+ class Post
+ {
+ const NUM_ITEMS = 10;
+
+ // ...
+ }
+
+The main advantage of defining constants is that you can use their values
+everywhere in your application. When using parameters, they are only available
+from places with access to the Symfony container.
+
+Constants can be used for example in your Twig templates thanks to the
+``constant()`` function:
+
+.. code-block:: html+jinja
+
+
+ Displaying the {{ constant('NUM_ITEMS', post) }} most recent results.
+
+
+And Doctrine entities and repositories can now easily access these values,
+whereas they cannot access the container parameters:
+
+.. code-block:: php
+
+ namespace AppBundle\Repository;
+
+ use Doctrine\ORM\EntityRepository;
+ use AppBundle\Entity\Post;
+
+ class PostRepository extends EntityRepository
+ {
+ public function findLatest($limit = Post::NUM_ITEMS)
+ {
+ // ...
+ }
+ }
+
+The only notable disadvantage of using constants for this kind of configuration
+values is that you cannot redefine them easily in your tests.
+
+Semantic Configuration: Don't Do It
+-----------------------------------
+
+.. best-practice::
+
+ Don't define a semantic dependency injection configuration for your bundles.
+
+As explained in :doc:`/cookbook/bundles/extension` article, Symfony bundles
+have two choices on how to handle configuration: normal service configuration
+through the ``services.yml`` file and semantic configuration through a special
+``*Extension`` class.
+
+Although semantic configuration is much more powerful and provides nice features
+such as configuration validation, the amount of work needed to define that
+configuration isn't worth it for bundles that aren't meant to be shared as
+third-party bundles.
+
+Moving Sensitive Options Outside of Symfony Entirely
+----------------------------------------------------
+
+When dealing with sensitive options, like database credentials, we also recommend
+that you store them outside the Symfony project and make them available
+through environment variables. Learn how to do it in the following article:
+:doc:`/cookbook/configuration/external_parameters`
+
+.. _`feature toggles`: http://en.wikipedia.org/wiki/Feature_toggle
+.. _`constant() function`: http://twig.sensiolabs.org/doc/functions/constant.html
diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst
new file mode 100644
index 00000000000..a31db8e21c6
--- /dev/null
+++ b/best_practices/controllers.rst
@@ -0,0 +1,218 @@
+Controllers
+===========
+
+Symfony follows the philosophy of *"thin controllers and fat models"*. This
+means that controllers should hold just the thin layer of *glue-code*
+needed to coordinate the different parts of the application.
+
+As a rule of thumb, you should follow the 5-10-20 rule, where controllers should
+only define 5 variables or less, contain 10 actions or less and include 20 lines
+of code or less in each action. This isn't an exact science, but it should
+help you realize when code should be refactored out of the controller and
+into a service.
+
+.. best-practice::
+
+ Make your controller extend the FrameworkBundle base controller and use
+ annotations to configure routing, caching and security whenever possible.
+
+Coupling the controllers to the underlying framework allows you to leverage
+all of its features and increases your productivity.
+
+And since your controllers should be thin and contain nothing more than a
+few lines of *glue-code*, spending hours trying to decouple them from your
+framework doesn't benefit you in the long run. The amount of time *wasted*
+isn't worth the benefit.
+
+In addition, using annotations for routing, caching and security simplifies
+configuration. You don't need to browse tens of files created with different
+formats (YAML, XML, PHP): all the configuration is just where you need it
+and it only uses one format.
+
+Overall, this means you should aggressively decouple your business logic
+from the framework while, at the same time, aggressively coupling your controllers
+and routing *to* the framework in order to get the most out of it.
+
+Routing Configuration
+---------------------
+
+To load routes defined as annotations in your controllers, add the following
+configuration to the main routing configuration file:
+
+.. code-block:: yaml
+
+ # app/config/routing.yml
+ app:
+ resource: "@AppBundle/Controller/"
+ type: annotation
+
+This configuration will load annotations from any controller stored inside the
+``src/AppBundle/Controller/`` directory and even from its subdirectories.
+So if your application defines lots of controllers, it's perfectly ok to
+reorganize them into subdirectories:
+
+.. code-block:: text
+
+ /
+ ├─ ...
+ └─ src/
+ └─ AppBundle/
+ ├─ ...
+ └─ Controller/
+ ├─ DefaultController.php
+ ├─ ...
+ ├─ Api/
+ │ ├─ ...
+ │ └─ ...
+ └─ Backend/
+ ├─ ...
+ └─ ...
+
+Template Configuration
+----------------------
+
+.. best-practice::
+
+ Don't use the ``@Template()`` annotation to configure the template used by
+ the controller.
+
+The ``@Template`` annotation is useful, but also involves some magic. For
+that reason, we don't recommend using it.
+
+Most of the time, ``@Template`` is used without any parameters, which makes
+it more difficult to know which template is being rendered. It also makes
+it less obvious to beginners that a controller should always return a Response
+object (unless you're using a view layer).
+
+Lastly, the ``@Template`` annotation uses a ``TemplateListener`` class that hooks
+into the ``kernel.view`` event dispatched by the framework. This listener introduces
+a measurable performance impact. In the sample blog application, rendering the
+homepage took 5 milliseconds using the ``$this->render()`` method and 26 milliseconds
+using the ``@Template`` annotation.
+
+How the Controller Looks
+------------------------
+
+Considering all this, here is an example of how the controller should look
+for the homepage of our app:
+
+.. code-block:: php
+
+ namespace AppBundle\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+ class DefaultController extends Controller
+ {
+ /**
+ * @Route("/", name="homepage")
+ */
+ public function indexAction()
+ {
+ $posts = $this->getDoctrine()
+ ->getRepository('AppBundle:Post')
+ ->findLatest();
+
+ return $this->render('default/index.html.twig', array(
+ 'posts' => $posts
+ ));
+ }
+ }
+
+.. _best-practices-paramconverter:
+
+Using the ParamConverter
+------------------------
+
+If you're using Doctrine, then you can *optionally* use the `ParamConverter`_
+to automatically query for an entity and pass it as an argument to your controller.
+
+.. best-practice::
+
+ Use the ParamConverter trick to automatically query for Doctrine entities
+ when it's simple and convenient.
+
+For example:
+
+.. code-block:: php
+
+ use AppBundle\Entity\Post;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+ /**
+ * @Route("/{id}", name="admin_post_show")
+ */
+ public function showAction(Post $post)
+ {
+ $deleteForm = $this->createDeleteForm($post);
+
+ return $this->render('admin/post/show.html.twig', array(
+ 'post' => $post,
+ 'delete_form' => $deleteForm->createView(),
+ ));
+ }
+
+Normally, you'd expect a ``$id`` argument to ``showAction``. Instead, by
+creating a new argument (``$post``) and type-hinting it with the ``Post``
+class (which is a Doctrine entity), the ParamConverter automatically queries
+for an object whose ``$id`` property matches the ``{id}`` value. It will
+also show a 404 page if no ``Post`` can be found.
+
+When Things Get More Advanced
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This works without any configuration because the wildcard name ``{id}`` matches
+the name of the property on the entity. If this isn't true, or if you have
+even more complex logic, the easiest thing to do is just query for the entity
+manually. In our application, we have this situation in ``CommentController``:
+
+.. code-block:: php
+
+ /**
+ * @Route("/comment/{postSlug}/new", name = "comment_new")
+ */
+ public function newAction(Request $request, $postSlug)
+ {
+ $post = $this->getDoctrine()
+ ->getRepository('AppBundle:Post')
+ ->findOneBy(array('slug' => $postSlug));
+
+ if (!$post) {
+ throw $this->createNotFoundException();
+ }
+
+ // ...
+ }
+
+You can also use the ``@ParamConverter`` configuration, which is infinitely
+flexible:
+
+.. code-block:: php
+
+ use AppBundle\Entity\Post;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
+ use Symfony\Component\HttpFoundation\Request;
+
+ /**
+ * @Route("/comment/{postSlug}/new", name = "comment_new")
+ * @ParamConverter("post", options={"mapping": {"postSlug": "slug"}})
+ */
+ public function newAction(Request $request, Post $post)
+ {
+ // ...
+ }
+
+The point is this: the ParamConverter shortcut is great for simple situations.
+But you shouldn't forget that querying for entities directly is still very
+easy.
+
+Pre and Post Hooks
+------------------
+
+If you need to execute some code before or after the execution of your controllers,
+you can use the EventDispatcher component to
+:doc:`set up before and after filters `.
+
+.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst
new file mode 100644
index 00000000000..d4eed5062f5
--- /dev/null
+++ b/best_practices/creating-the-project.rst
@@ -0,0 +1,195 @@
+Creating the Project
+====================
+
+Installing Symfony
+------------------
+
+In the past, Symfony projects were created with `Composer`_, the dependency manager
+for PHP applications. However, the current recommendation is to use the **Symfony
+Installer**, which has to be installed before creating your first project.
+
+Linux and Mac OS X Systems
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Open your command console and execute the following:
+
+.. code-block:: bash
+
+ $ curl -LsS http://symfony.com/installer > symfony.phar
+ $ sudo mv symfony.phar /usr/local/bin/symfony
+ $ chmod a+x /usr/local/bin/symfony
+
+Now you can execute the Symfony Installer as a global system command called
+``symfony``.
+
+Windows Systems
+~~~~~~~~~~~~~~~
+
+Open your command console and execute the following:
+
+.. code-block:: bash
+
+ c:\> php -r "readfile('http://symfony.com/installer');" > symfony.phar
+
+Then, move the downloaded ``symfony.phar`` file to your projects directory and
+execute it as follows:
+
+.. code-block:: bash
+
+ c:\> php symfony.phar
+
+Creating the Blog Application
+-----------------------------
+
+Now that everything is correctly set up, you can create a new project based on
+Symfony. In your command console, browse to a directory where you have permission
+to create files and execute the following commands:
+
+.. code-block:: bash
+
+ # Linux, Mac OS X
+ $ cd projects/
+ $ symfony new blog
+
+ # Windows
+ c:\> cd projects/
+ c:\projects\> php symfony.phar new blog
+
+This command creates a new directory called ``blog`` that contains a fresh new
+project based on the most recent stable Symfony version available. In addition,
+the installer checks if your system meets the technical requirements to execute
+Symfony applications. If not, you'll see the list of changes needed to meet those
+requirements.
+
+.. tip::
+
+ Symfony releases are digitally signed for security reasons. If you want to
+ verify the integrity of your Symfony installation, take a look at the
+ `public checksums repository`_ and follow `these steps`_ to verify the
+ signatures.
+
+Structuring the Application
+---------------------------
+
+After creating the application, enter the ``blog/`` directory and you'll see a
+number of files and directories generated automatically:
+
+.. code-block:: text
+
+ blog/
+ ├─ app/
+ │ ├─ console
+ │ ├─ cache/
+ │ ├─ config/
+ │ ├─ logs/
+ │ └─ Resources/
+ ├─ src/
+ │ └─ AppBundle/
+ ├─ vendor/
+ └─ web/
+
+This file and directory hierarchy is the convention proposed by Symfony to
+structure your applications. The recommended purpose of each directory is the
+following:
+
+* ``app/cache/``, stores all the cache files generated by the application;
+* ``app/config/``, stores all the configuration defined for any environment;
+* ``app/logs/``, stores all the log files generated by the application;
+* ``app/Resources/``, stores all the templates and the translation files for the
+ application;
+* ``src/AppBundle/``, stores the Symfony specific code (controllers and routes),
+ your domain code (e.g. Doctrine classes) and all your business logic;
+* ``vendor/``, this is the directory where Composer installs the application's
+ dependencies and you should never modify any of its contents;
+* ``web/``, stores all the front controller files and all the web assets, such
+ as stylesheets, JavaScript files and images.
+
+Application Bundles
+~~~~~~~~~~~~~~~~~~~
+
+When Symfony 2.0 was released, most developers naturally adopted the symfony
+1.x way of dividing applications into logical modules. That's why many Symfony
+apps use bundles to divide their code into logical features: UserBundle,
+ProductBundle, InvoiceBundle, etc.
+
+But a bundle is *meant* to be something that can be reused as a stand-alone
+piece of software. If UserBundle cannot be used *"as is"* in other Symfony
+apps, then it shouldn't be its own bundle. Moreover InvoiceBundle depends on
+ProductBundle, then there's no advantage to having two separate bundles.
+
+.. best-practice::
+
+ Create only one bundle called AppBundle for your application logic
+
+Implementing a single AppBundle bundle in your projects will make your code
+more concise and easier to understand. Starting in Symfony 2.6, the official
+Symfony documentation uses the AppBundle name.
+
+.. note::
+
+ There is no need to prefix the AppBundle with your own vendor (e.g.
+ AcmeAppBundle), because this application bundle is never going to be
+ shared.
+
+All in all, this is the typical directory structure of a Symfony application
+that follows these best practices:
+
+.. code-block:: text
+
+ blog/
+ ├─ app/
+ │ ├─ console
+ │ ├─ cache/
+ │ ├─ config/
+ │ ├─ logs/
+ │ └─ Resources/
+ ├─ src/
+ │ └─ AppBundle/
+ ├─ vendor/
+ └─ web/
+ ├─ app.php
+ └─ app_dev.php
+
+.. tip::
+
+ If your Symfony installation doesn't come with a pre-generated AppBundle,
+ you can generate it by hand executing this command:
+
+ .. code-block:: bash
+
+ $ php app/console generate:bundle --namespace=AppBundle --dir=src --format=annotation --no-interaction
+
+Extending the Directory Structure
+---------------------------------
+
+If your project or infrastructure requires some changes to the default directory
+structure of Symfony, you can
+:doc:`override the location of the main directories `:
+``cache/``, ``logs/`` and ``web/``.
+
+In addition, Symfony3 will use a slightly different directory structure when
+it's released:
+
+.. code-block:: text
+
+ blog-symfony3/
+ ├─ app/
+ │ ├─ config/
+ │ └─ Resources/
+ ├─ bin/
+ │ └─ console
+ ├─ src/
+ ├─ var/
+ │ ├─ cache/
+ │ └─ logs/
+ ├─ vendor/
+ └─ web/
+
+The changes are pretty superficial, but for now, we recommend that you use
+the Symfony directory structure.
+
+.. _`Composer`: https://getcomposer.org/
+.. _`Get Started`: https://getcomposer.org/doc/00-intro.md
+.. _`Composer download page`: https://getcomposer.org/download/
+.. _`public checksums repository`: https://github.com/sensiolabs/checksums
+.. _`these steps`: http://fabien.potencier.org/article/73/signing-project-releases
diff --git a/best_practices/forms.rst b/best_practices/forms.rst
new file mode 100644
index 00000000000..d72d189ccfb
--- /dev/null
+++ b/best_practices/forms.rst
@@ -0,0 +1,209 @@
+Forms
+=====
+
+Forms are one of the most misused Symfony components due to its vast scope and
+endless list of features. In this chapter we'll show you some of the best
+practices so you can leverage forms but get work done quickly.
+
+Building Forms
+--------------
+
+.. best-practice::
+
+ Define your forms as PHP classes.
+
+The Form component allows you to build forms right inside your controller
+code. This is perfectly fine if you don't need to reuse the form somewhere else.
+But for organization and reuse, we recommend that you define each
+form in its own PHP class::
+
+ namespace AppBundle\Form;
+
+ use Symfony\Component\Form\AbstractType;
+ use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+ class PostType extends AbstractType
+ {
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('title')
+ ->add('summary', 'textarea')
+ ->add('content', 'textarea')
+ ->add('authorEmail', 'email')
+ ->add('publishedAt', 'datetime')
+ ;
+ }
+
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'AppBundle\Entity\Post'
+ ));
+ }
+
+ public function getName()
+ {
+ return 'post';
+ }
+ }
+
+To use the class, use ``createForm`` and instantiate the new class::
+
+ use AppBundle\Form\PostType;
+ // ...
+
+ public function newAction(Request $request)
+ {
+ $post = new Post();
+ $form = $this->createForm(new PostType(), $post);
+
+ // ...
+ }
+
+Registering Forms as Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can also
+:ref:`register your form type as a service `.
+But this is *not* recommended unless you plan to reuse the new form type in many
+places or embed it in other forms directly or via the
+:doc:`collection type `.
+
+For most forms that are used only to edit or create something, registering
+the form as a service is over-kill, and makes it more difficult to figure
+out exactly which form class is being used in a controller.
+
+Form Button Configuration
+-------------------------
+
+Form classes should try to be agnostic to *where* they will be used. This
+makes them easier to re-use later.
+
+.. best-practice::
+
+ Add buttons in the templates, not in the form classes or the controllers.
+
+Since Symfony 2.5, you can add buttons as fields on your form. This is a nice
+way to simplify the template that renders your form. But if you add the buttons
+directly in your form class, this would effectively limit the scope of that form:
+
+.. code-block:: php
+
+ class PostType extends AbstractType
+ {
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ // ...
+ ->add('save', 'submit', array('label' => 'Create Post'))
+ ;
+ }
+
+ // ...
+ }
+
+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::
+
+ namespace AppBundle\Controller\Admin;
+
+ use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+ use AppBundle\Entity\Post;
+ use AppBundle\Form\PostType;
+
+ class PostController extends Controller
+ {
+ // ...
+
+ public function newAction(Request $request)
+ {
+ $post = new Post();
+ $form = $this->createForm(new PostType(), $post);
+ $form->add('submit', 'submit', array(
+ 'label' => 'Create',
+ 'attr' => array('class' => 'btn btn-default pull-right')
+ ));
+
+ // ...
+ }
+ }
+
+This is also an important error, because you are mixing presentation markup
+(labels, CSS classes, etc.) with pure PHP code. Separation of concerns is
+always a good practice to follow, so put all the view-related things in the
+view layer:
+
+.. code-block:: html+jinja
+
+ {{ form_start(form) }}
+ {{ form_widget(form) }}
+
+
+ {{ form_end(form) }}
+
+Rendering the Form
+------------------
+
+There are a lot of ways to render your form, ranging from rendering the entire
+thing in one line to rendering each part of each field independently. The
+best way depends on how much customization you need.
+
+One of the simplest ways - which is especially useful during development -
+is to render the form tags and use ``form_widget()`` to render all of the
+fields:
+
+.. code-block:: html+jinja
+
+ {{ form_start(form, {'attr': {'class': 'my-form-class'} }) }}
+ {{ form_widget(form) }}
+ {{ form_end(form) }}
+
+If you need more control over how your fields are rendered, then you should
+remove the ``form_widget(form)`` function and render your fields individually.
+See the :doc:`/cookbook/form/form_customization` article for more information
+on this and how you can control *how* the form renders at a global level
+using form theming.
+
+Handling Form Submits
+---------------------
+
+Handling a form submit usually follows a similar template:
+
+.. code-block:: php
+
+ public function newAction(Request $request)
+ {
+ // build the form ...
+
+ $form->handleRequest($request);
+
+ if ($form->isSubmitted() && $form->isValid()) {
+ $em = $this->getDoctrine()->getManager();
+ $em->persist($post);
+ $em->flush();
+
+ return $this->redirect($this->generateUrl(
+ 'admin_post_show',
+ array('id' => $post->getId())
+ ));
+ }
+
+ // render the template
+ }
+
+There are really only two notable things here. First, we recommend that you
+use a single action for both rendering the form and handling the form submit.
+For example, you *could* have a ``newAction`` that *only* renders the form
+and a ``createAction`` that *only* processes the form submit. Both those
+actions will be almost identical. So it's much simpler to let ``newAction``
+handle everything.
+
+Second, we recommend using ``$form->isSubmitted()`` in the ``if`` statement
+for clarity. This isn't technically needed, since ``isValid()`` first calls
+``isSubmitted()``. But without this, the flow doesn't read well as it *looks*
+like the form is *always* processed (even on the GET request).
diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst
new file mode 100644
index 00000000000..9da3fad3271
--- /dev/null
+++ b/best_practices/i18n.rst
@@ -0,0 +1,96 @@
+Internationalization
+====================
+
+Internationalization and localization adapt the applications and their contents
+to the specific region or language of the users. In Symfony this is an opt-in
+feature that needs to be enabled before using it. To do this, uncomment the
+following ``translator`` configuration option and set your application locale:
+
+.. code-block:: yaml
+
+ # app/config/config.yml
+ framework:
+ # ...
+ translator: { fallback: "%locale%" }
+
+ # app/config/parameters.yml
+ parameters:
+ # ...
+ locale: en
+
+Translation Source File Format
+------------------------------
+
+The Symfony Translation component supports lots of different translation
+formats: PHP, Qt, ``.po``, ``.mo``, JSON, CSV, INI, etc.
+
+.. best-practice::
+
+ Use the XLIFF format for your translation files.
+
+Of all the available translation formats, only XLIFF and gettext have broad
+support in the tools used by professional translators. And since it's based
+on XML, you can validate XLIFF file contents as you write them.
+
+Symfony 2.6 added support for notes inside XLIFF files, making them more
+user-friendly for translators. At the end, good translations are all about
+context, and these XLIFF notes allow you to define that context.
+
+.. tip::
+
+ The Apache-licensed `JMSTranslationBundle`_ offers you a web interface for
+ viewing and editing these translation files. It also has advanced extractors
+ that can read your project and automatically update the XLIFF files.
+
+Translation Source File Location
+--------------------------------
+
+.. best-practice::
+
+ Store the translation files in the ``app/Resources/translations/`` directory.
+
+Traditionally, Symfony developers have created these files in the
+``Resources/translations/`` directory of each bundle.
+
+But since the ``app/Resources/`` directory is considered the global location
+for the application's resources, storing translations in ``app/Resources/translations/``
+centralizes them *and* gives them priority over any other translation file.
+This lets you override translations defined in third-party bundles.
+
+Translation Keys
+----------------
+
+.. best-practice::
+
+ Always use keys for translations instead of content strings.
+
+Using keys simplifies the management of the translation files because you
+can change the original contents without having to update all of the translation
+files.
+
+Keys should always describe their *purpose* and *not* their location. For
+example, if a form has a field with the label "Username", then a nice key
+would be ``label.username``, *not* ``edit_form.label.username``.
+
+Example Translation File
+------------------------
+
+Applying all the previous best practices, the sample translation file for
+English in the application would be:
+
+.. code-block:: xml
+
+
+
+
+
+
+
+ title.post_list
+ Post List
+
+
+
+
+
+.. _`JMSTranslationBundle`: https://github.com/schmittjoh/JMSTranslationBundle
diff --git a/best_practices/index.rst b/best_practices/index.rst
new file mode 100644
index 00000000000..8df4abb1364
--- /dev/null
+++ b/best_practices/index.rst
@@ -0,0 +1,19 @@
+Official Symfony Best Practices
+===============================
+
+.. toctree::
+ :hidden:
+
+ introduction
+ creating-the-project
+ configuration
+ business-logic
+ controllers
+ templates
+ forms
+ i18n
+ security
+ web-assets
+ tests
+
+.. include:: /best_practices/map.rst.inc
diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst
new file mode 100644
index 00000000000..d1d0e760d32
--- /dev/null
+++ b/best_practices/introduction.rst
@@ -0,0 +1,97 @@
+.. index::
+ single: Symfony Framework Best Practices
+
+The Symfony Framework Best Practices
+====================================
+
+The Symfony framework is well-known for being *really* flexible and is used
+to build micro-sites, enterprise applications that handle billions of connections
+and even as the basis for *other* frameworks. Since its release in July 2011,
+the community has learned a lot about what's possible and how to do things *best*.
+
+These community resources - like blog posts or presentations - have created
+an unofficial set of recommendations for developing Symfony applications.
+Unfortunately, a lot of these recommendations are unneeded for web applications.
+Much of the time, they unnecessarily overcomplicate things and don't follow the
+original pragmatic philosophy of Symfony.
+
+What is this Guide About?
+-------------------------
+
+This guide aims to fix that by describing the **best practices for developing
+web apps with the Symfony full-stack framework**. These are best practices that
+fit the philosophy of the framework as envisioned by its original creator
+`Fabien Potencier`_.
+
+.. note::
+
+ **Best practice** is a noun that means *"a well defined procedure that is
+ known to produce near-optimum results"*. And that's exactly what this
+ guide aims to provide. Even if you don't agree with every recommendation,
+ we believe these will help you build great applications with less complexity.
+
+This guide is **specially suited** for:
+
+* Websites and web applications developed with the full-stack Symfony framework.
+
+For other situations, this guide might be a good **starting point** that you can
+then **extend and fit to your specific needs**:
+
+* Bundles shared publicly to the Symfony community;
+* Advanced developers or teams who have created their own standards;
+* Some complex applications that have highly customized requirements;
+* Bundles that may be shared internally within a company.
+
+We know that old habits die hard and some of you will be shocked by some
+of these best practices. But by following these, you'll be able to develop
+apps faster, with less complexity and with the same or even higher quality.
+It's also a moving target that will continue to improve.
+
+Keep in mind that these are **optional recommendations** that you and your
+team may or may not follow to develop Symfony applications. If you want to
+continue using your own best practices and methodologies, you can of course
+do it. Symfony is flexible enough to adapt to your needs. That will never
+change.
+
+Who this Book Is for (Hint: It's not a Tutorial)
+------------------------------------------------
+
+Any Symfony developer, whether you are an expert or a newcomer, can read this
+guide. But since this isn't a tutorial, you'll need some basic knowledge of
+Symfony to follow everything. If you are totally new to Symfony, welcome!
+Start with :doc:`The Quick Tour ` tutorial first.
+
+We've deliberately kept this guide short. We won't repeat explanations that
+you can find in the vast Symfony documentation, like discussions about dependency
+injection or front controllers. We'll solely focus on explaining how to do
+what you already know.
+
+The Application
+---------------
+
+In addition to this guide, you'll find a sample application developed with
+all these best practices in mind. **The application is a simple blog engine**,
+because that will allow us to focus on the Symfony concepts and features without
+getting buried in difficult details.
+
+Instead of developing the application step by step in this guide, you'll find
+selected snippets of code through the chapters. Please refer to the last chapter
+of this guide to find more details about this application and the instructions
+to install it.
+
+Don't Update Your Existing Applications
+---------------------------------------
+
+After reading this handbook, some of you may be considering refactoring your
+existing Symfony applications. Our recommendation is sound and clear: **you
+should not refactor your existing applications to comply with these best
+practices**. The reasons for not doing it are various:
+
+* Your existing applications are not wrong, they just follow another set of
+ guidelines;
+* A full codebase refactorization is prone to introduce errors in your
+ applications;
+* The amount of work spent on this could be better dedicated to improving
+ your tests or adding features that provide real value to the end users.
+
+.. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot
diff --git a/best_practices/map.rst.inc b/best_practices/map.rst.inc
new file mode 100644
index 00000000000..f9dfd0c3e9d
--- /dev/null
+++ b/best_practices/map.rst.inc
@@ -0,0 +1,11 @@
+* :doc:`/best_practices/introduction`
+* :doc:`/best_practices/creating-the-project`
+* :doc:`/best_practices/configuration`
+* :doc:`/best_practices/business-logic`
+* :doc:`/best_practices/controllers`
+* :doc:`/best_practices/templates`
+* :doc:`/best_practices/forms`
+* :doc:`/best_practices/i18n`
+* :doc:`/best_practices/security`
+* :doc:`/best_practices/web-assets`
+* :doc:`/best_practices/tests`
diff --git a/best_practices/security.rst b/best_practices/security.rst
new file mode 100644
index 00000000000..c7fd3a7725b
--- /dev/null
+++ b/best_practices/security.rst
@@ -0,0 +1,363 @@
+Security
+========
+
+Authentication and Firewalls (i.e. Getting the User's Credentials)
+------------------------------------------------------------------
+
+You can configure Symfony to authenticate your users using any method you
+want and to load user information from any source. This is a complex topic,
+but the :doc:`Security Cookbook Section ` has a
+lot of information about this.
+
+Regardless of your needs, authentication is configured in ``security.yml``,
+primarily under the ``firewalls`` key.
+
+.. best-practice::
+
+ Unless you have two legitimately different authentication systems and
+ users (e.g. form login for the main site and a token system for your
+ API only), we recommend having only *one* firewall entry with the ``anonymous``
+ key enabled.
+
+Most applications only have one authentication system and one set of users.
+For this reason, you only need *one* firewall entry. There are exceptions
+of course, especially if you have separated web and API sections on your
+site. But the point is to keep things simple.
+
+Additionally, you should use the ``anonymous`` key under your firewall. If
+you need to require users to be logged in for different sections of your
+site (or maybe nearly *all* sections), use the ``access_control`` area.
+
+.. best-practice::
+
+ Use the ``bcrypt`` encoder for encoding your users' passwords.
+
+If your users have a password, then we recommend encoding it using the ``bcrypt``
+encoder, instead of the traditional SHA-512 hashing encoder. The main advantages
+of ``bcrypt`` are the inclusion of a *salt* value to protect against rainbow
+table attacks, and its adaptive nature, which allows to make it slower to
+remain resistant to brute-force search attacks.
+
+With this in mind, here is the authentication setup from our application,
+which uses a login form to load users from the database:
+
+.. code-block:: yaml
+
+ # app/config/security.yml
+ security:
+ encoders:
+ AppBundle\Entity\User: bcrypt
+
+ providers:
+ database_users:
+ entity: { class: AppBundle:User, property: username }
+
+ firewalls:
+ secured_area:
+ pattern: ^/
+ anonymous: true
+ form_login:
+ check_path: security_login_check
+ login_path: security_login_form
+
+ logout:
+ path: security_logout
+ target: homepage
+
+ # ... access_control exists, but is not shown here
+
+.. tip::
+
+ The source code for our project contains comments that explain each part.
+
+Authorization (i.e. Denying Access)
+-----------------------------------
+
+Symfony gives you several ways to enforce authorization, including the ``access_control``
+configuration in :doc:`security.yml `, the
+:ref:`@Security annotation ` and using
+:ref:`isGranted ` on the ``security.authorization_checker``
+service directly.
+
+.. best-practice::
+
+ * For protecting broad URL patterns, use ``access_control``;
+ * Whenever possible, use the ``@Security`` annotation;
+ * Check security directly on the ``security.authorization_checker`` service whenever
+ you have a more complex situation.
+
+There are also different ways to centralize your authorization logic, like
+with a custom security voter or with ACL.
+
+.. best-practice::
+
+ * For fine-grained restrictions, define a custom security voter;
+ * For restricting access to *any* object by *any* user via an admin
+ interface, use the Symfony ACL.
+
+.. _best-practices-security-annotation:
+
+The @Security Annotation
+------------------------
+
+For controlling access on a controller-by-controller basis, use the ``@Security``
+annotation whenever possible. It's easy to read and is placed consistently
+above each action.
+
+In our application, you need the ``ROLE_ADMIN`` in order to create a new post.
+Using ``@Security``, this looks like:
+
+.. code-block:: php
+
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
+ // ...
+
+ /**
+ * Displays a form to create a new Post entity.
+ *
+ * @Route("/new", name="admin_post_new")
+ * @Security("has_role('ROLE_ADMIN')")
+ */
+ public function newAction()
+ {
+ // ...
+ }
+
+Using Expressions for Complex Security Restrictions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your security logic is a little bit more complex, you can use an `expression`_
+inside ``@Security``. In the following example, a user can only access the
+controller if their email matches the value returned by the ``getAuthorEmail``
+method on the ``Post`` object:
+
+.. code-block:: php
+
+ use AppBundle\Entity\Post;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
+
+ /**
+ * @Route("/{id}/edit", name="admin_post_edit")
+ * @Security("user.getEmail() == post.getAuthorEmail()")
+ */
+ public function editAction(Post $post)
+ {
+ // ...
+ }
+
+Notice that this requires the use of the `ParamConverter`_, which automatically
+queries for the ``Post`` object and puts it on the ``$post`` argument. This
+is what makes it possible to use the ``post`` variable in the expression.
+
+This has one major drawback: an expression in an annotation cannot easily
+be reused in other parts of the application. Imagine that you want to add
+a link in a template that will only be seen by authors. Right now you'll
+need to repeat the expression code using Twig syntax:
+
+.. code-block:: html+jinja
+
+ {% if app.user and app.user.email == post.authorEmail %}
+ ...
+ {% endif %}
+
+The easiest solution - if your logic is simple enough - is to add a new method
+to the ``Post`` entity that checks if a given user is its author:
+
+.. code-block:: php
+
+ // src/AppBundle/Entity/Post.php
+ // ...
+
+ class Post
+ {
+ // ...
+
+ /**
+ * Is the given User the author of this Post?
+ *
+ * @return bool
+ */
+ public function isAuthor(User $user = null)
+ {
+ return $user && $user->getEmail() == $this->getAuthorEmail();
+ }
+ }
+
+Now you can reuse this method both in the template and in the security expression:
+
+.. code-block:: php
+
+ use AppBundle\Entity\Post;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
+
+ /**
+ * @Route("/{id}/edit", name="admin_post_edit")
+ * @Security("post.isAuthor(user)")
+ */
+ public function editAction(Post $post)
+ {
+ // ...
+ }
+
+.. code-block:: html+jinja
+
+ {% if post.isAuthor(app.user) %}
+ ...
+ {% endif %}
+
+.. _best-practices-directly-isGranted:
+.. _checking-permissions-without-security:
+.. _manually-checking-permissions:
+
+Checking Permissions without @Security
+--------------------------------------
+
+The above example with ``@Security`` only works because we're using the
+:ref:`ParamConverter `, which gives the expression
+access to the a ``post`` variable. If you don't use this, or have some other
+more advanced use-case, you can always do the same security check in PHP:
+
+.. code-block:: php
+
+ /**
+ * @Route("/{id}/edit", name="admin_post_edit")
+ */
+ public function editAction($id)
+ {
+ $post = $this->getDoctrine()->getRepository('AppBundle:Post')
+ ->find($id);
+
+ if (!$post) {
+ throw $this->createNotFoundException();
+ }
+
+ if (!$post->isAuthor($this->getUser())) {
+ throw $this->createAccessDeniedException();
+ }
+
+ // ...
+ }
+
+Security Voters
+---------------
+
+If your security logic is complex and can't be centralized into a method
+like ``isAuthor()``, you should leverage custom voters. These are an order
+of magnitude easier than :doc:`ACLs ` and will give
+you the flexibility you need in almost all cases.
+
+First, create a voter class. The following example shows a voter that implements
+the same ``getAuthorEmail`` logic you used above:
+
+.. code-block:: php
+
+ namespace AppBundle\Security;
+
+ use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
+ use Symfony\Component\Security\Core\User\UserInterface;
+
+ // AbstractVoter class requires Symfony 2.6 or higher version
+ class PostVoter extends AbstractVoter
+ {
+ const CREATE = 'create';
+ const EDIT = 'edit';
+
+ protected function getSupportedAttributes()
+ {
+ return array(self::CREATE, self::EDIT);
+ }
+
+ protected function getSupportedClasses()
+ {
+ return array('AppBundle\Entity\Post');
+ }
+
+ protected function isGranted($attribute, $post, $user = null)
+ {
+ if (!$user instanceof UserInterface) {
+ return false;
+ }
+
+ if ($attribute === self::CREATE && in_array('ROLE_ADMIN', $user->getRoles(), true)) {
+ return true;
+ }
+
+ if ($attribute === self::EDIT && $user->getEmail() === $post->getAuthorEmail()) {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+To enable the security voter in the application, define a new service:
+
+.. code-block:: yaml
+
+ # app/config/services.yml
+ services:
+ # ...
+ post_voter:
+ class: AppBundle\Security\PostVoter
+ public: false
+ tags:
+ - { name: security.voter }
+
+Now, you can use the voter with the ``@Security`` annotation:
+
+.. code-block:: php
+
+ /**
+ * @Route("/{id}/edit", name="admin_post_edit")
+ * @Security("is_granted('edit', post)")
+ */
+ public function editAction(Post $post)
+ {
+ // ...
+ }
+
+You can also use this directly with the ``security.authorization_checker`` service or
+via the even easier shortcut in a controller:
+
+.. code-block:: php
+
+ /**
+ * @Route("/{id}/edit", name="admin_post_edit")
+ */
+ public function editAction($id)
+ {
+ $post = // query for the post ...
+
+ $this->denyAccessUnlessGranted('edit', $post);
+
+ // or without the shortcut:
+ //
+ // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
+ // throw $this->createAccessDeniedException();
+ // }
+ }
+
+Learn More
+----------
+
+The `FOSUserBundle`_, developed by the Symfony community, adds support for a
+database-backed user system in Symfony. It also handles common tasks like
+user registration and forgotten password functionality.
+
+Enable the :doc:`Remember Me feature ` to
+allow your users to stay logged in for a long period of time.
+
+When providing customer support, sometimes it's necessary to access the application
+as some *other* user so that you can reproduce the problem. Symfony provides
+the ability to :doc:`impersonate users `.
+
+If your company uses a user login method not supported by Symfony, you can
+develop :doc:`your own user provider ` and
+:doc:`your own authentication provider `.
+
+.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
+.. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html
+.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html
+.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle
diff --git a/best_practices/templates.rst b/best_practices/templates.rst
new file mode 100644
index 00000000000..801646587b2
--- /dev/null
+++ b/best_practices/templates.rst
@@ -0,0 +1,161 @@
+Templates
+=========
+
+When PHP was created 20 years ago, developers loved its simplicity and how
+well it blended HTML and dynamic code. But as time passed, other template
+languages - like `Twig`_ - were created to make templating even better.
+
+.. best-practice::
+
+ Use Twig templating format for your templates.
+
+Generally speaking, PHP templates are much more verbose than Twig templates because
+they lack native support for lots of modern features needed by templates,
+like inheritance, automatic escaping and named arguments for filters and
+functions.
+
+Twig is the default templating format in Symfony and has the largest community
+support of all non-PHP template engines (it's used in high profile projects
+such as Drupal 8).
+
+In addition, Twig is the only template format with guaranteed support in Symfony
+3.0. As a matter of fact, PHP may be removed from the officially supported
+template engines.
+
+Template Locations
+------------------
+
+.. best-practice::
+
+ Store all your application's templates in ``app/Resources/views/`` directory.
+
+Traditionally, Symfony developers stored the application templates in the
+``Resources/views/`` directory of each bundle. Then they used the logical name
+to refer to them (e.g. ``AcmeDemoBundle:Default:index.html.twig``).
+
+But for the templates used in your application, it's much more convenient
+to store them in the ``app/Resources/views/`` directory. For starters, this
+drastically simplifies their logical names:
+
+================================================= ==================================
+Templates Stored inside Bundles Templates Stored in ``app/``
+================================================= ==================================
+``AcmeDemoBundle:Default:index.html.twig`` ``default/index.html.twig``
+``::layout.html.twig`` ``layout.html.twig``
+``AcmeDemoBundle::index.html.twig`` ``index.html.twig``
+``AcmeDemoBundle:Default:subdir/index.html.twig`` ``default/subdir/index.html.twig``
+``AcmeDemoBundle:Default/subdir:index.html.twig`` ``default/subdir/index.html.twig``
+================================================= ==================================
+
+Another advantage is that centralizing your templates simplifies the work
+of your designers. They don't need to look for templates in lots of directories
+scattered through lots of bundles.
+
+Twig Extensions
+---------------
+
+.. best-practice::
+
+ Define your Twig extensions in the ``AppBundle/Twig/`` directory and
+ configure them using the ``app/config/services.yml`` file.
+
+Our application needs a custom ``md2html`` Twig filter so that we can transform
+the Markdown contents of each post into HTML.
+
+To do this, first, install the excellent `Parsedown`_ Markdown parser as
+a new dependency of the project:
+
+.. code-block:: bash
+
+ $ composer require erusev/parsedown
+
+Then, create a new ``Markdown`` service that will be used later by the Twig
+extension. The service definition only requires the path to the class:
+
+.. code-block:: yaml
+
+ # app/config/services.yml
+ services:
+ # ...
+ markdown:
+ class: AppBundle\Utils\Markdown
+
+And the ``Markdown`` class just needs to define one single method to transform
+Markdown content into HTML::
+
+ namespace AppBundle\Utils;
+
+ class Markdown
+ {
+ private $parser;
+
+ public function __construct()
+ {
+ $this->parser = new \Parsedown();
+ }
+
+ public function toHtml($text)
+ {
+ $html = $this->parser->text($text);
+
+ return $html;
+ }
+ }
+
+Next, create a new Twig extension and define a new filter called ``md2html``
+using the ``Twig_SimpleFilter`` class. Inject the newly defined ``markdown``
+service in the constructor of the Twig extension:
+
+.. code-block:: php
+
+ namespace AppBundle\Twig;
+
+ use AppBundle\Utils\Markdown;
+
+ class AppExtension extends \Twig_Extension
+ {
+ private $parser;
+
+ public function __construct(Markdown $parser)
+ {
+ $this->parser = $parser;
+ }
+
+ public function getFilters()
+ {
+ return array(
+ new \Twig_SimpleFilter(
+ 'md2html',
+ array($this, 'markdownToHtml'),
+ array('is_safe' => array('html'))
+ ),
+ );
+ }
+
+ public function markdownToHtml($content)
+ {
+ return $this->parser->toHtml($content);
+ }
+
+ public function getName()
+ {
+ return 'app_extension';
+ }
+ }
+
+Lastly define a new service to enable this Twig extension in the app (the service
+name is irrelevant because you never use it in your own code):
+
+.. code-block:: yaml
+
+ # app/config/services.yml
+ services:
+ app.twig.app_extension:
+ class: AppBundle\Twig\AppExtension
+ arguments: ["@markdown"]
+ public: false
+ tags:
+ - { name: twig.extension }
+
+.. _`Twig`: http://twig.sensiolabs.org/
+.. _`Parsedown`: http://parsedown.org/
diff --git a/best_practices/tests.rst b/best_practices/tests.rst
new file mode 100644
index 00000000000..0bbcbd665de
--- /dev/null
+++ b/best_practices/tests.rst
@@ -0,0 +1,114 @@
+Tests
+=====
+
+Roughly speaking, there are two types of test. Unit testing allows you to
+test the input and output of specific functions. Functional testing allows
+you to command a "browser" where you browse to pages on your site, click
+links, fill out forms and assert that you see certain things on the page.
+
+Unit Tests
+----------
+
+Unit tests are used to test your "business logic", which should live in classes
+that are independent of Symfony. For that reason, Symfony doesn't really
+have an opinion on what tools you use for unit testing. However, the most
+popular tools are `PhpUnit`_ and `PhpSpec`_.
+
+Functional Tests
+----------------
+
+Creating really good functional tests can be tough so some developers skip
+these completely. Don't skip the functional tests! By defining some *simple*
+functional tests, you can quickly spot any big errors before you deploy them:
+
+.. best-practice::
+
+ Define a functional test that at least checks if your application pages
+ are successfully loading.
+
+A functional test can be as easy as this:
+
+.. code-block:: php
+
+ /** @dataProvider provideUrls */
+ public function testPageIsSuccessful($url)
+ {
+ $client = self::createClient();
+ $client->request('GET', $url);
+
+ $this->assertTrue($client->getResponse()->isSuccessful());
+ }
+
+ public function provideUrls()
+ {
+ return array(
+ array('/'),
+ array('/posts'),
+ array('/post/fixture-post-1'),
+ array('/blog/category/fixture-category'),
+ array('/archives'),
+ // ...
+ );
+ }
+
+This code checks that all the given URLs load successfully, which means that
+their HTTP response status code is between ``200`` and ``299``. This may
+not look that useful, but given how little effort this took, it's worth
+having it in your application.
+
+In computer software, this kind of test is called `smoke testing`_ and consists
+of *"preliminary testing to reveal simple failures severe enough to reject a
+prospective software release"*.
+
+Hardcode URLs in a Functional Test
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some of you may be asking why the previous functional test doesn't use the URL
+generator service:
+
+.. best-practice::
+
+ Hardcode the URLs used in the functional tests instead of using the URL
+ generator.
+
+Consider the following functional test that uses the ``router`` service to
+generate the URL of the tested page:
+
+.. code-block:: php
+
+ public function testBlogArchives()
+ {
+ $client = self::createClient();
+ $url = $client->getContainer()->get('router')->generate('blog_archives');
+ $client->request('GET', $url);
+
+ // ...
+ }
+
+This will work, but it has one *huge* drawback. If a developer mistakenly
+changes the path of the ``blog_archives`` route, the test will still pass,
+but the original (old) URL won't work! This means that any bookmarks for
+that URL will be broken and you'll lose any search engine page ranking.
+
+Testing JavaScript Functionality
+--------------------------------
+
+The built-in functional testing client is great, but it can't be used to
+test any JavaScript behavior on your pages. If you need to test this, consider
+using the `Mink`_ library from within PHPUnit.
+
+Of course, if you have a heavy JavaScript frontend, you should consider using
+pure JavaScript-based testing tools.
+
+Learn More about Functional Tests
+---------------------------------
+
+Consider using `Faker`_ and `Alice`_ libraries to generate real-looking data
+for your test fixtures.
+
+.. _`Faker`: https://github.com/fzaninotto/Faker
+.. _`Alice`: https://github.com/nelmio/alice
+.. _`PhpUnit`: https://phpunit.de/
+.. _`PhpSpec`: http://www.phpspec.net/
+.. _`Mink`: http://mink.behat.org
+.. _`smoke testing`: http://en.wikipedia.org/wiki/Smoke_testing_(software)
diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst
new file mode 100644
index 00000000000..a45d85542b5
--- /dev/null
+++ b/best_practices/web-assets.rst
@@ -0,0 +1,97 @@
+Web Assets
+==========
+
+Web assets are things like CSS, JavaScript and image files that make the
+frontend of your site look and work great. Symfony developers have traditionally
+stored these assets in the ``Resources/public/`` directory of each bundle.
+
+.. best-practice::
+
+ Store your assets in the ``web/`` directory.
+
+Scattering your web assets across tens of different bundles makes it more
+difficult to manage them. Your designers' lives will be much easier if all
+the application assets are in one location.
+
+Templates also benefit from centralizing your assets, because the links are
+much more concise:
+
+.. code-block:: html+jinja
+
+
+
+
+ {# ... #}
+
+
+
+
+.. note::
+
+ Keep in mind that ``web/`` is a public directory and that anything stored
+ here will be publicly accessible. For that reason, you should put your
+ compiled web assets here, but not their source files (e.g. SASS files).
+
+Using Assetic
+-------------
+
+These days, you probably can't simply create static CSS and JavaScript files
+and include them in your template. Instead, you'll probably want to combine
+and minify these to improve client-side performance. You may also want to
+use LESS or Sass (for example), which means you'll need some way to process
+these into CSS files.
+
+A lot of tools exist to solve these problems, including pure-frontend (non-PHP)
+tools like GruntJS.
+
+.. best-practice::
+
+ Use Assetic to compile, combine and minimize web assets, unless you're
+ comfortable with frontend tools like GruntJS.
+
+:doc:`Assetic ` is an asset manager capable
+of compiling assets developed with a lot of different frontend technologies
+like LESS, Sass and CoffeeScript.
+Combining all your assets with Assetic is a matter of wrapping all the assets
+with a single Twig tag:
+
+.. code-block:: html+jinja
+
+ {% stylesheets
+ 'css/bootstrap.min.css'
+ 'css/main.css'
+ filter='cssrewrite' output='css/compiled/all.css' %}
+
+ {% endstylesheets %}
+
+ {# ... #}
+
+ {% javascripts
+ 'js/jquery.min.js'
+ 'js/bootstrap.min.js'
+ output='js/compiled/all.js' %}
+
+ {% endjavascripts %}
+
+Frontend-Based Applications
+---------------------------
+
+Recently, frontend technologies like AngularJS have become pretty popular
+for developing frontend web applications that talk to an API.
+
+If you are developing an application like this, you should use the tools
+that are recommended by the technology, such as Bower and GruntJS. You should
+develop your frontend application separately from your Symfony backend (even
+separating the repositories if you want).
+
+Learn More about Assetic
+------------------------
+
+Assetic can also minimize CSS and JavaScript assets
+:doc:`using UglifyCSS/UglifyJS ` to speed up your
+websites. You can even :doc:`compress images `
+with Assetic to reduce their size before serving them to the user. Check out
+the `official Assetic documentation`_ to learn more about all the available
+features.
+
+.. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic
diff --git a/book/controller.rst b/book/controller.rst
index 17902efcf9e..dc8a7c663d9 100644
--- a/book/controller.rst
+++ b/book/controller.rst
@@ -4,7 +4,7 @@
Controller
==========
-A controller is a PHP function you create that takes information from the
+A controller is a PHP callable you create that takes information from the
HTTP request and constructs and returns an HTTP response (as a Symfony
``Response`` object). The response could be an HTML page, an XML document,
a serialized JSON array, an image, a redirect, a 404 error or anything else
@@ -12,7 +12,7 @@ you can dream up. The controller contains whatever arbitrary logic *your
application* needs to render the content of a page.
See how simple this is by looking at a Symfony controller in action.
-The following controller would render a page that simply prints ``Hello world!``::
+This renders a page that prints the famous ``Hello world!``::
use Symfony\Component\HttpFoundation\Response;
@@ -40,9 +40,9 @@ common examples:
* *Controller C* handles the form submission of a contact form. It reads
the form information from the request, saves the contact information to
- the database and emails the contact information to the webmaster. Finally,
- it creates a ``Response`` object that redirects the client's browser to
- the contact form "thank you" page.
+ the database and emails the contact information to you. Finally, it creates
+ a ``Response`` object that redirects the client's browser to the contact
+ form "thank you" page.
.. index::
single: Controller; Request-controller-response lifecycle
@@ -51,8 +51,8 @@ Requests, Controller, Response Lifecycle
----------------------------------------
Every request handled by a Symfony project goes through the same simple lifecycle.
-The framework takes care of the repetitive tasks and ultimately executes a
-controller, which houses your custom application code:
+The framework takes care of all the repetitive stuff: you just need to write
+your custom code in the controller function:
#. Each request is handled by a single front controller file (e.g. ``app.php``
or ``app_dev.php``) that bootstraps the application;
@@ -87,14 +87,13 @@ A Simple Controller
-------------------
While a controller can be any PHP callable (a function, method on an object,
-or a ``Closure``), in Symfony, a controller is usually a single method inside
-a controller object. Controllers are also called *actions*.
+or a ``Closure``), a controller is usually a method inside a controller class.
+Controllers are also called *actions*.
.. code-block:: php
- :linenos:
- // src/Acme/HelloBundle/Controller/HelloController.php
- namespace Acme\HelloBundle\Controller;
+ // src/AppBundle/Controller/HelloController.php
+ namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
@@ -146,12 +145,32 @@ to the controller:
.. configuration-block::
+ .. code-block:: php-annotations
+
+ // src/AppBundle/Controller/HelloController.php
+ namespace AppBundle\Controller;
+
+ use Symfony\Component\HttpFoundation\Response;
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+ class HelloController
+ {
+ /**
+ * @Route("/hello/{name}", name="hello")
+ */
+ public function indexAction($name)
+ {
+ return new Response('Hello '.$name.'!');
+ }
+ }
+
.. code-block:: yaml
# app/config/routing.yml
hello:
path: /hello/{name}
- defaults: { _controller: AcmeHelloBundle:Hello:index }
+ # uses a special syntax to point to the controller - see note below
+ defaults: { _controller: AppBundle:Hello:index }
.. code-block:: xml
@@ -163,7 +182,8 @@ to the controller:
http://symfony.com/schema/routing/routing-1.0.xsd">
- AcmeHelloBundle:Hello:index
+
+ AppBundle:Hello:index
@@ -175,34 +195,30 @@ to the controller:
$collection = new RouteCollection();
$collection->add('hello', new Route('/hello/{name}', array(
- '_controller' => 'AcmeHelloBundle:Hello:index',
+ // uses a special syntax to point to the controller - see note below
+ '_controller' => 'AppBundle:Hello:index',
)));
return $collection;
-Going to ``/hello/ryan`` now executes the ``HelloController::indexAction()``
-controller and passes in ``ryan`` for the ``$name`` variable. Creating a
-"page" means simply creating a controller method and associated route.
-
-Notice the syntax used to refer to the controller: ``AcmeHelloBundle:Hello:index``.
-Symfony uses a flexible string notation to refer to different controllers.
-This is the most common syntax and tells Symfony to look for a controller
-class called ``HelloController`` inside a bundle named ``AcmeHelloBundle``. The
-method ``indexAction()`` is then executed.
+Now, you can go to ``/hello/ryan`` (e.g. ``http://localhost:8000/app_dev.php/hello/ryan``
+if you're using the :doc:`built-in web server `)
+and Symfony will execute the ``HelloController::indexAction()`` controller
+and pass in ``ryan`` for the ``$name`` variable. Creating a "page" means
+simply creating a controller method and an associated route.
-For more details on the string format used to reference different controllers,
-see :ref:`controller-string-syntax`.
+Simple, right?
-.. note::
+.. sidebar:: The AppBundle:Hello:index controller syntax
- This example places the routing configuration directly in the ``app/config/``
- directory. A better way to organize your routes is to place each route
- in the bundle it belongs to. For more information on this, see
- :ref:`routing-include-external-resources`.
+ If you use the YML or XML formats, you'll refer to the controller using
+ a special shortcut syntax: ``AppBundle:Hello:index``. For more details
+ on the controller format, see :ref:`controller-string-syntax`.
-.. tip::
+.. seealso::
- You can learn much more about the routing system in the :doc:`Routing chapter `.
+ You can learn much more about the routing system in the
+ :doc:`Routing chapter `.
.. index::
single: Controller; Controller arguments
@@ -212,38 +228,55 @@ see :ref:`controller-string-syntax`.
Route Parameters as Controller Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-You already know that the ``_controller`` parameter ``AcmeHelloBundle:Hello:index``
-refers to a ``HelloController::indexAction()`` method that lives inside the
-``AcmeHelloBundle`` bundle. What's more interesting is the arguments that are
-passed to that method::
+You already know that the route points to the
+``HelloController::indexAction()`` method that lives inside AppBundle. What's
+more interesting is the argument that is passed to that method::
- // src/Acme/HelloBundle/Controller/HelloController.php
- namespace Acme\HelloBundle\Controller;
+ // src/AppBundle/Controller/HelloController.php
+ // ...
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
- use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-
- class HelloController extends Controller
+ /**
+ * @Route("/hello/{name}", name="hello")
+ */
+ public function indexAction($name)
{
- public function indexAction($name)
- {
- // ...
- }
+ // ...
}
The controller has a single argument, ``$name``, which corresponds to the
-``{name}`` parameter from the matched route (``ryan`` in the example). In
-fact, when executing your controller, Symfony matches each argument of
-the controller with a parameter from the matched route. Take the following
-example:
+``{name}`` parameter from the matched route (``ryan`` if you go to ``/hello/ryan``).
+When executing your controller, Symfony matches each argument with a parameter
+from the route. So the value for ``{name}`` is passed to ``$name``.
+
+Take the following more-interesting example:
.. configuration-block::
+ .. code-block:: php-annotations
+
+ // src/AppBundle/Controller/HelloController.php
+ // ...
+
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+
+ class HelloController
+ {
+ /**
+ * @Route("/hello/{firstName}/{lastName}", name="hello")
+ */
+ public function indexAction($firstName, $lastName)
+ {
+ // ...
+ }
+ }
+
.. code-block:: yaml
# app/config/routing.yml
hello:
path: /hello/{firstName}/{lastName}
- defaults: { _controller: AcmeHelloBundle:Hello:index, color: green }
+ defaults: { _controller: AppBundle:Hello:index }
.. code-block:: xml
@@ -255,8 +288,7 @@ example:
http://symfony.com/schema/routing/routing-1.0.xsd">
- AcmeHelloBundle:Hello:index
- green
+ AppBundle:Hello:index
@@ -268,36 +300,28 @@ example:
$collection = new RouteCollection();
$collection->add('hello', new Route('/hello/{firstName}/{lastName}', array(
- '_controller' => 'AcmeHelloBundle:Hello:index',
- 'color' => 'green',
+ '_controller' => 'AppBundle:Hello:index',
)));
return $collection;
-The controller for this can take several arguments::
+Now, the controller can have two arguments::
- public function indexAction($firstName, $lastName, $color)
+ public function indexAction($firstName, $lastName)
{
// ...
}
-Notice that both placeholder variables (``{firstName}``, ``{lastName}``)
-as well as the default ``color`` variable are available as arguments in the
-controller. When a route is matched, the placeholder variables are merged
-with the ``defaults`` to make one array that's available to your controller.
-
Mapping route parameters to controller arguments is easy and flexible. Keep
the following guidelines in mind while you develop.
* **The order of the controller arguments does not matter**
- Symfony is able to match the parameter names from the route to the variable
- names in the controller method's signature. In other words, it realizes that
- the ``{lastName}`` parameter matches up with the ``$lastName`` argument.
- The arguments of the controller could be totally reordered and still work
- perfectly::
+ Symfony matches the parameter **names** from the route to the variable
+ **names** of the controller. The arguments of the controller could be totally
+ reordered and still work perfectly::
- public function indexAction($lastName, $color, $firstName)
+ public function indexAction($lastName, $firstName)
{
// ...
}
@@ -307,7 +331,7 @@ the following guidelines in mind while you develop.
The following would throw a ``RuntimeException`` because there is no ``foo``
parameter defined in the route::
- public function indexAction($firstName, $lastName, $color, $foo)
+ public function indexAction($firstName, $lastName, $foo)
{
// ...
}
@@ -315,7 +339,7 @@ the following guidelines in mind while you develop.
Making the argument optional, however, is perfectly ok. The following
example would not throw an exception::
- public function indexAction($firstName, $lastName, $color, $foo = 'bar')
+ public function indexAction($firstName, $lastName, $foo = 'bar')
{
// ...
}
@@ -325,7 +349,7 @@ the following guidelines in mind while you develop.
If, for example, the ``lastName`` weren't important for your controller,
you could omit it entirely::
- public function indexAction($firstName, $color)
+ public function indexAction($firstName)
{
// ...
}
@@ -334,100 +358,70 @@ the following guidelines in mind while you develop.
Every route also has a special ``_route`` parameter, which is equal to
the name of the route that was matched (e.g. ``hello``). Though not usually
- useful, this is equally available as a controller argument.
+ useful, this is also available as a controller argument. You can also
+ pass other variables from your route to your controller arguments. See
+ :doc:`/cookbook/routing/extra_information`.
.. _book-controller-request-argument:
The ``Request`` as a Controller Argument
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-For convenience, you can also have Symfony pass you the ``Request`` object
-as an argument to your controller. This is especially convenient when you're
-working with forms, for example::
+What if you need to read query parameters, grab a request header or get access
+to an uploaded file? All of that information is stored in Symfony's ``Request``
+object. To get it in your controller, just add it as an argument and
+**type-hint it with the Request class**::
use Symfony\Component\HttpFoundation\Request;
- public function updateAction(Request $request)
+ public function indexAction($firstName, $lastName, Request $request)
{
- $form = $this->createForm(...);
+ $page = $request->query->get('page', 1);
- $form->handleRequest($request);
// ...
}
-.. index::
- single: Controller; Base controller class
+.. seealso::
-Creating Static Pages
----------------------
+ Want to know more about getting information from the request? See
+ :ref:`Access Request Information `.
-You can create a static page without even creating a controller (only a route
-and template are needed).
-
-Use it! See :doc:`/cookbook/templating/render_without_controller`.
+.. index::
+ single: Controller; Base controller class
The Base Controller Class
-------------------------
-For convenience, Symfony comes with a base ``Controller`` class that assists
-with some of the most common controller tasks and gives your controller class
-access to any resource it might need. By extending this ``Controller`` class,
-you can take advantage of several helper methods.
+For convenience, Symfony comes with an optional base ``Controller`` class.
+If you extend it, you'll get access to a number of helper methods and all
+of your service objects via the container (see :ref:`controller-accessing-services`).
Add the ``use`` statement atop the ``Controller`` class and then modify the
``HelloController`` to extend it::
- // src/Acme/HelloBundle/Controller/HelloController.php
- namespace Acme\HelloBundle\Controller;
+ // src/AppBundle/Controller/HelloController.php
+ namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
- use Symfony\Component\HttpFoundation\Response;
class HelloController extends Controller
{
- public function indexAction($name)
- {
- return new Response('Hello '.$name.'!');
- }
+ // ...
}
-This doesn't actually change anything about how your controller works. In
-the next section, you'll learn about the helper methods that the base controller
-class makes available. These methods are just shortcuts to using core Symfony
-functionality that's available to you with or without the use of the base
-``Controller`` class. A great way to see the core functionality in action
-is to look in the
-:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class
-itself.
-
-.. tip::
-
- Extending the base class is *optional* in Symfony; it contains useful
- shortcuts but nothing mandatory. You can also extend
- :class:`Symfony\\Component\\DependencyInjection\\ContainerAware` or use
- the class:`Symfony\\Component\\DependencyInjection\\ContainerAwareTrait` trait
- (if you have PHP 5.4). The service container object will then be accessible
- via the ``container`` property.
-
-.. versionadded:: 2.4
- The ``ContainerAwareTrait`` was introduced in Symfony 2.4.
-
-.. note::
-
- You can also define your :doc:`Controllers as Services `.
- This is optional, but can give you more control over the exact dependencies
- that are injected into your controllers.
+This doesn't actually change anything about how your controller works: it
+just gives you access to helper methods that the base controller class makes
+available. These are just shortcuts to using core Symfony functionality that's
+available to you with or without the use of the base ``Controller`` class.
+A great way to see the core functionality in action is to look in the
+`Controller class`_.
-.. index::
- single: Controller; Common tasks
-
-Common Controller Tasks
------------------------
+.. seealso::
-Though a controller can do virtually anything, most controllers will perform
-the same basic tasks over and over again. These tasks, such as redirecting,
-forwarding, rendering templates and accessing core services, are very easy
-to manage in Symfony.
+ If you're curious about how a controller would work that did *not* extend
+ this base class, check out :doc:`Controllers as Services `.
+ This is optional, but can give you more control over the exact objects/dependencies
+ that are injected into your controller.
.. index::
single: Controller; Redirecting
@@ -435,7 +429,9 @@ to manage in Symfony.
Redirecting
~~~~~~~~~~~
-If you want to redirect the user to another page, use the ``redirect()`` method::
+If you want to redirect the user to another page, use the
+:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::redirect`
+method::
public function indexAction()
{
@@ -463,73 +459,6 @@ perform a 301 (permanent) redirect, modify the second argument::
return new RedirectResponse($this->generateUrl('homepage'));
-.. index::
- single: Controller; Forwarding
-
-Forwarding
-~~~~~~~~~~
-
-You can also easily forward to another controller internally with the
-:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward`
-method. Instead of redirecting the user's browser, it makes an internal sub-request,
-and calls the specified controller. The ``forward()`` method returns the ``Response``
-object that's returned from that controller::
-
- public function indexAction($name)
- {
- $response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
- 'name' => $name,
- 'color' => 'green',
- ));
-
- // ... further modify the response or return it directly
-
- return $response;
- }
-
-Notice that the ``forward()`` method uses the same string representation of
-the controller used in the routing configuration. In this case, the target
-controller class will be ``HelloController`` inside some ``AcmeHelloBundle``.
-The array passed to the method becomes the arguments on the resulting controller.
-This same interface is used when embedding controllers into templates (see
-:ref:`templating-embedding-controller`). The target controller method should
-look something like the following::
-
- public function fancyAction($name, $color)
- {
- // ... create and return a Response object
- }
-
-And just like when creating a controller for a route, the order of the arguments
-to ``fancyAction`` doesn't matter. Symfony matches the index key names
-(e.g. ``name``) with the method argument names (e.g. ``$name``). If you
-change the order of the arguments, Symfony will still pass the correct
-value to each variable.
-
-.. tip::
-
- Like other base ``Controller`` methods, the ``forward`` method is just
- a shortcut for core Symfony functionality. A forward can be accomplished
- directly by duplicating the current request. When this
- :ref:`sub request ` is executed via the ``http_kernel``
- service the ``HttpKernel`` returns a ``Response`` object::
-
- use Symfony\Component\HttpKernel\HttpKernelInterface;
-
- $path = array(
- '_controller' => 'AcmeHelloBundle:Hello:fancy',
- 'name' => $name,
- 'color' => 'green',
- );
- $request = $this->container->get('request');
- $subRequest = $request->duplicate(array(), null, $path);
-
- $httpKernel = $this->container->get('http_kernel');
- $response = $httpKernel->handle(
- $subRequest,
- HttpKernelInterface::SUB_REQUEST
- );
-
.. index::
single: Controller; Rendering templates
@@ -538,71 +467,43 @@ value to each variable.
Rendering Templates
~~~~~~~~~~~~~~~~~~~
-Though not a requirement, most controllers will ultimately render a template
-that's responsible for generating the HTML (or other format) for the controller.
-The ``renderView()`` method renders a template and returns its content. The
-content from the template can be used to create a ``Response`` object::
-
- use Symfony\Component\HttpFoundation\Response;
-
- $content = $this->renderView(
- 'AcmeHelloBundle:Hello:index.html.twig',
- array('name' => $name)
- );
+If you're serving HTML, you'll want to render a template. The ``render()``
+method renders a template **and** puts that content into a ``Response``
+object for you::
- return new Response($content);
+ // renders app/Resources/views/Hello/index.html.twig
+ return $this->render('Hello/index.html.twig', array('name' => $name));
-This can even be done in just one step with the ``render()`` method, which
-returns a ``Response`` object containing the content from the template::
+You can also put templates in deeper sub-directories. Just try to avoid creating
+unnecessarily deep structures::
- return $this->render(
- 'AcmeHelloBundle:Hello:index.html.twig',
- array('name' => $name)
- );
-
-In both cases, the ``Resources/views/Hello/index.html.twig`` template inside
-the ``AcmeHelloBundle`` will be rendered.
+ // renders app/Resources/views/Hello/Greetings/index.html.twig
+ return $this->render('Hello/Greetings/index.html.twig', array('name' => $name));
The Symfony templating engine is explained in great detail in the
:doc:`Templating ` chapter.
-.. tip::
-
- You can even avoid calling the ``render`` method by using the ``@Template``
- annotation. See the
- :doc:`FrameworkExtraBundle documentation `
- more details.
-
-.. tip::
-
- The ``renderView`` method is a shortcut to direct use of the ``templating``
- service. The ``templating`` service can also be used directly::
-
- $templating = $this->get('templating');
- $content = $templating->render(
- 'AcmeHelloBundle:Hello:index.html.twig',
- array('name' => $name)
- );
-
-.. note::
+.. sidebar:: Referencing Templates that Live inside the Bundle
- It is possible to render templates in deeper subdirectories as well, however
- be careful to avoid the pitfall of making your directory structure unduly
- elaborate::
-
- $templating->render(
- 'AcmeHelloBundle:Hello/Greetings:index.html.twig',
- array('name' => $name)
- );
- // index.html.twig found in Resources/views/Hello/Greetings
- // is rendered.
+ You can also put templates in the ``Resources/views`` directory of a
+ bundle and reference them with a
+ ``BundleName:DirectoryName:FileName`` syntax. For example,
+ ``AppBundle:Hello:index.html.twig`` would refer to the template located in
+ ``src/AppBundle/Resources/views/Hello/index.html.twig``. See :ref:`template-referencing-in-bundle`.
.. index::
single: Controller; Accessing services
+.. _controller-accessing-services:
+
Accessing other Services
~~~~~~~~~~~~~~~~~~~~~~~~
+Symfony comes packed with a lot of useful objects, called services. These
+are used for rendering templates, sending emails, querying the database and
+any other "work" you can think of. When you install a new bundle, it probably
+brings in even *more* services.
+
When extending the base controller class, you can access any Symfony service
via the ``get()`` method. Here are several common services you might need::
@@ -612,13 +513,15 @@ via the ``get()`` method. Here are several common services you might need::
$mailer = $this->get('mailer');
-There are countless other services available and you are encouraged to define
-your own. To list all available services, use the ``container:debug`` console
-command:
+What other services exist? You can list all services, use the ``debug:container``
+console command:
.. code-block:: bash
- $ php app/console container:debug
+ $ php app/console debug:container
+
+.. versionadded:: 2.6
+ Prior to Symfony 2.6, this command was called ``container:debug``.
For more information, see the :doc:`/book/service_container` chapter.
@@ -644,7 +547,8 @@ If you're extending the base controller class, do the following::
return $this->render(...);
}
-The ``createNotFoundException()`` method creates a special ``NotFoundHttpException``
+The ``createNotFoundException()`` method is just a shortcut to create a
+special :class:`Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException`
object, which ultimately triggers a 404 HTTP response inside Symfony.
Of course, you're free to throw any ``Exception`` class in your controller -
@@ -654,9 +558,11 @@ Symfony will automatically return a 500 HTTP response code.
throw new \Exception('Something went wrong!');
-In every case, a styled error page is shown to the end user and a full debug
-error page is shown to the developer (when viewing the page in debug mode).
-Both of these error pages can be customized. For details, read the
+In every case, an error page is shown to the end user and a full debug
+error page is shown to the developer (i.e. when you're using ``app_dev.php`` -
+see :ref:`page-creation-environments`).
+
+You'll want to customize the error page your user sees. To do that, see the
":doc:`/cookbook/controller/error_pages`" cookbook recipe.
.. index::
@@ -701,7 +607,7 @@ Flash Messages
You can also store small messages that will be stored on the user's session
for exactly one additional request. This is useful when processing a form:
-you want to redirect and have a special message shown on the *next* request.
+you want to redirect and have a special message shown on the *next* page.
These types of messages are called "flash" messages.
For example, imagine you're processing a form submit::
@@ -717,7 +623,7 @@ For example, imagine you're processing a form submit::
if ($form->isValid()) {
// do some sort of processing
- $this->get('session')->getFlashBag()->add(
+ $request->getSession()->getFlashBag()->add(
'notice',
'Your changes were saved!'
);
@@ -729,11 +635,11 @@ For example, imagine you're processing a form submit::
}
After processing the request, the controller sets a ``notice`` flash message
-and then redirects. The name (``notice``) isn't significant - it's just what
-you're using to identify the type of the message.
+in the session and then redirects. The name (``notice``) isn't significant -
+it's just something you invent and reference next.
-In the template of the next action, the following code could be used to render
-the ``notice`` message:
+In the template of the next page (or even better, in your base layout template),
+the following code will render the ``notice`` message:
.. configuration-block::
@@ -751,7 +657,7 @@ the ``notice`` message:
$message
" ?>
-
+
By design, flash messages are meant to live for exactly one request (they're
"gone in a flash"). They're designed to be used across redirects exactly as
@@ -764,9 +670,9 @@ The Response Object
-------------------
The only requirement for a controller is to return a ``Response`` object. The
-:class:`Symfony\\Component\\HttpFoundation\\Response` class is a PHP
-abstraction around the HTTP response - the text-based message filled with HTTP
-headers and content that's sent back to the client::
+:class:`Symfony\\Component\\HttpFoundation\\Response` class is an abstraction
+around the HTTP response: the text-based message filled with headers and
+content that's sent back to the client::
use Symfony\Component\HttpFoundation\Response;
@@ -777,25 +683,26 @@ headers and content that's sent back to the client::
$response = new Response(json_encode(array('name' => $name)));
$response->headers->set('Content-Type', 'application/json');
-.. versionadded:: 2.4
- Support for HTTP status code constants was introduced in Symfony 2.4.
+The ``headers`` property is a :class:`Symfony\\Component\\HttpFoundation\\HeaderBag`
+object and has some nice methods for getting and setting the headers. The
+header names are normalized so that using ``Content-Type`` is equivalent to
+``content-type`` or even ``content_type``.
-.. tip::
+There are also special classes to make certain kinds of responses easier:
- The ``headers`` property is a
- :class:`Symfony\\Component\\HttpFoundation\\HeaderBag` object with several
- useful methods for reading and mutating the ``Response`` headers. The
- header names are normalized so that using ``Content-Type`` is equivalent
- to ``content-type`` or even ``content_type``.
+* For JSON, there is :class:`Symfony\\Component\\HttpFoundation\\JsonResponse`.
+ See :ref:`component-http-foundation-json-response`.
-.. tip::
+* For files, there is :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`.
+ See :ref:`component-http-foundation-serving-files`.
+
+* For streamed responses, there is :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse`.
+ See :ref:`streaming-response`.
- There are also special classes to make certain kinds of responses easier:
+.. seealso::
- - For JSON, there is :class:`Symfony\\Component\\HttpFoundation\\JsonResponse`.
- See :ref:`component-http-foundation-json-response`.
- - For files, there is :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`.
- See :ref:`component-http-foundation-serving-files`.
+ Don't worry! There is a lot more information about the Response object
+ in the component documentation. See :ref:`component-http-foundation-response`.
.. index::
single: Controller; Request object
@@ -824,13 +731,69 @@ controller if a variable is type-hinted with
Like the ``Response`` object, the request headers are stored in a ``HeaderBag``
object and are easily accessible.
+.. seealso::
+
+ Don't worry! There is a lot more information about the Request object
+ in the component documentation. See :ref:`component-http-foundation-request`.
+
+Creating Static Pages
+---------------------
+
+You can create a static page without even creating a controller (only a route
+and template are needed).
+
+See :doc:`/cookbook/templating/render_without_controller`.
+
+.. index::
+ single: Controller; Forwarding
+
+Forwarding to Another Controller
+--------------------------------
+
+Though not very common, you can also forward to another controller internally
+with the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward`
+method. Instead of redirecting the user's browser, it makes an internal sub-request,
+and calls the controller. The ``forward()`` method returns the ``Response``
+object that's returned from *that* controller::
+
+ public function indexAction($name)
+ {
+ $response = $this->forward('AppBundle:Something:fancy', array(
+ 'name' => $name,
+ 'color' => 'green',
+ ));
+
+ // ... further modify the response or return it directly
+
+ return $response;
+ }
+
+Notice that the ``forward()`` method uses a special string representation
+of the controller (see :ref:`controller-string-syntax`). In this case, the
+target controller function will be ``SomethingController::fancyAction()``
+inside the AppBundle. The array passed to the method becomes the arguments on
+the resulting controller. This same idea is used when embedding controllers
+into templates (see :ref:`templating-embedding-controller`). The target
+controller method would look something like this::
+
+ public function fancyAction($name, $color)
+ {
+ // ... create and return a Response object
+ }
+
+Just like when creating a controller for a route, the order of the arguments of
+``fancyAction`` doesn't matter. Symfony matches the index key names (e.g.
+``name``) with the method argument names (e.g. ``$name``). If you change the
+order of the arguments, Symfony will still pass the correct value to each
+variable.
+
Final Thoughts
--------------
Whenever you create a page, you'll ultimately need to write some code that
contains the logic for that page. In Symfony, this is called a controller,
-and it's a PHP function that can do anything it needs in order to return
-the final ``Response`` object that will be returned to the user.
+and it's a PHP function where you can do anything in order to return the
+final ``Response`` object that will be returned to the user.
To make life easier, you can choose to extend a base ``Controller`` class,
which contains shortcut methods for many common controller tasks. For example,
@@ -846,3 +809,5 @@ Learn more from the Cookbook
* :doc:`/cookbook/controller/error_pages`
* :doc:`/cookbook/controller/service`
+
+.. _`Controller class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
diff --git a/book/doctrine.rst b/book/doctrine.rst
index 39a07663ad0..e60ab0118fe 100644
--- a/book/doctrine.rst
+++ b/book/doctrine.rst
@@ -5,11 +5,13 @@ Databases and Doctrine
======================
One of the most common and challenging tasks for any application
-involves persisting and reading information to and from a database. Fortunately,
-Symfony comes integrated with `Doctrine`_, a library whose sole goal is to
-give you powerful tools to make this easy. In this chapter, you'll learn the
-basic philosophy behind Doctrine and see how easy working with a database can
-be.
+involves persisting and reading information to and from a database. Although
+the Symfony full-stack framework doesn't integrate any ORM by default,
+the Symfony Standard Edition, which is the most widely used distribution,
+comes integrated with `Doctrine`_, a library whose sole goal is to give
+you powerful tools to make this easy. In this chapter, you'll learn the
+basic philosophy behind Doctrine and see how easy working with a database
+can be.
.. note::
@@ -20,7 +22,7 @@ be.
easy, and explained in the ":doc:`/cookbook/doctrine/dbal`" cookbook entry.
You can also persist data to `MongoDB`_ using Doctrine ODM library. For
- more information, read the ":doc:`/bundles/DoctrineMongoDBBundle/index`"
+ more information, read the "`DoctrineMongoDBBundle`_"
documentation.
A Simple Example: A Product
@@ -30,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
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -84,8 +77,10 @@ information. By convention, this information is usually configured in an
+ xsi:schemaLocation="http://symfony.com/schema/dic/services
+ http://symfony.com/schema/dic/services/services-1.0.xsd
+ http://symfony.com/schema/dic/doctrine
+ http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">
+ xsi:schemaLocation="http://symfony.com/schema/dic/services
+ http://symfony.com/schema/dic/services/services-1.0.xsd
+ http://symfony.com/schema/dic/doctrine
+ http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">
+
-
+
@@ -348,7 +343,7 @@ see the :ref:`book-doctrine-field-types` section.
You can also check out Doctrine's `Basic Mapping Documentation`_ for
all details about mapping information. If you use annotations, you'll
- need to prepend all annotations with ``ORM\`` (e.g. ``ORM\Column(..)``),
+ need to prepend all annotations with ``ORM\`` (e.g. ``ORM\Column(...)``),
which is not shown in Doctrine's documentation. You'll also need to include
the ``use Doctrine\ORM\Mapping as ORM;`` statement, which *imports* the
``ORM`` annotations prefix.
@@ -366,7 +361,7 @@ see the :ref:`book-doctrine-field-types` section.
.. note::
- When using another library or program (ie. Doxygen) that uses annotations,
+ When using another library or program (e.g. Doxygen) that uses annotations,
you should place the ``@IgnoreAnnotation`` annotation on the class to
indicate which annotations Symfony should ignore.
@@ -392,9 +387,9 @@ 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
+This command makes sure that all the getters and setters are generated
for the ``Product`` class. This is a safe command - you can run it over and
over again: it only generates getters and setters that don't exist (i.e. it
doesn't replace your existing methods).
@@ -433,13 +428,16 @@ 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::
Doctrine doesn't care whether your properties are ``protected`` or ``private``,
- or whether or not you have a getter or setter function for a property.
+ or whether you have a getter or setter function for a property.
The getters and setters are generated here only because you'll need them
to interact with your PHP object.
@@ -469,10 +467,10 @@ in your application. To do this, run:
new column to the existing ``product`` table.
An even better way to take advantage of this functionality is via
- :doc:`migrations `, which allow you to
- generate these SQL statements and store them in migration classes that
- can be run systematically on your production server in order to track
- and migrate your database schema safely and reliably.
+ `migrations`_, which allow you to generate these SQL statements and store
+ them in migration classes that can be run systematically on your production
+ server in order to track and migrate your database schema safely and
+ reliably.
Your database now has a fully-functional ``product`` table with columns that
match the metadata you've specified.
@@ -483,17 +481,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();
@@ -502,6 +499,7 @@ of the bundle:
$product->setDescription('Lorem ipsum dolor');
$em = $this->getDoctrine()->getManager();
+
$em->persist($product);
$em->flush();
@@ -524,17 +522,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
@@ -542,13 +540,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 most efficient query/queries possible. For example, if you persist a
- total of 100 ``Product`` objects and then subsequently call ``flush()``,
- Doctrine will create a *single* prepared statement and re-use it for each
- insert. This pattern is called *Unit of Work*, and it's used because it's
- fast and efficient.
+ 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
@@ -558,7 +555,7 @@ an ``UPDATE`` query if the record already exists in the database.
Doctrine provides a library that allows you to programmatically load testing
data into your project (i.e. "fixture data"). For information, see
- :doc:`/bundles/DoctrineFixturesBundle/index`.
+ the "`DoctrineFixturesBundle`_" documentation.
Fetching Objects from the Database
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -570,7 +567,7 @@ on its ``id`` value::
public function showAction($id)
{
$product = $this->getDoctrine()
- ->getRepository('AcmeStoreBundle:Product')
+ ->getRepository('AppBundle:Product')
->find($id);
if (!$product) {
@@ -585,8 +582,7 @@ on its ``id`` value::
.. tip::
You can achieve the equivalent of this without writing any code by using
- the ``@ParamConverter`` shortcut. See the
- :doc:`FrameworkExtraBundle documentation `
+ the ``@ParamConverter`` shortcut. See the `FrameworkExtraBundle documentation`_
for more details.
When you query for a particular type of object, you always use what's known
@@ -595,12 +591,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 +656,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 +722,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')
@@ -747,8 +743,8 @@ normal ``Query`` object, which can be used to get the result of the query.
(``:price`` in the example above) as it prevents SQL injection attacks.
The ``getResult()`` method returns an array of results. To get only one
-result, you can use ``getSingleResult()`` (which throws exception there is no
-result) or ``getOneOrNullResult()``::
+result, you can use ``getSingleResult()`` (which throws an exception if there
+is no result) or ``getOneOrNullResult()``::
$product = $query->getOneOrNullResult();
@@ -764,7 +760,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,13 +769,13 @@ 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).
The DQL syntax is incredibly powerful, allowing you to easily join between
entities (the topic of :ref:`relations ` will be
-covered later), group, etc. For more information, see the official Doctrine
+covered later), group, etc. For more information, see the official
`Doctrine Query Language`_ documentation.
Custom Repository Classes
@@ -796,13 +792,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 +807,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,16 +835,16 @@ 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,
+repository class. This method will query for all the ``Product`` entities,
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 +854,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,8 +868,8 @@ 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')
- ->findAllOrderedByName();
+ $products = $em->getRepository('AppBundle:Product')
+ ->findAllOrderedByName();
.. note::
@@ -893,7 +889,9 @@ 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 +906,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,26 +928,27 @@ 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:
products:
targetEntity: Product
mappedBy: category
- # don't forget to init the collection in the __construct() method of the entity
+ # don't forget to init the collection in the __construct() method
+ # of the entity
.. code-block:: xml
-
+
-
+
+
-
+ `.
+ `migrations`_.
Saving Related Entities
~~~~~~~~~~~~~~~~~~~~~~~
@@ -1091,8 +1090,8 @@ Now you can see this new code in action! Imagine you're inside a controller::
// ...
- use Acme\StoreBundle\Entity\Category;
- use Acme\StoreBundle\Entity\Product;
+ use AppBundle\Entity\Category;
+ use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends Controller
@@ -1105,6 +1104,7 @@ Now you can see this new code in action! Imagine you're inside a controller::
$product = new Product();
$product->setName('Foo');
$product->setPrice(19.99);
+ $product->setDescription('Lorem ipsum dolor');
// relate this product to the category
$product->setCategory($category);
@@ -1135,7 +1135,7 @@ did before. First, fetch a ``$product`` object and then access its related
public function showAction($id)
{
$product = $this->getDoctrine()
- ->getRepository('AcmeStoreBundle:Product')
+ ->getRepository('AppBundle:Product')
->find($id);
$categoryName = $product->getCategory()->getName();
@@ -1159,10 +1159,10 @@ the category (i.e. it's "lazily loaded").
You can also query in the other direction::
- public function showProductAction($id)
+ public function showProductsAction($id)
{
$category = $this->getDoctrine()
- ->getRepository('AcmeStoreBundle:Category')
+ ->getRepository('AppBundle:Category')
->find($id);
$products = $category->getProducts();
@@ -1183,12 +1183,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 +1220,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 +1243,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 +1304,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 +1316,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 +1325,14 @@ the current date, only when the entity is first persisted (i.e. inserted):
.. code-block:: xml
-
+
-
+
@@ -1360,7 +1360,7 @@ Doctrine's `Lifecycle Events documentation`_.
transforming data in the entity (e.g. setting a created/updated field,
generating a slug value).
- If you need to do some heavier lifting - like perform logging or send
+ If you need to do some heavier lifting - like performing logging or sending
an email - you should register an external class as an event listener
or subscriber and give it access to whatever resources you need. For
more information, see :doc:`/cookbook/doctrine/event_listeners_subscribers`.
@@ -1370,7 +1370,7 @@ Doctrine's `Lifecycle Events documentation`_.
Doctrine Field Types Reference
------------------------------
-Doctrine comes with a large number of field types available. Each of these
+Doctrine comes with numerous field types available. Each of these
maps a PHP data type to a specific column type in whatever database you're
using. For each field type, the ``Column`` can be configured further, setting
the ``length``, ``nullable`` behavior, ``name`` and other options. To see a
@@ -1380,7 +1380,7 @@ list of all available types and more information, see Doctrine's
Summary
-------
-With Doctrine, you can focus on your objects and how they're useful in your
+With Doctrine, you can focus on your objects and how they're used in your
application and worry about database persistence second. This is because
Doctrine allows you to use any PHP object to hold your data and relies on
mapping metadata information to map an object's data to a particular database
@@ -1399,8 +1399,8 @@ For more information about Doctrine, see the *Doctrine* section of the
* :doc:`/cookbook/doctrine/common_extensions`
* :doc:`/cookbook/doctrine/console`
-* :doc:`/bundles/DoctrineFixturesBundle/index`
-* :doc:`/bundles/DoctrineMongoDBBundle/index`
+* `DoctrineFixturesBundle`_
+* `DoctrineMongoDBBundle`_
.. _`Doctrine`: http://www.doctrine-project.org/
.. _`MongoDB`: http://www.mongodb.org/
@@ -1413,3 +1413,7 @@ For more information about Doctrine, see the *Doctrine* section of the
.. _`Lifecycle Events documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events
.. _`Reserved SQL keywords documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words
.. _`Persistent classes`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#persistent-classes
+.. _`DoctrineMongoDBBundle`: http://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html
+.. _`migrations`: http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
+.. _`DoctrineFixturesBundle`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
+.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
diff --git a/book/forms.rst b/book/forms.rst
index 11e2219f4df..7bdfd2d991c 100644
--- a/book/forms.rst
+++ b/book/forms.rst
@@ -6,14 +6,15 @@ Forms
Dealing with HTML forms is one of the most common - and challenging - tasks for
a web developer. Symfony integrates a Form component that makes dealing with
-forms easy. In this chapter, you'll build a complex form from the ground-up,
+forms easy. In this chapter, you'll build a complex form from the ground up,
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
@@ -103,10 +93,10 @@ from inside a controller::
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
- ->add('save', 'submit', array('label' => 'Create Post'))
+ ->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,27 @@ 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 ``