<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title type="text">Jeremy Barthe</title>
    <generator uri="https://github.com/jekyll/jekyll">Jekyll</generator>
    <link rel="self" type="application/atom+xml" href="http://jeremybarthe.com/feed.xml" />
    <link rel="alternate" type="text/html" href="http://jeremybarthe.com" />
    <updated>2017-06-20T15:21:33+00:00</updated>
    <id>http://jeremybarthe.com/</id>
    <author>
        <name>Jeremy Barthe</name>
        <uri>http://jeremybarthe.com/</uri>
        <email>contact@jeremybarthe.com</email>
    </author>

    
    <entry>
        <title type="html"><![CDATA[Speed-up your Vagrant environment for Symfony2]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2015/02/02/speed-up-vagrant-environment-symfony2/" />
        <id>http://jeremybarthe.com/2015/02/02/speed-up-vagrant-environment-symfony2</id>
        <published>2015-02-02T00:00:00+00:00</published>
        <updated>2015-02-02T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p&gt;This is my first post in english and probably not the last one for this new 
year. First of all, I’m really happy to say that I’ve joined the awesome KNP 
Labs team! I’ve just met all these cool guys during a hackathon in the north of 
France. It was a really nice experience.&lt;/p&gt;

&lt;p&gt;On this post I want to share with you some tips about Symfony2 development on a 
Virtual Machine. I know that &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; is really trendy and cool nowadays
and we used it a lot during the KNP hackathon but for project virtualization 
I’m still using &lt;a href=&quot;https://www.vagrantup.com/&quot;&gt;Vagrant&lt;/a&gt; for now because it’s a stable solution, 
multi-platform and easy to provision.&lt;/p&gt;

&lt;p&gt;There was a lot of articles about how to optimize your Vagrant environment, 
especially for Symfony2. Some of them date back to 2013:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.whitewashing.de/2013/08/19/speedup_symfony2_on_vagrant_boxes.html&quot;&gt;Speedup Symfony2 on Vagrant boxes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.erikaheidi.com/blog/optimizing-symfony-applications-vagrant-boxes&quot;&gt;Optimizing Symfony applications on Vagrant boxes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one more recent:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://by-examples.net/2014/12/09/symfony2-on-vagrant.html&quot;&gt;Running the Symfony application on Vagrant without NFS below 100ms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these articles point the slow filesystem performance of shared folders in 
Virtual Machines, but the related techniques/tips to boost performance have a 
big drawback to me. You need to modify the source files of your project…&lt;/p&gt;

&lt;p&gt;They advice to update the &lt;code class=&quot;highlighter-rouge&quot;&gt;composer.json&lt;/code&gt; file, or even the &lt;code class=&quot;highlighter-rouge&quot;&gt;autoload.php&lt;/code&gt;, 
&lt;code class=&quot;highlighter-rouge&quot;&gt;AppKernel.php&lt;/code&gt;. And the idea is to move some directories, like &lt;code class=&quot;highlighter-rouge&quot;&gt;vendor/&lt;/code&gt; or 
&lt;code class=&quot;highlighter-rouge&quot;&gt;app/{cache,logs}&lt;/code&gt;, outside of the shared folder to prevent too much I/O. I 
don’t really like this technique because it makes your code coupled to Vagrant, 
and in my opinion Vagrant is only a development platform for your project, it 
should not be required to adapt your code to this platform.&lt;/p&gt;

&lt;p&gt;A better way should be to add some symbolic links of these folders to a 
destination inside of the Virtual Machine.&lt;/p&gt;

&lt;p&gt;Hopefully there’s a Vagrant plugin that handle this kind of problem. His name is
&lt;a href=&quot;http://fgrehm.viewdocs.io/vagrant-cachier&quot;&gt;vagrant-cachier&lt;/a&gt;. This plugin creates some “cache buckets” 
that you can use in the Virtual Machine and they are also symlinked to the host 
machine. Initially this plugin was created to share some folders between Virtual 
Machines, especially for packages in common (like APT packages or Ruby, npm, 
etc.). But you can use it for any kind of packages or files, so it can be used 
for cache, logs and vendors files of Symfony2!&lt;/p&gt;

&lt;p&gt;Finally, it speeds-up the Virtual Machine performance through symbolic links, 
and you can also access to these files from your host machine. It can be very 
useful for your text editor or your IDE to be able to access all the vendor 
files :-)&lt;/p&gt;

&lt;p&gt;The plugin installation process is very easy (make sure you have Vagrant 1.4+):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;vagrant plugin install vagrant-cachier&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can see below a configuration example for Vagrant and with the 
vagrant-cachier plugin configured for Symfony2 with &lt;code class=&quot;highlighter-rouge&quot;&gt;vendor/&lt;/code&gt; or 
&lt;code class=&quot;highlighter-rouge&quot;&gt;app/{cache,logs}&lt;/code&gt; folders into cache buckets.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Configure the box&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;your-box&quot;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Example of shared folder&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;synced_folder&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/var/www&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;id: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:nfs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Vagrant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;has_plugin?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vagrant-cachier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:machine&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;synced_folder_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type: :nfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;mount_options: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'rw'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'vers=3'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'tcp'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'nolock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:generic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;cache&quot;&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;cache_dir: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/var/www/app/cache&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;logs&quot;&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;cache_dir: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/var/www/app/logs&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;vendor&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;cache_dir: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/var/www/vendor&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I hope this Vagrant tip could help ;-)&lt;/p&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2015/02/02/speed-up-vagrant-environment-symfony2/&quot;&gt;Speed-up your Vagrant environment for Symfony2&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on February 02, 2015.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Exemple d'implémentation d'une API Symfony2 et d'un client AngularJS]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2015/01/14/exemple-implementation-api-symfony2-et-client-angularjs/" />
        <id>http://jeremybarthe.com/2015/01/14/exemple-implementation-api-symfony2-et-client-angularjs</id>
        <published>2015-01-14T00:00:00+00:00</published>
        <updated>2015-01-14T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p&gt;On parle souvent en ce moment de Single Page Applications (SPA), ces 
applications web riches et interactives à base de Javascript et d’API. Dans cet 
article, j’ai voulu présenter une application complète client / serveur. 
Cependant comme ces 2 aspects sont très différents et complexes, qu’ils 
pourraient faire l’objet d’articles dédiés, j’ai choisi d’utiliser des 
composants permettant de les mettre en place rapidement.&lt;/p&gt;

&lt;p&gt;Je vais vous présenter 2 solutions :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; côté client, il s’agit d’une librairie &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; réalisée par la 
société &lt;a href=&quot;http://marmelab.com/blog/&quot;&gt;Marmelab&lt;/a&gt;, elle permet de créer une interface d’administration 
(également appelé CRUD) basée sur les données d’une API REST ;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://lakion.com/lionframe&quot;&gt;Lionframe&lt;/a&gt; côté serveur, et plus précisemment &lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; issu de 
la solution e-commerce &lt;a href=&quot;http://sylius.org/&quot;&gt;Sylius&lt;/a&gt;, qui permet d’exposer sous la forme d’une API 
REST des entités Doctrine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans cet article et dans le tutoriel associé 
(&lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial&quot;&gt;que vous trouverez sur Github&lt;/a&gt;), j’ai repris la version 
de démonstration de ng-admin mais avec une API dynamique en &lt;a href=&quot;http://symfony.com/&quot;&gt;Symfony2&lt;/a&gt; (à la 
place d’un mock). Il s’agit donc de gérer des posts, des commentaires et enfin 
des tags.&lt;/p&gt;

&lt;h2 id=&quot;api-rest-en-symfony2&quot;&gt;API REST en Symfony2&lt;/h2&gt;

&lt;p&gt;Je ne vais pas présenter la stack classique pour développer une API avec 
&lt;a href=&quot;http://symfony.com/&quot;&gt;Symfony2&lt;/a&gt;, je pense bien sûr aux bundles &lt;a href=&quot;https://github.com/FriendsOfSymfony/FOSRestBundle&quot;&gt;FOSRestBundle&lt;/a&gt;, 
&lt;a href=&quot;https://github.com/schmittjoh/JMSSerializerBundle&quot;&gt;JMSSerializerBundle&lt;/a&gt;, &lt;a href=&quot;https://github.com/willdurand/Hateoas&quot;&gt;Hateoas&lt;/a&gt;, &lt;a href=&quot;https://github.com/nelmio/NelmioApiDocBundle&quot;&gt;NelmioApiDocBundle&lt;/a&gt;, etc. D’autres articles 
s’en chargent très bien et je vous recommande vivement leurs lecture :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/&quot;&gt;REST APIs with Symfony2: The Right Way&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://welcometothebundle.com/symfony2-rest-api-the-best-2013-way/&quot;&gt;Symfony2 REST API: the best way&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://afsy.fr/avent/2013/06-best-practices-pour-vos-apis-rest-http-avec-symfony2&quot;&gt;Best practices pour vos APIs REST HTTP avec Symfony2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cela faisait quelques semaines que je voulais prendre un moment pour tester 
&lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; pour développer une API REST. Les développeurs de 
&lt;a href=&quot;http://lakion.com/&quot;&gt;Lakion&lt;/a&gt;, également principaux développeurs de la solution e-commerce &lt;a href=&quot;http://sylius.org/&quot;&gt;Sylius&lt;/a&gt;, 
l’ont fait avant moi. Leur article &lt;a href=&quot;http://lakion.com/blog/rapid-rest-api-development-with-lionframe&quot;&gt;Rapid REST API Development with Lionframe&lt;/a&gt; 
présente comment développer rapidement une API avec &lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; et 
indirectement avec &lt;a href=&quot;https://github.com/FriendsOfSymfony/FOSRestBundle&quot;&gt;FOSRestBundle&lt;/a&gt; et les librairies &lt;a href=&quot;https://github.com/schmittjoh/JMSSerializerBundle&quot;&gt;JMSSerializerBundle&lt;/a&gt; et 
&lt;a href=&quot;https://github.com/willdurand/Hateoas&quot;&gt;Hateoas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; est évidemment utilisé dans &lt;a href=&quot;http://sylius.org/&quot;&gt;Sylius&lt;/a&gt;, et permet 
d’automatiser toutes les tâches “backend” autour d’une entité Doctrine, en 
d’autres termes le bundle automatise les actions, les formulaires, les listes, 
les filtres, etc. sans pour autant définir de présentation. C’est en ce sens là 
qu’il diffère des autres CRUD et admin generator qu’on peut voir habituellement 
dans &lt;a href=&quot;http://symfony.com/&quot;&gt;Symfony2&lt;/a&gt;. Et surtout, une de ses fonctionnalités est d’exposer toutes les 
actions “backend” sous la forme d’une API REST.&lt;/p&gt;

