From f8f3c0345b494087fd9286bffb7740de7103a7be Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Thu, 16 Oct 2014 03:26:21 +0200 Subject: [PATCH 1/7] Add the best practices book --- best_practices/business-logic.rst | 344 ++++++++++++++++++++++ best_practices/configuration.rst | 183 ++++++++++++ best_practices/controllers.rst | 212 ++++++++++++++ best_practices/creating-the-project.rst | 252 ++++++++++++++++ best_practices/forms.rst | 231 +++++++++++++++ best_practices/i18n.rst | 96 +++++++ best_practices/index.rst | 16 ++ best_practices/introduction.rst | 101 +++++++ best_practices/security.rst | 363 ++++++++++++++++++++++++ best_practices/templates.rst | 164 +++++++++++ best_practices/tests.rst | 114 ++++++++ best_practices/web-assets.rst | 97 +++++++ 12 files changed, 2173 insertions(+) create mode 100644 best_practices/business-logic.rst create mode 100644 best_practices/configuration.rst create mode 100644 best_practices/controllers.rst create mode 100644 best_practices/creating-the-project.rst create mode 100644 best_practices/forms.rst create mode 100644 best_practices/i18n.rst create mode 100644 best_practices/index.rst create mode 100644 best_practices/introduction.rst create mode 100644 best_practices/security.rst create mode 100644 best_practices/templates.rst create mode 100644 best_practices/tests.rst create mode 100644 best_practices/web-assets.rst diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst new file mode 100644 index 000000000..fe7364ec2 --- /dev/null +++ b/best_practices/business-logic.rst @@ -0,0 +1,344 @@ +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 + + symfoy2-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 + + symfoy2-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 + 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, + ideally just one simple word. + +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('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: + 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 of 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 +`the Symfony Code 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 +.. _`Toran Proxy`: https://toranproxy.com/ +.. _`Composer`: https://getcomposer.org/ +.. _`MVC architecture`: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller +.. _`Doctrine project`: http://www.doctrine-project.org/ +.. _`fixture class`: http://symfony.com/doc/master/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/ +.. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html +.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst new file mode 100644 index 000000000..0c4632397 --- /dev/null +++ b/best_practices/configuration.rst @@ -0,0 +1,183 @@ +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. + +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 `execution 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 wih 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 `How to Expose a semantic Configuration for a Bundle`_ 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: +`How to Set external Parameters in the Service Container`_ + +.. _`feature toggles`: http://en.wikipedia.org/wiki/Feature_toggle +.. _`execution environment`: http://symfony.com/doc/current/cookbook/configuration/environments.html +.. _`constant() function`: http://twig.sensiolabs.org/doc/functions/constant.html +.. _`How to Expose a semantic Configuration for a Bundle`: http://symfony.com/doc/current/cookbook/bundles/extension.html +.. _`How to Set external Parameters in the Service Container`: http://symfony.com/doc/current/cookbook/configuration/external_parameters.html diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst new file mode 100644 index 000000000..05cd7a1af --- /dev/null +++ b/best_practices/controllers.rst @@ -0,0 +1,212 @@ +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() + { + $em = $this->getDoctrine()->getManager(); + $posts = $em->getRepository('App: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 + + /** + * @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 Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + + /** + * @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 `set up before/after filters`_. + +.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`set up before/after filters`: http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst new file mode 100644 index 000000000..244cb6741 --- /dev/null +++ b/best_practices/creating-the-project.rst @@ -0,0 +1,252 @@ +Creating the Project +==================== + +Installing Symfony +------------------ + +There is only one recommended way to install Symfony: + +.. best-practice:: + + Always use `Composer`_ to install Symfony. + +Composer is the dependency manager used by modern PHP applications. Adding or +removing requirements for your project and updating the third-party libraries +used by your code is a breeze thanks to Composer. + +Dependency Management with Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before installing Symfony, you need to make sure that you have Composer installed +globally. Open your terminal (also called *command console*) and run the following +command: + +.. code-block:: bash + + $ composer --version + Composer version 1e27ff5e22df81e3cd0cd36e5fdd4a3c5a031f4a 2014-08-11 15:46:48 + +You'll probably see a different version identifier. Never mind because Composer +is updated on a continuous basis and its specific version doesn't matter. + +Installing Composer Globally +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In case you don't have Composer installed globally, execute the following two +commands if you use Linux or Mac OS X (the second command will ask for your +user password): + +.. code-block:: bash + + $ curl -sS https://getcomposer.org/installer | php + $ sudo mv composer.phar /usr/local/bin/composer + +.. note:: + + Depending on your Linux distribution, you may need to execute ``su`` command + instead of ``sudo``. + +If you use a Windows system, download the executable installer from the +`Composer download page`_ and follow the steps to install it. + +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 + + $ cd projects/ + $ composer create-project symfony/framework-standard-edition blog/ + +This command will create a new directory called ``blog`` that will contain +a fresh new project based on the most recent stable Symfony version available. + +Checking the Symfony Installation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once the installation is finished, enter the ``blog/`` directory and check that +Symfony is correctly installed by executing the following command: + +.. code-block:: bash + + $ cd blog/ + $ php app/console --version + + Symfony version 2.6.* - app/dev/debug + +If you see the installed Symfony version, everything worked as expected. If not, +you can execute the following *script* to check what does prevent your system +from correctly executing Symfony applications: + +.. code-block:: bash + + $ php app/check.php + +Depending on your system, you can see up to two different lists when executing the +`check.php` script. The first one shows the mandatory requirements which your +system must meet to execute Symfony applications. The second list shows the +optional requirements suggested for an optimal execution of Symfony applications: + +.. code-block:: bash + + Symfony2 Requirements Checker + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + > PHP is using the following php.ini file: + /usr/local/zend/etc/php.ini + + > Checking Symfony requirements: + .....E.........................W..... + + [ERROR] + Your system is not ready to run Symfony2 projects + + Fix the following mandatory requirements + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * date.timezone setting must be set + > Set the "date.timezone" setting in php.ini* (like Europe/Paris). + + Optional recommendations to improve your setup + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * short_open_tag should be disabled in php.ini + > Set short_open_tag to off in php.ini*. + + +.. 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 you are using Symfony 2.6 or a newer version, the ``AppBundle`` bundle + is already generated for you. If you are using an older Symfony version, + 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 `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 Symfony2 directory structure. + +.. _`Composer`: https://getcomposer.org/ +.. _`Get Started`: https://getcomposer.org/doc/00-intro.md +.. _`Composer download page`: https://getcomposer.org/download/ +.. _`override the location of the main directories`: http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html +.. _`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 000000000..6d70561e9 --- /dev/null +++ b/best_practices/forms.rst @@ -0,0 +1,231 @@ +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. Honestly, unless you need to reuse the form somewhere else, that's +totally fine. But for organize and reuse, we recommend that you define each +form in its own PHP class: + +.. code-block:: php + + 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: + +.. code-block:: php + + 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 `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 `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: + +.. code-block:: php + + 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_widget(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. + +The simplest way - which is especially useful during development - is to render +the form tags manually and then use ``form_widget()`` to render all of the fields: + +.. code-block:: html+jinja + +
+ {{ form_widget(form) }} +
+ +.. best-practice:: + + Don't use the ``form()`` or ``form_start()`` functions to render the + starting and ending form tags. + +Experienced Symfony developers will recognize that we're rendering the ``
`` +tags manually instead of using the ``form_start()`` or ``form()`` functions. +While those are convenient, they take away from some clarity with little +benefit. + +.. tip:: + + The exception is a delete form because it's really just one button and + so benefits from some of these extra shortcuts. + +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 `How to Customize Form Rendering`_ 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). + +.. _`register your form type as a service`: http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html#creating-your-field-type-as-a-service +.. _`collection type`: http://symfony.com/doc/current/reference/forms/types/collection.html +.. _`How to Customize Form Rendering`: http://symfony.com/doc/current/cookbook/form/form_customization.html +.. _`form event system`: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst new file mode 100644 index 000000000..1e7a86f31 --- /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 000000000..e6e712f58 --- /dev/null +++ b/best_practices/index.rst @@ -0,0 +1,16 @@ +Official Symfony Best Practices +=============================== + +.. toctree:: + + introduction + creating-the-project + configuration + business-logic + controllers + templates + forms + i18n + security + web-assets + tests diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst new file mode 100644 index 000000000..4700bb455 --- /dev/null +++ b/best_practices/introduction.rst @@ -0,0 +1,101 @@ +.. 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 **official 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 `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 +.. _`The Quick Tour`: http://symfony.com/doc/current/quick_tour/the_big_picture.html +.. _`The Official Symfony Book`: http://symfony.com/doc/current/book/index.html +.. _`The Symfony Cookbook`: http://symfony.com/doc/current/cookbook/index.html +.. _`github.com/.../...`: http://github.com/.../... diff --git a/best_practices/security.rst b/best_practices/security.rst new file mode 100644 index 000000000..026c672bc --- /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 `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 + + 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 `security.yml`_, the :ref:`@Security annotation ` +and using :ref:`isGranted ` on the ``security.context`` +service directly. + +.. best-practice:: + + * For protecting broad URL patterns, use ``access_control``; + * Whenever possible, use the ``@Security`` annotation; + * Check security directly on the ``security.context`` 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-directy-isGranted: + +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 `ACL's`_ 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.context`` 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 ... + + if (!$this->get('security.context')->isGranted('edit', $post)) { + throw $this->createAccessDeniedException(); + } + } + +Learn More +---------- + +The `FOSUserBundle`_, developed by the Symfony community, adds support for a +database-backed user system in Symfony2. It also handles common tasks like +user registration and forgotten password functionality. + +Enable the `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 `impersonate users`_. + +If your company uses a user login method not supported by Symfony, you can +develop `your own user provider`_ and `your own authentication provider`_. + +.. _`Security Cookbook Section`: http://symfony.com/doc/current/cookbook/security/index.html +.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html +.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html +.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html +.. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html +.. _`Acces Control List`: http://symfony.com/doc/current/cookbook/security/acl.html +.. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html +.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html +.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle +.. _`Remember Me feature`: http://symfony.com/doc/current/cookbook/security/remember_me.html +.. _`impersonate users`: http://symfony.com/doc/current/cookbook/security/impersonating_user.html +.. _`your own user provider`: http://symfony.com/doc/current/cookbook/security/custom_provider.html +.. _`your own authentication provider`: http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html diff --git a/best_practices/templates.rst b/best_practices/templates.rst new file mode 100644 index 000000000..a53b0a304 --- /dev/null +++ b/best_practices/templates.rst @@ -0,0 +1,164 @@ +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 in Twig 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/`` +================================================== ================================== +``AcmeDemoBunde: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"] + tags: + - { name: twig.extension } + + +.. _`Twig`: http://twig.sensiolabs.org/ +.. _`Parsedown`: http://parsedown.org/ +.. _`Twig global variables`: http://symfony.com/doc/master/cookbook/templating/global_variables.html +.. _`override error pages`: http://symfony.com/doc/current/cookbook/controller/error_pages.html +.. _`render a template without using a controller`: http://symfony.com/doc/current/cookbook/templating/render_without_controller.html diff --git a/best_practices/tests.rst b/best_practices/tests.rst new file mode 100644 index 000000000..0bbcbd665 --- /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 000000000..e77e3db77 --- /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. + +`Assetic`_ is an asset manager capable of compiling assets developed with +a lot of different frontend technologies like LESS, Sass and CoffeScript. +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 `using UglifyCSS/UglifyJS`_ +to speed up your websites. You can even `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. + +.. _`Assetic`: http://symfony.com/doc/current/cookbook/assetic/asset_management.html +.. _`using UglifyCSS/UglifyJS`: http://symfony.com/doc/current/cookbook/assetic/uglifyjs.html +.. _`compress images`: http://symfony.com/doc/current/cookbook/assetic/jpeg_optimize.html +.. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic From c61694885eb9f38b743d2247a42f5f69c26cb2c5 Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Thu, 16 Oct 2014 03:33:28 +0200 Subject: [PATCH 2/7] Translate the introduction --- best_practices/introduction.rst | 174 +++++++++++++++++--------------- 1 file changed, 90 insertions(+), 84 deletions(-) diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index 4700bb455..38a077c1f 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -1,98 +1,104 @@ .. index:: - single: Symfony Framework Best Practices + single: Les bonnes pratiques du framework Symfony -The Symfony Framework Best Practices -==================================== +Les bonnes pratiques du framework Symfony +========================================= -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*. +Le framework Symfony est bien connu pour être *réellement* flexible et utilisé +pour construire des micro-site, des applications d'entreprise permettant de tenir +des milliard de connexions et être la base d'autres frameworks. Depuis qu'il est +sorti en juillet 2011, la communauté a appris énormément sur ce qui est possible +et comment faire les choses *de la meilleure manière*. -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. +Les ressources de la communauté - comme les blogs ou les présentations - ont créées +un ensemble de recommandations non-officielles pour développer des applications +Symfony. Malheureusement, un certain nombre de ces recommandations ne sont pas +nécessaires pour les applications web. La plupart du temps, elles compliquent +inutilement les choses et ne suivent pas la philosophie pragmatique de Symfony. -What is this Guide About? -------------------------- +Qu'apporte ce guide ? +--------------------- -This guide aims to fix that by describing the **official 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`_. +Ce guide entends définir ceci en décrivant les **bonnes pratiques officielles +permettant de développer des applications web avec le framework Symfony**. Elles +sont les bonnes pratiques qui collent à la philosophie du framework telle +qu'imaginée par le créateur original `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) + **Bonnes pratiques** est une expression désignant *"un ensemble de procédures + définies qui permettent de produire des résultats optimums"*. Et c'est exactement + ce que ce guide entend procurer. Sauf si vous n'êtes pas d'accord avec + l'ensemble des recommandations, nous pensons qu'elles peuvent vous aider + à construire de grosses application avec moins de complexité. + +Ce guide est *particulièrement adapté* pour : + +* Les sites internet et les applications web développés avec le framework Symfony. + +Pour les autres cas, ce guide devrait être un bon **point de départ** que vous +pouvez ensuite **amender et adapter à vos besoins**: + +* Bundles partagés avec la communauté Symfony; +* Développeurs avancés ou équipe créant leurs propres standards; +* Quelques applications complexe ayant des pré-requis fortement personnalisés; +* Bundles devant être partagés en interne dans une entreprise. + +Nous savons que les vieilles habitudes ont la vie dure et que certains d'entre +vous seront choqués par certaines de ces bonnes pratiques. Mais en suivant +celles-ci vous serez capable de développer des applications rapides, en +retirant de la complexité et avec le même niveau ou plus de qualité. C'est +également un objet mouvant qui continuera à s'améliorer. + +Gardez en tête que ce sont des **recommandations facultatives** que vous +et votre équipe pouvait ou ne pouvait pas suivre pour développer des +applications Symfony. Si vous souhaitez continuer à suivre vos propres +bonnes pratiques et méthodologies, vous pouvez bien entendu faire cela. +Symfony est assez flexible pour s'adapter à vos besoins. Cela ne changera +jamais. + +À qui s'adresse ce livre (Indice : ce n'est pas un tutoriel) +------------------------------------------------------------ + +Chaque développeur Symfony, que vous soyez expert ou novice, peut lire ce +guide. Mais, comme ce n'est pas un tutoriel, quelques connaissances de base +sur Symfony sont requises pour pouvoir tout comprendre. Si vous êtes totalement +novice avec Symfony, bienvenu ! Commencez par le premier tutoriel `The Quick Tour`_. + +Nous avons volontairement garder ce guide court. Nous ne voulons par répéter des +explications que vous pouvez trouver dans la vaste documentation de Symfony, +comme les discussions autour de l'injection de dépendance ou des ///front controllers///. +Nous allons uniquement mettre l'accent sur l'explication de la façon de faire de +ce que vous connaissez déjà. + +L'application +------------- + +En complément de ce guide, vous trouverez un exemple d'application développée +avec à l'esprit l'ensemble des bonnes pratiques. **L'application est un simple +moteur de blog**, car cela permet de ne se concentrer que sur les concepts et +fonctionnalités de Symfony sans s'embarrasser de détails complexes. + +Au lieu de développer l'application étape par étape dans ce guide, vous trouverez +une sélection d'extrait de code à travers les chapitres. Veuillez vous référer +au dernier chapitre de ce guide pour trouver plus de détails au sujet de +l'application et des instructions pour l'installer. + +Ne mettez pas à jour vos applications existantes ------------------------------------------------ -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 `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. +Après avoir lu ce manuel, certains d'entre vous pourrait vouloir refactoriser +leurs applications Symfony existantes. Notre recommandation est simple et +claire : **vous ne devriez pas refactoriser vos applications existante pour +suivre ces bonnes pratiques**. Les raisons de ne pas le faire sont nombreuses : + +* Vos applications existantes ne sont pas mauvaises, elles suivent simplement + un ensemble d'autres lignes directrices; +* Une refactorisation complète d'une base de code présente un risque d'introduire + des erreurs dans vos applications; +* La somme de travail nécessaire à cela pourrait être mieux dépenser à + améliorer vos tests ou à ajouter des fonctionnalités procurant une réelle + plus-value à vos utilisateurs finaux. .. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot .. _`The Quick Tour`: http://symfony.com/doc/current/quick_tour/the_big_picture.html From 14b0ecd31e8843858bfb47c7565e36fea6abe942 Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Thu, 16 Oct 2014 05:05:52 +0200 Subject: [PATCH 3/7] Translate 'Creating the project' --- best_practices/creating-the-project.rst | 192 ++++++++++++------------ 1 file changed, 98 insertions(+), 94 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 244cb6741..b42ef90e7 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -1,40 +1,42 @@ -Creating the Project -==================== +Créer le projet +=============== -Installing Symfony ------------------- +Installer Symfony +----------------- -There is only one recommended way to install Symfony: +Il n'y a qu'une seule voie recommandée pour installer Symfony : .. best-practice:: - Always use `Composer`_ to install Symfony. + Utilisez toujours `Composer`_ pour installer Symfony. -Composer is the dependency manager used by modern PHP applications. Adding or -removing requirements for your project and updating the third-party libraries -used by your code is a breeze thanks to Composer. +Composer est le gestionnaire de dépendances utilisé par les applications PHP modernes. +Ajoutez ou supprimez des prérequis à votre projet et mettez à jour les bibliothèques +tierces utilisées par votre code est un vrai bonheur avec Composer. -Dependency Management with Composer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Gestion des dépendances avec Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Before installing Symfony, you need to make sure that you have Composer installed -globally. Open your terminal (also called *command console*) and run the following -command: +Avant d'installer Symfony, vous devez être sûr que vous avez Composer d'installé +globalement. Ouvrez votre terminal (aussi appelé *console de commandes*) et exécutez +la commande suivante : .. code-block:: bash $ composer --version Composer version 1e27ff5e22df81e3cd0cd36e5fdd4a3c5a031f4a 2014-08-11 15:46:48 -You'll probably see a different version identifier. Never mind because Composer -is updated on a continuous basis and its specific version doesn't matter. +Vous verrez probablement un identifiant de version différent. Ce n'est pas grave +car Composer est mis à jour de manière continuelle et sa version spécifique n'a +pas d'importance. -Installing Composer Globally -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In case you don't have Composer installed globally, execute the following two -commands if you use Linux or Mac OS X (the second command will ask for your -user password): +Installer Composer globalement +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dans le cas où vous n'auriez pas Composer d'installer globalement, exécutez les +deux commandes suivantes si vous utilisez Linux ou Max OS X (la seconde commande +vous demandera votre mot de passe utilisateur) : .. code-block:: bash @@ -43,32 +45,32 @@ user password): .. note:: - Depending on your Linux distribution, you may need to execute ``su`` command - instead of ``sudo``. + En fonction de votre distribution Linux, vous devrez exécuter la commande ``su`` + au lieu de ``sudo``. -If you use a Windows system, download the executable installer from the -`Composer download page`_ and follow the steps to install it. +Si vous utilisez un système Windows, téléchargez l'installateur depuis la +`page de téléchargement de Composer`_ et suivez les étapes pour l'installer. -Creating the Blog Application ------------------------------ +Créer l'application de blog +--------------------------- -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: +Maintenant que tout est correctement paramétré, vous pouvez créer un nouveau +projet basé sur Symfony. Dans votre console, allez dans un répertoire où vous +avez le droit de créer des fichiers et exécutez les commandes suivantes : .. code-block:: bash $ cd projects/ $ composer create-project symfony/framework-standard-edition blog/ -This command will create a new directory called ``blog`` that will contain -a fresh new project based on the most recent stable Symfony version available. +Cette commande créera un nouveau répertoire appelé ``blog`` qui contiendra +un nouveau projet basé sur la version stable la plus récente de Symfony disponible. -Checking the Symfony Installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Vérifier l'installation de Symfony +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Once the installation is finished, enter the ``blog/`` directory and check that -Symfony is correctly installed by executing the following command: +Une fois l'installation terminée, allez dans le répertoire ``blog/`` et vérifiez +que Symfony est correctement installé en exécutant la commande suivante : .. code-block:: bash @@ -77,18 +79,19 @@ Symfony is correctly installed by executing the following command: Symfony version 2.6.* - app/dev/debug -If you see the installed Symfony version, everything worked as expected. If not, -you can execute the following *script* to check what does prevent your system -from correctly executing Symfony applications: +Si vous voyez la version de Symfony installée, tout fonctionne comme attendu. Sinon, +vous pouvez exécuter le *script* suivant pour vérifier ce qui empêche votre système +d'exécuter correctement des applications Symfony : .. code-block:: bash $ php app/check.php -Depending on your system, you can see up to two different lists when executing the -`check.php` script. The first one shows the mandatory requirements which your -system must meet to execute Symfony applications. The second list shows the -optional requirements suggested for an optimal execution of Symfony applications: +En fonction de votre système, vous pouvez voir jusqu'à deux listes différentes +lors de l'exécution du script `check.php`. La première montre les prérequis +obligatoires que votre système doit avoir pour pouvoir exécuter des applications +Symfony. La seconde liste montre les prérequis facultatifs suggérés pour une +exécution optimal des applications Symfony : .. code-block:: bash @@ -119,16 +122,15 @@ optional requirements suggested for an optimal execution of Symfony applications .. 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. + Les distributions de Symfony sont signées numériquement pour des raisons de sécurité. + Si vous souhaitez vérifier l'intégrité de votre installation Symfony, regardez le + `public checksums repository`_ et suivez `these steps`_ pour vérifier les signatures. -Structuring the Application ---------------------------- +Structurer l'application +------------------------ -After creating the application, enter the ``blog/`` directory and you'll see a -number of files and directories generated automatically: +Après avoir créé l'application, allez dans le répertoire ``blog/`` et vous verrez un +certain nombre de fichiers et répertoires générés automatiquement : .. code-block:: text @@ -144,51 +146,51 @@ number of files and directories generated automatically: ├─ 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. +Cette architecture de fichers et de répertoires est une convention proposée par +Symfony pour la structure de vos application. L'usage recommandé pour chaque +répertoire est le suivant : + +* ``app/cache/``, stocke tous les fichiers de cache générés par l'application; +* ``app/config/``, stocke toute la configuration définie pour chaque environnement; +* ``app/logs/``, stocke tous les fichiers de journaux (logs) générés par l'application; +* ``app/Resources/``, stocke tous les fichiers de templates et de traduction pour l'application; +* ``src/AppBundle/``, stocke tout le code Symfony spécifique (contrôleurs et routes), + ///your domain code/// (ex: classes Doctrine) et toute votre logique métier; +* ``vendor/``, c'est le répertoire où Composer installe les dépendances de votre application + et vous ne devez jamais modifier son contenu; +* ``web/``, stocke tous ///the front controller files/// et toutes les ressources web, telles que + les feuilles de style, les fichiers JavaScript et les 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. +Quand Symfony 2.0 est sorti, beaucoup de développeurs ont naturellement adopté +la voie de symfony 1.x en divisant leurs applications en modules logiques. C'est +pourquoi beaucoup d'applications Symfony utilisent les bundles pour diviser leur +code en fonctionnalités logiques : ``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. +Mais un bundle *entend* être quelque chose pouvant être réutilisé comme un élément +de logiciel à part. Si ``UserBundle`` ne peut pas être réutilisé *"en l'état"* dans +une autre application Symfony, alors il ne devrait pas être son propre bundle. Tout +comme si ``InvoiceBundle`` dépend de ``ProductBundle``, alors il n'y a pas d'avantage +à avoir deux bundles spérarés. .. best-practice:: - Create only one bundle called ``AppBundle`` for your application logic + Créez seulement un bundle appelé ``AppBundle`` pour votre application métier -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. +Implémenter un simple bundle ``AppBundle`` dans vos projet rendra votre code plus +concis et plus simple à comprendre. À partir de Symfony 2.6, la documentation +officielle de Symfony utilisera le nom ``AppBundle``. .. 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. + Il n'est pas nécessaire de préfixer le ``AppBundle`` avec votre propre ///vendor/// + (ex: ``AcmeAppBundle``), car ce bundle applicatif n'a aucune vocation à être partagé. -All in all, this is the typical directory structure of a Symfony application -that follows these best practices: +Au final, ceci est la structure typique d'une application Symfony suivant ces bonnes +pratiques : .. code-block:: text @@ -208,23 +210,25 @@ that follows these best practices: .. tip:: - If you are using Symfony 2.6 or a newer version, the ``AppBundle`` bundle - is already generated for you. If you are using an older Symfony version, - you can generate it by hand executing this command: + Si vous utilisez Symfony 2.6 ou une version plus récente, le bundle ``AppBundle`` + est déjà généré pour vous. Si vous utilisez une ancienne version de Symfony, vous + pouvez le générer à la main en exécutant cette commande : .. code-block:: bash $ php app/console generate:bundle --namespace=AppBundle --dir=src --format=annotation --no-interaction -Extending the Directory Structure ---------------------------------- +Étendre la structure des répertoires +------------------------------------ -If your project or infrastructure requires some changes to the default directory -structure of Symfony, you can `override the location of the main directories`_: +Si vos projets ou votre infrastructure requiert quelques changement dans les +répertoires par défaut de la structure de Symfony, vous pouvez +///`override the location of the main directories`_/// +`surcharger l'emplacement des répertoires principaux`_ : ``cache/``, ``logs/`` and ``web/``. -In addition, Symfony3 will use a slightly different directory structure when -it's released: +En plus, Symfony3 utilisera une structure de répertoire légèrement différentes +lorsqu'il sortira : .. code-block:: text @@ -241,8 +245,8 @@ it's released: ├─ vendor/ └─ web/ -The changes are pretty superficial, but for now, we recommend that you use -the Symfony2 directory structure. +Les changements sont vraiment superficiels, mais pour le moment, nous vous +recommandons d'utiliser la structure de répertoire de Symfony2. .. _`Composer`: https://getcomposer.org/ .. _`Get Started`: https://getcomposer.org/doc/00-intro.md From 4130957a29ed29551e9461f8e71c1c2c4cf5b340 Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Wed, 22 Oct 2014 00:43:20 +0200 Subject: [PATCH 4/7] update sphinx configuration for build --- _exts | 2 +- conf.py | 2 +- index.rst | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/_exts b/_exts index 03bc1c601..e58edd22d 160000 --- a/_exts +++ b/_exts @@ -1 +1 @@ -Subproject commit 03bc1c60172a280619e3476f22b111b4a187895d +Subproject commit e58edd22d16cb247267025d557410dcbfa5fa959 diff --git a/conf.py b/conf.py index 9cd282cd9..fc47c272a 100644 --- a/conf.py +++ b/conf.py @@ -32,7 +32,7 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', - 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode'] + 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/index.rst b/index.rst index c96005fa0..8c750e2f8 100644 --- a/index.rst +++ b/index.rst @@ -38,6 +38,16 @@ Cookbook Lisez le :doc:`Cookbook`. +Bonnes pratiques +---------------- + +.. toctree:: + :hidden: + + best_practices/index + +Lisez le :doc:`Official Best Practices `. + Composants ---------- From bc7db848656c1f35ac772b00e03f1e728dc49911 Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Wed, 22 Oct 2014 00:55:56 +0200 Subject: [PATCH 5/7] edit link : build success --- best_practices/creating-the-project.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index b42ef90e7..5d6a96198 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -124,7 +124,7 @@ exécution optimal des applications Symfony : Les distributions de Symfony sont signées numériquement pour des raisons de sécurité. Si vous souhaitez vérifier l'intégrité de votre installation Symfony, regardez le - `public checksums repository`_ et suivez `these steps`_ pour vérifier les signatures. + `dépôt public des sommes de contrôle`_ et suivez `ces étapes`_ pour vérifier les signatures. Structurer l'application ------------------------ @@ -223,7 +223,6 @@ pratiques : Si vos projets ou votre infrastructure requiert quelques changement dans les répertoires par défaut de la structure de Symfony, vous pouvez -///`override the location of the main directories`_/// `surcharger l'emplacement des répertoires principaux`_ : ``cache/``, ``logs/`` and ``web/``. @@ -250,7 +249,7 @@ recommandons d'utiliser la structure de répertoire de Symfony2. .. _`Composer`: https://getcomposer.org/ .. _`Get Started`: https://getcomposer.org/doc/00-intro.md -.. _`Composer download page`: https://getcomposer.org/download/ -.. _`override the location of the main directories`: http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html -.. _`public checksums repository`: https://github.com/sensiolabs/checksums -.. _`these steps`: http://fabien.potencier.org/article/73/signing-project-releases +.. _`page de téléchargement de Composer`: https://getcomposer.org/download/ +.. _`surcharger l'emplacement des répertoires principaux`: http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html +.. _`dépôt public des sommes de contrôle`: https://github.com/sensiolabs/checksums +.. _`ces étapes`: http://fabien.potencier.org/article/73/signing-project-releases From 9a28d6663dd22c521c24f2480ffe75eaaaa61b3a Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Wed, 22 Oct 2014 01:06:46 +0200 Subject: [PATCH 6/7] Update translation with @gregquat suggestions --- best_practices/creating-the-project.rst | 6 +++--- best_practices/introduction.rst | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 5d6a96198..f14190c2e 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -155,10 +155,10 @@ répertoire est le suivant : * ``app/logs/``, stocke tous les fichiers de journaux (logs) générés par l'application; * ``app/Resources/``, stocke tous les fichiers de templates et de traduction pour l'application; * ``src/AppBundle/``, stocke tout le code Symfony spécifique (contrôleurs et routes), - ///your domain code/// (ex: classes Doctrine) et toute votre logique métier; + votre code métier (ex: classes Doctrine) et toute votre logique métier; * ``vendor/``, c'est le répertoire où Composer installe les dépendances de votre application et vous ne devez jamais modifier son contenu; -* ``web/``, stocke tous ///the front controller files/// et toutes les ressources web, telles que +* ``web/``, stocke tous les fichiers des contrôleurs frontaux et toutes les ressources web, telles que les feuilles de style, les fichiers JavaScript et les images. Application Bundles @@ -186,7 +186,7 @@ officielle de Symfony utilisera le nom ``AppBundle``. .. note:: - Il n'est pas nécessaire de préfixer le ``AppBundle`` avec votre propre ///vendor/// + Il n'est pas nécessaire de préfixer le ``AppBundle`` avec votre propre nom d'organisation (ex: ``AcmeAppBundle``), car ce bundle applicatif n'a aucune vocation à être partagé. Au final, ceci est la structure typique d'une application Symfony suivant ces bonnes diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index 38a077c1f..f5d4ef5db 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -67,7 +67,7 @@ novice avec Symfony, bienvenu ! Commencez par le premier tutoriel `The Quick Tou Nous avons volontairement garder ce guide court. Nous ne voulons par répéter des explications que vous pouvez trouver dans la vaste documentation de Symfony, -comme les discussions autour de l'injection de dépendance ou des ///front controllers///. +comme les discussions autour de l'injection de dépendance ou des contrôleur frontaux. Nous allons uniquement mettre l'accent sur l'explication de la façon de faire de ce que vous connaissez déjà. From ae0652b9487f2ff9b9a090a05a7f357ed0a489ab Mon Sep 17 00:00:00 2001 From: Simon Leblanc Date: Wed, 22 Oct 2014 02:35:46 +0200 Subject: [PATCH 7/7] Translate configuration in the best practices --- best_practices/configuration.rst | 170 ++++++++++++++++--------------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst index 0c4632397..ab340bbe7 100644 --- a/best_practices/configuration.rst +++ b/best_practices/configuration.rst @@ -1,21 +1,21 @@ 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. +La configuration implique généralement différentes parties de l'application (comme +l'infrastructure et la sécurité) et différents environnements (développement, production). +C'est pourquoi Symfony recommande de diviser la configuration de l'application en trois +parties. -Infrastructure-Related Configuration ------------------------------------- +Configuration liée à l'infrastructure +------------------------------------- .. best-practice:: - Define the infrastructure-related configuration options in the - ``app/config/parameters.yml`` file. + Définissez les options de configuration liée à l'infrastructure dans + le fichier ``app/config/parameters.yml``. -The default ``parameters.yml`` file follows this recommendation and defines the -options related to the database and mail server infrastructure: +Le fichier par défaut ``parameters.yml`` suit cette recommandation et défini les +options relatives à la base de données et au serveur de mail : .. code-block:: yaml @@ -35,63 +35,66 @@ options related to the database and mail server infrastructure: # ... -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. +Ces options ne sont pas définies dans le fichier ``app/config/config.yml`` car +elles n'ont pas de rapport avec le comportement de l'application. En d'autres termes, +votre application ne se soucie pas de l'emplacement de la base de données ou des +droits permettant d'y avoir accès, tant que la base de données est bien configurée. -Canonical Parameters +Paramètres standards ~~~~~~~~~~~~~~~~~~~~ .. best-practice:: - Define all your application's parameters in the - ``app/config/parameters.yml.dist`` file. + Définissez tous les paramètres de votre application dans le fichier + ``app/config/parameters.yml.dist``. -Since version 2.3, Symfony includes a configuration file called ``parameters.yml.dist``, -which stores the canonical list of configuration parameters for the application. +Depuis la version 2.3, Symfony inclus un fichier de configuration appelé +``parameters.yml.dist``, qui stocke la liste des paramètres de configuration +standard de votre 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. +Chaque fois qu'un nouveau paramètre de configuration est défini pour votre application, +vous devriez également l'ajouter à ce fichier et envoyer cette modification à votre +gestionnaire de source. Ensuite, à chaque fois qu'un développeur met à jour le projet +ou le déploie sur un serveur, Symfony vérifiera s'il y a des différences entre le fichier +standard ``parameters.yml.dist`` et votre fichier local ``parameters.yml``. S'il existe +une différence, Symfony vous demandera d'indiquer une valeur pour le nouveau paramètre +et l'ajoutera à votre fichier local ``parameters.yml``. -Application-Related Configuration ---------------------------------- +Configuration liée à l'application +---------------------------------- .. best-practice:: - Define the application behavior related configuration options in the - ``app/config/config.yml`` file. + Définissez les options de configuration liées au comportement de + l'application dans le fichier ``app/config/config.yml``. -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. +Le fichier ``config.yml`` contient les options utilisées par l'application pour +modifier son comportement, comme l'expéditeur des notifications par email ou +l'activation de `fonctionnalitées conditionnelles`_. Définir ces valeurs +dans le fichier ``parameters.yml`` serait ajouter une couche supplémentaire de +configuration qui ne serait pas nécessaire car vous ne voulez pas ou n'avez pas +besoin de modifier ces valeurs de configuration sur chaque serveur. -The configuration options defined in the ``config.yml`` file usually vary from -one `execution 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. +Les options de configuration définies dans le fichier ``config.yml`` varient +généralement d'un `environnement d'exécution`_ à l'autre. C'est pourquoi Symfony +inclut déjà les fichiers ``app/config/config_dev.yml`` et ``app/config/config_prod.yml`` +de sorte que vous puissiez indiquer des valeurs spécifiques à chaque environnement. -Constants vs Configuration Options -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Constantes vs Options de Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -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. +L'une des erreurs les plus commune lors de la définition de la configuration +de l'application est de créer de nouvelles options pour les valeurs qui ne +change jamais, comme le nombre d'éléments pour des résultats paginés. .. best-practice:: - Use constants to define configuration options that rarely change. + Utilisez des constantes pour définir les options de configuration ne changeant que rarement. -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: +L'approche traditionnelle pour définir les options de configuration a impliqué +que beaucoup d'application Symfony ont inclut une option comme ce qui suit, qui +serait utilisée pour gérer le nombre de messages à afficher sur la page d'accueil +d'un blog : .. code-block:: yaml @@ -99,11 +102,12 @@ to control the number of posts to display on the blog homepage: 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: +Si vous vous demandez quand est-ce la dernière fois que vous avez changé la valeur +d'une option de ce type, il y a de fortes chances pour que ce soit *jamais*. Créer une +option de configuration pour une valeur que vous ne configurerez jamais n'est pas +nécessaire. Notre recommandation est de définir ces valeurs en tant que constantes +dans votre application. Vous pourriez, par exemple, définir une constante ``NUM_ITEMS`` +dans l'entité ``Post`` : .. code-block:: php @@ -117,12 +121,12 @@ You could, for example, define a ``NUM_ITEMS`` constant in the ``Post`` entity: // ... } -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 wih access to the Symfony container. +Le principal avantage à définir des constantes est que vous pouvez utiliser leur +valeur partout dans votre application. Lorsque vous utilisez des paramètres, ils +ne sont disponibles que lorsque vous avez accès au container Symfony. -Constants can be used for example in your Twig templates thanks to the -``constant()`` function: +Les constantes peuvent être utilisées par exemple dans vos templates Twig grâce +à la fonction ``constant()`` : .. code-block:: html+jinja @@ -130,8 +134,8 @@ Constants can be used for example in your Twig templates thanks to the 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: +Et les entités et repositories Doctrine peuvent accéder facilement à ces valeurs, +alors qu'elles n'ont pas accès aux paramètres du container : .. code-block:: php @@ -148,36 +152,36 @@ whereas they cannot access the container parameters: } } -The only notable disadvantage of using constants for this kind of configuration -values is that you cannot redefine them easily in your tests. +Le seul inconvénient notable de l'utilisation des constantes pour ce type de +configuration est que vous ne pouvez pas les redéfinir facilement dans vos tests. -Semantic Configuration: Don't Do It ------------------------------------ +Configuration Sémantique: Ne le faites pas +------------------------------------------ .. best-practice:: - Don't define a semantic dependency injection configuration for your bundles. + Ne définissez pas une configuration sémantique d'injection de dépendance pour vos bundle. -As explained in `How to Expose a semantic Configuration for a Bundle`_ 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. +Comme expliqué dans l'article `Comment exposer une configuration sémantique pour un Bundle`_, +les bundles Symfony ont deux possibilités concernant la gestion de la configuration : la +configuration normale des serveur via le fichier ``services.yml`` et la configuration +sémantique via une classe spécifique ``*Extension``. -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. +Bien que la configuration sémantique soit beaucoup plus puissante et fournisse des +fonctionnalités intéressante comme la validation, la charge de travail nécessaire +pour définir cette configution n'est pas valable pour vos bundles qui ne sont pas +destinés à être partagés en tant que bundle réutilisable. -Moving Sensitive Options Outside of Symfony Entirely ----------------------------------------------------- +Déplacez les options sensibles entièrement en dehors de Symfony +--------------------------------------------------------------- -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: -`How to Set external Parameters in the Service Container`_ +Lorsque vous manipulez des options sensibles, comme des accès à une base de données, nous +vous recommendons de les stocker en dehors du projet Symfony et de les rendre disponible +au travers des variables d'environnement. Apprenez comme faire en suivant cet article : +`Comment configurer les paramètres externes dans le conteneur de services`_ -.. _`feature toggles`: http://en.wikipedia.org/wiki/Feature_toggle -.. _`execution environment`: http://symfony.com/doc/current/cookbook/configuration/environments.html +.. _`fonctionnalitées conditionnelles`: http://en.wikipedia.org/wiki/Feature_toggle +.. _`environnement d'exécution`: http://symfony.com/doc/current/cookbook/configuration/environments.html .. _`constant() function`: http://twig.sensiolabs.org/doc/functions/constant.html -.. _`How to Expose a semantic Configuration for a Bundle`: http://symfony.com/doc/current/cookbook/bundles/extension.html -.. _`How to Set external Parameters in the Service Container`: http://symfony.com/doc/current/cookbook/configuration/external_parameters.html +.. _`Comment exposer une configuration sémantique pour un Bundle`: http://symfony.com/fr/doc/current/cookbook/bundles/extension.html +.. _`Comment configurer les paramètres externes dans le conteneur de services`: http://symfony.com/fr/doc/current/cookbook/configuration/external_parameters.html