&lt;p&gt;La mise en place est très simple puisqu’elle se limite à l’installation de 
quelques bundles (voir ci-dessous) et à la définition d’entités Doctrine, des 
validations et la configuration du Serializer.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;s2&quot;&gt;&quot;require&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;sylius/resource-bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.13.*@dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;sylius/resource&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.13.*@dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;sylius/storage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0.13.*@dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;friendsofsymfony/rest-bundle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1.5.*@dev&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans le &lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial/tree/master/server/src/AppBundle/Entity&quot;&gt;projet associé&lt;/a&gt;, j’ai créé les entités 
Post, Tag et Comment, et ajouté les validations ainsi que la configuration du 
Serializer.&lt;/p&gt;

&lt;p&gt;Il ne reste plus qu’à demander à &lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; d’exposer nos ressources 
dans une API REST. Pour cela, 2 configurations sont nécessaires, au niveau de la 
configuration de &lt;a href=&quot;http://symfony.com/&quot;&gt;Symfony2&lt;/a&gt; et du routing :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial/blob/master/server/app/config/api.yml&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;app/config/api.yml&lt;/code&gt;&lt;/a&gt; (qu’il faudra penser 
à importer depuis le &lt;code class=&quot;highlighter-rouge&quot;&gt;config.yml&lt;/code&gt; principal) :&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;sylius_resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;app.post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;doctrine/orm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;classes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AppBundle\Entity\Post&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;app.comment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;doctrine/orm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;classes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AppBundle\Entity\Comment&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;app.tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;doctrine/orm&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;classes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AppBundle\Entity\Tag&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La création de ressource dans &lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; engendre la création de 
services, et plus spécifiquement de controlleurs, dont le but est de gérer les 
tâches “backend” autour de l’entité Doctrine :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Service ID              Class name
app.controller.comment  Sylius&lt;span class=&quot;se&quot;&gt;\B&lt;/span&gt;undle&lt;span class=&quot;se&quot;&gt;\R&lt;/span&gt;esourceBundle&lt;span class=&quot;se&quot;&gt;\C&lt;/span&gt;ontroller&lt;span class=&quot;se&quot;&gt;\R&lt;/span&gt;esourceController
app.controller.post     Sylius&lt;span class=&quot;se&quot;&gt;\B&lt;/span&gt;undle&lt;span class=&quot;se&quot;&gt;\R&lt;/span&gt;esourceBundle&lt;span class=&quot;se&quot;&gt;\C&lt;/span&gt;ontroller&lt;span class=&quot;se&quot;&gt;\R&lt;/span&gt;esourceController
app.controller.tag      Sylius&lt;span class=&quot;se&quot;&gt;\B&lt;/span&gt;undle&lt;span class=&quot;se&quot;&gt;\R&lt;/span&gt;esourceBundle&lt;span class=&quot;se&quot;&gt;\C&lt;/span&gt;ontroller&lt;span class=&quot;se&quot;&gt;\R&lt;/span&gt;esourceController&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial/blob/master/server/app/config/routing.yml&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;app/config/routing.yml&lt;/code&gt;&lt;/a&gt; :&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;app_post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app.post&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;sylius.api&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;/api&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;app_comment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app.comment&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;sylius.api&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;/api&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;app_tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app.tag&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;sylius.api&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;/api&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour rentrer un peu plus dans les détails, le type de Route &lt;code class=&quot;highlighter-rouge&quot;&gt;sylius.api&lt;/code&gt; permet 
de charger dynamiquement les différentes routes de notre API REST permettant de 
gérer la ressource sous la forme d’un CRUD (liste, lecture, création, édition, 
suppression). Ci-dessous la liste des routes générées par la 1ère ligne du 
routing &lt;code class=&quot;highlighter-rouge&quot;&gt;app_post&lt;/code&gt; et associé à la ressource &lt;code class=&quot;highlighter-rouge&quot;&gt;app.post&lt;/code&gt; (et donc associé à 
l’entité Doctrine &lt;code class=&quot;highlighter-rouge&quot;&gt;AppBundle\Entity\Post&lt;/code&gt;) :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Name                Method    Scheme Host Path
app_api_post_index  GET       ANY    ANY  /api/posts/
app_api_post_show   GET       ANY    ANY  /api/posts/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;id&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
app_api_post_create POST      ANY    ANY  /api/posts/
app_api_post_update PUT|PATCH ANY    ANY  /api/posts/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;id&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
app_api_post_delete DELETE    ANY    ANY  /api/posts/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;id&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il aurait été possible de définir ces routes manuellement, mais également d’en 
rajouter, comme par exemple une route permettant de lister les commentaires 
associés à un post 
(cf. &lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial/blob/master/server/app/config/routing.yml&quot;&gt;routing.yml sur Github&lt;/a&gt;) :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;app_api_post_comments&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/api/posts/{id}/comments/&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;defaults&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;_controller&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app.controller.comment:indexAction&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;_sylius&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;filterable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;criteria&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$id&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et voilà ! Nous avons maintenant une API REST fonctionnelle et qui respecte les 
spécifications d’&lt;a href=&quot;http://stateless.co/hal_specification.html&quot;&gt;HAL&lt;/a&gt; pour l’exposition de ressources dans une API. &lt;a href=&quot;http://stateless.co/hal_specification.html&quot;&gt;HAL&lt;/a&gt; nous 
permet d’obtenir une réelle API REST puisque le niveau 3  du 
&lt;a href=&quot;http://martinfowler.com/articles/richardsonMaturityModel.html&quot;&gt;Richardson Maturity Model&lt;/a&gt; nous impose d’exposer des liens hypermedias entre 
les ressources, souvent appelé HATEOAS. Vous pouvez d’ailleurs lire 
&lt;a href=&quot;/2013/05/13/introduction-a-hateoas-aux-humantalks/&quot;&gt;mes slides d’introduction à HATEOAS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;client-en-angularjs&quot;&gt;Client en AngularJS&lt;/h2&gt;

&lt;p&gt;Toujours dans l’idée d’avoir un résultat fonctionnel et rapidement, pour la 
partie client, je vais utiliser &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; pour gérer les posts, commentaires 
et tags. Je vous recommande la lecture de 
&lt;a href=&quot;http://marmelab.com/blog/2014/09/15/easy-backend-for-your-restful-api.html&quot;&gt;Add an AngularJS admin GUI to any RESTful API&lt;/a&gt; pour mieux comprendre le 
fonctionnement de &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt;. Comme la version de démonstration de ce dernier 
est basée sur les mêmes données, il va être intéressant de voir les 
modifications à apporter pour rendre notre administration fonctionnelle.&lt;/p&gt;

&lt;p&gt;Grâce à &lt;a href=&quot;http://bower.io/&quot;&gt;Bower&lt;/a&gt;, l’équivalent de &lt;a href=&quot;https://getcomposer.org/&quot;&gt;Composer&lt;/a&gt; pour gérer la dépendance vers des 
assets (projets CSS ou Javascript), il est très simple d’installer &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;api-ng-admin-tutorial&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;ng-admin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~0.4.0&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;resolutions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;angular&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~1.3.8&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’initialisation de &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; dans le fichier 
&lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial/blob/master/client/public/index.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;index.html&lt;/code&gt; (voir sur Github)&lt;/a&gt; est 
également simpliste.&lt;/p&gt;

&lt;p&gt;Une fois &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; initialisé :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'ng-admin backend demo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;baseApiUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'http://localhost:8000/api/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'posts'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;comment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'comments'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'tags'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEntity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEntity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEntity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;comment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il devient possible d’implémenter une liste des Posts :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'All posts'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'ID'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'title'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ReferenceMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'tags'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;targetEntity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;targetField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'name'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listActions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'show'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'edit'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'delete'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et c’est là que les premiers problèmes se posent :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;notre API expose les données en JSON selon les spécifications &lt;a href=&quot;http://stateless.co/hal_specification.html&quot;&gt;HAL&lt;/a&gt; donc les 
posts se trouvent dans &lt;code class=&quot;highlighter-rouge&quot;&gt;_embedded&lt;/code&gt; (et non à la racine comme les attends 
&lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt;) ;&lt;/li&gt;
  &lt;li&gt;comment gérer la pagination et le nombre total d’éléments ? ;&lt;/li&gt;
  &lt;li&gt;la liste des tags attendue est une liste d’identifiants là où nous exposons 
pour l’instant une collection d’objet, il faudra voir comment adapter cela ;&lt;/li&gt;
  &lt;li&gt;toutes les requêtes sont faites sur l’URL &lt;code class=&quot;highlighter-rouge&quot;&gt;/posts&lt;/code&gt;, là où notre API nécessite 
l’ajout du “trailing slash” (notamment pour la requête POST sur &lt;code class=&quot;highlighter-rouge&quot;&gt;/posts/&lt;/code&gt;) ;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Sylius/SyliusResourceBundle&quot;&gt;SyliusResourceBundle&lt;/a&gt; utilise les formulaires Symfony2 pour la création et la 
modification d’un élément via l’API, il est donc interdit d’envoyer dans la 
requête des champs additionnels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les 2 premiers problèmes se solutionnent en partie, par exemple pour les données
dans &lt;code class=&quot;highlighter-rouge&quot;&gt;_embedded&lt;/code&gt; la solution est d’utiliser l’option &lt;code class=&quot;highlighter-rouge&quot;&gt;interceptor&lt;/code&gt; qui permet de 
transformer le résultat d’une requête :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;interceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;what&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;deferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'getList'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;angular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_embedded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_embedded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour la pagination il est également possible de personnaliser les paramètres 
envoyés à l’API. Quant au nombre total d’éléments, je n’ai pas trouvé de 
solution satisfaisante (la variable globale proposée dans 
&lt;a href=&quot;https://github.com/marmelab/ng-admin/blob/master/examples/marvel.js&quot;&gt;l’exemple de l’API de Marvel&lt;/a&gt; ne fonctionne pas à 100%).&lt;/p&gt;

&lt;p&gt;Toutes ces raisons m’ont poussé à utiliser la version master de &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; qui 
propose beaucoup plus de flexibilité, notamment dans les interactions avec l’API 
REST. En effet &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; utilise en interne la librairie &lt;a href=&quot;https://github.com/mgonto/restangular&quot;&gt;Restangular&lt;/a&gt; pour 
réaliser les requêtes à l’API. Avec la version master, les fonctionnalités 
natives de &lt;a href=&quot;https://github.com/mgonto/restangular&quot;&gt;Restangular&lt;/a&gt; sont beaucoup plus facilement accessibles, il est ainsi 
possible de personnaliser les requêtes, les réponses, transformer les résultats, 
etc.&lt;/p&gt;

&lt;p&gt;Par exemple pour gérer la récupération des données dans &lt;code class=&quot;highlighter-rouge&quot;&gt;_embedded&lt;/code&gt; et le nombre 
total de résultat retourné par l’API, il suffit de faire :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;RestangularProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addResponseInterceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;what&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;deferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'getList'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;angular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_embedded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;totalCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_embedded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Les mêmes fonctionnalités existent pour personnaliser la requête à l’API, et par 
exemple supprimer des champs supplémentaires ou gérer la pagination :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;RestangularProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addFullRequestInterceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;what&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ignore id element on update&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'put'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// custom pagination params&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;operation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;getList&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;page&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_perPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et enfin une fonctionnalité a été ajouté dans &lt;a href=&quot;https://github.com/marmelab/ng-admin&quot;&gt;ng-admin&lt;/a&gt; pour personnaliser 
l’URL d’une ressource, notamment pour gérer le “trailing slash” :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// customize Post URL with trailing slash&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;entityId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'posts/'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;angular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isDefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;entityId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;entityId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Le fichier &lt;code class=&quot;highlighter-rouge&quot;&gt;app.js&lt;/code&gt; complet est 
&lt;a href=&quot;https://github.com/jeremyb/api-ng-admin-tutorial/blob/master/client/public/app.js&quot;&gt;disponible sur Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2015-01-14-exemple-implementation-api-symfony2-et-client-angularjs/ng-admin.png&quot; alt=&quot;ng-admin&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Cet article avait pour but de vous présenter comment réaliser une Single Page 
Application (SPA) avec Symfony2 sur la partie serveur et AngularJS en client. 
Mais surtout comment développer un prototype rapidement grâce à 
&lt;code class=&quot;highlighter-rouge&quot;&gt;SyliusResourceBundle&lt;/code&gt; qui automatise toutes les opérations “backend” possibles 
sur une entité Doctrine. Et sur la partie AngularJS, comment développer 
rapidement une interface de gestion aux données de l’API. Comme je pouvais m’y 
attendre c’est cette partie qui a été la plus complexe et la plus chronophage 
mais c’est évidemment lié à la jeunesse des outils.&lt;/p&gt;

&lt;p&gt;J’espère que ça vous donnera envie de vous lancer dans la création de Single 
Page Applications, d’en apprendre plus sur l’architecture REST, ou encore sur 
le framework AngularJS.&lt;/p&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2015/01/14/exemple-implementation-api-symfony2-et-client-angularjs/&quot;&gt;Exemple d'implémentation d'une API Symfony2 et d'un client AngularJS&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on January 14, 2015.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Hierarchical facets avec Elasticsearch]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2014/11/13/hierarchical-facets-avec-elasticsearch/" />
        <id>http://jeremybarthe.com/2014/11/13/hierarchical-facets-avec-elasticsearch</id>
        <published>2014-11-13T00:00:00+00:00</published>
        <updated>2014-11-13T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p class=&quot;alert alert-warning&quot;&gt;&lt;a href=&quot;http://devblog.lexik.fr/symfony2/hierarchical-facets-avec-elasticsearch-2762&quot;&gt;Cet article a été intialement publié sur le blog de Lexik&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.elasticsearch.org&quot;&gt;Elasticsearch&lt;/a&gt; est devenu un outil incontournable 
pour l’indexation de documents et la recherche textuelle. Un des rares reproches 
qui pouvait lui être fait il y a quelques mois était son absence de support des 
facets hiérarchiques. Il s’agit en effet d’une fonctionnalité très utile et 
demandée dans les sites e-commerce.&lt;/p&gt;

&lt;p&gt;Pour ceux qui ne seraient pas familiers avec le vocabulaire des moteurs de 
recherche, une « facet » (ou recherche à facette en français) permet de 
regrouper des données sur des résultats de recherche. Elles sont calculées en 
temps réel et on les retrouve très souvent sur les catégories d’un site 
e-commerce. Les facets permettent par exemple de retrouver toutes les
catégories associées à des résultats de recherche, avec le nombre d’éléments
correspondants.&lt;/p&gt;

&lt;p&gt;Les facets hiérarchiques sont basées sur le même système mais avec une notion de 
hiérarchie (de profondeur) supplémentaire. On voit cette technique sur des sites 
e-commerce très connus, comme Amazon.fr comme vous pouvez voir sur la capture 
d’écran suivante :&lt;/p&gt;

&lt;p class=&quot;pull-right&quot;&gt;&lt;img src=&quot;/images/2014-11-13-hierarchical-facets-avec-elasticsearch/amazon.png&quot; alt=&quot;Catégories sur Amazon&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Jusqu’à la version 1.0 d’Elasticsearch il était impossible de faire ce style de 
requête tandis que &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Solr&lt;/a&gt; les proposait via ses 
&lt;a href=&quot;http://wiki.apache.org/solr/HierarchicalFaceting#Pivot_Facets&quot;&gt;« Pivot Facets»&lt;/a&gt;. 
En réponse à ce manque et aux limitations des facets, l’équipe d’Elasticsearch a
introduit le concept d’agrégations. Outre le fait que le nom est beaucoup plus
explicite, les agrégations sont également beaucoup plus flexibles. Leurs 2 
atouts majeurs sont de pouvoir être définies en cascade (autrement dit de façon 
hiérarchique, une agrégation peut avoir une ou plusieurs sous-agrégations), et 
de pouvoir combiner différents types de filtre / groupe (comme des facets ou des
calculs de statistiques).&lt;/p&gt;

&lt;p&gt;Nous allons maintenant voir comment utiliser ces agrégations, et notre cas
pratique sera un site e-commerce avec des catégories à plusieurs niveaux. Par
simplicité l’exemple ci-dessous sera illustré via le service en ligne &lt;a href=&quot;https://www.found.no/play/&quot;&gt;Play de
found.no&lt;/a&gt; qui permet de définir son schéma, ses
données et ses recherches Elasticsearch sur une instance publique. &lt;em&gt;(La
communication avec Elasticsearch se fait normalement en JSON, là où found.no a
choisi un format Yaml un peu plus lisible.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Supposons que nous ayons 3 produits, rangés dans 3 niveaux de catégories :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;product&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level0&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Books&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Computers &amp;amp; Technology&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Network Programming&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pro AngularJS&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;product&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level0&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Books&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Computers &amp;amp; Technology&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Online Searching&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ElasticSearch Server Second Edition&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;_type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;product&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level0&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Books&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Arts &amp;amp; Photography&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;category_level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Photography &amp;amp; Video&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Humans of New York&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Source : &lt;a href=&quot;https
://www.found.no/play/gist/c6fc7c11b8a4b5967008#documents&quot;&gt;https://www.found.no/play/gist/c6fc7c11b8a4b5967008#documents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le schéma Elasticsearch associé est le suivant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;category_level0&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;not_analyzed&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;category_level1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;not_analyzed&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;category_level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;not_analyzed&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Source : &lt;a href=&quot;https:
//www.found.no/play/gist/c6fc7c11b8a4b5967008#mappings&quot;&gt;https://www.found.no/play/gist/c6fc7c11b8a4b5967008#mappings&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On choisi de ne pas analyser les catégories pour qu’elles soient indexées
strictement comme spécifié. Elles ne seront donc pas optimisées pour être
retrouvées via un moteur de recherche mais le seront pour nos agrégations.&lt;/p&gt;

&lt;p&gt;Nous avons vu que les agrégations peuvent être définies en cascade, nous allons 
donc définir une agrégation sur le terme de la catégorie de 1er niveau et ainsi 
de suite jusqu’au niveau 3 :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;aggregations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;terms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;category_level0&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;aggs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;level1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;terms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;category_level1&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;aggs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;s&quot;&gt;terms&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                            &lt;span class=&quot;s&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;category_level2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Source : &lt;a href=&quot;https://
www.found.no/play/gist/c6fc7c11b8a4b5967008#search&quot;&gt;https://www.found.no/play/gist/c6fc7c11b8a4b5967008#search&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avec cette recherche, Elasticsearch va être capable de nous agréger les 
résultats des 3 produits sur les 3 niveaux de catégories :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;aggregations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;buckets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Books&quot;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;doc_count&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;level1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;buckets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Computers&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Technology&quot;&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;doc_count&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;buckets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Network&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Programming&quot;&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;doc_count&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Online&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Searching&quot;&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;doc_count&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Arts&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Photography&quot;&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;doc_count&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;level2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;buckets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Photography&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Video&quot;&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;doc_count&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec ces données, nous allons facilement pouvoir afficher une liste de ce type :&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Books (3)
    - Computers &amp;amp; Technology (2)
        - Network Programming (1)
        - Online Searching (1)
    - Arts &amp;amp; Photography (1)
        - Photography &amp;amp; Video (1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Pour aller plus loin avec les agrégations d’Elasticsearch je vous recommande les 
2 lectures suivantes :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.found.no/foundation/elasticsearch-aggregations/&quot;&gt;https://www.found.no/foundation/elasticsearch-aggregations/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://obtao.com/blog/2014/10/elasticsearch-et-symfony-statistiques-avec-les-aggregations/&quot;&gt;http://obtao.com/blog/2014/10/elasticsearch-et-symfony-statistiques-avec-les-aggregations/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2014/11/13/hierarchical-facets-avec-elasticsearch/&quot;&gt;Hierarchical facets avec Elasticsearch&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on November 13, 2014.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Mode maintenance avec nginx]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2014/01/22/mode-maintenance-avec-nginx/" />
        <id>http://jeremybarthe.com/2014/01/22/mode-maintenance-avec-nginx</id>
        <published>2014-01-22T00:00:00+00:00</published>
        <updated>2014-01-22T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p&gt;Je cherchais une méthode simple pour mettre un site en maintenance, par exemple 
pendant des mises à jour de code. Je voulais une solution la plus simple et la 
plus légère possible, inutile de charger autre chose que du HTML pour une page 
de maintenance.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://nginx.org/&quot;&gt;Nginx&lt;/a&gt; propose dans ses fichiers de configuration, un langage de script 
qui permet entre autre de définir des variables ou encore des conditions, afin 
par exemple de tester la présence d’un fichier sur le filesystem. Le mode 
maintenance ci-dessous ne s’activera que sous la présence d’un fichier :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysite.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/home/project/web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# etc.
&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(-f&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/home/project/maintenance.lock&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$maintenance&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Remplacez ici par votre IP locale
&lt;/span&gt;    &lt;span class=&quot;kn&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$remote_addr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;127.0.0.1&quot;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$maintenance&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$maintenance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;503&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;error_page&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;503&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@maintenance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@maintenance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;rewrite&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;^(.*)&lt;/span&gt;$ &lt;span class=&quot;n&quot;&gt;/maintenance.html&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Concrêtement la présence du fichier &lt;code class=&quot;highlighter-rouge&quot;&gt;maintenance.lock&lt;/code&gt; va activer le mode 
maintenance, sauf si l’IP du client est une IP autorisée. Cela vous permettra de 
tester votre site alors que les autres internautes auront un accès coupé au 
site.&lt;/p&gt;

&lt;p&gt;Il ne vous reste plus qu’à créer une &lt;a href=&quot;https://gist.github.com/pitch-gist/2999707&quot;&gt;simple page&lt;/a&gt; 
&lt;code class=&quot;highlighter-rouge&quot;&gt;maintenance.html&lt;/code&gt; pour avertir vos visiteurs.&lt;/p&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2014/01/22/mode-maintenance-avec-nginx/&quot;&gt;Mode maintenance avec nginx&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on January 22, 2014.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Introduction à HATEOAS aux Human Talks]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2013/05/13/introduction-a-hateoas-aux-humantalks/" />
        <id>http://jeremybarthe.com/2013/05/13/introduction-a-hateoas-aux-humantalks</id>
        <published>2013-05-13T00:00:00+00:00</published>
        <updated>2013-05-13T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p&gt;Human Talks, c’est un événement mensuel pour les développeurs. L’idée est de 
présenter en 10 minutes un sujet technique, que ce soit sur un langage, une 
méthodo, un projet, etc. J’ai eu l’occasion de présenter le fonctionnement d’une 
API REST HATEOAS lors des Human Talks de mai 2013.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sous cet acronyme étrange et quasi imprononçable se cache un élément 
fondamental dans la mise en place d’une API REST, les liens hypermedias.&lt;/p&gt;

  &lt;p&gt;Dans les slides nous verrons le cas d’une API sans hateoas, puis son 
intégration et les avantages et les questions que cela soulève.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;550&quot; src=&quot;//jeremybarthe.com/slides/humantalks-hateoas/&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;

&lt;p class=&quot;center&quot;&gt;&lt;a href=&quot;http://jeremybarthe.com/slides/humantalks-hateoas/&quot;&gt;Voir les slides en plein écran&lt;/a&gt;&lt;/p&gt;

            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2013/05/13/introduction-a-hateoas-aux-humantalks/&quot;&gt;Introduction à HATEOAS aux Human Talks&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on May 13, 2013.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Convertir une Exception en Response dans Symfony2]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2013/02/04/convertir-une-exception-en-response-dans-symfony2/" />
        <id>http://jeremybarthe.com/2013/02/04/convertir-une-exception-en-response-dans-symfony2</id>
        <published>2013-02-04T00:00:00+00:00</published>
        <updated>2013-02-04T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p&gt;La méthode traditionnelle pour retourner une page avec le framework Symfony2 est 
de retourner un objet &lt;code class=&quot;highlighter-rouge&quot;&gt;Response&lt;/code&gt;. Toutefois on peut remarquer que les pages 
d’erreurs (notamment les 404) ne suivent pas ce principe. En effet pour 
déclencher un code d’erreur 404 et afficher la page correspondante, il suffit 
de lever une Exception comme ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;throw new NotFoundHttpException();&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il peut parfois s’avérer intéressant de reproduire ce système pour des 
Exceptions métiers dans son projet. Supposons par exemple que nous devons gérer 
des verrous sur l’édition d’un objet. C’est une demande fréquente sur des 
applications web (intranet par exemple) où lorsqu’un utilisateur se trouve sur 
le formulaire d’édition d’un objet, les autres utilisateurs, qui tentent 
d’accéder à ce même formulaire, doivent être bloqués et notifiés de l’édition 
en cours d’un utilisateur. Chez Lexik, nous avons récemment eu cette demande et 
nous l’avons traité via une &lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;L’article suivant ne donnera pas une implémentation complète du bundle de 
gestion des verrous d’édition, que nous appellerons &lt;code class=&quot;highlighter-rouge&quot;&gt;LockBundle&lt;/code&gt; tout au long de 
l’article, mais servira d’exemple pour montrer comment transformer une 
&lt;code class=&quot;highlighter-rouge&quot;&gt;Exception&lt;/code&gt; dans Symfony2 en &lt;code class=&quot;highlighter-rouge&quot;&gt;Response&lt;/code&gt;. Si vous êtes intéressés pour avoir une 
version Open Source du &lt;code class=&quot;highlighter-rouge&quot;&gt;LockBundle&lt;/code&gt;, il suffit de le demander ;-)&lt;/p&gt;

&lt;p&gt;Supposons que nous avons un service &lt;code class=&quot;highlighter-rouge&quot;&gt;lock_manager&lt;/code&gt; capable d’ajouter et 
supprimer un verrou, ainsi que savoir si un objet est verrouillé ou non. La 
seule contrainte est que l’objet passé en paramètre doit implémenter l’interface 
&lt;code class=&quot;highlighter-rouge&quot;&gt;LockableInterface&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Acme\LockBundle\Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Acme\LockBundle\Exception\LockedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LockManager&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LockableInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LockableInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LockableInterface&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* some condition */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;LockedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans un contexte classique, depuis un Controller Symfony2, il faudrait retourner 
une &lt;code class=&quot;highlighter-rouge&quot;&gt;Response&lt;/code&gt; avec une vue attachée, comme ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Acme\DemoBundle\Controller&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Acme\LockBundle\Exception\LockedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArticleController&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Controller&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;editAction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'lock_manager'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$article&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LockedException&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'::locked.html.twig'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;// ...
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Si cette action doit être réalisée à plusieurs reprises, depuis plusieurs 
Controller, ce code peut s’avérer rébarbatif et verbeux. Le plus simple serait 
d’appeler seulement la méthode &lt;code class=&quot;highlighter-rouge&quot;&gt;verify&lt;/code&gt; de notre service et lever l’Exception 
si un verrou est présent. Par contre, dans ce contexte, c’est une page 500 qui 
s’affichera ce qui n’est pas acceptable.&lt;/p&gt;

&lt;p&gt;Heureusement, Symfony2 et son composant &lt;code class=&quot;highlighter-rouge&quot;&gt;HttpKernel&lt;/code&gt; nous offre beaucoup de 
souplesse et dispose d’un évènement lorsqu’une Exception est levée et non 
catchée. Il s’agit de l’évènement &lt;code class=&quot;highlighter-rouge&quot;&gt;kernel.exception&lt;/code&gt; comme sa documentation 
l’indique &lt;code class=&quot;highlighter-rouge&quot;&gt;this event allows you to create a response for a thrown exception&lt;/code&gt;.
Il nous suffit de créer un listener :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Acme\LockBundle\Listener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Symfony\Bundle\TwigBundle\TwigEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Acme\LockBundle\Exception\LockedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LockedExceptionListener&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$templating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TwigEngine&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$templating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;templating&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$templating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onKernelException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GetResponseForExceptionEvent&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;LockedException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;templating&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;renderResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'::locked.html.twig'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne reste plus qu’à la définir en tant que service et l’associer à l’évènement
&lt;code class=&quot;highlighter-rouge&quot;&gt;kernel.exception&lt;/code&gt; :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;listener.locked_exception&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Acme\LockBundle\Listener\LockedExceptionListener&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;templating&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;kernel.event_listener&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;kernel.exception&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;onKernelException&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Avec ce listener, nous pouvons maintenant simplifier le code de notre 
Controller comme ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function editAction(Request $request, Article $article)
{
    $this-&amp;gt;get('lock_manager')-&amp;gt;verify($article);

    // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;C’est très confortable car ce code peut être appelé depuis Controller ou même 
depuis classe modèle/service et le rendu pour l’utilisateur sera le même.&lt;/p&gt;

&lt;p&gt;Nous pouvons même enrichir la &lt;code class=&quot;highlighter-rouge&quot;&gt;Response&lt;/code&gt; du listener pour retourner un status 
code HTTP d’erreur (comme ce qui est fait pour les 404 par exemple). Pour cela 
il suffit d’éditer la classe &lt;code class=&quot;highlighter-rouge&quot;&gt;LockedExceptionListener&lt;/code&gt; comme ceci :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$response = $this-&amp;gt;templating-&amp;gt;renderResponse(
    '::locked.html.twig',
    array(),
    // le 3ème paramètre permet de fournir un objet Response :
    new Response(null, 423)
);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2013/02/04/convertir-une-exception-en-response-dans-symfony2/&quot;&gt;Convertir une Exception en Response dans Symfony2&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on February 04, 2013.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Astuces de développement javascript avec symfony]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2011/01/17/astuces-de-developpement-javascript-avec-symfony/" />
        <id>http://jeremybarthe.com/2011/01/17/astuces-de-developpement-javascript-avec-symfony</id>
        <published>2011-01-17T00:00:00+00:00</published>
        <updated>2011-01-17T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p class=&quot;alert alert-warning&quot;&gt;&lt;a href=&quot;http://devblog.lexik.fr/symfony/astuces-de-developpement-javascript-avec-symfony-1382&quot;&gt;Cet article a été intialement publié sur le blog de Lexik&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;alert alert-warning&quot;&gt;&lt;strong&gt;Attention :&lt;/strong&gt; cet article a été rédigé en 2011, le contenu ou les éléments abordées peuvent être obsolète.&lt;/p&gt;

&lt;p&gt;Dans cet article nous allons voir quelques astuces et bonnes pratiques, non pas directement de développement symfony mais de développement javascript au sein d’un projet symfony.&lt;/p&gt;

&lt;p&gt;Si l’on reprend quelques bases de bonnes pratiques de développement javascript, on constate :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;que vos javascripts doivent être non-intrusifs, autrement dit là pour améliorer l’expérience utilisateur et en aucun cas être indispensable au fonctionnement d’une page ;&lt;/li&gt;
  &lt;li&gt;que les javascript doivent être combinés en 1 seul fichier et minifié (ceci afin de limiter le nombre de requête HTTP et car le chargement de la page est arrêté à chaque balise script, notamment à cause d’un éventuel &lt;code class=&quot;highlighter-rouge&quot;&gt;document.write();&lt;/code&gt; ;&lt;/li&gt;
  &lt;li&gt;que l’appel au javascript doit être en bas de page ;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cette liste est bien entendu non exhaustive, vous trouverez une &lt;a href=&quot;http://developer.yahoo.com/performance/rules.html&quot;&gt;liste plus détaillée par ici&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Toutes ces bonnes pratiques de développement Javascript n’ont pas toujours été facilitées dans symfony, notamment à la grande époque des helpers link_to_remote() &amp;amp; co. qui en ont ravi certains et fait cauchemarder d’autres. Et aujourd’hui encore bon nombre de widgets de formulaire retournent directement du code javascript (dépendant de jQuery ou autre) et qui ne fonctionneront évidemment plus dès lors que vos javascripts se trouveront en bas de page.&lt;/p&gt;

&lt;h3 id=&quot;1-où-placer-les-javascripts-dans-le-document&quot;&gt;1. Où placer les javascripts dans le document&lt;/h3&gt;

&lt;p&gt;Idéalement et selon les recommandations de Yslow, en bas de page, donc juste avant la balise fermante &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; de votre layout.php. Ceci afin de ne pas ralentir inutilement le début du chargement de votre page, puisque chaque balise script bloque le chargement de la page (notamment pour gérer le &lt;code class=&quot;highlighter-rouge&quot;&gt;document.write();&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Source : &lt;a href=&quot;http://developer.yahoo.com/performance/rules.html#js_bottom&quot;&gt;http://developer.yahoo.com/performance/rules.html#js_bottom&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si cette méthode ne vous gênera pas pour les librairies tierces telles que jQuery, qu’en est-il par contre de vos codes javascript ? Ils doivent également se trouver dans des fichiers séparés, dissociés de votre document HTML. Néanmoins comme je vois souvent le besoin pour les développeurs d’avoir le code javascript au sein du template symfony, voici une technique pour qu’il soit tout de même placé en fin de page : via un slot.&lt;/p&gt;

&lt;p&gt;Un petit exemple de code :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// indexSuccess.php :
&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'javascript'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'javascript @ bottom'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;end_slot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;

// layout.php :
&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;include_javascripts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;include_slot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'javascript'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous pourrez ainsi éditer votre code javascript depuis votre template PHP, bénéficier par exemple de valeurs dynamiques de PHP et pour autant que le code soit bien inclus en bas de page. Toutefois je vous recommande d’écrire du javascript là où il doit se trouver, à savoir dans un fichier JS…&lt;/p&gt;

&lt;p&gt;De plus l’inconvénient majeur du slot est qu’il ne peut contenir qu’une valeur, il est écrasé à chaque utilisation. La technique fonctionne mais est très limitée. Pour ces besoins nous avons écrit chez Lexik un petit plugin “lxJavascript”, très similaire à l’utilisation d’un slot mais optimisé pour l’inclusion de javascripts. Il gère donc le multiple ajout, supprime les éventuelles multiples balises script pour en conserver qu’une seule, etc. Vous en saurez plus demain, le plugin sort en OpenSource sur le &lt;a href=&quot;https://github.com/lexik&quot;&gt;Github de Lexik&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;2-compression&quot;&gt;2. Compression&lt;/h3&gt;

&lt;p&gt;La compression des javascripts (c’est valable aussi pour les CSS) est une des tâches d’optimisations les plus importantes et également une des plus simples, du moins via les plugins symfony. Cette technique consiste à compresser tous vos fichiers javascript en 1 seul et surtout d’y appliquer un numéro de version (un timestamp le plus souvent) afin de prévenir des problèmes de cache dans vos mises à jour.&lt;/p&gt;

&lt;p&gt;Les principaux plugins :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.symfony-project.org/plugins/sfCombinePlugin&quot;&gt;sfCombinePlugin&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.symfony-project.org/plugins/npAssetsOptimizerPlugin&quot;&gt;npAssetsOptimizerPlugin&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.symfony-project.org/plugins/swCombinePlugin&quot;&gt;swCombinePlugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chez Lexik, nous utilisons &lt;a href=&quot;http://www.symfony-project.org/plugins/npAssetsOptimizerPlugin&quot;&gt;npAssetsOptimizerPlugin&lt;/a&gt;, un plugin de &lt;a href=&quot;http://www.akei.com/&quot;&gt;Nicolas Perriault&lt;/a&gt;, extrêmement simple à installer et paramétrer. Sur votre environnement de production, vous obtiendrez ainsi un fichier javascript compressé &lt;code class=&quot;highlighter-rouge&quot;&gt;/js/optimized.js?1294759429&lt;/code&gt; qui contient un timestamp permettant à chaque modifications des fichiers d’être prises en compte sans problème de cache.&lt;/p&gt;

&lt;p&gt;Nous n’appliquons pas la compression pour les librairies externes telles que jQuery que nous déléguons aux &lt;a href=&quot;http://code.google.com/apis/libraries/devguide.html&quot;&gt;serveurs &lt;abbr title=&quot;Content Delivery Network&quot;&gt;CDN&lt;/abbr&gt; de Google&lt;/a&gt;, cela apporte ces avantages notables :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;l’avantage du &lt;abbr title=&quot;Content Delivery Network&quot;&gt;CDN&lt;/abbr&gt;, à savoir de proposer des données statiques via un réseau de multiples serveurs dans de multiples points géographiques, afin de permettre un accès très court ;&lt;/li&gt;
  &lt;li&gt;les navigateurs sont limités sur le nombre de connexions HTTP simultanées sur un même domaine, le téléchargement d’un javascript sur les serveurs de Google se fera en parallèle ;&lt;/li&gt;
  &lt;li&gt;les librairies du &lt;abbr title=&quot;Content Delivery Network&quot;&gt;CDN&lt;/abbr&gt; ont les bonnes en-têtes de cache, par exemple la version 1.4.4 de jQuery restera en cache pendant 1 an sur le client, cela signifie également qu’un internaute peut bénéficier du cache téléchargé depuis un autre site, pas forcément sur le votre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;3-accéder-à-certaines-données-dynamiques-depuis-javascript&quot;&gt;3. Accéder à certaines données dynamiques depuis javascript&lt;/h3&gt;

&lt;p&gt;Il peut parfois être très utile depuis javascript d’accéder à certaines données dynamiques telles que la culture de l’utilisateur, du routing pour des appels Ajax ou encore de l’i18n. Il suffit de s’appuyer sur un simple fichier de configuration via un tableau de données JSON généré depuis une action symfony. J’ai écrit sur mon blog personnel un article détaillé sur le sujet &lt;a href=&quot;http://blog.jeremybarthe.com/post/1465024153/configurations-javascript-dynamiques-en-symfony&quot;&gt;Configurations Javascript dynamiques en Symfony&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;4-javascript-depuis-les-widget&quot;&gt;4. Javascript depuis les widget&lt;/h3&gt;

&lt;p&gt;Maintenant que vos inclusions de javascripts se trouvent en bas de page, jQuery par exemple, que faire de nos chers widgets jQuery Autocompleter &lt;code class=&quot;highlighter-rouge&quot;&gt;sfWidgetFormJQueryAutocompleter&lt;/code&gt; et autre Datepicker &lt;code class=&quot;highlighter-rouge&quot;&gt;sfWidgetFormJQueryDate&lt;/code&gt; qui retournent des champs de formulaire et le javascript d’initialisation du composant ? Eh bien, ne plus les utiliser… Simplement car ils seront inclus avant jQuery et vous aurez donc droit à l’erreur “jQuery is not defined”. Sans être aussi extrême, vous pouvez charger jQuery en haut de page (via les &lt;abbr title=&quot;Content Delivery Network&quot;&gt;CDN&lt;/abbr&gt; de Google) et votre script compressé en bas de page, les widgets de formulaire seront alors à nouveau fonctionnels.&lt;/p&gt;

&lt;p&gt;Cela fait déjà quelques temps que chez Lexik nous avons fait le choix d’enlever tous ces widgets pour les ré-écrire et les adapter à nos besoins. Les javascripts des widgets passent par notre plugin lxJavascript ce qui permet de les inclure là où on le souhaite. Egalement, le javascript retourné par ces widgets correspond à une utilisation basique du composant, dans un cas concrêt il est souvent nécessaire d’en ré-écrire une bonne part. Il peut être pratique par contre que le widget retourne seulement la sémantique HTML adaptée, par exemple dans le cas d’un Autocomplete un champ hidden pour stocker la valeur et un champ texte pour l’autocomplete, aussi nos widgets ont une option pour retourner ou non le code javascript.&lt;/p&gt;

&lt;h3 id=&quot;5-optimisation-frontend&quot;&gt;5. Optimisation frontend&lt;/h3&gt;

&lt;p&gt;Presque aussi important que la compression, la configuration d’apache pour mettre en cache certains de vos fichiers statiques. Typiquement, maintenant que vos fichiers javascripts et CSS sont compressés en 1 seul et possèdent un timestamp unique par version, il serait dommage de les recharger à chaque chargement de page… Vous devez donc configurer le module Expires d’apache via le fichier de config du vhost ou via le .htaccess, en voici un exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;&amp;lt;&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod_expires&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&amp;gt;
  &lt;span class=&quot;n&quot;&gt;ExpiresActive&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ExpiresByType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;css&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;access plus 1 year&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ExpiresByType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;/&lt;span class=&quot;n&quot;&gt;javascript&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;access plus 1 year&quot;&lt;/span&gt;
&amp;lt;/&lt;span class=&quot;n&quot;&gt;IfModule&lt;/span&gt;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vos fichiers javascript et CSS seront ainsi en cache pendant 1 an, attention du coup à ne pas avoir de fichier sans numéro de version, car ils resteraient en cache et vos internautes ne bénéficieraient pas de vos mises à jour…&lt;/p&gt;

&lt;p&gt;Dernière chose, si ce n’est pas encore fait, installez et testez &lt;a href=&quot;http://developer.yahoo.com/yslow/&quot;&gt;Yslow&lt;/a&gt; pour trouver des pistes d’optimisations de performance de vos sites.&lt;/p&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2011/01/17/astuces-de-developpement-javascript-avec-symfony/&quot;&gt;Astuces de développement javascript avec symfony&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on January 17, 2011.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Valider simplement ses formulaires Symfony en JS]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2010/11/11/valider-simplement-ses-formulaires-symfony-en-js/" />
        <id>http://jeremybarthe.com/2010/11/11/valider-simplement-ses-formulaires-symfony-en-js</id>
        <published>2010-11-11T00:00:00+00:00</published>
        <updated>2010-11-11T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p class=&quot;alert alert-warning&quot;&gt;&lt;strong&gt;Attention :&lt;/strong&gt; cet article a été rédigé en 2010, le contenu ou les éléments abordées peuvent être obsolète.&lt;/p&gt;

&lt;p&gt;Dans cet article nous allons voir comment facilement valider un formulaire Symfony via les &lt;code class=&quot;highlighter-rouge&quot;&gt;sfEvent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Il existe de nombreuses méthodes pour valider ses formulaires en Javascript, mais celle que j’apprécie consiste à passer des données à la sémantique HTML des éléments de formulaire pour indiquer au javascript qu’elles sont les conditions à valider. En d’autres termes cela revient par exemple à renseigner un attribut &lt;code class=&quot;highlighter-rouge&quot;&gt;class&lt;/code&gt; avec des valeurs telles que &lt;code class=&quot;highlighter-rouge&quot;&gt;required&lt;/code&gt; ou encore &lt;code class=&quot;highlighter-rouge&quot;&gt;email&lt;/code&gt;, etc. ou encore via un JSON toujours placé dans l’attribut class, récupéré ensuite grâce à &lt;a href=&quot;http://plugins.jquery.com/project/metadata&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jquery.metadata&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.vanadiumjs.com/&quot;&gt;VanadiumJS&lt;/a&gt;, une librairie jQuery, est basée sur ce principe, elle ajoute une couche de validation javascript via des classes CSS de type &lt;code class=&quot;highlighter-rouge&quot;&gt;:required&lt;/code&gt;, etc. Malheureusement elle souffre de quelques défauts, notamment l’I18N.
Le &lt;a href=&quot;http://bassistance.de/jquery-plugins/jquery-plugin-validation/&quot;&gt;jQuery plugin validation&lt;/a&gt; permet également de valider sous cette forme, il permet aussi une écriture des règles en javascript, hors de la sémantique HTML.&lt;/p&gt;

&lt;p&gt;Le code ci-dessous a été testé avec ces 2 librairies Javascript. J’ai choisi de vous présenter le code correspondant à la librairie &lt;a href=&quot;http://bassistance.de/jquery-plugins/jquery-plugin-validation/&quot;&gt;jQuery plugin validation&lt;/a&gt; car je la considère plus pérenne et elle gère l’I18N.&lt;/p&gt;

&lt;p&gt;Prenons un formulaire d’exemple, avec différents widgets mais surtout un panel variés de validators :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;class ExampleDataForm extends BaseForm
{
  public function configure()
  {
    $this-&amp;gt;setWidgets(array(
      'name'        =&amp;gt; new sfWidgetFormInputText(),
      'email'       =&amp;gt; new sfWidgetFormInputText(),
      'number'      =&amp;gt; new sfWidgetFormInputText(),
      'price'       =&amp;gt; new sfWidgetFormInputText(),
      'description' =&amp;gt; new sfWidgetFormTextarea(),
      'url'         =&amp;gt; new sfWidgetFormInputText(),
      'cgu'         =&amp;gt; new sfWidgetFormInputCheckbox(),
    ));

    $this-&amp;gt;setValidators(array(
      'name'        =&amp;gt; new sfValidatorString(array('min_length' =&amp;gt; 5, 'max_length' =&amp;gt; 255)),
      'email'       =&amp;gt; new sfValidatorEmail(array('required' =&amp;gt; true)),
      'number'      =&amp;gt; new sfValidatorInteger(array('required' =&amp;gt; false)),
      'price'       =&amp;gt; new sfValidatorNumber(array('required' =&amp;gt; true)),
      'description' =&amp;gt; new sfValidatorString(array('max_length' =&amp;gt; 4000)),
      'url'         =&amp;gt; new sfValidatorUrl(array('required' =&amp;gt; true)),
      'cgu'         =&amp;gt; new sfValidatorBoolean(array('required' =&amp;gt; true)),
    ));

    $this-&amp;gt;widgetSchema-&amp;gt;setNameFormat('example_data[%s]');
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;L’événement &lt;a href=&quot;http://www.symfony-project.org/reference/1_4/en/15-Events#chapter_15_sub_form_post_configure&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;form.post_configure&lt;/code&gt;&lt;/a&gt; est notifié lorsque le formulaire est configuré, autrement dit après que les widgets et les validators soient initialisés. A ce moment là, il sera très simple de modifier les attributs des widgets pour leur ajouter quelques classes en fonction du validator et de ses options.&lt;/p&gt;

&lt;p&gt;Il faut donc se connecter à l’événement, je vous laisse libre d’implémenter ceci dans votre &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectConfiguration&lt;/code&gt; ou encore dans la configuration d’une de vos applications :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$this-&amp;gt;dispatcher-&amp;gt;connect('form.post_configure', array($this, 'listenToFormPostConfigureEvent'));&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ci-dessous un exemple d’implémentation de la méthode &lt;code class=&quot;highlighter-rouge&quot;&gt;listenToFormPostConfigureEvent&lt;/code&gt; pour gérer quelques validators :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function listenToFormPostConfigureEvent(sfEvent $event)
{
  $validatorSchema = $event-&amp;gt;getSubject()-&amp;gt;getValidatorSchema();

  // iterates over the widgets:
  foreach ($event-&amp;gt;getSubject() as $formField)
  {
    $widget = $formField-&amp;gt;getWidget();

    // retrieve validator associated to widget:
    $validator = $validatorSchema[$formField-&amp;gt;getName()];

    // required
    if ($validator-&amp;gt;getOption('required'))
    {
      $this-&amp;gt;mergeClass($widget, 'required');
    }

    // min length
    if ($validator-&amp;gt;getOption('min_length'))
    {
      $widget-&amp;gt;setAttribute('minlength', $validator-&amp;gt;getOption('min_length'));
    }

    // max length
    if ($validator-&amp;gt;getOption('max_length'))
    {
      $widget-&amp;gt;setAttribute('maxlength', $validator-&amp;gt;getOption('max_length'));
    }

    // number
    if ($validator instanceof sfValidatorInteger || $validator instanceof sfValidatorNumber)
    {
      $this-&amp;gt;mergeClass($widget, 'number');
    }

    // email
    if ($validator instanceof sfValidatorEmail)
    {
      $this-&amp;gt;mergeClass($widget, 'email');
    }

    // url
    if ($validator instanceof sfValidatorUrl)
    {
      $this-&amp;gt;mergeClass($widget, 'url');
    }
  }
}

/**
 * Merge CSS classes to the given widget.
 *
 * @param $widget
 * @param $className
 */
public function mergeClass($widget, $className)
{
  return $widget-&amp;gt;setAttribute('class', (null !== $widget-&amp;gt;getAttribute('class')) ? sprintf('%s %s', $widget-&amp;gt;getAttribute('class'), $className) : $className);
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Pour résumer, la méthode &lt;code class=&quot;highlighter-rouge&quot;&gt;listenToFormPostConfigureEvent&lt;/code&gt; va &lt;strong&gt;itérer sur les différents widgets du formulaire&lt;/strong&gt;, et pour chacun d’eux va récupérer son validator. Elle va alors modifier les attributs du widget (le plus souvent l’attribut &lt;code class=&quot;highlighter-rouge&quot;&gt;class&lt;/code&gt;) pour y indiquer quel type de validation devra opérer la partie Javascript.&lt;/p&gt;

&lt;p&gt;Libre à vous d’implémenter d’autres validators. Il serait même possible de passer par un JSON (technique metadata) pour ainsi gérer plus de cas, plus d’options liés à &lt;a href=&quot;http://docs.jquery.com/Plugins/Validation&quot;&gt;la documentation de jQuery plugin validation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;N’oubliez pas de charger les différents javascripts, à savoir jQuery et ceux du plugin de validation (vous pouvez en option ajouter un JS localisé proposé par le plugin ou encore un fichier contenant vos propres traductions pour les messages d’erreurs, c’est très pratique) :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;javascripts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;//ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jquery-validation/jquery.validate.js&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jquery-validation/localization/messages_fr.js&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# optional&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et maintenant dans votre vue (considérons que vous avez correctement affiché votre formulaire) il ne reste plus qu’à initialiser le javascript du plugin de validation, via :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;form&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Voici le résultat que j’obtiens :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2010-11-11-valider-simplement-ses-formulaires-symfony-en-js/validationjs.png&quot; alt=&quot;Validation JS facile de formulaire Symfony via les events&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Si vous souhaitez adapter ce code à &lt;a href=&quot;http://www.vanadiumjs.com/&quot;&gt;VanadiumJS&lt;/a&gt;, ce ne sera pas très compliqué, il suffira de faire quelques adaptations au niveau des classes, par exemple : &lt;code class=&quot;highlighter-rouge&quot;&gt;required&lt;/code&gt; devient &lt;code class=&quot;highlighter-rouge&quot;&gt;:required&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;min_length&lt;/code&gt; et &lt;code class=&quot;highlighter-rouge&quot;&gt;max_length&lt;/code&gt; ne sont plus des attributs du widget mais des classes, etc. Il suffit de se référer à la &lt;a href=&quot;http://www.vanadiumjs.com/&quot;&gt;documentation de VanadiumJS&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;conclusion-&quot;&gt;Conclusion :&lt;/h4&gt;

&lt;p&gt;Pour conclure, on constate qu’il a été très simple d’étendre les fonctionnalités de &lt;code class=&quot;highlighter-rouge&quot;&gt;sfForm&lt;/code&gt; mais surtout de généraliser ce comportement à tous les formulaires. Evidemment n’utilisez pas forcément ce code là en production, car il va s’exécuter pour tous vos formulaires, ce qui n’est pas forcément souhaitable. Il serait toutefois assez simple de définir via un &lt;code class=&quot;highlighter-rouge&quot;&gt;app.yml&lt;/code&gt; une collection de noms de formulaires pour lesquels on souhaite appliquer des validations côté client.&lt;/p&gt;

&lt;p&gt;Sachez qu’il existe également un plugin Symfony de validation javascript : &lt;a href=&quot;http://www.symfony-project.org/plugins/sfJqueryFormValidationPlugin&quot;&gt;sfJqueryFormValidationPlugin&lt;/a&gt; (qui au passage se base sur le même plugin jQuery que cet article). Par contre ce dernier ne se base pas sur les events Symfony, il fonctionne via un filter qui ajoute des fichiers javascripts dynamiques qui précise les règles de validation des formulaires.&lt;/p&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2010/11/11/valider-simplement-ses-formulaires-symfony-en-js/&quot;&gt;Valider simplement ses formulaires Symfony en JS&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on November 11, 2010.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Halte aux hacks CSS IE]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2010/11/06/halte-aux-hacks-css-ie/" />
        <id>http://jeremybarthe.com/2010/11/06/halte-aux-hacks-css-ie</id>
        <published>2010-11-06T00:00:00+00:00</published>
        <updated>2010-11-06T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p class=&quot;alert alert-warning&quot;&gt;&lt;strong&gt;Attention :&lt;/strong&gt; cet article a été rédigé en 2010, le contenu ou les éléments abordées peuvent être obsolète.&lt;/p&gt;

&lt;p&gt;Autant pendant un temps ils n’avaient plus la côte, autant ces derniers mois je trouve qu’on lit trop d’articles sur les hacks CSS pour IE…&lt;/p&gt;

&lt;p&gt;Que ce soit &lt;a href=&quot;http://dimox.net/personal-css-hacks-for-ie6-ie7-ie8/&quot;&gt;ici&lt;/a&gt; ou &lt;a href=&quot;http://net.tutsplus.com/tutorials/html-css-techniques/quick-tip-how-to-target-ie6-ie7-and-ie8-uniquely-with-4-characters/&quot;&gt;là&lt;/a&gt; ou encore &lt;a href=&quot;http://www.lafermeduweb.net/billet/-memo-selectionnez-ie6-ie7-et-ie8-en-css-en-quelques-caracteres-804.html&quot;&gt;là&lt;/a&gt;, on lit les louanges des &lt;code class=&quot;highlighter-rouge&quot;&gt;* html&lt;/code&gt; et autres &lt;code class=&quot;highlighter-rouge&quot;&gt;*+html&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt; ou encore pire avec l’utilisation de &lt;code class=&quot;highlighter-rouge&quot;&gt;\9&lt;/code&gt;… mais wtf?!&lt;/p&gt;

&lt;p&gt;Le principal problème lié à l’utilisation d’un hack CSS, c’est que son &lt;strong&gt;périmètre d’application n’est pas maîtrisé&lt;/strong&gt;, étant donné qu’il exploite une faille, son comportement futur ne peut pas être contrôlé…&lt;/p&gt;

&lt;p&gt;Imaginons un instant qu’IE corrige son interprétation des CSS, même si ce n’est pas leur genre… Ou encore que gecko ou webkit se retrouve impacté par l’une de ses failles, il récoltera alors les propriétés initialement prévue pour IE, pas sûr qu’il apprécie… En plus bien souvent les hacks rendent vos CSS invalides au validateur W3C.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pour résumer ces hacks ne sont pas une solution pérenne.&lt;/strong&gt; D’autant plus lorsqu’on peut exploiter une &lt;strong&gt;réelle &lt;code class=&quot;highlighter-rouge&quot;&gt;feature&lt;/code&gt;&lt;/strong&gt; pour cibler différentes versions d’IE via les &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms537512(VS.85).aspx&quot;&gt;commentaires conditionnels&lt;/a&gt; &lt;a href=&quot;http://www.blog-and-blues.org/articles/Les_syntaxes_de_commentaires_conditionnels_pour_IE_Windows&quot;&gt;(article en français)&lt;/a&gt;. Et si comme moi, créer une CSS avec les règles spécifiques pour une version d’IE vous rebute, il existe depuis déjà longtemps la technique du &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; conditionnel, un exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if lt IE 7]&amp;gt; &amp;lt;body id=&quot;ie6&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if IE 7]&amp;gt;    &amp;lt;body id=&quot;ie7&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if IE 8]&amp;gt;    &amp;lt;body id=&quot;ie8&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if IE 9]&amp;gt;    &amp;lt;body id=&quot;ie9&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if gt IE 9]&amp;gt; &amp;lt;body&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if !IE]&amp;gt;&amp;lt;!--&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;J’utilise cette technique sur la plupart de mes sites depuis 2 ans et j’en suis ravi. Je l’ai découverte via &lt;a href=&quot;http://www.paulhammond.org/2008/10/conditional/&quot;&gt;ce post&lt;/a&gt; et ensuite via &lt;a href=&quot;http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/&quot;&gt;un article bien plus complet de Paul Irish&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;L’inconvénient majeur que je trouvais aux commentaires conditionnels, c’était de devoir créer différentes CSS et de devoir dupliquer certains sélecteurs pour compenser certaines défaillances d’IE. Ca peut vite devenir compliqué à maintenir dans le temps.&lt;/p&gt;

&lt;p&gt;Avec un &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; conditionnel, j’édite mes conditions directement dans ma CSS :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nf&quot;&gt;#test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;#ie6&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#test&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Et comme dans l’absolu votre CSS ne devrait pas nécessiter de hacks, nous pourrions simplifier l’identifiant conditionnel à :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if lt IE 7]&amp;gt; &amp;lt;body id=&quot;ie6&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if IE 7]&amp;gt;    &amp;lt;body id=&quot;ie7&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if gt IE 7]&amp;gt; &amp;lt;body&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if !IE]&amp;gt;&amp;lt;!--&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ca vous laisse la possibilité de spécifier pour IE 6 et 7, ce qui est largement suffisant.&lt;/p&gt;

&lt;p&gt;Voyons maintenant un petit exemple appliqué à Symfony :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if lt IE 7]&amp;gt; &amp;lt;body id=&quot;ie6&quot; class=&quot;ie lang-&amp;lt;?php echo $sf_user-&amp;gt;getCulture() ?&amp;gt; env-&amp;lt;?php echo sfConfig::get('sf_environment') ?&amp;gt;&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if IE 7]&amp;gt;    &amp;lt;body id=&quot;ie7&quot; class=&quot;ie lang-&amp;lt;?php echo $sf_user-&amp;gt;getCulture() ?&amp;gt; env-&amp;lt;?php echo sfConfig::get('sf_environment') ?&amp;gt;&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if gt IE 7]&amp;gt; &amp;lt;body class=&quot;ie lang-&amp;lt;?php echo $sf_user-&amp;gt;getCulture() ?&amp;gt; env-&amp;lt;?php echo sfConfig::get('sf_environment') ?&amp;gt;&quot;&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;&amp;lt;!--[if !IE]&amp;gt;&amp;lt;!--&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lang-&amp;lt;?php echo $sf_user-&amp;gt;getCulture() ?&amp;gt; env-&amp;lt;?php echo sfConfig::get('sf_environment') ?&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;![endif]--&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Comme vous voyez, en plus des attributs liés à IE, je fais passer la culture et l’environnement de l’utilisateur. Cela permet de gérer d’éventuels background liés à la langue d’affichage. Et pour l’environnement, je m’en sers pour modifier le background du body d’un site pour son environnement de pré-production, cela permet au client de bien différencier les 2 sites.&lt;/p&gt;

&lt;p&gt;Pour finir, c’est ok pour tout le monde ? Fini les hacks CSS, hein ? ;)&lt;/p&gt;


            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2010/11/06/halte-aux-hacks-css-ie/&quot;&gt;Halte aux hacks CSS IE&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on November 06, 2010.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Du routing depuis les tests fonctionnels]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2010/11/02/routing-depuis-les-tests-fonctionnels/" />
        <id>http://jeremybarthe.com/2010/11/02/routing-depuis-les-tests-fonctionnels</id>
        <published>2010-11-02T00:00:00+00:00</published>
        <updated>2010-11-02T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p class=&quot;alert alert-warning&quot;&gt;&lt;strong&gt;Attention :&lt;/strong&gt; cet article a été rédigé en 2010, le contenu ou les éléments abordées peuvent être obsolète.&lt;/p&gt;

&lt;p&gt;Lorsque l’on développe des tests fonctionnels en symfony avec lime, on se rend compte du lien fort qu’il y à la fois entre la sémantique HTML et les tests mais également entre les URLs et les tests. Voici un exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$browser-&amp;gt;get('/category/index');&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Utiliser des URL comme celle-ci dans les tests fonctionnels peut être handicapant si en fin de projet votre client demande une optimisation des URLs. Vous allez probablement retravailler votre routing en conséquence. Et dès lors une bonne part de vos tests fonctionnels risquent de ne plus fonctionner. Alors, pourquoi ne pas tout simplement utiliser des routes dans vos tests fonctionnels ? D’autant plus que le routing.yml référence l’intégralité des URLs de votre application, ce serait dommage de s’en priver !&lt;/p&gt;

&lt;p&gt;Voici 2 méthodes pour utiliser le routing symfony depuis vos tests fonctionnels :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$app = 'frontend';
include(dirname(__FILE__).'/../../bootstrap/functional.php');

$browser = new sfTestFunctional(new sfBrowser());

$context = sfContext::getInstance();

$browser-&amp;gt;
  info(sprintf('Context way: %s', $context-&amp;gt;getRouting()-&amp;gt;generate('example_route')));

$configuration-&amp;gt;loadHelpers(array('Url'));

$browser-&amp;gt;
  info(sprintf('Helper way: %s', url_for('@example_route')));&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Dans ces 2 techniques, inutile de modifier quoi que ce soit dans votre bootstrap &lt;code class=&quot;highlighter-rouge&quot;&gt;functional.php&lt;/code&gt;. A part si le &lt;code class=&quot;highlighter-rouge&quot;&gt;sfContext::getInstance()&lt;/code&gt; vous pique les yeux, ce que je comprendrai allègrement, vous pouvez modifier &lt;code class=&quot;highlighter-rouge&quot;&gt;functional.php&lt;/code&gt;, en remplaçant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;// replace:
sfContext::createInstance($configuration);
// by:
$context = sfContext::createInstance($configuration);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Vous pouvez maintenant utiliser &lt;code class=&quot;highlighter-rouge&quot;&gt;$context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Personnellement, j’utilise la méthode via le helper URL ;-)&lt;/p&gt;

            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2010/11/02/routing-depuis-les-tests-fonctionnels/&quot;&gt;Du routing depuis les tests fonctionnels&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on November 02, 2010.&lt;/p&gt;
        </content>
    </entry>
    
    <entry>
        <title type="html"><![CDATA[Configurations Javascript dynamiques en Symfony]]></title>
        <link rel="alternate" type="text/html" href="http://jeremybarthe.com/2010/11/02/configurations-javascript-dynamiques-en-symfony/" />
        <id>http://jeremybarthe.com/2010/11/02/configurations-javascript-dynamiques-en-symfony</id>
        <published>2010-11-02T00:00:00+00:00</published>
        <updated>2010-11-02T00:00:00+00:00</updated>
        <author>
            <name>Jeremy Barthe</name>
            <uri>http://jeremybarthe.com/</uri>
            <email>contact@jeremybarthe.com</email>
        </author>
        <content type="html">
            &lt;p class=&quot;alert alert-warning&quot;&gt;&lt;strong&gt;Attention :&lt;/strong&gt; cet article a été rédigé en 2010, le contenu ou les éléments abordées peuvent être obsolète.&lt;/p&gt;

&lt;p&gt;Dans cet article nous allons voir comment accéder à des données dynamiques provenant de symfony, en Javascript. Pour quelle raison ? Car il est parfois nécessaire depuis JS d’avoir à accéder à des données telles que : de l’i18n, des URLs générés à partir du routing, la culture de l’utilisateur, etc.&lt;/p&gt;

&lt;p&gt;I18n pour le titre d’une modalbox par exemple, dont le site serait internationalisé. URLs pour des appels Ajax, où il est déconseillé de recopier l’URL en dur dans le fichier JS… Et la culture pour bien configurer certains JS qui proposent des fichiers de traductions.&lt;/p&gt;

&lt;p&gt;Pour cela, je vous propose de préparer un &lt;strong&gt;tableau JSON&lt;/strong&gt;, en voici un exemple :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;globalConfigs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;                     &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&amp;lt;?php echo sfConfig::get('&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sf_environment&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;') ?&amp;gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;userCulture&lt;/span&gt;             &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&amp;lt;?php echo $sf_user-&amp;gt;getCulture() ?&amp;gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ajaxAutocomplete&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&amp;lt;?php echo url_for('&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;homepage&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;') ?&amp;gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ajaxModalBox&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&amp;lt;?php echo url_for('&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;homepage&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;') ?&amp;gt;'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;i18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;example&lt;/span&gt;               &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;?php echo esc_specialchars(__('example')) ?&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;exampleModalboxTitle&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;?php echo esc_specialchars(__('example_modalbox_title')) ?&amp;gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nous pouvons ainsi accéder à l’environnement, la culture de l’utilisateur, des URLs et de l’I18n.
&lt;strong&gt;Maintenant, où placer ce JS ?&lt;/strong&gt; 2 solutions s’offrent à nous :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;directement dans votre layout.php (via un partial ou component) ;&lt;/li&gt;
  &lt;li&gt;dans un module &amp;gt; action avec un routing spécifique.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Personnellement je trouve plus propre la deuxième méthode même si elle est plus complexe à mettre en place ;)&lt;/p&gt;

&lt;p&gt;C’est parti, nous créons donc un simpliste module nommé &lt;code class=&quot;highlighter-rouge&quot;&gt;js&lt;/code&gt;, associé à son action tout aussi simpliste &lt;code class=&quot;highlighter-rouge&quot;&gt;configs&lt;/code&gt; dont nous imposons le &lt;code class=&quot;highlighter-rouge&quot;&gt;sf_format&lt;/code&gt;  à &lt;code class=&quot;highlighter-rouge&quot;&gt;js&lt;/code&gt;, soit un template nommé &lt;code class=&quot;highlighter-rouge&quot;&gt;configsSuccess.js.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le routing correspondant :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;s&quot;&gt;javascript_configs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;/js/configs.:sf_format&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;configs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sf_format&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;js&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Il ne reste plus qu’à inclure ce JS dans toutes nos pages. Pour cela nous pouvons utiliser le système d’événement de Symfony. Etant donné que j’ai besoin à la fois de la &lt;code class=&quot;highlighter-rouge&quot;&gt;sfWebResponse&lt;/code&gt; et de &lt;code class=&quot;highlighter-rouge&quot;&gt;sfRouting&lt;/code&gt; je vais passer par l’event &lt;code class=&quot;highlighter-rouge&quot;&gt;context.load_factories&lt;/code&gt;. Même si j’aurai préféré proposer un event plus approprié, car on a tendance à &lt;code class=&quot;highlighter-rouge&quot;&gt;overused&lt;/code&gt; cet event pour tout et n’importe quoi…&lt;/p&gt;

&lt;p&gt;Vous pouvez placer ce code dans votre &lt;code class=&quot;highlighter-rouge&quot;&gt;ProjectConfiguration&lt;/code&gt; ou plutôt dans la configuration de votre application :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;public function configure()
{
  $this-&amp;gt;dispatcher-&amp;gt;connect('context.load_factories', array($this, 'listenToContextLoadFactoriesEvent'));
}

public function listenToContextLoadFactoriesEvent(sfEvent $event)
{
  $event-&amp;gt;getSubject()-&amp;gt;getResponse()-&amp;gt;addJavascript($event-&amp;gt;getSubject()-&amp;gt;getRouting()-&amp;gt;generate('javascript_configs'));
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;La configuration JS est dorénavant accessible, voici quelques exemples d’utilisation :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// env condition:&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;globalConfigs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'dev'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// jQuery DOM manipulation:&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#example'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;globalConfigs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// jQuery ajax call:&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;globalConfigs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;urls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajaxAutocomplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// enjoy!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;En allant plus loin, vous pouvez par exemple piloter les éditeurs wysiwygs de vos applications et cloisoner leurs configurations. Dans l’exemple ci-dessous, les boutons de l’éditeur sont configurés via le &lt;code class=&quot;highlighter-rouge&quot;&gt;app.yml&lt;/code&gt;, vous pouvez ainsi offrir une configuration différente pour vos applications.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;globalConfigs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;wysiwyg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;script_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/js/tiny_mce/tiny_mce.js'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;advanced&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;element_format&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;xhtml&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cleanup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme_advanced_buttons1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;?php echo sfConfig::get('app_tinymce_theme_advanced_buttons1') ?&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme_advanced_buttons2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;?php echo sfConfig::get('app_tinymce_theme_advanced_buttons2') ?&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme_advanced_buttons3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;?php echo sfConfig::get('app_tinymce_theme_advanced_buttons3') ?&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme_advanced_buttons4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;?php echo sfConfig::get('app_tinymce_theme_advanced_buttons4') ?&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme_advanced_toolbar_location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;top&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;theme_advanced_toolbar_align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;left&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;100%&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paste_auto_cleanup_on_paste&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;De plus cela permet de centraliser des configurations pour éviter de se répéter (&lt;abbr title=&quot;Don't repeat yourself&quot;&gt;DRY&lt;/abbr&gt;) :&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// TinyMCE via jQuery plugin:&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#wysiwyg'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tinymce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;globalConfigs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;wysiwyg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Voilà, qu’en pensez vous ? Utilisez vous des systèmes similaires ? Ou pas ? ;)&lt;/p&gt;

            &lt;p&gt;&lt;a href=&quot;http://jeremybarthe.com/2010/11/02/configurations-javascript-dynamiques-en-symfony/&quot;&gt;Configurations Javascript dynamiques en Symfony&lt;/a&gt; was originally published by  at &lt;a href=&quot;http://jeremybarthe.com&quot;&gt;Jeremy Barthe&lt;/a&gt; on November 02, 2010.&lt;/p&gt;
        </content>
    </entry>
    
</feed>