<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://odolbeau.fr/xsl/atom.xsl" media="all"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://odolbeau.fr/</id>
  <title>Olivier Dolbeau</title>
  <subtitle><![CDATA[Hello. My name is Olivier Dolbeau (@odolbeau).
I currently live in France and work as a freelancer!
]]></subtitle>
  <link href="https://odolbeau.fr/atom.xml" rel="self" type="application/atom+xml" />
  <link href="https://odolbeau.fr/" rel="alternate" type="text/html" />
  <updated>2026-02-04T00:00:00+00:00</updated>
  <author>
    <name>Olivier Dolbeau</name>
    <uri>https://odolbeau.fr</uri>
  </author>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/announcing-tested-routes-checker-bundle-fr/</id>
    <title>🚀 Découvrez TestedRoutesCheckerBundle !</title>
    <content type="html">
      <![CDATA[<p>Un court article pour vous annoncer la sortie d'un nouveau bundle : <strong>TestedRoutesCheckerBundle</strong> !</p>
<p>Un petit outil simple et efficace pour vérifier que toutes les routes de votre app sont couvertes par des tests.
Idéal pour éviter d'introduire de nouvelles routes sans leurs tests associés.</p>
<p>Toutes les infos sont disponibles ici :
👉 <a href="https://github.com/odolbeau/TestedRoutesCheckerBundle" rel="noopener noreferrer">https://github.com/odolbeau/TestedRoutesCheckerBundle</a></p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/announcing-tested-routes-checker-bundle-fr/" rel="alternate" type="text/html" />
    <published>2026-02-04T00:00:00+00:00</published>
    <updated>2026-02-05T10:29:38+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/announcing-tested-routes-checker-bundle/</id>
    <title>🚀 Introducing TestedRoutesCheckerBundle!</title>
    <content type="html">
      <![CDATA[<p>A short post to announce the release of a new bundle: <strong>TestedRoutesCheckerBundle</strong>!</p>
<p>A small, simple, and effective tool to ensure that all the routes in your app are covered by tests.
Perfect to avoid introducing new routes without their associated test.</p>
<p>All the details are available here:
👉 <a href="https://github.com/odolbeau/TestedRoutesCheckerBundle" rel="noopener noreferrer">https://github.com/odolbeau/TestedRoutesCheckerBundle</a></p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/announcing-tested-routes-checker-bundle/" rel="alternate" type="text/html" />
    <published>2026-02-04T00:00:00+00:00</published>
    <updated>2026-02-05T10:29:38+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/phpunit-data-provider-usage/</id>
    <title>À quel moment est-il pertinent d&#039;utiliser un data provider PHPUnit ?</title>
    <content type="html">
      <![CDATA[<p>Lors de mes dernières missions, il m'est arrivé plusieurs fois de tomber sur <strong>un usage des Data Provider qui ne me paraissait pas forcément pertinent</strong>. Voici donc un petit article pour parler de tout ça.</p>
<h2 id="qu-est-ce-qu-un-data-provider">Qu'est-ce qu'un Data Provider ?</h2>
<p>Un <em>&quot;fournisseur de données&quot;</em> en bon français est une fonctionnalité bien pratique de PHPUnit qui permet de <strong>lancer plusieurs fois le même test avec des données d'entrée différentes</strong>.</p>
<p>Voici un exemple très simple (que nous réutiliserons) permettant de tester la méthode <code>isValid</code> d'une classe <code>EmailValidator</code> :</p>
<pre><code class="language-php">&lt;?php declare(strict_types=1);
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

final class EmailValidatorTest extends TestCase
{
    #[DataProvider('emailProvider')]
    #[TestDox('Ensure $email is a valid email')]
    public function testValidator(string $email)
    {
        $validator = new EmailValidator();

        $this-&gt;assertTrue($validator-&gt;isValid($email));
    }

    public static function emailProvider()
    {
        return [
            'data set 1' =&gt; ['foo@bar.com'],
            'data set 2' =&gt; ['foo@bar.fr'],
            'data set 3' =&gt; ['foo@bar.br'],
            'data set 4' =&gt; ['foo@bar.es']
        ];
    }
}</code></pre>
<p>Comme vous le savez peut-être déjà, le test <code>testValidator</code> sera lancé pour chaque données renvoyées par la méthode statique <code>emailProvider</code>.</p>
<p>C'est super, <strong>pas besoin de réécrire plusieurs fois le même test ou bien de tester plusieurs choses dans la même méthode</strong>. 👍</p>
<h2 id="quand-s-abstenir-d-ecrire-un-data-provider">Quand s'abstenir d'écrire un Data Provider ?</h2>
<p>Il n'est pas rare, lorsque l'on découvre cette petite fonctionnalité bien utile de PHPUnit, qu'on en use et surtout, en abuse !</p>
<p>Voilà deux cas précis où je vous encourage fortement de vous passer de ces Data Provider. :)</p>
<h3 id="quand-il-n-y-a-qu-un-seul-test">Quand il n'y a qu'un seul test</h3>
<p>Prenons le test suivant :</p>
<pre><code class="language-php">&lt;?php declare(strict_types=1);
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

final class ExampleTest extends TestCase
{
    #[DataProvider('additionProvider')]
    #[TestDox('Adding $a to $b results in $expected')]
    public function testAdd(int $expected, int $a, int $b): void
    {
        $this-&gt;assertSame($expected, $a + $b);
    }

    public static function additionProvider(): iterable
    {
        yield 'data set 1' =&gt; [0, 0, 0];
    }
}</code></pre>
<p>Dans la mesure où notre data provider ne renvoie qu'un seul jeu de donnée, son existence même est-elle réellement pertinente ? Probablement pas !</p>
<pre><code class="language-php">&lt;?php declare(strict_types=1);
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

final class ExampleTest extends TestCase
{
    #[TestDox('Adding $a to $b results in $expected')]
    public function testAdd(): void
    {
        $this-&gt;assertSame(0, 0 + 0);
    }
}</code></pre>
<p>Et voilà, c'est plus lisible non ?</p>
<blockquote>
<p><em>&quot;Oui, mais peut-être qu'un jour, j'aurai besoin d'ajouter d'autres cas de tests&quot;.</em></p>
</blockquote>
<p>Peut-être. Peut-être pas. Peut-être également que ce test ne sera plus pertinent ? Ou que le cas à tester méritera son propre test ? Bref, <strong>inutile d'essayer de prévoir l'avenir au risque de tomber dans l'over-engineering</strong> : <em>&quot;Less is more&quot;</em>, <em>&quot;Keep it simple, stupid!&quot;</em>, <em>&quot;You aren't gonna need it&quot;</em> : <a href="https://en.wikipedia.org/wiki/Overengineering" rel="noopener noreferrer">Wikipedia en parle bien mieux que moi</a> 😜.</p>
<h2 id="quand-le-data-provider-vient-ajouter-de-la-complexite-a-votre-test-de-base">Quand le data provider vient ajouter de la complexité à votre test de base</h2>
<p>Reprenons notre exemple de départ et admettons que nous voulions maintenant nous assurer que des emails invalides sont bien considérés comme tels par la méthode <code>isValid</code>. Une approche possible est de modifier le test et son Data Provider :</p>
<pre><code class="language-php">&lt;?php declare(strict_types=1);
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

final class EmailValidatorTest extends TestCase
{
    #[DataProvider('emailProvider')]
    #[TestDox('Ensure $email is a valid email (or not)')]
    public function testValidator(string $email, bool $isValid)
    {
        $validator = new EmailValidator();

        if ($isValid) {
            $this-&gt;assertTrue($validator-&gt;isValid($email));
        } else {
            $this-&gt;assertFalse($validator-&gt;isValid($email));
        }
    }

    public static function emailProvider()
    {
        return [
            'data set 1' =&gt; ['foo@bar.com', true],
            'data set 2' =&gt; ['foo@bar.fr', true],
            'data set 3' =&gt; ['foo@bar.br', true],
            'data set 4' =&gt; ['foo@bar.es', true]
            'data set error 1' =&gt; ['@bar', false]
            'data set error 2' =&gt; ['foo@bar', false]
            'data set error 3' =&gt; ['foo@.gr', false]
        ];
    }
}</code></pre>
<p>Cependant, cela vient ajouter de la complexité (un <code>if</code>) dans notre test de base. Plutôt que de gérer ce cas dans le data provider existant, il suffit de rajouter un nouveau test et un nouveau Data Provider dans notre classe :</p>
<pre><code class="language-php">#[DataProvider('invalidEmailProvider')]
#[TestDox('Ensure $email is not a valid email')]
public function testInvalid(string $email)
{
    $validator = new EmailValidator();

    $this-&gt;assertFalse($validator-&gt;isValid($email));
}

public static function invalidEmailProvider()
{
    return [
        'data set error 1' =&gt; ['@bar']
        'data set error 2' =&gt; ['foo@bar']
        'data set error 3' =&gt; ['foo@.gr']
    ];
}</code></pre>
<p>Certes, le résultat final fait quelques lignes de plus néanmois <strong>on gagne en lisibilité et on s'assure que chaque test s'occupe d'une chose précise</strong> (<a href="https://fr.wikipedia.org/wiki/S%C3%A9paration_des_pr%C3%A9occupations" rel="noopener noreferrer">voir la <em>&quot;Séparation des préoccupations&quot;</em></a>, plus connue sous le nom de <em>&quot;Separation of Concerns&quot;</em> ou <em>SoC</em> en anglais).</p>
<div class="alert alert-info">
De manière générale, il faut garder en tête qu'<b>un data provider vient rajouter de la complexité dans nos tests</b>.<br>
Bien que relativement faible, <b>cette complexité n'est justifiée que si elle permet d'en éviter une plus importante encore</b> (typiquement en dupliquant des tests).
</div>
<h2 id="quelques-conseils-lors-de-l-utilisation-de-data-providers">Quelques conseils lors de l'utilisation de Data Providers</h2>
<h3 id="1-toujours-commencer-sans-data-provider">1. Toujours commencer SANS Data Provider</h3>
<p>Plutôt que de commencer par écrire directement un Data Provider, <strong>commencez par écrire un test simple</strong>. Ne créez un Data Provider qu'une fois que le test est fonctionnel et uniquement si cela vous parait toujours pertinent.</p>
<h3 id="2-utiliser-le-mot-cle-yield">2. Utiliser le mot clé <code>yield</code></h3>
<p>L'usage d'un générateur permettra d'éviter l'utilisation d'énormes tableaux PHP pour renvoyer vos données de test. Dans le cas d'un long data provider, ça simplifie grandement la lecture mais également l'écriture. :)</p>
<h3 id="3-nommez-vos-jeux-de-donnees">3. Nommez vos jeux de données</h3>
<p>Ca permet aux copains de savoir précisément ce qui est testé et ça n'empêche pas de lancer uniquement le test qui nous intéresse avec <code>phpunit --filter "testAdd@data set 3"̀</code>.</p>
<h3 id="4-evitez-au-maximum-les-data-provider-avec-trop-d-arguments">4. Evitez au maximum les Data Provider avec trop d'arguments</h3>
<p>C'est un constat personnel que j'ai pu faire sur de nombreux projets : <strong>plus un test a besoin d'arguments différents, moins son data provider et lui sont lisibles</strong>. Bien souvent, ce symptôme peut également être le signe d'un mauvais usage des Data Provider : un découpage en plusieurs tests distincts pourrait grandement simplifier la lecture.</p>
<p>Si d'aventure il vous reste tout de même des Data Providers retournant une liste d'arguments longue comme le bras avec des entiers ou des booléens dont on aura tôt fait d'oublier à quels arguments ils correspondent : n'hésitez pas à <strong>nommer les clés de votre tableau de retours avec le nom des arguments</strong> !</p>
<pre><code class="language-php">public static function myProvider()
{
    // Sans arguments nommés
    yield [0, 0, 0, true, 'bar'];

    // Avec les arguments nommés
    yield ['expected' =&gt; 0, 'a' =&gt; 0, 'b' =&gt; 0, 'foo' =&gt; true, 'other' =&gt; 'bar'];
}</code></pre>
<h3 id="le-resultat-final">Le résultat final</h3>
<p>Voilà ce que ça donne si on reprend les quelques conseils ci-dessus :</p>
<pre><code class="language-php">public static function additionProvider(): iterable
{
    yield 'Double zero' =&gt; ['expected' =&gt; 0, 'a' =&gt; 0, 'b' =&gt; 0];
    yield 'Zero left' =&gt; ['expected' =&gt; 1, 'a' =&gt; 0, 'b' =&gt; 1];
    yield 'Zero right' =&gt; ['expected' =&gt; 1, 'a' =&gt; 1, 'b' =&gt; 0];
    yield 'No zero' =&gt; ['expected' =&gt; 3, 'a' =&gt; 1, 'b' =&gt; 1];
}</code></pre>
<p>Bon, c'est sur qu'avec un exemple aussi simple, les avantages ne sont pas forcément tous évidents mais j'espère que vous aurez l'occasion de vous rendre compte de leur efficacité dans vos propres tests ! 😉</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/phpunit-data-provider-usage/" rel="alternate" type="text/html" />
    <published>2024-12-02T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:39+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/throw-new-exception-oui-mais-laquelle/</id>
    <title>Throw new \Exception(); Oui, mais laquelle ?</title>
    <content type="html">
      <![CDATA[<p>Voir <a href="https://afup.org/talks/4867-throw-new-exception-oui-mais-laquelle" rel="noopener noreferrer">la page de la conférence sur le site de l'AFUP</a> et <a href="https://openfeedback.io/forumphp2024/2024-10-10/4867" rel="noopener noreferrer">les avis sur openfeedback</a>.</p>]]>
    </content>
    <link href="https://odolbeau.fr/talks/throw-new-exception-oui-mais-laquelle/" rel="alternate" type="text/html" />
    <published>2024-10-10T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/makefile-custom-targets/</id>
    <title>Définir ses propres tâches GNU Make dans son coin</title>
    <content type="html">
      <![CDATA[<h2 id="pourquoi-voudrais-je-faire-ca">Pourquoi voudrais-je faire ça ?</h2>
<p>Lorsque j'arrive dans un projet qui utilise <strong><a href="https://www.gnu.org/software/make/" rel="noopener noreferrer">GNU Make</a></strong>, je suis content ! 🥰</p>
<p>Cependant, il est courant que j'ai besoin (ou plutôt envie) de définir <strong>mes propres targets</strong> : parfois spécifiques à mon environnement, parfois très proches d'une target existante mais avec LA variation que j'apprécie, parfois spécifiques à une problématique, ...</p>
<p>Ces targets n'ont souvent <strong>pas grand intérêt pour les collègues</strong> (qui ont sans doute leurs propres envies avec LA variation qu'iels apprécient), les ajouter directement dans le Makefile n'est donc pas pertinent, voire serait contreproductif: si tout le monde faisait pareil, on se retrouverait bien vite avec un fichier de plusieurs centaines de lignes et donc difficilement lisible.</p>
<p>Heureusement, il existe plusieurs solutions !</p>
<h2 id="definir-des-taches-dans-son-coin">Définir des tâches dans son coin</h2>
<h3 id="proprement-avec-un-makefile-local">Proprement, avec un Makefile.local</h3>
<p>La première solution consiste à modifier le fichier Makefile existant pour y glisser une petite ligne toute bête mais diablement efficace :</p>
<pre><code class="language-makefile">-include Makefile.local</code></pre>
<p>la ligne parle d'elle même : cela permet d'inclure un fichier <code>Makefile.local</code> (situé dans le même répertoire) et le <code>-</code> devant l'instruction permet de ne pas lever d'erreur si ce fichier n'existe pas.</p>
<p>Il suffit ensuite d'ajouter <code>Makefile.local</code> dans le <code>.gitignore</code> du projet, de commiter le tout et <strong>mission accomplie</strong> : toute l'équipe pourra déclarer ses propres targets dans un fichier <code>Makefile.local</code> sans impacter les collègues ! 🥳</p>
<h3 id="rapidement-avec-un-gnumakefile">Rapidement, avec un GNUmakefile</h3>
<p>Par fois il ne sera peut-être pas possible (ou trop long) de modifier le fichier Makefile existant. Pas de panique, il existe une autre solution !</p>
<p>Lorsqu'elle est lancée, la commande <code>make</code> va vérifier l'existence des fichiers <code>GNUmakefile</code>, <code>makefile</code> et <code>Makefile</code> (dans cet ordre) pour trouver sa configuration.</p>
<p>C'est l'utilisation d'un fichier <code>Makefile</code> qui est officiellement recommandé dans la documentation et si on en croit github, c'est effectivement la solution la plus plébiscitée : <a href="https://github.com/search?q=path%3A%2F%28%3F-i%29%5C%2FGNUmakefile%24%2F&amp;type=code" rel="noopener noreferrer">30k occurrences de GNUmakefile</a>, <a href="https://github.com/search?q=path%3A%2F%28%3F-i%29%5C%2Fmakefile%24%2F&amp;type=code" rel="noopener noreferrer">224k occurrences de makefile</a> et... pas moins de <strong><a href="https://github.com/search?q=path%3A%2F%28%3F-i%29%5C%2FMakefile%24%2F&amp;type=code" rel="noopener noreferrer">3,1 millions d'occurrences de Makefile</a></strong>.</p>
<p>Sachant ça, il est donc possible de jouer avec cette notion de priorité pour créer un fichier <code>GNUmakefile</code> et y ajouter la ligne suivante :</p>
<pre><code class="language-makefile">include Makefile</code></pre>
<p>Même stratégie que précédemment mais cette fois inversée : on inclut le fichier Makefile existant avant de déclarer ses propres targets.</p>
<p>Pour ma part, j'ai ajouté <code>GNUmakefile</code> dans mon <code>.gitignore</code> global pour être sur que ce fichier ne soit pas versionné.</p>
<h2 id="bonus-un-gnumakefile-pret-a-l-emploi">Bonus : un GNUmakefile prêt à l'emploi</h2>
<p>Voici un exemple de <code>GNUmakefile</code> que j'utilise lorsque j'en ai besoin. Je vous laisse copier ce fichier dans un projet existant pour admirer le résultat. :)</p>
<script src="https://gist.github.com/odolbeau/d61a35e4767c723de59221234b766828.js"></script>]]>
    </content>
    <link href="https://odolbeau.fr/blog/makefile-custom-targets/" rel="alternate" type="text/html" />
    <published>2023-09-13T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:39+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/jane-and-webby/</id>
    <title>Jane &amp; Webby</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/jane-and-webby/" rel="alternate" type="text/html" />
    <published>2020-10-23T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/doctrine-migrations-rollup/</id>
    <title>Why &amp; How to use Doctrine Migrations Rollup?</title>
    <content type="html">
      <![CDATA[<h2 id="why-removing-your-old-migrations">Why removing your old migrations?</h2>
<p>If you're working on a fast moving project, it's easy to have hundreds of migrations living in your project.</p>
<p><strong>I have a question for you:</strong> Do you really think you will have to rollback the migration <code>Version20190503193054.php</code> (which is in your repository since more than 1 year) one day or another?</p>
<p>If the answer is &quot;<em>yes</em>&quot;, I'll be glad to hear your arguments on twitter. Otherwise, you may be interested by this article.</p>
<h2 id="introducing-doctrine-migrations-rollup-command">Introducing <code>doctrine:migrations:rollup</code> command</h2>
<p>Even if it's not documented (yet?), doctrine provides a feature to get rid of all your useless migrations.</p>
<p><strong>On paper</strong>, it's pretty simple:</p>
<ol>
<li>Remove all your existing migrations</li>
<li>Generate a new migration with <code>doctrine:migrations:dump-schema</code>.</li>
<li>Create a commit. Push your code.</li>
<li>Deploy!</li>
<li>Run the command <code>doctrine:migrations:rollup</code> in production</li>
</ol>
<p>As you may have noticed, even if it looks simple, deploying a new migration containing the whole creation of your database in production is <strong>not a good idea</strong>. The migration contains all queries needed to create your whole schema but you don't want to run them on an existing database (it will fail anyway as your database already contains those tables).</p>
<p>To avoid this problem, there is a simple solution. You can alter your migration manually to skip it when tables already exist in the schema. To achieve this, you can use the schema manager at the beginning of the migration.</p>
<pre><code class="language-php">if ($this-&gt;sm-&gt;tablesExist('member')) {
    return;
}</code></pre>
<p>If the <code>member</code> table already exists (which is probably the case in production if you have a table named like this) the migration will be skipped.</p>
<p>Everything's fine, you can now deploy your migration &amp; run the <code>doctrine:migrations:rollup</code> command.</p>
<h2 id="how-to-automate-the-rollup-command">How to automate the rollup command</h2>
<p>With the process described in the previous chapter, you have to manually run the rollup command on your production. This step can easily be avoided!</p>
<p>In your deployment process, you probably have post deployment scripts (to automatically apply your migrations in production for example?). If it's the case, you can add those few lines to automatically launch the rollup command if relevant.</p>
<pre><code class="language-bash">if [ 1 == `ls -1 $PATH_TO_MIGRATIONS/ | wc -l` ]; then
    php bin/console doctrine:migrations:rollup
fi</code></pre>
<p>Here it is! If there is one (and only one) migration available, it means you can make a rollup automatically. Don't worry, this command won't fail (nor do anything) if you launch it several times with the same migration.</p>
<p>I hope this article will help you remove all these useless files living in your project. :)</p>
<p>Let met know <a href="https://twitter.com/odolbeau" rel="noopener noreferrer">on twitter</a> if it helps or f you have any question!</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/doctrine-migrations-rollup/" rel="alternate" type="text/html" />
    <published>2020-05-07T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:39+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/translating-monolingual-application/</id>
    <title>Translating a monolingual application</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/translating-monolingual-application/" rel="alternate" type="text/html" />
    <published>2020-02-13T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/dx-developer-experience-afsy/</id>
    <title>DX: Developer eXperience</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/dx-developer-experience-afsy/" rel="alternate" type="text/html" />
    <published>2019-06-26T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/dx-developer-experience/</id>
    <title>DX: Developer eXperience</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/dx-developer-experience/" rel="alternate" type="text/html" />
    <published>2019-05-21T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/easyadminbundle-introduction/</id>
    <title>EasyAdminBundle introduction</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/easyadminbundle-introduction/" rel="alternate" type="text/html" />
    <published>2019-01-15T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/rex-api-platform/</id>
    <title>REX API Platform</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/rex-api-platform/" rel="alternate" type="text/html" />
    <published>2017-05-19T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/25-feature-flag-at-blablacar/</id>
    <title>Feature flags at BlaBlaCar</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/25-feature-flag-at-blablacar/" rel="alternate" type="text/html" />
    <published>2016-09-30T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/25-million-members-in-22-countries/</id>
    <title>25+ million members in 22 countries, how to scale with Symfony 2?</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/25-million-members-in-22-countries/" rel="alternate" type="text/html" />
    <published>2016-09-03T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/how-to-install-your-laptop-with-ansible/</id>
    <title>How to install your laptop with ansible?</title>
    <content type="html">
      <![CDATA[<h2 id="why-ansible">Why Ansible?</h2>
<p>As you may know, I'm pretty familiar with <a href="https://www.chef.io/chef/" title="Official chef website" rel="noopener noreferrer">chef</a> and I use it almost every day, for both professional &amp; personal stuff.
Despite that, I am quite willing to try something else and <a href="https://www.ansible.com/" title="Official Ansible website" rel="noopener noreferrer">Ansible</a> is a well known (and used!) configuration management tool. I know a lot of people who are quite pleased to use it!</p>
<p>Furthermore, I changed my laptop last week so it was the perfect occasion to give it a try :).</p>
<h2 id="let-the-journey-begin">Let the journey begin!</h2>
<p>As you will see, Ansible is really easy to use.</p>
<h3 id="what-s-the-goal">What's the goal?</h3>
<p>As I said, my goal is to automatically install a laptop development. I use debian and I will only focus on it. :)</p>
<p><strong>I would like:</strong></p>
<ul>
<li>2 steps maximum (bootstrap + run)</li>
<li>as less manual actions as possible</li>
<li>an easy to understand / maintain project</li>
</ul>
<h3 id="implementation">Implementation</h3>
<p>First of all, let's start with the project tree:</p>
<pre><code class="language-bash">.
├── bin
│   └── bootstrap
├── laptop.yml
├── Makefile
├── README.md
└── roles
    └── common
        ├── files
        │   └── ssh
        │       ├── config
        │       ├── id_rsa
        │       └── id_rsa.pub
        └── tasks
            ├── main.yml
            └── nginx.yml</code></pre>
<p>There aren't a lot of files which is a good point for maintainability, right? :)</p>
<p>Furthermore, the installation process is <strong>very</strong> simple:</p>
<ol>
<li>Clone the repository</li>
<li>Boostrap the laptop with the <code>bin/boostrap</code> command</li>
<li>Install the laptop with <code>make install</code></li>
</ol>
<p>As you can see, I only have 2 commands to run: it seems one of my goal is already reached! \o/</p>
<p>Let's explain those 2 steps.</p>
<h4 id="bootstrap">Bootstrap</h4>
<p>As <code>ansible</code> isn't installed by default on your laptop, the goal of the bootstrap is to install it. Furthermore, I don't want to deal with the <code>ansible</code> command line because there are several arguments to include and I'm used too <code>make install</code> everything; that's why I need <code>make</code> too. Finally, as ansible will be run by my user and not by root, I need to have some privileges, that's why I also install <code>sudo</code> and grant all privileges to the current user.</p>
<p>Here is the boostrap script:</p>
<pre><code class="language-bash">#!/bin/bash

echo "Installing sudo, make &amp; ansible, and allow user \"${USER}\" to run any command with sudo..."

LOCAL_USER=${USER} su -c 'apt-get install sudo make ansible &amp;&amp; echo "${LOCAL_USER}      ALL=(ALL:ALL) NOPASSWD:ALL" &gt; /etc/sudoers.d/${LOCAL_USER}'</code></pre>
<p>Once all prerequisites are installed, we can use ansible.</p>
<h4 id="installing-the-laptop">Installing the laptop</h4>
<p>As I said, I use a Makefile. It contains only one command:</p>
<pre><code class="language-makefile">.PHONY: ${TARGETS}

install:
        ansible-playbook -i '127.0.0.1,' laptop.yml --ask-vault-pass</code></pre>
<p>We simply ask ansible to run the playbook named <code>laptop.yml</code> on <code>127.0.0.1</code>.
Forget the <code>--ask-vault-pass</code> option for now, we'll discuss it later! ;)</p>
<h5 id="playbook">Playbook</h5>
<p>As said before, we ask ansible to run a playbook. In our case, it's called <code>laptop.yml</code> and here is the content of this file:</p>
<pre><code class="language-yaml">---

- hosts: 127.0.0.1
  connection: local
  roles:
    - common</code></pre>
<p>The only impacted host is <code>127.0.0.1</code>.
We use the local connection (you can use ssh to configure a remote server for instance).
Then we list all roles which concern our host.</p>
<p>It's a <strong>very</strong> simple playbook and I won't go into details on this subject for two reasons:</p>
<ul>
<li>you don't need anything else to configure a personal laptop</li>
<li>that's a huge subject I'm definitively not the best specialist of to talk about it :)</li>
</ul>
<p>If you're interested in anyway, you can take a look at <a href="http://docs.ansible.com/ansible/playbooks.html" title="Ansible&#039;s playbooks documentation" rel="noopener noreferrer">the official documentation</a>.</p>
<h5 id="roles-tasks">Roles &amp; Tasks</h5>
<p>Our playbook mention only 1 role: <code>common</code>.</p>
<p>Let's have a look at it:</p>
<pre><code class="language-bash">roles/common/
├── files
│   └── ssh
│       ├── config
│       ├── id_rsa
│       └── id_rsa.pub
└── tasks
    ├── main.yml
    └── nginx.yml</code></pre>
<p>It contains several files related to ssh and two tasks respectively called <code>main.yml</code> and <code>nginx.yml</code>.</p>
<p>You will always have a <code>main.yml</code> task in a role as it's the default entry point. Here is an extract of this file:</p>
<pre><code class="language-yaml">---

- name: install packages
  become: true
  apt: name="{{item}}" state=present
  with_items:
    - ack-grep
    - composer
    - curl
    - make
    # ...

- name: Install slack
  apt:
    deb: https://downloads.slack-edge.com/linux_releases/slack-desktop-2.1.0-amd64.deb
    state: present
  become: true

- name: Install ssh keys
  copy:
    src: "ssh/{{ item }}"
    mode: "0644"
    dest: /home/odolbeau/.ssh/
  with_items:
    - id_rsa
    - id_rsa.pub

- name: Install ssh config
  copy:
    src: "ssh/config"
    mode: "0644"
    dest: /home/odolbeau/.ssh/

- name: Download dot files from github
  git: repo=ssh://git@github.com/odolbeau/dot-files.git dest=/home/odolbeau/dot-files

- name: Install dot files
  command: make -C /home/odolbeau/dot-files install

- name: Download VIM configuration from github
  git: repo=ssh://git@github.com/odolbeau/vim-config.git dest=/home/odolbeau/vim-config

- name: Install VIM configuration
  command: make -C /home/odolbeau/vim-config install

- include: nginx.yml</code></pre>
<p>There are several instructions in this file. As you may have noticed, everything is in yaml and clearly understandable.</p>
<p>Let's explain some of this instructions:</p>
<pre><code class="language-yaml">- name: install packages
  become: true
  apt: name="{{ item }}" state=present
  with_items:
    - ack-grep
    - composer
    - curl
    - make
    # ...</code></pre>
<p>Most of the ansible instructions speak for themselves!</p>
<p>In this case, we create a task which will use the <code>apt</code> module to install a package. This task will be run with several items listed under <code>with_items</code> key.</p>
<p>The <code>become: true</code> option is used to run this task as root (cause the default value for <code>become_user</code> is root).</p>
<pre><code class="language-yaml">- name: Install slack
  apt:
    deb: https://downloads.slack-edge.com/linux_releases/slack-desktop-2.1.0-amd64.deb
    state: present
  become: true</code></pre>
<p>In this case, we still use the <code>apt</code> module to install a remote package.
Notice that you can use an inline syntax like in the first example with <code>apt: deb="..."</code> or the extended syntax like here.</p>
<pre><code class="language-yaml">- name: Install ssh config
  copy:
    src: "ssh/config"
    mode: "0644"
    dest: /home/odolbeau/.ssh/</code></pre>
<p>Again, a very easy to understand task! I simply want to copy files coming from my roles (placed under <code>my_role/files/</code>) on my laptop. Easy! \o/</p>
<pre><code class="language-yaml">- name: Download dot files from github
  git: repo=ssh://git@github.com/odolbeau/dot-files.git dest=/home/odolbeau/dot-files

- name: Install dot files
  command: make -C /home/odolbeau/dot-files install</code></pre>
<p>Those 2 tasks are used to install my <a href="https://github.com/odolbeau/dot-files" title="My dot files" rel="noopener noreferrer">dot-files</a>. The first one uses git to download the repository and the second executes a <code>make install</code> inside the correct folder.</p>
<p>I won't list all modules I use though. There are plenty of them and <a href="http://docs.ansible.com/ansible/modules_by_category.html" title="Ansible modules documentation" rel="noopener noreferrer">their documentation</a> is very clear! Don't forget to have a look at existing modules before running a command by yourself. :)</p>
<h4 id="that-s-it">That's it!</h4>
<p>You know everything you need to start to use ansible by yourself for a single host!</p>
<h2 id="bonus">Bonus</h2>
<h3 id="ask-the-user-to-do-something-for-you">Ask the user to do something for you</h3>
<p>Let's confess: sometimes, it's hard / painful / time-consuming / impossible to do everything with a configuration management.</p>
<p>For instance, in my case, I need to install a VPN client and to create a tunnel in order to download some private projects.</p>
<p>Once the VPN is installed, here is what I use:</p>
<pre><code class="language-yaml">- command: ping -c 1 "a.private.url"
  register: vpn_connected
  ignore_errors: True

- pause:
    prompt: "Make sure to run the VPN in order to continue the installation. [Press any key once done]"
  when: vpn_connected|failed</code></pre>
<p>I try to ping a private URL. I register the result of this command inside the <code>vpn_connected</code> var.</p>
<p>Then I use the <code>pause</code> module. If the tunnel isn't running, I simply ask the user to launch it, otherwise, it keeps going!</p>
<p>Of course, the goal is not to use this trick every time: if your users have to do everything manually, you're not using a configuration management tool correctly! Use this only when you <strong>really</strong> can't configure something automatically.</p>
<h3 id="deal-with-sensitive-data">Deal with sensitive data</h3>
<p>As previously explained, I use ansible to install my private ssh keys. Even if all my private keys are protected by a passphrase, I don't want to version them without encryption!</p>
<p>In my case, I use a private repository to store my ansible configuration. In this situation, it's not really necessary to encrypt your keys but as you will see, it's very easy to do! :)</p>
<p>Of course encrypt keys / passwords / files is a common use case and Ansible propose a very powerful solution to deal with it: <a href="http://docs.ansible.com/ansible/playbooks_vault.html" title="Ansible vault documentation" rel="noopener noreferrer">Vault</a>.</p>
<p>It's shipped with the <code>ansible</code> package and it's easy to use! I mean, <strong>really</strong> easy!</p>
<p>If you want to encrypt your ssh keys:</p>
<pre><code class="language-bash"># Copy files into your role
cp ~/.ssh/id_rsa ~/.ssh/id_rsa.pub my_role/files/
# Encrypt them!
ansible-vault crypt my_role/files/id_rsa my_role/files/id_rsa.pub
New Vault password:
Confirm New Vault password:
Encryption successful</code></pre>
<p>And that's it! Your files are now encrypted —congrats by the way! :)</p>
<p>As soon as you have encrypted file in a role, you'll have to add the <code>--ask-vault-pass</code> option when running the <code>ansible-playbook</code> command.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope I convinced you: it's pretty convenient to write an ansible playbook to install your laptop! In case you change it, you will gain a lot of time running 2 commands instead of installing everything manually! :)</p>
<p>Ansible is easy to use which make it a good choice to fulfill this need, but there are a lot of other configuration management tools out there, so don't hesitate to take a look at them! :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/how-to-install-your-laptop-with-ansible/" rel="alternate" type="text/html" />
    <published>2016-08-30T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:39+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/be-gentle-with-your-prod/</id>
    <title>Be gentle with your prod</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/be-gentle-with-your-prod/" rel="alternate" type="text/html" />
    <published>2016-05-24T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/elasticsearch-at-blablacar/</id>
    <title>Elasticsearch at BlaBlaCar</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/elasticsearch-at-blablacar/" rel="alternate" type="text/html" />
    <published>2016-04-07T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/symfony-paris-2015-symfony-at-blablacar/</id>
    <title>Symfony at BlaBlaCar</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/symfony-paris-2015-symfony-at-blablacar/" rel="alternate" type="text/html" />
    <published>2015-12-04T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/from-1-to-20-million-users/</id>
    <title>From 1 to 20 million users: the technical story of BlaBlaCar.</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/from-1-to-20-million-users/" rel="alternate" type="text/html" />
    <published>2015-11-14T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/swarrot-a-lib-to-consume-them-all/</id>
    <title>Swarrot: A library to consume them all!</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/swarrot-a-lib-to-consume-them-all/" rel="alternate" type="text/html" />
    <published>2015-10-20T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/slicing-up-a-monolithic-application/</id>
    <title>Slicing up a monolithic application: Why and How?</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/slicing-up-a-monolithic-application/" rel="alternate" type="text/html" />
    <published>2015-10-02T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/symfony2-killed-me/</id>
    <title>Symfony2 killed me!</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/symfony2-killed-me/" rel="alternate" type="text/html" />
    <published>2015-09-28T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/logs-hunting/</id>
    <title>Logs hunting</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/logs-hunting/" rel="alternate" type="text/html" />
    <published>2015-04-09T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/dont-let-your-log-go-away/</id>
    <title>Don&#039;t let your log go away!</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/dont-let-your-log-go-away/" rel="alternate" type="text/html" />
    <published>2015-03-24T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/microservices-at-blablacar/</id>
    <title>Microservices at BlaBlaCar</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/microservices-at-blablacar/" rel="alternate" type="text/html" />
    <published>2015-03-17T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/which-tool-to-use-to-make-marketing-dashboards/</id>
    <title>Which tool to use to make marketing dashboard?</title>
    <content type="html">
      <![CDATA[<h2 id="the-history">The history</h2>
<p>For those who don't know it, I made <a href="https://www.youtube.com/watch?v=1r1SOeaDqH4" rel="noopener noreferrer">a presentation of the ELK
stack</a> at the PHP Forum in Paris
(in French). During this talk, I mainly talked about our
(<a href="http://www.blablacar.com" rel="noopener noreferrer">BlaBlaCar</a>) technical logs. I also mentioned that
we use ELK to make some marketing dashboards too (signups by country, payments
and so on.) even if I said thaht I don't consider ELK as the best tool to do
this.</p>
<p>Last week Claude Duvergier (<a href="https://twitter.com/C_Duv" rel="noopener noreferrer">@C_Duv</a>) asked me <a href="https://twitter.com/C_Duv/status/530869087531184129" rel="noopener noreferrer">on
twitter</a> which tool I
would recommend instead of ELK. This make me think that in fact, ELK is not a
bad choice, even if some others tools exist.</p>
<p>Disclaimer: I'm definitely not aware of all existing tools to make marketing
dashboards. I will talk only about solutions I already used. Sorry if I miss
something important. This article is more my personal feedback regarding some
tools we use than a real comparatif.</p>
<h2 id="tools">Tools</h2>
<h3 id="elk">ELK</h3>
<p>Let me start by the one I probably know the most.  ELK is <strong>very</strong> easy to
setup and to use. You just have to send all important events to logstash or
anything else (in our case, we send it to a RabbitMQ broker) and store them in
ElasticSearch. Kibana will then help you to display all this events.</p>
<p>By design, all elasticsearch queries are made on the fly. ElasticSearch is
&quot;just&quot; a data store and don't aggregate anything. You're able to update your
queries when you want, filter results, etc directly in Kibana which is really
appreciable. The drawback is the time needed to render some graphs for a long
period.</p>
<p>Another point to consider with ELK is the storage needed for all your data. It
can be quite huge depending on what you choose to store and on your retention
policy.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>Very easy to setup and to install</li>
<li>Kibana is very easy to use (I didn't try the latest version with aggregations
support but I'm sure it's even better)</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>Rendering time depends on the interval size</li>
<li>Storage space needed (sometimes)</li>
</ul>
<h3 id="newrelic">NewRelic</h3>
<p>For those who don't know it, NewRelic is an <a href="http://en.wikipedia.org/wiki/Application_performance_management" rel="noopener noreferrer">Application Performance
Management</a>
(APM). I won't talk a lot about it because even if you <strong>can</strong> make some graphs
based on pages frequentation, it's definitely not the solution to our problem.</p>
<p>I choose to put it in this list only because we use some New Relic dashboards
for technical needs (to check registrations &amp; payments after a deployment for
example) but it can looks like a marketing dashboard.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>Easy to make some basic dashboards (once you already installed it)</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>Non free</li>
<li>Very basic</li>
<li>Can only track pages frequentation</li>
<li>Not possible to change time aggregation interval</li>
</ul>
<h3 id="influxdb">InfluxDB</h3>
<p>Recently introduced at BlaBlaCar, I am fallen in love with <a href="http://influxdb.com/" rel="noopener noreferrer">this time series
database</a>. For now, we use it for code instrumentation
purpose and we only have technical dashboards (and a bunch of issues with
collected data. :P). The latest stable release of InfluxDB is the 0.8.5 and the
cluster support is experimental. Despite all of this, it works well. :) It's
not the goal here so I will not talk about the design of InfluxDB. You are
strongly enouraged to <a href="http://influxdb.com/docs/v0.8/introduction/overview.html" rel="noopener noreferrer">take a
look</a> at it.</p>
<p>Like ELK, you need to send events with all relevant informations you need.
This time however, you shouldn't query the created serie. You have to create
some <a href="http://influxdb.com/docs/v0.8/api/continuous_queries.html" rel="noopener noreferrer">continuous
queries</a> in order to
split your data.</p>
<p>For example in our case, we send an event for each http queries. This event
contains the called route and we have a continuous query which create new
series per route and calculate the average response time and memory usage with
an aggregation of 1 minute. Another continuous query make the same, every hour.</p>
<p>To create dashboards with data from InfluxDB, we use
<a href="http://grafana.org/" rel="noopener noreferrer">grafana</a>, a kibana clone which supports InfluxDB,
Graphite and OpenTSDB.  With this system, we always query the same series. It's
<strong>really</strong> fast, even if you display a lot of series on a large period.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>Really fast to display</li>
<li>Can store all kinds of events (even system events for example).</li>
<li>Grafana is as simple as Kibana (maybe more)</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>Harder to setup than an ELK stack</li>
<li>Need to define all continuous queries to be able to use them after</li>
<li>Cluster support not ready yet</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>I know that there is a lot of others solutions available to create marketing
dashboards. I hesitated to talk about Hadoop / Vertica / Tableau which are also
used at BlaBlaCar for data analysis. However as I don't personally use these
tools, they doesn't fill my initial requirements.</p>
<p>Despite what I told during my talk at ForumPHP Paris, ELK <strong>IS</strong> a very good
choice even more if you already use this stack to analyze your logs and if you
don't need to display a long period for your marketing dashboard.  If you have
more time and want to use a time series database, I recommend you InfluxDB even
if it's a really young software. :)</p>
<p>If you use another tool and think it's THE perfect solution, let me know. :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/which-tool-to-use-to-make-marketing-dashboards/" rel="alternate" type="text/html" />
    <published>2014-11-09T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/benchmark-php-amqp-lib-amqp-extension-swarrot/</id>
    <title>[Benchmark] PHP amqp-lib VS amqp-ext with Swarrot</title>
    <content type="html">
      <![CDATA[<h2 id="the-history">The history</h2>
<p>Everything start on 7 April 2014. We gave a talk with <a href="https://twitter.com/lyrixx" rel="noopener noreferrer">Grégoire Pineau (aka
@lyrixx)</a> called &quot;Making asynchronous tasks in PHP&quot;
(slides are available <a href="https://speakerdeck.com/odolbeau/making-asynchronous-tasks-in-php" rel="noopener noreferrer">on
speakerdeck</a>).</p>
<p>During this talk we trolled about the
<a href="https://github.com/videlalvaro/php-amqplib" rel="noopener noreferrer">php-amqplib</a> created by <a href="https://www.twitter.com/old_sound" rel="noopener noreferrer">Alavaro
Videla</a> and maintained by <a href="https://github.com/videlalvaro/php-amqplib/graphs/contributors" rel="noopener noreferrer">a lot of
users</a>.</p>
<p>This library is one of the 2 main ways to talk to an amqp broker. The other one
is the <a href="https://github.com/pdezwart/php-amqp" rel="noopener noreferrer">php extension</a>.</p>
<p>Anyway, the subject has come back a few days ago <a href="https://twitter.com/oscherler/status/524113790687010816" rel="noopener noreferrer">on
twitter</a> (conversation
is in French) when <a href="https://twitter.com/oscherler" rel="noopener noreferrer">Ölbaum</a> asked us if we still
recommend to not use the php-amqplib. Some tweets later, I proposed to make a
small benchmark to compare these 2 ways of talking to a broker. Because
trolling is good but sometimes, having some real arguments is better.</p>
<h2 id="environment">Environment</h2>
<ul>
<li>The broker used is <a href="http://www.rabbitmq.com/" rel="noopener noreferrer">RabbitMQ</a> (v3.3.5).</li>
<li>I launched tests on a Mac Book Pro (2,8 GHz Intel Core i7, 16 GB 1600 MHz
DDR3 with OSX 10.10).</li>
<li>I used PHP 5.6.2.</li>
<li>I used the last stable version for both the extension (1.4.0) and the library
(2.4.1).</li>
</ul>
<p>I chose to use <a href="https://github.com/swarrot/swarrot" rel="noopener noreferrer">Swarrot</a> to write as few
code as possible for each implementation.</p>
<p>The full project used for this benchmark can be found <a href="https://github.com/odolbeau/php-amqp-bench" rel="noopener noreferrer">on
github</a>. Of course, feel free to
contribute and complete it!</p>
<h2 id="results">Results</h2>
<p>And now, the results.
Every tests have been launched 3 times.
What you see here is the average time of these 3 launches.</p>
<h3 id="publish-1-million-messages-in-a-direct-exchange">Publish 1 million messages in a direct exchange</h3>
<p>Code <a href="https://github.com/odolbeau/php-amqp-bench/blob/master/src/Bab/Console/Command/PublishCommand.php" rel="noopener noreferrer">is
here</a>
The concerned queue has been purged before each launch.</p>
<pre><code class="language-bash">./bench publish [ext|lib] -m 1000000</code></pre>
<h4 id="with-the-extension">With the extension</h4>
<pre><code class="language-bash">+-------------+-------------+
| Duration    |  36 seconds |
+-------------+-------------+
| Memory peak | 1.5 MiB     |
+-------------+-------------+</code></pre>
<h4 id="with-the-library">With the library</h4>
<pre><code class="language-bash">+-------------+-------------+
| Duration    |  46 seconds |
+-------------+-------------+
| Memory peak | 2.5 MiB     |
+-------------+-------------+</code></pre>
<h4 id="so-what">So what?</h4>
<p>The duration difference is pretty small between the extension and the library.
In both case the memory consumption is very stable (I tried with 100, 1k, 10k,
100k messages, the memory consumption is near the same).</p>
<h3 id="get-100k-messages-from-a-queue-ack">Get 100k messages from a queue (+ ack)</h3>
<p>Code <a href="https://github.com/odolbeau/php-amqp-bench/blob/master/src/Bab/Console/Command/GetCommand.php" rel="noopener noreferrer">is
here</a>.</p>
<pre><code class="language-bash">./bench get [ext|lib] -m 1000000</code></pre>
<h4 id="with-the-extension-1">With the extension</h4>
<pre><code class="language-bash">+-------------+-------------+
| Duration    |  19 seconds |
+-------------+-------------+
| Memory peak | 1.8 MiB     |
+-------------+-------------+</code></pre>
<h4 id="with-the-library-1">With the library</h4>
<pre><code class="language-bash">+-------------+-------------+
| Duration    |  43 seconds |
+-------------+-------------+
| Memory peak | 2.8 MiB     |
+-------------+-------------+</code></pre>
<h4 id="so-what-1">So what?</h4>
<p>For the memory consumption, again, nothing to say. It's low and stable in both
case. To consume messages, the pecl extension is more than 2 times faster than
the library.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The extension is faster than the library. C is faster than PHP. Is it really
surprising? No! It's not really pertinent to compare 2 tools which obviously
make the same job but have different implementations.</p>
<p>The main difference is the installation. The library is VERY simple to install!
You just have to add <code>"videlalvaro/php-amqplib": "~2.4"</code> in your composer.json
and you're done. On the contrary, for the extension, you generally need to
compile the <code>rabbitmq-c</code> (an AMQP client in C used by the php extension) which
can be a bit boring.</p>
<p>So, if you already installed the extension or if it's not a problem for you, go
for it! Otherwise, don't panic and use the library!</p>
<p>In both case, take a look at <a href="https://github.com/swarrot/swarrot" rel="noopener noreferrer">Swarrot</a> (and
the <a href="https://github.com/swarrot/SwarrotBundle" rel="noopener noreferrer">SwarrotBundle</a>) to be able to
change your choice if needed.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/benchmark-php-amqp-lib-amqp-extension-swarrot/" rel="alternate" type="text/html" />
    <published>2014-10-30T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:40+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/laisse-pas-trainer-ton-log/</id>
    <title>Laisse pas trainer ton log</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/laisse-pas-trainer-ton-log/" rel="alternate" type="text/html" />
    <published>2014-10-23T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/speed-up-cookbooks-tests-docker/</id>
    <title>Speed up your cookbooks tests with docker</title>
    <content type="html">
      <![CDATA[<p>If you're not familiar with <a href="https://www.getchef.com/" rel="noopener noreferrer">chef</a>, a configuration
management tool (like Puppet or Ansible for example) you should probably <a href="https://learn.getchef.com/" rel="noopener noreferrer">click
here</a> and learn how to use it. :)</p>
<p>If you're not familiar with <a href="https://www.docker.com/" rel="noopener noreferrer">docker</a> it's not a
problem! You just need to <a href="https://docs.docker.com/installation/#installation" rel="noopener noreferrer">install
it</a> and to continue
reading. :)</p>
<p>For those who already use chef, I'm sur you write a lot of tests to check your
cookbooks isn't it? :) And you know that it can take a (very) long time to run
the full test suite on different VM.</p>
<p>But don't worry, from now on, it's over! Look at
<a href="https://github.com/portertech/kitchen-docker" rel="noopener noreferrer">kitchen-docker</a>.</p>
<p>Just add <code>kitchen-docker</code> in your <code>Gemfile</code> (or install it directly with <code>gem install kitchen-docker</code>) and you can now start to use docker instead of
Vagrant. \o/ Just replace you're current driver in <code>.kitchen.yml</code>:</p>
<pre><code class="language-yaml">driver:
  name: docker</code></pre>
<p>If you use docker on a Mac (with
<a href="https://github.com/boot2docker/boot2docker" rel="noopener noreferrer">boot2docker</a>) or inside another
machine, you also need to change the socket used by the docker daemon :</p>
<pre><code class="language-yaml">platforms:
- name: ubuntu-12.04
  driver_config:
    socket: tcp://docker.example.com:4242</code></pre>
<p>And that's all, you can now launch <code>kitchen test</code> and see the result. :)</p>
<p>On some cookbooks used at <a href="http://www.blablacar.com" rel="noopener noreferrer">BlaBlaCar</a>, running the
full test suite is near 60% quicker than before.</p>
<p>Of course, it's not the perfect solution and there is some drawbacks. For
example :</p>
<ul>
<li>the <code>cron</code> service is not automatically launched</li>
<li>on my local environment (mac), when chef change the DNS used in your docker,
some tests fails.</li>
<li>some of our tests need a VM with more than 1 disk. Not possible with docker.</li>
</ul>
<p>Apart from this problems, we save time everyday. \o/</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/speed-up-cookbooks-tests-docker/" rel="alternate" type="text/html" />
    <published>2014-09-28T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:40+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/when-monolog-meet-elk/</id>
    <title>When Monolog meet ELK</title>
    <content type="html">
      <![CDATA[<p>For this first article since 2 years (I know, it was long, did you miss me ? :D)
I'm going to talk about Monolog, Gelf and ELK. It's just a quick introduction
but you will find a lot of resources in this article.</p>
<h2 id="monolog">Monolog</h2>
<p>I'm sure you already know <a href="https://github.com/Seldaek/monolog" rel="noopener noreferrer">Monolog</a>, the
(almost) perfect logging library for PHP. :)</p>
<p>I strongly suggest you to read the <a href="https://github.com/Seldaek/monolog#core-concepts" rel="noopener noreferrer">core
concepts</a> of Monolog if
you're not familiar with channels, handlers and processors. In 2 words, channel
is the name of the logger, handlers are its outputs and processors are here to
add extra information in your logs.</p>
<p>As we will see at the end of this article, you can make really interesting
filters with your channels and the extra data added by your processors.</p>
<h2 id="gelf">Gelf</h2>
<p><a href="http://graylog2.org/gelf" rel="noopener noreferrer">Gelf</a> means Graylog Extended Log Format. This new
format has been created by Graylog to avoid all syslog inconvenients like the
length limit and the lack of data types and compression.</p>
<p>Gelf messages can be sent by UDP (fortunately !) and of course, the awesome
Monolog provides a
<a href="https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/GelfHandler.php" rel="noopener noreferrer">GelfHandler</a>.</p>
<p>Here is an example of a gelf message (can be found <a href="http://graylog2.org/gelf#specs" rel="noopener noreferrer">in the
specs</a>) :</p>
<pre><code class="language-json">{
  "version": "1.1",
  "host": "example.org",
  "short_message": "A short message that helps you identify what is going on",
  "full_message": "Backtrace here\n\nmore stuff",
  "timestamp": 1385053862.3072,
  "level": 1,
  "_user_id": 9001,
  "_some_info": "foo",
  "_some_env_var": "bar"
}</code></pre>
<p>As described in the specs, some information are mandatory (but don't worry
about it, just let Monolog deal with it) and you can add as many information
as you wish.</p>
<p>Here is a small example of a custom handler to log gelf messages in logstash:</p>
<pre><code class="language-yml">#config_dev.yml
monolog:
    ...
    handlers:
        my_logstash_handler:
            type: gelf
            publisher:
                hostname: %logstash_host%
                port: %logstash_port%
            formatter: monolog.formatter.gelf_message
            level: INFO</code></pre>
<h2 id="elk">ELK</h2>
<p>ELK is an acronym for <a href="http://www.elasticsearch.org/" rel="noopener noreferrer">ElasticSearch</a> /
<a href="http://logstash.net/" rel="noopener noreferrer">Logstash</a> /
<a href="http://www.elasticsearch.org/overview/kibana/" rel="noopener noreferrer">Kibana</a>.</p>
<h3 id="elasticsearch">ElasticSearch</h3>
<p>ElasticSearch is a very powerful distributed search engine which provides a RESTful
API. In the ELK stack, ElasticSearch is the storage backend. All our logs will
be stored insite an index.</p>
<h3 id="logstash">Logstash</h3>
<p>Logstash has been created to manage logs. It collects, parses and stores them.
There is a lot of existing inputs (41), filters (50) and outputs (55). For
example, look at this configuration file :</p>
<pre><code class="language-json">input {
    gelf {
        codec =&gt; "json"
    }
}

output {
    elasticsearch {
        hosts =&gt; "elasticsearch:9200"
    }
}</code></pre>
<p>We have configured a single input which is gelf. As we saw it, by default, gelf
logs are sent through UDP on port 12201 and of course, logstash knows it.</p>
<p>There is no filter in this configuraton as we don't really need it for this
example. By the way, the gelf message will be directly sent to ElasticSearch.</p>
<p>And finally, there is an elasticsearch_http output. So, logstash will call the
ElasticSearch API to insert logs into an index, generated per day.</p>
<p>You can take a look at the full documentation for the <a href="http://logstash.net/docs/1.4.2/inputs/gelf" rel="noopener noreferrer">gelf
input</a> and the <a href="http://logstash.net/docs/1.4.2/outputs/elasticsearch_http" rel="noopener noreferrer">elasticsearch_http
output</a> to have more
information.</p>
<h3 id="kibana">Kibana</h3>
<p>Kibana is a very powerful tool to see and interact with your data. It's very
easy to use and you can create a lot of dashboards to visualize all your logs.
Take a look at the <a href="http://www.elasticsearch.org/overview/kibana/" rel="noopener noreferrer">project
homepage</a> to see some examples.</p>
<h2 id="tips">Tips</h2>
<h3 id="create-dashboards-for-everything">Create dashboards for everything !</h3>
<p>Is there a particular error in your production environment ? Create a dashboard
just for it ! You will have all available information in a single place, it
will be easier to aggregate information and understand when the error occurred.</p>
<h3 id="context-is-your-friend-bro">Context is your friend bro !</h3>
<p>Of course, you all know
<a href="https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md" rel="noopener noreferrer">PSR-3</a>,
which defined a standard <code>PSR\Log\LoggerInterface</code> (used by Monolog obviously).
But did you read the <a href="https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#13-context" rel="noopener noreferrer">&quot;Context&quot;
section</a>
? Did you notice that all methods defined in the interface take a <code>$context</code>
array as second argument ? Do you use it ? No ? You should !</p>
<p>This context is the best way to provide more information with your log. You can
easily add all needed information to know WHEN an error (for example)
occurred. And once you send all this context to the ELK stack, you can easily
filter your logs according to the context. Does this error occur every time
with the same user ? Is it only with this particular entity ? Anyway, just add
context and you will be able to group your logs according to it. :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/when-monolog-meet-elk/" rel="alternate" type="text/html" />
    <published>2014-07-18T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:40+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/when-monolog-meet-elk/</id>
    <title>When Monolog meet ELK</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/when-monolog-meet-elk/" rel="alternate" type="text/html" />
    <published>2014-07-15T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/doctrine-lexer-use-case/</id>
    <title>Doctrine Lexer use case</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/doctrine-lexer-use-case/" rel="alternate" type="text/html" />
    <published>2014-06-24T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/making-asynchronous-tasks-in-php-2/</id>
    <title>Making asynchronous tasks in PHP</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/making-asynchronous-tasks-in-php-2/" rel="alternate" type="text/html" />
    <published>2014-06-24T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/making-asynchronous-tasks-in-php/</id>
    <title>Making asynchronous tasks in PHP</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/making-asynchronous-tasks-in-php/" rel="alternate" type="text/html" />
    <published>2014-04-07T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/sf-live-paris-swarrot/</id>
    <title>Swarrot: A library to consume them all!</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/sf-live-paris-swarrot/" rel="alternate" type="text/html" />
    <published>2014-04-07T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/forum-php-paris-technical-history-blablacar/</id>
    <title>3 millions users in 10 countries: Technical history of BlaBlaCar</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/forum-php-paris-technical-history-blablacar/" rel="alternate" type="text/html" />
    <published>2013-11-21T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/talks/sfpot-paris-rabbitmq/</id>
    <title>Retour d&#039;expérience sur l&#039;utilisation de RabbitMQ chez BlaBlaCar</title>
    <content type="html">
      <![CDATA[]]>
    </content>
    <link href="https://odolbeau.fr/talks/sfpot-paris-rabbitmq/" rel="alternate" type="text/html" />
    <published>2013-03-13T00:00:00+00:00</published>
    <updated>2026-01-04T11:08:10+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/symfony2-cache-warmer/</id>
    <title>Symfony2 cache warmer</title>
    <content type="html">
      <![CDATA[<p>For this first post since 6 months, let me introduce to you the CacheWarmer class.</p>
<h2 id="cache-warmer-what-is-this">Cache warmer? What is this?</h2>
<p>A cache warmer is just a class that writes in a file to store data. Really simple.</p>
<p>Look into you're <code>app/cache</code> directory. Here is an example of what you can find:</p>
<ul>
<li>Global configuration</li>
<li>Translations</li>
<li>Assetic configuration</li>
<li>Doctrine proxies classes</li>
<li>Twig compiled templates</li>
<li>...</li>
</ul>
<p>Lot of things, created by cache warmers.</p>
<h2 id="how-to-use-cache-warmer">How to use cache warmer?</h2>
<p>Juste create a file that extend the <a href="https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php" title="CacheWarmerInterface definition" rel="noopener noreferrer">CacheWarmerInterface</a> provided by the HttpKernel Component.</p>
<pre><code class="language-php">&lt;?php

use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;

class MyCustomeCacheWarmer implements CacheWarmerInterface
{
    public function warmUp($cacheDir)
    {
        // Create a file here
        // Write in it
    }

    public function isOptional()
    {
        // By default, all CacheWarmer are called by the `app/console cache:warmup` command.
        // But you can specify the `--no-optional-warmers` to skip non optional ones.
        return false;
    }
}</code></pre>
<p>For the implementation, you can take a look at the <a href="https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TemplatePathsCacheWarmer.php" title="Symfony2 TemplatePathsCacheWarmer" rel="noopener noreferrer">TemplatePathsCacheWarmer</a> for example.</p>
<p>Of course, you have to declare your CacheWarmer as a service. Do not forget to tag it with <code>kernel.cache_warmer</code>:</p>
<pre><code class="language-xml">&lt;service id="my_custom.cache_warmer" class="path/to/MyCustomCacheWarmer"&gt;
    &lt;tag name="kernel.cache_warmer" /&gt;
&lt;/service&gt;</code></pre>
<p>Now, if you run the command <code>app/console cache:warmup</code> (or <code>app/console cache:clear</code> without the <code>--no-warmup</code> option) your cache file should be created. (Search it in the <code>app/cache/</code> folder).</p>
<p>To use your cached data, it's very simple. Look at <a href="https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Templating/Loader/TemplateLocator.php" title="Symfony TemplateLocator" rel="noopener noreferrer">TemplateLocator</a>.</p>
<p>The <code>TemplateLocator</code> simply require the cached file and store the result into a var. To do the same thing, simply store a return statement in your file. For example, the <code>app/cache/dev/templates.php</code> file looks like this:</p>
<pre><code class="language-php">&lt;?php return array (
    'template_name' =&gt; 'path/to/template',
    // ...
),</code></pre>
<p>You're done. You can now use your custom CacheWarmer in your application. :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/symfony2-cache-warmer/" rel="alternate" type="text/html" />
    <published>2012-07-15T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:40+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/use-virtuals-forms-with-symfony2/</id>
    <title>Use virtuals forms with Symfony2</title>
    <content type="html">
      <![CDATA[<div class="alert alert-info">
This article is now an official <a href="http://www.symfony.com/doc/current/cookbook/form/use_virtuals_forms.html">cookbook</a> since <a href="https://github.com/symfony/symfony-docs/commit/a7781433cf6af9b43f924d2f61525537141d27bf">February 2012</a>.
</div>
<p><em>Wait! What? You already write <a href="/blog/utiliser-les-forms-virtuals-avec-symfony2.html" title="Utiliser les forms virtuals avec Symfony2">this</a> before! Oo</em></p>
<p>Yes I do! But in french! And it seems it was not very clear... So let me explain virtuals forms again and this time... in english!</p>
<p>We have 2 entities. A Company and a Customer :</p>
<pre><code class="language-php">&lt;?php

namespace ...;

class Company
{
    private $name;
    private $website;

    private $address;
    private $zipcode;
    private $city;
    private $country;

    // Some nice getters / setters here.
}</code></pre>
<pre><code class="language-php">&lt;?php

namespace ...;

class Customer
{
    private $firstName;
    private $lastName;

    private $address;
    private $zipcode;
    private $city;
    private $country;

    // Some nice getters / setters here.
}</code></pre>
<p>Like you can see, both of our entities have these fields: <code>address</code>, <code>zipcode</code>, <code>city</code>, <code>country</code>.</p>
<p>Now, we want to build 2 forms. One for create/update a Company and the second to create/update a Customer.</p>
<p>Of course, we have only two entities which have to contains some location informations... for now! Maybe later, some entities will have this fields. So, we have to find a solution to not duplicate our code!</p>
<p>First, we create very simple CompanyType and CustomerType:</p>
<pre><code class="language-php">&lt;?php

namespace ...;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class CompanyType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            -&gt;add('name', 'text')
            -&gt;add('website', 'text')
        ;
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' =&gt; '...\Company',
        );
    }

    public function getName()
    {
        return 'company';
    }
}</code></pre>
<pre><code class="language-php">&lt;?php

namespace ...;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class CustomerType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            -&gt;add('firstName', 'text')
            -&gt;add('lastName', 'text')
        ;
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' =&gt; '...\Customer',
        );
    }

    public function getName()
    {
        return 'customer';
    }
}</code></pre>
<p>Definitely nothing complicated here.</p>
<p>Now, we have to deal with our four duplicated fields...
Here is a (simple) location FormType:</p>
<pre><code class="language-php">&lt;?php

namespace ...;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class LocationType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            -&gt;add('address', 'textarea')
            -&gt;add('zipcode', 'string')
            -&gt;add('city', 'string')
            -&gt;add('country', 'text')
        ;
    }

    public function getDefaultOptions(array $options)
    {
        return array(
        );
    }

    public function getName()
    {
        return 'location';
    }
}</code></pre>
<p>We can't specify a data_class option in this FormType because, we don't have a Location Entity.<br>
We don't have a location field in our entity so we can't directly link our LocationType.<br>
Of course, we absolutely want to have a dedicated FormType to deal with location (remember, DRY!)</p>
<p><strong>There is a solution!</strong></p>
<p>We can set the option <code>'virtual' =&gt; true</code> in the <code>getDefaultOptions</code> method of our LocationType and directly start use it in our 2 first types.</p>
<p>Look at the result:</p>
<pre><code class="language-php">&lt;?php
// CompanyType

public function buildForm(FormBuilder $builder, array $options)
{
    $builder-&gt;add('foo', new LocationType());
}</code></pre>
<pre><code class="language-php">&lt;?php
// CustomerType

public function buildForm(FormBuilder $builder, array $options)
{
    $builder-&gt;add('bar', new LocationType());
}</code></pre>
<p>With the virtual option set to false (default behavior), the Form Component expect a Foo (or Bar) object or array which contains our four location fields. Of course, we don't have this object/array in our entities and we don't want it!</p>
<p>With the virtual option set to true, the Form Component skip our Foo (or Bar) object or array. So, it directly access to our 4 location fields which are in the parent entity!</p>
<p>(One more time, thank to <a href="http://alexandre-salome.fr/" title="Alexandre Salome website" rel="noopener noreferrer">Alexandre Salomé</a> for the tips)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/use-virtuals-forms-with-symfony2/" rel="alternate" type="text/html" />
    <published>2012-01-23T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:41+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/utiliser-les-forms-virtuals-avec-symfony2/</id>
    <title>Utiliser les forms virtuals avec Symfony2</title>
    <content type="html">
      <![CDATA[<p>2012 commence tout juste (à ce propos, bonne année!) et commence plutôt bien!</p>
<p>J'ai été amené, il y a quelques jours, à utiliser <b>l'attribut virtual du composant Form de symfony2</b>. Le besoin était on ne peut plus simple: Créer un FormType adapté à nos besoins pour afficher un formulaire d'adresse. Ce dernier devant bien entendu être utilisé au sein de plusieurs entités disposant déjà des propriétés à éditer (address, city, zipcode, ...) et de leur getter / setters associés.<br>
Partant de ce constat, <a href="http://alexandre-salome.fr/" alt="Site web d'Alexandre Salome">Alexandre Salomé</a> m'a proposé de créer un <b>FormType "virtual"</b>! Lorsque cet attribut est passé à true, le FormType créé utilisera les propriétés de l'objet parent!<br>
Mais un exemple concret sera sans aucun doute bien plus parlant!</p>
<p>Nous avons donc une entité contenant entres autres, différents champs d'adresse:</p>
<pre><code class="language-php">&lt;?php

namespace ...;

class Company
{
    private $name;
    private $address;
    private $city;

    public function getName()
    {
        return $this-&gt;name;
    }
    public function setName($name)
    {
        $this-&gt;name = $name;
    }

    public function getAddress()
    {
        return $this-&gt;address;
    }
    public function setAddress($address)
    {
        $this-&gt;address = $address;
    }

    public function getCity()
    {
        return $this-&gt;city;
    }
    public function setCity($city)
    {
        $this-&gt;city = $city;
    }
}</code></pre>
<p>On se contentera ici de trois propriétés seulement (dont une qui ne nous sera pas utile pour notre FormType).</p>
<p>Passons à notre FormType:</p>
<pre><code class="language-php">&lt;?php

namespace ...;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class LocalisationType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            -&gt;add('address', 'textarea')
            -&gt;add('city', 'text')
        ;
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'virtual' =&gt; true
        );
    }

    public function getName()
    {
        return 'localisation';
    }
}</code></pre>
<p>Notez l'utilisation de l'option "virtual".<br>
Lors du bind de votre formulaire, notre FormType étant virtuel, ce sont les propriétés de notre objet parent (ici Company) qui seront mis à jour par le PropertyPath.</p>
<p>Bien entendu, il s'agit d'un FormType extrèmement basique! Vous souhaiterez prabablement ajouter quelques champs supplémentaires...</p>
<p>Dernière chose, n'héitez pas à passer les noms des champs qui composent votre adresse en option au FormType Localisation de manière à être plus souple.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/utiliser-les-forms-virtuals-avec-symfony2/" rel="alternate" type="text/html" />
    <published>2012-01-09T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:39+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/utiliser-le-bootstrap-twitter-avec-symfony2/</id>
    <title>Utiliser le bootstrap twitter avec symfony2</title>
    <content type="html">
      <![CDATA[<p>Je me l'étais promis depuis longtemps, je l'ai enfin fait! Tester un minimum <a href="http://lesscss.org/" alt="Site officiel de Less">Less</a> avec <a href="https://github.com/kriswallsmith/assetic" alt="Repo d'Assetic sur github.">Assetic</a> (tant qu'à faire!).</p>
<p>Et histoire de jeter un petit coup d'oeil sur cet outil magnifique, autant le faire dans les meilleurs conditions. Pour ça, merci twitter et son <a href="http://twitter.github.com/bootstrap/" alt="Page officielle du bootstrap twitter">bootstrap</a>, qui utilise lui aussi Less (avec <a href="http://markdotto.com/bootstrap/" alt="Site officiel de Preboot.less">Preboot.less</a> pour être plus précis).</p>
<p>Première chose à faire, installer less et configurer Assetic dans votre projet sf2. Pour ce faire, vous pouvez suivre l'<a href="http://www.funstaff.ch/2011/08/22/symfony2-assetic-less-et-yui-compressor-installation-sur-mac" alt="Tutoriel d'installation d'Assetic, less et yui compressor pour Symfony2">excellent tuto de Bertrand Zuchuat</a> (qui s'adresse avant tout aux possesseurs de Mac, mais vous devriez pouvoir le suivre sans trop d'adaptations quelque soit votre OS).</p>
<p>Une fois fait, il ne vous reste plus qu'à utiliser le bootstrap twitter.</p>
<p>Ce que devrait être rapide puisque <a href="http://bundles.knplabs.org/fr/phiamo/profil" alt="Profile de phiamo sur bundles.knplabs.org">phiamo</a> et <a href="http://bundles.knplabs.org/fr/hidenorigoto/profil" alt="Profil de hidenorigoto sur bundles.knplabs.org">hidenorigoto</a> ont déjà fait tout le travail pour vous avec le <a href="http://bundles.knplabs.org/fr/phiamo/MopaBootstrapBundle" alt="MopaBootstrapBundle">MopaBootstrapBundle</a>.</p>
<p>Il ne vous reste donc qu'à installer ce bundle en suivant les instructions d'installation et vous pourrez ainsi tirer pleinement profit du bootstrap twitter (y compris dans vos formulaires puisque le style par défaut a été revu et travaillé pour l'occasion!)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/utiliser-le-bootstrap-twitter-avec-symfony2/" rel="alternate" type="text/html" />
    <published>2011-11-11T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/des-blogs-bds-a-la-pelle/</id>
    <title>Des blogs BDs à la pelle...</title>
    <content type="html">
      <![CDATA[<p>Fans de BD? Ce post est pour vous! :)</p>
<p>J'ai récemment partagé <a href="http://twitter.com/#!/odolbeau/status/101929449624305665" title="Tweet annonçant la création d&#039;une liste de BD" rel="noopener noreferrer">sur twitter</a> une liste de flux rss de blogs bds, connus ou moins connus et majoritairement en français (avec quelques exceptions tout de même).
Cette liste est <a href="http://www.google.fr/reader/bundle/user%2F08560476452179525239%2Fbundle%2FBD" title="Liste de blogs BD" rel="noopener noreferrer">ici</a>!</p>
<p>Vous pouvez directement vous y abonner grâce au <a href="http://www.google.com/reader/public/atom/user%2F08560476452179525239%2Fbundle%2FBD" title="Flux ATOM de la liste de BDs" rel="noopener noreferrer">flux ATOM</a> (si celle-ci vous semble parfaite! :P) ou récupérer le <a href="http://www.google.fr/reader/public/subscriptions/user%2F08560476452179525239%2Fbundle%2FBD" title="Fichier OPML de la liste de BDs" rel="noopener noreferrer">fichier OPML</a> contenant la liste des flux contenus dans cette liste! :) Ce dernier vous permettra de supprimer certains flux rss si ceux-ci ne vous intéressent pas! :)</p>
<p>Quelques-uns des 18 flux actuellement présents dans la liste:</p>
<ul>
<li><a href="http://www.labandepasdessinee.com/bpd" title="La Bande Pas Dessinée" rel="noopener noreferrer">La Bande Pas Dessinée</a> (grand classique! :))</li>
<li><a href="http://boutanox.blogspot.com/" title="Les réflexions inutiles de Boutanox" rel="noopener noreferrer">Les réflexions inutiles de Boutanox</a> (des personnages... attachants! ^^)</li>
<li><a href="http://piratesourcil.blogspot.com/" title="PirateSourcil" rel="noopener noreferrer">PirateSourcil</a> (certaines bds ne plairont pas à tout le monde! :P)</li>
<li><a href="http://vidberg.blog.lemonde.fr" title="L&#039;actu en patates" rel="noopener noreferrer">L'actu en patates</a> (le nom résume parfaitement l'esprit de ces courtes BDs! :))</li>
<li><a href="http://www.smbc-comics.com" rel="noopener noreferrer">Saturday Morning Breakfast Cereal</a> (ainsi que la version française <a href="http://cereales.lapin.org" title="Les Céréales du Dimanche Matin" rel="noopener noreferrer">Les Céréales du Dimanche Matin</a>)</li>
<li><a href="http://xkcd.com/" title="xkcd" rel="noopener noreferrer">xkcd</a> (les geeks connaissent surement! :))</li>
<li><a href="http://hmm-la-bd.eu" title="Hmm..." rel="noopener noreferrer">Hmm...</a> (petite BD sans prétention faite par un geek! :))</li>
<li>...</li>
</ul>
<p>Bien entendu, si vous connaissez d'autres blogs BDs dans le genre de ceux réunis dans cette liste, n'hésitez pas à m'en faire part dans les commentaires! :D</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/des-blogs-bds-a-la-pelle/" rel="alternate" type="text/html" />
    <published>2011-08-14T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/la-puissance-des-data-providers-de-phpunit/</id>
    <title>La puissance des data providers de PHPUnit</title>
    <content type="html">
      <![CDATA[<p>A n'en pas douter, vous êtes très certainement de grands fans des <strong>tests unitaires</strong> et, pourquoi pas, de <a href="https://github.com/sebastianbergmann/phpunit/" title="PHPUnit official repository">PHPUnit</a>.</p>
<p>Si vous l'utilisez depuis un certain temps, cet article ne vous apprendra probablement rien (désolé! :P). Dans le cas contraire, vous devriez être heureux (en tout cas, moi, je l'étais! :D)</p>
<p>Il y a de ça quelques semaines, je devait tester un service <strong>symfony2</strong> permettant de vérifier les droits d'un utilisateur sur une entité précise pour une action donnée. Inutile de préciser que l'éventail des possibilités était large! :D</p>
<p>Si comme moi, une subite envie de fuire vous prends à l'idée de devoir écrire tous ces tests à la main, restez! Et admirez:</p>
<p><strong>SANS</strong> les datas providers:</p>
<pre><code class="language-php">&lt;?php
class MonTestCase extends WebTestCase
{
    public function testRightsForCreateBusinessWithAdmin() { ... }
    public function testRightsForUpdateBusinessWithAdmin() { ... }
    public function testRightsForDeleteBusinessWithAdmin() { ... }
}</code></pre>
<p>Nous sommes ici obligés d'écrire chacun de nos tests un par un...</p>
<p><strong>AVEC</strong> les datas providers:</p>
<pre><code class="language-php">&lt;?php
class MonTestCase extends WebTestCase
{
    public static function providerRights()
    {
        return array(
            array('create', 'business', 'admin'),
            array('update', 'business', 'admin'),
            array('delete', 'business', 'admin'),
        );
    }

    /**
    * @dataProvider providerRights
    */
    public function testRightsOK($action, $entity, $role) { ... }
}</code></pre>
<p>Cette fois, <strong>LE</strong> test (oui, il n'y en a plus qu'un! \o/) attends certains <strong>paramètres</strong> qui lui sont fournis par le <strong>data provider</strong> déclaré juste au dessus. Ce comportement est spécifié à PHPUnit via l'utilisation d'une <strong>annotation</strong> toute simple.</p>
<p>De cette manière, le même test sera lancé pour chacune des données renvoyées par notre data provider! :)</p>
<p>Fantastique non?</p>
<p>Moins de code, moins de bugs, moins de maintenance, plus de lisibilité. Bref, que des avantages! :D
Si vous voulez plus d'informations sur les data providers, n'hésitez pas à consulter l'excellente documentation de PHUnit <a href="http://www.phpunit.de/manual/3.5/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers" title="Documentation de PHPUnit: les data providers">ici</a>!</p>
<p>(Et encore une fois, merci <a href="http://twitter.com/#!/futurecat" title="@futurecat">@futurecat</a>! :))</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/la-puissance-des-data-providers-de-phpunit/" rel="alternate" type="text/html" />
    <published>2011-08-08T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:36+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/html5-desactiver-la-validation-de-vos-forms/</id>
    <title>HTML5: Désactiver la validation de vos forms</title>
    <content type="html">
      <![CDATA[<p>Comme vous le savez certainement, les navigateurs récents vous offrent la possibilité de valider vos champs de formulaires pour peu que vous utilisiez <a href="http://www.w3.org/TR/html5/forms.html#forms">les nouvelles directives HTML5</a> dédiées à la validation.</p>
<p>Ces nouveaux attributs vous permettent d'avoir une validation côté client. Votre formulaire ne sera donc pas envoyé si des erreurs sont détectées par le navigateur. Difficile de tester vos messages d'erreurs dans ces conditions!</p>
<p>Heureusement, ces nouveaux attributs ont un maître! Il suffit d'un seul pour les dominer tous: <a href="http://www.w3.org/TR/html5/forms.html#the-form-element">l'atribut <strong>novalidate</strong> de la balise form</a>!</p>
<p>Avec lui, plus de validation côté client! Vous pouvez à nouveau tester vos formulaires et vos messages d'erreurs comme il se doit! (bien entendu, n'oubliez pas de l'enlever une fois vos tests terminés! \o/)</p>
<p>Et on remercie tous <a href="http://twitter.com/#!/futurecat">@futurecat</a> pour cette astuce tout bête qui nous sauve la vie! (c'était <a href="http://twitter.com/#!/futurecat/status/90066313304739840">ici</a>)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/html5-desactiver-la-validation-de-vos-forms/" rel="alternate" type="text/html" />
    <published>2011-08-07T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/admin-generator-objet-string-primary-key/</id>
    <title>Admin generator: Objet ayant un string en primary key</title>
    <content type="html">
      <![CDATA[<p>Petite astuce pour ceux qui seraient amenés a utiliser autre chose un <strong>string</strong> comme <strong>clé primaire</strong> d'une table et qui voudrait tout de même utiliser l'<a title="admin generator" href="http://www.odolbeau.fr/tag/admin-generator">admin generator</a> sans obtenir d'erreur...</p>
<p>Il vous suffit simplement de modifier votre fichier routing.yml de manière à préciser la nature de la clé primaire attendue dans les requirements:</p>
<pre><code class="language-yaml">annonce:
  class: sfPropelRouteCollection
  requirements: { id: \w+ }
  options:
    model: Annonce
    module: annonce
    prefix_path: annonce
    column: id
    with_wildcard_routes: true</code></pre>
<p>N'oubliez pas de changer le champ soumis à ce prérequis si celui-ci ne s'appelle pas &quot;id&quot; comme dans l'exemple! :)</p>
<p>En espérant que ce <a href="http://www.odolbeau.fr/tag/snippet" title="snippet" >snippet </a>puisse vous servir! ;)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/admin-generator-objet-string-primary-key/" rel="alternate" type="text/html" />
    <published>2010-10-18T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:27+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/interview-de-damien-djaouti/</id>
    <title>Interview de Damien DJAOUTI</title>
    <content type="html">
      <![CDATA[<h2 id="presentation">Présentation</h2>
<p>Au cours de la réalisation de mon <a title="Mémoire de recherche: Déroulement" href="http://www.odolbeau.fr/memoire-de-recherche-deroulement">mémoire de recherche</a> sur les solutions <strong>e-learning</strong> dans les <strong>organisations</strong>, j'ai eu la chance d'interviewer <strong>Damien DJAOUTI</strong>, Doctorant en Informatique, au sein des laboratoires IRIT et LARA de l’Université de Toulouse (31).</p>
<p>Développeur et concepteur de <strong>jeux éducatifs</strong> et de <strong>casual games</strong> en parallèle à ses travaux de recherche, il travaille actuellement sur les méthodologies de conception de Serious Games. A la croisée entre ses recherches académiques et ses réalisations professionnelles, il dispense également des cours et formations sur le multimédia et les jeux vidéo.</p>
<p>L'objectif de cet interview était bien sur d'obtenir l'avis d'un expert concernant les <strong>serious games</strong> et leur pénétration sur le marché du <strong>e-learning</strong>.</p>
<h2 id="interview">Interview</h2>
<p><strong>Quels sont les atouts des serious games face aux solutions e-learning existantes?</strong></p>
<p><em>Tout d'abord, peut-être faudrait-il préciser ce que j'entends par &quot;serious game&quot;. En effet, nombreux sont les acteurs de ce secteur, et chacun possède sa propre définition. Pour mes collègues et moi, un serious game se définit comme &quot;tout jeu dont la finalité première est autre que le simple divertissement&quot;. Concrètement, il s'agit d'associer au sein d'un seul logiciel, une dimension utilitaire (serious) et une dimension ludique (game).</em></p>
<p><em>Partant de là, les Serious Games peuvent ensuite viser plusieurs fonctions &quot;utilitaires&quot;. Aujourd'hui nous en recensons trois :</em></p>
<ul>
<li><em>diffuser un message (educatif, informatif, persuasif ou subjectif),</em></li>
<li><em>prodiguer un entrainement (cognitif ou physique)</em></li>
<li><em>favoriser l'échange de données.</em></li>
</ul>
<p><em>Ensuite, ces familles de Serious Games se retrouvent dans un très grand nombre de domaines d'applications (santé, education, défense, publicité, politique...).</em></p>
<p><em>La valeur ajouté d'un Serious Game va donc varier selon la fonction et le marché qu'il vise. Pour celui de l'éducation, une des &quot;valeur ajoutée&quot; que l'on mentionne souvent par rapport à des solutions d'e-learning plus &quot;classique&quot; repose dans l'ajout de la dimension ludique. Celle-ci peut avoir plusieurs avantages : maintien de la motivation des usagers, possibilités d'interaction plus riche, appui sur une culture vidéoludique commune...</em></p>
<p><strong>Les solutions e-learning classiques semblent actuellement rencontrer un succès mitigé auprès des utilisateurs pour diverses raisons (manque de motivation, absence d'encadrement, etc.). De plus, les jeux vidéos en général peuvent parfois pâtir d'une mauvaise image auprès des apprenants. Par conséquent, quelles sont les clés de l'intégration des serious games dans une organisation ?</strong></p>
<p><em>Vaste question qui relève à mon avis plutôt du &quot;cas par cas&quot;. Les freins que l'on peut rencontrer face à l'adoption du Serious Game sont effectivement de plusieurs natures.</em></p>
<ul>
<li><em><strong>Appréhension liée au jeu vidéo.</strong> Celle-ci peut être de deux sortes : une méconnaissance de l'objet qui entraine une crainte des apprenants face à un objet technologique qu'ils ne maitrisent pas. Dans l'industrie du divertissement, la vague du &quot;Casual Game&quot; vise justement à dépasser cette barrière culturelle en proposant des jeux plus facile d'accès, moins chronophage et traitant de thème plus en phase avec un public large. Les concepteurs de Serious Games peuvent donc s'appuyer sur cette population de jeu pour essayer de concevoir des jeux en adéquation avec les connaissances préalables des apprenants (certains découvrant par exemple le jeu vidéo par le biais du Serious Game)
L'autre appréhension est liée à l'image plus ou moins négative que peut avoir le jeu vidéo dans l'inconscient collectif, image résultant de plusieurs facteurs (fossé des générations, évolution historique du jeu vidéo, scandales médiatiques...). Un des pays dans lequel la notion de &quot;jeu vidéo&quot; est la plus péjorative se trouve être les États-Unis. Au début des années 2000, plusieurs instances publiques américaines souhaitaient utiliser le jeu vidéo, mais ne pouvaient le faire directement sous peine de choquer l'opinion publique (Columbine et les débats autour du jeu vidéo qui en ont suivi restant encore dans les mémoires). En 2002, le terme &quot;Serious Game&quot;, dont le concept vient des années 60, a été réactualisé pour le cas du jeu vidéo. Pour pouvoir utiliser du jeu vidéo à des fins &quot;sérieuses&quot; sans être trop gênée par l'image péjorative du jeu vidéo, un nouveau terme a donc été employé. Il s'agit de l'oxymore &quot;Serious Game&quot;. Une fois cette barrière culturelle passée, lorsque le grand public aura assimilé le fait que, à l'image d'un film ou d'un livre, un jeu peut traiter de différent sujets et s'adresser à différents public, nous supposons que ne nous parlerons plus de &quot;Serious Game&quot;, mais tout simplement de jeu vidéo.</em></li>
<li><em><strong>Manque de recul sur le Serious Game.</strong> Comme il s'agit d'un domaine relativement récent et jeune, la place de l'expérimentation est encore grande. Si cela est intéressant sur de nombreux aspects (notamment pour la recherche), pour son utilisation par des institutions publiques ou privées cela représente plutôt un frein. Un des défis des entreprises qui créent des Serious Game est donc d'arriver à convaincre leur clients de tenter l'aventure, alors qu'il existe encore peu d'études de terrain à grande échelle sur leur efficacité et valeur ajoutée. En clair, nous sommes encore dans une phase &quot;montante&quot; du Serious Game.</em></li>
<li><em><strong>Importance des couts.</strong> La réalisation d'un Serious Game peut s'avérer sensiblement plus onéreuse qu'une campagne de communication &quot;classique&quot; ou qu'un simple support de cours interactif. Il faut donc que l'organisation ait un budget à consacrer au Serious Game, et arriver à la convaincre de l'intérêt d'un tel investissement.</em></li>
</ul>
<p><em>Les clés de l'intégration du SG dans une organisation donnée serait donc d'arriver à identifier les blocages ou appréhensions de ladite organisation pour essayer d'y apporter une réponse.</em></p>
<p><strong>Les serious games actuels peuvent ils prétendre remplacer totalement tout autre forme de formation (e-learning ou présentielle)? Cela est-il envisageable à moyen ou long terme?</strong></p>
<p><em>Dans la pratique, le serious game et l'elearning ne sont pas contradictoires et peuvent tout à fait aller de pair. De même, l'utilisation du serious game n'exclue pas la présence d'un enseignant ou formateur, bien au contraire. Si le discours actuel, focalisé sur le &quot;Serious Game&quot; pourrait laisser penser qu'il se suffit à lui-même, dans la pratique il faut le voir comme un outil supplémentaire dans la trousse du pédagogue.</em></p>
<p><em>Après, tout cela est modulable selon l'intention et le public visée par le concepteur d'un Serious Game. S'il y a effectivement des cas ou le Serious Game peut être envisagé comme un outil autonome (publicité notamment). Mais pour le contexte de l'éducation je pense qu'il faut vraiment le voir comme un complément aux modes de formations existants. A mes yeux, les exemples les plus intéressants de jeux vidéo pour l'éducation sont ceux utilisés en classe avec un professeur (ou en stage avec un formateur), car celui-ci est le plus à même d'assurer la qualité du savoir transmis, mais peut aussi aider à contextualiser le Serious Game, aspect fondamental pour quiconque souhaite utiliser un jeu en classe. D'ailleurs, nombre de Serious Game destiné au secteur de l'éducation, tels que Lure of the Labyrinth (<a href="http://serious.gameclassification.com/11511" rel="noopener noreferrer">http://serious.gameclassification.com/11511</a>) et Stop Disasters! (<a href="http://serious.gameclassification.com/1334" rel="noopener noreferrer">http://serious.gameclassification.com/1334</a>), sont fournis avec des fiches pédagogiques à destination des enseignants, qui leur proposent de nombreuses pistes d'utilisations.</em></p>
<p><strong>Quelles sont les difficultés généralement rencontrées dans la gestion du changement de solutions e-learning?</strong></p>
<p><em>Un des gros défis des acteurs du e-learning qui se tournent vers leSerious Game est d'arriver à mélanger efficacement la dimension ludique sans pour autant perdre leur savoir faire sur la partie sérieuse. Par exemple, pour l'éducation, les notions de suivi des apprenants, de leur évaluation et de la manière dont est validé une transmission de connaissance par l'intermédiaire du jeu font partie des gros défis de conception actuels.</em></p>
<p><strong><em>N'hésitez pas à rajouter ce que vous voulez, ces questions ne sont que des pistes de réflexion.</em></strong></p>
<p><em>Il ne s'agit là que de réponses &quot;générales&quot; et rapides qui n'engagent que moi. Si le sujet des Serious Game vous intéresse, je me permet de vous signaler qu'avec mon collègue Julian Alvarez, nous allons bientôt publier un ouvrage &quot;Introduction au Serious Game&quot;. Après une discussion sur sa définition et un passage en revue de l'état actuel du marché (avec de nombreux exemples), nous revenons sur l'histoire du Serious Game. Le dernier chapitre sera surement susceptible de vous intéresser, car nous y avons interviewé une trentaine d'acteurs impliqués dans le Serious Game (créateurs, enseignants, formateurs, consultants...) pour qu'il nous exposent leur vision du serious game, et les avantages/inconvénients qu'ils y associent.</em></p>
<p><em><a href="http://livre.fnac.com/a2843459/Julian-Alvarez-Introduction-au-Serious-game"><a href="http://livre.fnac.com/a2843459/Julian-Alvarez-Introduction-au-Serious-game" rel="noopener noreferrer">http://livre.fnac.com/a2843459/Julian-Alvarez-Introduction-au-Serious-game</a></a></em></p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/interview-de-damien-djaouti/" rel="alternate" type="text/html" />
    <published>2010-06-23T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/beaucoup-de-changements/</id>
    <title>Beaucoup de changements</title>
    <content type="html">
      <![CDATA[<p>Il fait beau, les oiseaux chantent et l'été approche (d'accord, encore un peu de patience tout de même!). Bref, la saison est propice au <strong>changement</strong> donc, en avant!</p>
<h2 id="nouvelles-parutions">Nouvelles parutions</h2>
<p>Jusqu'à présent, le <strong>nombre d'articles</strong> sur mon blog reste assez <strong>restreint</strong>, principalement par <strong>manque de temps</strong> (et de motivation peut-être?). Quoi qu'il en soit, j'espère inverser la tendance et <strong>augmenter la fréquence de parution</strong>! Bien entendu, cela ne se fera pas au dépens de la <strong>qualité</strong> (mais je vous laisse les seuls juges concernant ce point!)</p>
<p>Les <strong>thématiques</strong> resteront les mêmes et tourneront pour la plupart autour du <strong>développement</strong> (et plus particulièrement du développement web en <strong><a title="Tous les articles concernant PHP" href="http://www.odolbeau.fr/tag/php">PHP</a></strong> avec le framework <strong><a title="Tous les articles concernant Symfony" href="http://www.odolbeau.fr/tag/symfony">Symfony</a></strong>) mais je vous parlerais aussi de l'avancement de mon <strong><a title="Tous les articles relatifs à mon mémoire de recherche" href="http://www.odolbeau.fr/tag/memoire-de-recherche">mémoire de recherche</a></strong> sur les solutions e-learning dans les organisations et des divers autres sujets qui attireront mon attention au fil des jours!</p>
<div class="img-container-medium alignright">
    <img title="Ancien design du blog" src="/images/posts/2010-05-19/ancien-design.png" alt="Ancien design du blog" width="235" height="235">
    <p class="legend">
        Ancien design du blog
    </p>
</div>
<h2 id="nouveau-design">Nouveau design</h2>
<p>Comme vous vous en êtes aperçus (du moins je l'espère!) le site a subi un relooking intégral! Oui, mais pourquoi?</p>
<h3 id="les-gouts-et-les-couleurs">Les gouts et les couleurs...</h3>
<p>... ne se discutent pas! Lassitude? Besoin de changement? Qu'importe, l'ancien design ne me convenait plus, place au nouveau!</p>
<h3 id="oui-je-suis-auto-entrepreneur">Oui, je suis auto-entrepreneur!</h3>
<p>Très (trop?) orienté blog, l'ancien design ne mettait visiblement pas suffisamment en avant mon statut d'auto-entrepreneur! J'espère que cette fois ce défaut a été supprimé et je vous invite d'ailleurs à me contacter via le <a title="Contact" href="http://www.odolbeau.fr/contact">formulaire prévu à cet effet</a> si toutefois mon profil vous intéresse.</p>
<h2 id="le-mot-de-la-fin">Le mot de la fin</h2>
<p>Je finirai par une phrase un peu bateau sur les blogs mais cependant toujours aussi réelle: <strong>Votre avis m'intéresse!</strong></p>
<p>N'hésitez surtout pas à <strong>réagir</strong> à cet article dans les <strong>commentaires</strong>! Vos avis (notamment sur le design, la nouvelle organisation du blog, etc.) seront les bienvenus!</p>
<p>En vous remerciant! :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/beaucoup-de-changements/" rel="alternate" type="text/html" />
    <published>2010-05-19T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/le-mind-mapping/</id>
    <title>Le mind mapping.</title>
    <content type="html">
      <![CDATA[<p>Une <strong>mind map</strong> (carte heuristique en français) est un <strong>diagramme</strong> de plus en plus utilisé. Plan de document, représentation d'idées diverses, brainstorming, les mind map sont aujourd'hui utilisées à toute les sauces. Les clés du succès? Facilité de réalisation, lisibilité,  clarté!</p>
<p>Selon <a href="http://fr.wikipedia.org/wiki/Carte_heuristique" title="Définition d&#039;une carte heuristique sur wikipedia" rel="noopener noreferrer">Wikipedia</a>:</p>
<blockquote>
<p>Une carte heuristique (mind map en anglais), également appelée carte des idées, carte conceptuelle, schema de pensée, carte mentale, arbre à idées ou topogramme, est un diagramme qui représente lesconnexions sémantiques entre différentes idées, les liens hiérarchiques entre différents concepts intellectuels. Au contraire du schéma conceptuel (concept map en anglais)1, la carte heuristique est le plus souvent une représentation arborescente des données.</p>
</blockquote>
<p>Concrètement, une carte heuristique est une <strong>représentation hiérarchique d'une idée ou d'un concept</strong>. Son organisation permet une <strong>facilité de lecture</strong> étonnante.</p>
<p>A titre d'exemple, voici la mind map que j'ai réalisée dans le but de définir le schéma conducteur qui me permettra de réaliser l'analyse de l'existant concernant l'intégration des solutions e-learning dans les organisations (sujet de mon <a href="http://www.odolbeau.fr/memoire-de-recherche-deroulement" title="Déroulement du mémoire de recherche" rel="noopener noreferrer">mémoire de recherche</a>).</p>
<div class="img-container-full">
    <img class="size-large wp-image-428 " title="Exemple de carte heuristique" src="/images/posts/2010-01-04/carte-heuristique.png" alt="Exemple de carte heuristique" width="940" height="437">
    <p class="legend">Exemple de carte heuristique</p>
</div>
<p>Cette <strong>carte heuristique</strong> a été réalisée avec <a href="https://www.mindjet.com/products/mindmanager-8-win/overview?google_sou=mindmanager&amp;gclid=CMTxiIrwip8CFeZr4wodzVYzJw" title="MindManager" rel="noopener noreferrer">MindManager</a> de <a href="https://www.mindjet.com/" title="Mindjet" rel="noopener noreferrer">Mindjet</a>, un outil (payant mais avec période d'essai) que je ne peux que vous recommander! Très simple à utiliser, de nombreuses options sont intégrées (export vers PDF, image, flash, page web,  document word, gestion des taches, etc.). Et comme vous pouvez le voir, le résultat est plutôt pas mal du tout! :)</p>
<p>D'autres outils sont bien entendu disponible (<a href="http://freemind.sourceforge.net/" title="FreeMind" rel="noopener noreferrer">FreeMind</a>ou <a href="http://www.mindmeister.com" title="MindMeister" rel="noopener noreferrer">MindMeister</a> pour ne citer qu'eux). Les cartes heuristiques sont également un excellent outil pour faciliter la <strong>gestion d'un projet</strong> ou pour <strong>réaliser un travail collaboratif</strong>. N'hésitez pas à tester! :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/le-mind-mapping/" rel="alternate" type="text/html" />
    <published>2010-01-04T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/quand-nos-hommes-politiques-parlent-dinternet/</id>
    <title>Quand nos hommes politiques parlent d&#039;Internet...</title>
    <content type="html">
      <![CDATA[<p><strong>Internet</strong> occupe une place de plus en plus importante dans notre vie et de plus en plus de d'<strong>hommes politiques</strong> expriment leur opinion sur le sujet.</p>
<p>En internautes avisés que vous êtes, vous avez forcément entendu parler d'<a title="Loi création et internet sur Wikipedia" href="http://fr.wikipedia.org/wiki/Loi_Cr%C3%A9ation_et_Internet">HADOPI</a>, de <a title="Loi DADVSI sur Wikipedia" href="http://fr.wikipedia.org/wiki/Loi_DADVSI">DADVSI</a>, de <a title="LOPPSI sur Wikipedia" href="http://fr.wikipedia.org/wiki/LOPPSI">LOPPSI </a>(ou LOPPSI 2) ou encore des pare-feux disponibles dans Open Office.</p>
<p>Plus récemment, Jacques Myard affirme qu'il est nécessaire de <strong>nationaliser Internet</strong> en s'inspirant du <strong>modèle chinois</strong> (plus d'infos sur son <a title="Site officiel de Jacques Myard" href="http://www.jacques-myard.org/">site officiel</a> -&gt; Dans les médias -&gt; Communiqués). Je vous conseille d'ailleurs de lire la <a title="Réaction de Benjamin Bayard suite au communiqué de presse de Jacques Myard" href="http://blog.fdn.fr/post/2009/12/18/Il-faut-r%C3%A9pondre-%C3%A0-Jacques-Myard">réaction de Benjamin Bayard</a>, président de <a title="Site du FDN" href="http://www.fdn.fr/">FDN</a>.</p>
<div class="img-container-medium alignright">
    <img title="Logo du Parti Pirate" src="/images/posts/2009-12-21/logo-parti-pirate.png" alt="Logo du Parti Pirate" width="230" height="94">
    <p class="legend">Logo du parti pirate français</p>
</div>
<p>Bref, <strong>Internet</strong> s'invite peu à peu dans la <strong>vie politique française</strong>, pour le meilleur mais surtout (pour le moment du moins), pour le pire. En effet, lorsqu'on entend ce qu'on entend et qu'on voit ce qu'on voit, on s'interroge! Que nos représentants ne soient pas des <strong>experts informatiques</strong>, je le conçois. Mais dans ce cas, <strong>pourquoi donner leur avis sur des sujets qu'ils ne maitrisent absolument pas</strong> avec, comme seul effet, de se <strong>discréditer</strong> au yeux d'internautes sensibles à la question? D'autant plus qu'un tel comportement de la part de nos hommes politiques ne se limite peut-être pas au domaine informatique...</p>
<p>J'en profite pour vous rappeler que le <strong>Parti Pirate</strong> <strong>présentera une liste en Ile-de-France lors des prochaines</strong> <strong>élections régionales</strong> (qui se dérouleront les 14 et 21 mars 2010). En faveur du <em>&quot;partage de la culture et des connaissances&quot;</em>, le <em>&quot;projet de programme coopératif&quot;</em> du PP <em>&quot;concerne toutes les libertés fondamentales en relation avec les nouvelles technologies&quot;</em>.</p>
<p>Je ne peux que vous encourager à visiter leur <a title="Site officiel du parti pirate français" href="http://www.partipirate.org/">site officiel</a> et notamment leur <a title="FAQ du parti pirate français" href="http://www.partipirate.org/faq/">FAQ</a> dans laquelle vous trouverez nombre d'informations concernant le parti, son but et ses objectifs.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/quand-nos-hommes-politiques-parlent-dinternet/" rel="alternate" type="text/html" />
    <published>2009-12-21T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/svn-definition-et-enjeux/</id>
    <title>SVN: Définition et enjeux</title>
    <content type="html">
      <![CDATA[<p>Ou comment un serveur SVN (Subversion) peut vous <strong>simplifier le développement</strong> d'un projet collaboratif ou personnel.</p>
<p>J'ai très récemment été surpris lorsque, entouré de six étudiants en <strong>troisième année d'informatique</strong>, ces derniers m'ont avoué de pas savoir ce qu'était un SVN. Voici donc un article qui je l'espère convaincra les plus indécis d'utiliser un SVN pour <strong>tous leurs projets</strong>.</p>
<h2 id="qu-est-ce-qu-un-svn">Qu'est-ce qu'un SVN?</h2>
<p>D'abord, qu'est-ce qu'un SVN? Rien de plus simple, il s'agit d'un <strong>gestionnaire de versions</strong>. Pour être plus précis, il s'agit du système de gestion de versions qui succède à <strong>CVS</strong>. Mais fini le cours d'histoire. Concrètement, à quoi ça sert? Comment ça marche? Pourquoi je m'en sers?</p>
<h2 id="a-quoi-ca-sert">A quoi ça sert?</h2>
<p>Et bien ça sert à <strong>gérer les différentes versions des fichiers d'un projet</strong>, tout simplement! :) Cette <strong>sauvegarde</strong> permet de récupérer les anciennes versions d'un fichier, de le restaurer si ce dernier à été supprimé, de le récupérer s'il a été perdu. Bref, sur un serveur SVN, Vous aurez toutes les versions de votre projet que vous aurez envoyez.</p>
<p>Mais là ou l'utilisation d'une serveur SVN prend tout son intérêt, c'est dans le cadre d'un <strong>projet &quot;collaboratif&quot;</strong>. Vous êtes plus de deux à développer un projet? L'utilisation d'un serveur SVN vous permettra de travailler sur la même version de développement, sans vous envoyer les modifications par mail, par clé USB ou par je ne sais quel autre moyen existant! :)</p>
<h2 id="comment-ca-marche">Comment ça marche?</h2>
<p>Très simplement. Il vous faut un serveur SVN et un client SVN. Pour le serveur, vous pouvez bien sur <strong>l'héberger vous-même</strong> sur votre bon vieux linux. Vous pouvez également faire appel au service de <strong>tiers spécialisés</strong> dans ce domaine (ou dans d'autres). Si votre projet est <strong>libre de droits</strong>, vous avez bien entendu <a title="SourceForge" href="http://sourceforge.net/">Sourceforge </a>ou encore <a title="GoogleCode" href="http://code.google.com/intl/fr/">Google Code</a>. Si ce n'est pas le cas, vous pouvez utiliser <a title="XP-Dev" href="http://www.xp-dev.com/">XP-Dev</a> (gratuit et marche parfaitement) ou <a title="Assembla" href="http://www.assembla.com/">Assembla </a>(période d'essai gratuite, non testé mais semble avoir de bons retours).</p>
<div class="img-container-medium alignright">
    <img title="TortoiseSVN intégré dans l'explorateur" src="/images/posts/2009-12-03/TortoiseSVN-integration-navigateur.png" alt="TortoiseSVN intégré dans l'explorateur" width="230" height="315">
    <p class="legend">TortoiseSVN intégré dans l'explorateur</p>
</div>
<p>Je ne m'attarderai pas sur l'installation du serveur SVN, de nombreux tutos sont disponibles sur le net.</p>
<p>Concernant le logiciel <strong>SVN client</strong> (qui sera donc installé en local) de nombreuses solutions sont encore une fois disponibles. Certainement la plus connue d'entre elles (pour ceux qui utilisent Windows), <a title="TortoiseSVN" href="http://tortoisesvn.tigris.org/">TortoiseSVN</a> est très complet. Une fois installé, TortoiseSVN sera directement <strong>intégré dans l'explorateur</strong>.</p>
<p>Une fois que tout est installé correctement, le fonctionnement est très simple. Lors de la première utilisation, vous devrez faire un <strong>Checkout</strong>, qui vous permettra de récupérer toutes les données présentes sur le SVN et de lier un dossier local au serveur SVN. Par la suite, deux commandes vont seront utiles: <strong>SVN Update</strong> et <strong>SVN Commit</strong>.</p>
<p>La commande <strong>SVN Update mettra à jour votre copie locale du projet</strong> avec celle du serveur SVN.  Cela vous permettra de récupérer les éventuelles modifications faites par vos collaborateurs.</p>
<p>La commande <strong>SVN Commit vous permettra d'envoyer vos modifications sur le serveur SVN</strong>, afin que ces dernières soient accessibles par vos collaborateurs.</p>
<h2 id="pourquoi-je-m-en-sers">Pourquoi je m'en sers?</h2>
<p>Et bien pour toutes les raisons citées précédemment. Dans le cadre d'un <strong>projet collaboratif</strong>, disposer d'un serveur SVN vous épargne tous les habituels transferts de fichier entre les différents participants et vous fait <strong>gagner un temps précieux</strong>.</p>
<p>Dans le cadre d'un <strong>projet personnel</strong>, l'utilisation d'un serveur SVN permet d'avoir continuellement une sauvegarde à jour de votre projet mais vous autorise également à <strong>travailler sur votre projet depuis différents PC</strong>.</p>
<p>Enfin, bien que ce sujet n'ai pas été abordé dans cet article, vous pourrez installer <strong>d'autres solutions</strong> sur votre serveur SVN. Ainsi l'installation de <a title="TRAC" href="http://trac.edgewall.org/">TRAC </a>par exemple, vous permettra de <strong>gérer votre projet</strong> grâce à différents outils tels qu'un <strong>wiki</strong>, un <strong>gestionnaire de bugs</strong>, un <strong>explorateur Subversion</strong>, etc.</p>
<p>Voilà, ce rapide aperçu des avantages d'un serveur SVN s'achève ici. Je ne peux que vous encourager une fois de plus à utiliser un serveur SVN (si ce n'est déjà le cas) pour <strong>tous vos projets</strong>, collaboratifs ou personnels, que vous travailliez sur une ou plusieurs machines, que vous en ayez ou non envie.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/svn-definition-et-enjeux/" rel="alternate" type="text/html" />
    <published>2009-12-03T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/memoire-de-recherche-deroulement/</id>
    <title>Mémoire de recherche: Déroulement</title>
    <content type="html">
      <![CDATA[<p>Ce mémoire sera a réaliser au cours des huit prochains mois. La version finale doit être rendu le 15 juillet 2010 peu avant la soutenance orale qui se déroulera au cours du mois de septembre.</p>
<p>Ce <strong>mémoire de recherche</strong> qui me permettra (si tout se passe bien) d'obtenir le diplôme <a href="http://www.itin.fr/itin/detail/Fiche_formation?item_id=279758" title="Descriptif du diplome M2IRT à l&#039;ITIN" rel="noopener noreferrer">M2IRT</a> (Bac +5) à l'<a href="http://www.itin.fr/itin" title="Site de l&#039;ITIN" rel="noopener noreferrer">ITIN</a> portera sur <strong>les solutions e-learning dans les organisations</strong>.</p>
<p>Différents jalons imposés par l'école viendront rythmer les mois à venir au cours desquels je l'espère, <strong>les solutions e-learning dans les organisations</strong> n'auront plus aucun secret pour moi. Loin d'être un simple exposé sur un sujet distribué aléatoirement, ce mémoire devra être, je cite, &quot;<em>une oeuvre originale et unique</em>&quot; au sein duquel l'auteur présentera &quot;<em>le résultat de ses recherches et découvertes</em>&quot; avant de &quot;<em>défendre ses conclusions et les solutions pratiques proposées</em>&quot;.</p>
<p>Pour mener à bien ce projet, chaque étudiant pourra compter sur le soutien de son <strong>directeur de mémoire</strong>, qui n'est autre que la personne ayant proposé le sujet choisi.</p>
<p>En ce qui me concerne, j'aurais la chance de collaborer avec <a href="http://sites.google.com/site/margaridaromero/" title="Margarida Romero" rel="noopener noreferrer">Margarida Romero</a>, chercheuse et consultante en gestion du changement en formation et e-learning (que vous pouvez suivre sur <a href="http://twitter.com/margaridaromero" title="Profil Twitter de Margarida Romero" rel="noopener noreferrer">Twitter</a>. Pour ceux que l'espagnol et l'anglais ne rebutent pas, vous pouvez également visiter <a href="http://margarida-romero.blogspot.com/" title="Blog de Margarida Romero" rel="noopener noreferrer">son blog</a>.</p>
<p>Le <strong>premier livrable</strong> de ce mémoire est un document de <strong>définition du sujet</strong> de quatres pages, structuré de la manière suivante:</p>
<ul>
<li>Définition du sujet</li>
<li>La question fondamentale</li>
<li>Le contexte, l’environnement (scientifique, technique, marché…)</li>
<li>Quelles seront les méthodes de recherche et de résolution des problèmes utilisées</li>
</ul>
<p>L'heure est donc à la recherche d'informations concernant les solutions e-learning, en espérant pouvoir vous faire une présentation succincte de ces dernières dans un prochain article! :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/memoire-de-recherche-deroulement/" rel="alternate" type="text/html" />
    <published>2009-11-20T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/gerer-plusieurs-sites-virtuals-hosts-apache/</id>
    <title>Gérer plusieurs sites avec les virtuals hosts d&#039;Apache</title>
    <content type="html">
      <![CDATA[<p>Vous connaissez certainement déjà les <a href="http://httpd.apache.org/docs/2.0/mod/mod_alias.html#alias" title="Apache mod_alias" rel="noopener noreferrer">alias</a>, qui permettent de <strong>gérer plusieurs sites</strong> et qui sont très simple à mettre en place. Mais peut-être n'avez-vous jamais entendu parler des <a href="http://httpd.apache.org/docs/2.2/en/vhosts/" title="Documentation des virtuals hosts d&#039;Apache" rel="noopener noreferrer">virtuals hosts d'Apache</a>.</p>
<p>Pour simplifier, les alias permettent <strong>uniquement</strong> de créer un <strong>&quot;lien symbolique&quot;</strong> entre un dossier choisi (par exemple le dossier contenant toutes le sources de phpmyadmin) et votre <strong>serveur web</strong>. Une fois votre alias créé, il vous suffit d'appeler l'URL que vous auriez appelé si le dossier choisi se trouvait <strong>physiquement</strong> à la racine de votre serveur web. (<em><a href="http://localhost/phpmyadmin" rel="noopener noreferrer">http://localhost/phpmyadmin</a></em>).</p>
<p>Avec les <strong>virtuals hosts</strong>, le fonctionnement est fondamentalement différent. Ici, le but est d'avoir <strong>plusieurs racines</strong> de sites web avec <strong>un seul et unique serveur apache</strong>. L'intérêt? Pouvoir gérer divers sites <strong>indépendamment</strong> les uns des autres, ce qui n'est pas le cas en utilisant les <strong>alias</strong>, puisque cette solution n'utilise qu'<strong>une seule racine</strong>.</p>
<p>Maintenant que vous êtes convaincu de l'intérêt des virtuals hosts (du moins je l'espère), passons à la <strong>configuration d'apache</strong>! :)</p>
<p>La configuration des virtuals hosts se fera dans le fichier <code>httpd-vhosts.conf</code> (dossier <code>/conf/extra</code> du répertoire d'installation d'apache). Mais avant de commencer, il faut vérfier que ce fichier est bien appelé par le <strong>fichier de configuration principal</strong>, à savoir <code>httpd.conf</code> (dossier <code>/conf</code> du répertoire d'installation d'apache).</p>
<p>Ouvrez donc ce fichier et vérifiez que la ligne d'include de <code>httpd-vhosts.conf</code> est bien décommentée:</p>
<pre><code class="language-apacheconf"># Virtual hosts
Include conf/extra/httpd-vhosts.conf</code></pre>
<p>Maintenant, on commence les choses sérieuses en ouvrant le-dit fichier <code>httpd-vhosts.conf</code>.</p>
<p>Vous pouvez commencer par supprimer les exemples avant d'ajouter cette ligne <strong>INDISPENSABLE</strong> à votre fichier:</p>
<pre><code class="language-apacheconf">NameVirtualHost *:80</code></pre>
<p>Ensuite, voici la configuration pour un virtual host pour le site &quot;monSiteWeb&quot;:</p>
<pre><code class="language-apacheconf">&lt;VirtualHost *:80&gt;
    DocumentRoot "c:/racine/de/monSiteWeb/"
    ServerName local.monsiteweb.fr
    ServerAdmin webmaster@monsiteweb.fr
    ErrorLog logs/local.monsiteweb.fr-error_log
    TransferLog logs/local.monsiteweb.fr-access_log
&lt;/VirtualHost&gt;</code></pre>
<p>Quelques explications:</p>
<ul>
<li><strong>DocumentRoot:</strong> Le chemin d'accès à la racine de votre projet</li>
<li><strong>ServerName:</strong> Le nom qui sera utilisé pour accéder au site. Vous pouvez mettre ce que vous souhaitez! Personnellement, j'ai pris l'habitude de mettre &quot;local.&quot; suivi du nom du site afin d'être certain que cette URL est bien locale.</li>
<li><strong>ServerAdmin <em>(facultatif)</em>:</strong> L'adresse mail de l'administrateur du site.</li>
<li><strong>ErrorLog <em>(facultatif)</em>:</strong> le fichier dans lequel seront stockés les logs d'erreurs de votre site.</li>
<li><strong>TransferLog <em>(facultatif)</em>:</strong> le fichier dans lequel seront stockés les logs d'accès de votre site.</li>
<li>D'autres paramètres sont bien entendu disponibles. je vous laisse vous référer à la <a href="http://httpd.apache.org/docs/2.2/en/vhosts/" title="Documentation officiel des virtuals hosts d&#039;Apache" rel="noopener noreferrer">documentation officielle</a> si besoin est.</li>
</ul>
<p><strong>Redémarrez</strong> maitenant votre <strong>serveur apache</strong> afin que les changements soient pris en compte.</p>
<p>Votre virtual host est maintenant fonctionnel! Dernière manipulation avant de pouvoir y accéder, <strong>mettre à jour</strong> votre fichier <strong>hosts</strong>!</p>
<p>Ce dernier se trouve:</p>
<ul>
<li>pour Windows: <code>C:\WINDOWS\system32\drivers\etc\hosts</code></li>
<li>pour Linux: <code>/etc/hosts</code></li>
</ul>
<p>Ajoutez la ligne suivante:</p>
<pre><code class="language-apacheconf">127.0.0.1     local.monsiteweb.fr</code></pre>
<p>Cela signifie que <strong>toutes</strong> les requêtes adressées à <code>local.monsiteweb.fr</code> seront transférées au serveur <code>127.0.0.1</code>, c'est à dire <strong>VOTRE serveur</strong>.</p>
<p>Vous devriez maintenant pouvoir tester votre configuration en vous rendant à l'adresse suivante: <em><a href="http://local.monsiteweb.fr" rel="noopener noreferrer">http://local.monsiteweb.fr</a></em></p>
<p>Si vous avez une erreur 404, assurez-vous que votre navigateur a bien pris en compte les modifications effectuées dans le fichier hosts. Pour cela effacer le <strong>cache</strong> et rédemarrez-le.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/gerer-plusieurs-sites-virtuals-hosts-apache/" rel="alternate" type="text/html" />
    <published>2009-09-11T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:32+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/referentiel-regions-departements-villes-de-france/</id>
    <title>Référentiel des régions, départements et villes de France</title>
    <content type="html">
      <![CDATA[<p>Tout formulaire d'inscription ou presque impose la saisie d'une adresse qui devra être sévèrement contrôlée afin d'éviter les informations erronées.</p>
<p>Une solution simple pour s'éviter une partie de ces contrôles rébarbatifs (et bien souvent faillibles): obliger l'utilisateur à <strong>sélectionner la ville</strong> qu'il souhaite dans une liste déroulante. Et la difficulté principale liée à la mise en place d'un système semblable n'est bien sur pas d'ordre technique.</p>
<p>En effet, la première étape consiste à trouver un <strong>référentiel</strong> complet des communes françaises. Et c'est là que les choses se compliquent. Les nomenclatures officielles semblent rares et sont généralement peu complètes.</p>
<p>A ce jour, le référentiel le plus complet que je suis parvenu a trouvé est celui de l'<strong>INSEE</strong> (disponible <a href="http://www.insee.fr/fr/methodes/nomenclatures/cog/telechargement.asp" title="Nomclatures INSEE" rel="noopener noreferrer">ici</a> au format txt et mbf). Il contient la liste complète des communes, cantons, arrondissements, départements et régions de France au 1^er^ janvier 2009 (les référentiels des années précédentes sont également disponibles sur la même page). Malheureusement, ce référentiel ne contient pas les <strong>code postaux</strong> des communes.</p>
<p>Je me suis personnellement limité aux régions, départements et communes, jugeant les cantons et les arrondissements d'un intérêt limité. J'ai également supprimé de nombreuses informations pour ne garder que l'essentiel, c'est à dire pour les communes: le nom, le département et la région.</p>
<p>Voici donc le <a href="/uploads/posts/2009-09-06/script.sql" title="Script SQL des régions, départements et communes de France">script SQL</a> (généré par phpmyadmin) qui vous permettra de créer les trois tables (régions, départements et communes) puis de les remplir. Vous pouvez également récupérer l'<a href="/uploads/posts/2009-09-06/script.zip" title="Archive Script SQL des régions départements et communes de France">archive (zip)</a> contenant ce fichier.</p>
<p>Petit bonus pour celles et ceux qui utilisent <strong>Symfony</strong>, voici les 3 fichiers <strong>fixtures</strong> (pour Propel uniquement): <a href="/uploads/posts/2009-09-06/0100-region.yml" title="Fixtures Propel d&#039;insertion des régions">régions</a>, <a href="/uploads/posts/2009-09-06/0200-department.yml" title="Fixtures Propel d&#039;insertion des départements">départements</a> et <a href="/uploads/posts/2009-09-06/0300-city.yml" title="Fixtures Propel d&#039;insertion des communes">communes</a>. Vous pouvez également récupérer l'<a href="/uploads/posts/2009-09-06/fixtures.zip" title="Archive contenant les fixtures Propel d&#039;insertion des régions, départements et communes de France">archive (zip)</a> contenant ces trois fichiers.</p>
<p>Il ne vous reste plus qu'à créer vos listes déroulantes! :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/referentiel-regions-departements-villes-de-france/" rel="alternate" type="text/html" />
    <published>2009-09-06T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/templates-gratuits/</id>
    <title>Templates gratuits</title>
    <content type="html">
      <![CDATA[<p>Parce que faire un design sympa soit-même, ce n'est pas forcément le plus rapide et certainement pas le plus facile, voici quelques bonnes adresses pour trouver des templates gratuits! :)</p>
<h2 id="sites-specialises">Sites spécialisés</h2>
<ul>
<li><a href="http://www.oswd.org" title="Open Source Web Design - OSWD" rel="noopener noreferrer">Open Source Web Design - OSWD</a>:
Certainement l'un des plus connus, vous y trouverez plus de 2000 templates gratuits. La qualité de la plupart d'entre eux laisse à désirer mais vous devrez néanmoins pouvoir y trouver quelques designs d'excellente qualité. Attention, le site propose également quelques designs payants et ne permet plus l'ajout de nouveaux designs depuis 2007.</li>
<li><a href="http://openwebdesign.org" title="OpenWebDesign" rel="noopener noreferrer">OpenWebDesign</a>:
Fork de OSWD créé en 2005 il semble prendre le pas sur son ainé. Ce sont pas moins de 3700 designs qui vous attendent (dont la plupart de ceux d'OSWD). Encore une fois la qualité des templates dépend très fortement de leurs auteurs.</li>
<li><a href="http://www.opendesigns.org/" title="OpenDesigns" rel="noopener noreferrer">OpenDesigns</a>:
Un site de plus en plus connu et pour cause: plus de 1300 designs disponibles et généralement de qualité bien meilleure que sur OSWD et OpenWebDesign. L'outil de recherche laisse malheureusement à désirer et vous oblige à parcourir les nombreuses pages du site pour trouver votre bonheur.</li>
<li><a href="http://www.oswt.co.uk/index.html" title="Open Source Web Templates - OSWT" rel="noopener noreferrer">Open Source Web Templates - OSWT</a>:
Un peu plus de 250 designs de qualité hétérogène pour ce site peu intuitif. Open Source Web Templates a au moins le mérite d'intégrer un moteur de recherche plus ou moins fonctionnel ce qui n'est pas tout le temps le cas de ses concurrents.</li>
<li><a href="http://www.solucija.com" title="Solucija" rel="noopener noreferrer">Solucija</a>:
Très peu connu et contenant un nombre restreints de designs gratuits (à peine plus de 30), il mérite cependant sa place dans cette liste en raison de la qualité des designs proposés. Ces derniers sont en effet très bien réalisés et facilement modifiables.</li>
</ul>
<h2 id="solutions-alternatives">Solutions alternatives</h2>
<p>Certains graphistes / web agency n'hésitent pas à mettre à la disposition de tous quelques designs d'excellente qualité sur leur site (généralement pour promouvoir leurs designs payants).
Citons par exemple:</p>
<ul>
<li><a href="http://andreasviklund.com/templates/" title="Free websites templates - Andreas Viklund" rel="noopener noreferrer">Andreas Viklund</a></li>
<li><a href="http://fullahead.org/index.php/work/all" title="FullaHead" rel="noopener noreferrer">FullaHead</a></li>
<li><a href="http://templates.arcsin.se/category/website-templates/" title="Arcsin - Free websites templates" rel="noopener noreferrer">Arcsin</a></li>
<li><a href="http://www.csstemplateheaven.com/free-css-templates/" title="CssTemplateHeaven" rel="noopener noreferrer">CssTemplateHeaven</a></li>
<li><a href="http://www.sixshootermedia.com/shop/ozone/" title="Six shooter media" rel="noopener noreferrer">Six shooter media</a></li>
<li>...</li>
</ul>
<p>N'hésitez pas à me signaler d'autres sites! :)</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/templates-gratuits/" rel="alternate" type="text/html" />
    <published>2009-08-26T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/quelques-cheatsheets-symfony/</id>
    <title>Quelques cheatsheets Symfony...</title>
    <content type="html">
      <![CDATA[<p>Vous connaissez certainement les <a href="http://fr.wikipedia.org/wiki/Cheatsheet" title="Cheatsheets sur wikipedia" rel="noopener noreferrer">cheatsheets</a> (traduisez antisèches).</p>
<p>Et bien en voici quelques unes pour symfony! :)</p>
<ul>
<li>
<p><strong>Directory Structure and CLI</strong> par <a href="http://andreiabohner.wordpress.com/2007/03/03/symfony-cheat-sheet-estrutura-de-diretorio-e-cli-linha-de-comando/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Un rappel de la structure par défaut des répertoires dans Symfony ainsi qu'un brève description de la plupart des commandes <a href="http://fr.wikipedia.org/wiki/Command-line_interface" title="Définition de CLI sur Wikipedia" rel="noopener noreferrer">CLI</a> existantes. A consulter impérativement et à garder sous la main (pour les commandes)!
<a href="/uploads/posts/2009-08-24/directory-structure-and-cli-en.pdf">Télécharger le PDF (en)</a> - <a href="/uploads/posts/2009-08-24/directory-structure-and-cli-fr.pdf">Télécharger le PDF (fr)</a></p>
</li>
<li>
<p><strong>View</strong> par <a href="http://andreiabohner.wordpress.com/2007/08/06/symfony-cheat-sheet-view/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Récapitulatif des différents éléments liés aux vues. Localisations, templates, variables, configuration, helpers, etc. Peu intéressant.
<a href="/uploads/posts/2009-08-24/view-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>View: Partials, Components, Slots and Component Slots</strong> par <a href="http://andreiabohner.wordpress.com/2007/08/12/dry-partials-components-slots-e-component-slots-do-symfony/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Résumé plutôt complet permettant d'avoir un aperçu des possibilités / rôles des partials, components, slots et component-slots. Très utile à titre de rappel.
<a href="/uploads/posts/2009-08-24/view2-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Form Helpers</strong> par <a href="http://andreiabohner.wordpress.com/2007/06/28/symfony-cheat-sheet-form-helpers/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Cheat sheet bien réalisée qui reprend l'ensemble des Form Helpers avec pour certain une brève description. Complet et clair. Permet d'avoir la liste des helpers existants lors de la construction d'un formulaire.
<a href="/uploads/posts/2009-08-24/form-helpers-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Javascript and Ajax Helpers</strong> par <a href="http://andreiabohner.wordpress.com/2007/06/08/symfony-cheat-cheet-helpers-javascript-e-ajax/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Même objectif que précédemment mais cette fois-ci avec les helpers Javascript/Ajax. Description détaillée pour les principaux helpers, listing pour les autres. Utile lors de la construction de formulaire.
<a href="/uploads/posts/2009-08-24/helpers-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Server Validation</strong> par <a href="http://andreiabohner.wordpress.com/2007/07/14/symfony-cheat-sheet-validacao-no-servidor/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Un ensemble d'informations sur la validation et la repopulation de formulaires.  Cette cheat sheet fourni une description succincte de quelque validators et explique brièvement la manière de les employer. Trop basique pour être utile.
<a href="/uploads/posts/2009-08-24/validation-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Model</strong> par <a href="http://andreiabohner.wordpress.com/2007/08/29/symfony-cheat-sheet-modelo/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Petite cheat-sheet dédiée au model; classes, collections, types de colonnes, un exemple de mapping simple en fin de document, bref, rien de bien excitant.
<a href="/uploads/posts/2009-08-24/model-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Model: Schema</strong> par <a href="http://andreiabohner.wordpress.com/2007/09/01/symfony-cheat-sheet-schema/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Etude complète d'un exemple de mapping propel. Très bien réalisé, complet et intéressant. Peut-être pas indispensable au quotidien, mais à (re)lire impérativement.
<a href="/uploads/posts/2009-08-24/model-schema-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Lime Unit &amp; Functional Testing</strong> par Benjamin Meynell
Récapitulatif de l'ensemble des commandes utiles lors de la réalisations de tests unitaires ou de tests fonctionnels. Idéal à titre de rappel mais pas indispensable.
<a href="/uploads/posts/2009-08-24/lime-cheat-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>ORM</strong> par <a href="http://andreiabohner.wordpress.com/2007/02/11/orm-symfony-e-propel/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Schéma récapitulatif du rôle des différents fichiers de conf et commandes CLI liés à l'<a href="http://fr.wikipedia.org/wiki/Mapping_objet-relationnel" title="Définition d&#039;un ORM sur wikipedia" rel="noopener noreferrer">ORM</a> (Mapping Objet-Relationnel). Extrêmement clair et par la même idéal pour bien appréhender le fonctionnement interne du framework à ce niveau.
<a href="/uploads/posts/2009-08-24/orm-en.pdf">Télécharger le PDF (en)</a></p>
</li>
<li>
<p><strong>Criteria/Criterion/RS</strong> par <a href="http://andreiabohner.wordpress.com/2008/01/04/symfony-cheat-sheet-criteriacriterionrs/" title="Blog de Andreia Bohner" rel="noopener noreferrer">Andréia Bohner</a>
Quelques exemples de Criteria/Criterions assez simplistes dans l'ensemble, encadrés par la liste des méthodes des classes Resultset et Criteria. Peu intéressant dans l'ensemble.
<a href="/uploads/posts/2009-08-24/criteria-en.pdf">Télécharger le PDF (en)</a></p>
</li>
</ul>
<p><a href="http://trac.symfony-project.org/wiki/CheatSheets" title="Liste des cheat-sheets disponibles sur le TRAC de symfony" rel="noopener noreferrer">Liste complète des cheat-sheets disponibles sur le TRAC de symfony</a></p>
<hr>
<p><strong>MAJ: D'autres cheat-sheets disponible sur <a href="http://devcheatsheet.com/tag/symfony/" title="Cheat Sheets Symfony" rel="noopener noreferrer">ce site</a></strong>.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/quelques-cheatsheets-symfony/" rel="alternate" type="text/html" />
    <published>2009-08-24T00:00:00+00:00</published>
    <updated>2026-01-04T11:07:09+00:00</updated>
  </entry>
  <entry xml:lang="en">
    <id>https://odolbeau.fr/blog/installation-de-symfony-sous-windows/</id>
    <title>Installation de symfony sous windows</title>
    <content type="html">
      <![CDATA[<p>Voici un petit tutoriel simple et rapide qui vous permettra de préparer votre environnement de travail puis d'installer symfony. Étapes nécessaires avant de pouvoir utiliser cet excellent framework! :)</p>
<p>La procédure d'installation de symfony sur une architecture WAMP ne présente aucune difficulté particulière et peux aisément se réaliser en suivant les instructions de la <a href="http://www.symfony-project.org/book/1_2/03-Running-Symfony#chapter_03_installing_the_symfony_libraries" title="Documentation officielle permettant d&#039;installer le framework symfony" rel="noopener noreferrer">documentation officielle</a> du framework symfony ou du <a href="http://www.symfony-project.org/jobeet/1_2/Propel/en/01#chapter_01_symfony_installation" title="Premier chapitre du tutoriel Jobeet" rel="noopener noreferrer">premier chapitre</a> de l'excellent tutoriel <a href="http://www.symfony-project.org/jobeet/1_2/" title="Tutoriel Jbeet" rel="noopener noreferrer">Jobeet</a>.</p>
<p>Cependant, pour celles et ceux que l'anglais horripile ou qui préfèrent suivre des explications adaptées à leur plateforme, voici les étapes à suivre pour installer le framework symfony.</p>
<p>Seul prérequis avant de démarrer l'installation, assurez-vous d'avoir une version de <a href="http://www.wampserver.com/" title="Site officiel de WampServer" rel="noopener noreferrer">WampServer</a> à jour.</p>
<h2 id="premiere-etape-installation-de-pear">Première étape: Installation de PEAR</h2>
<p>Définition de <a href="http://fr.wikipedia.org/wiki/PEAR" title="Définition de PEAR sur Wikipedia" rel="noopener noreferrer">Wikipedia</a> pour <a href="http://pear.php.net/" title="Site officiel de PEAR" rel="noopener noreferrer">PEAR</a>:</p>
<blockquote>
<p><em>PEAR</em> (pour <em>PHP Extension and Application Repository</em>) est une collection de bibliothèques PHP. C'est aussi une application qui permet de gérer les bibliothèques (installer ou mettre à jour une bibliothèque).</p>
</blockquote>
<p>PEAR va permettre d'installer symfony (<a href="http://pear.php.net/packages.php" title="Liste des packaches PEAR disponibles" rel="noopener noreferrer">ainsi que de nombreux autres packages</a>) grâce à une simple ligne de commande.</p>
<p><em>MAJ: La manipulation permettant d'installer PEAR dépend maintenant de votre version de WampServer (merci à <a href="http://www.odolbeau.fr/installation-de-symfony-sous-windows/comment-page-1#comment-12">LeoSquall</a> pour la remarque!)</em></p>
<ul>
<li>
<p><strong>Version 2.0h ou inférieure:</strong></p>
<p>Pour installer PEAR, lancez <code>go-pear.bat</code> situé dans le dossier <code>bin/php/php5.x.x</code> de votre répertoire d'installation de WampServer en utilisant l'invite de commande.</p>
<p>Ce qui donne dans mon cas:</p>
<pre><code class="language-bash">cd C:\APPS\WampServer\bin\php\php5.2.6
go-pear.bat</code></pre>
</li>
<li>
<p><strong>Version 2.0i ou supérieure:</strong></p>
<p>Créez un fichier <code>script.bat</code> dans le dossier <code>bin/php/php5.x.x</code> de votre répertoire d'installation de WampServer puis copiez-y le code suivant:</p>
<pre><code class="language-bash">php -d phar.require_hash=0 PEAR\go-pear.phar</code></pre>
<p>Exécutez le script en double cliquant dessus ou via l'invite de commande</p>
</li>
</ul>
<p><em>MAJ: Vous pouvez continuer de suivre ce tutorial, quelque soit votre version de WampServer! :)</em></p>
<div class="img-container-medium alignright">
    <img title="Modification de la variable Path" src="/images/posts/2009-08-12/variables-environnement.jpg" alt="Modification de la variable Path" width="235" height="270">
    <p class="legend">Modification de la variable Path</p>
</div>
<p>Au cours de l'installation, contentez-vous d'appuyez sur Entrée à chaque nouvelle demande et de répondre par l'affirmative lorsque que le script vous demandera si vous souhaitez qu'il modifie votre fichier php.ini (<em>Would you like to alter php.ini ?</em>).</p>
<p>Maintenant que PEAR est installé, il ne reste plus qu'à s'en servir! :) Pour cela il va falloir rajouter une nouvelle variable système. Rien de plus simple:
Clic droit sur le poste de travail -&gt; Propriétés -&gt; Onglet &quot;Avancé&quot; -&gt; Variables système.</p>
<p>Modifiez à présent la variable Path en ajoutant simplement à la fin de la chaine l'adresse du dossier utilisé auparavant (celui dans lequel se situe le script go-pear.bat) précédé d'un point virgule.
Dans mon cas: <code>; C:\APPS\WampServer\bin\php\php5.2.6</code></p>
<p>Fermer l'invite de commande puis ré-ouvrez la afin que les modifications soient prises en compte.</p>
<p>Voilà, PEAR est installé et fonctionnel!</p>
<h2 id="deuxieme-etape-installation-de-symfony">Deuxième étape: Installation de symfony</h2>
<p>Comme expliqué ci-dessus, PEAR va maintenant nous permettre d'installer symfony grâce à une simple ligne de commande! Ou plutôt deux! La première va permettre d'enregistrer un nouveau <a href="http://pear.php.net/channels/" title="Liste des channels PEAR" rel="noopener noreferrer">channel</a> contenant une liste de packages. Une fois ce channel ajouté, il suffit d'installer le package de son choix, en l'occurrence, la dernière version stable de symfony. Voici les commandes:</p>
<pre><code class="language-bash">pear channel-discover pear.symfony-project.com</code></pre>
<p>puis</p>
<pre><code class="language-bash">pear install symfony/symfony</code></pre>
<p>A présent que symfony est installé, reste à mettre en place un minimum de configuration.</p>
<h2 id="troisieme-etape-configuration">Troisième étape: Configuration</h2>
<p>Afin de faire fonctionner symfony, il faut vérifier et éventuellement modifier votre configuration de php et d'<a href="http://www.apache.org/" title="Site officiel d&#039;apache" rel="noopener noreferrer">apache</a>.</p>
<h3 id="modification-de-la-configuration-de-php">Modification de la configuration de php</h3>
<p>Tout d'abord, ouvrez le fichier <code>php.ini</code> situé dans le même dossier qu'auparavant. Si vous utilisez WampServer, une fois celui-ci démarré, vous pouvez accéder rapidement au fichier en cliquant (gauche) sur l'icône de WampServer dans la barre des taches, puis PHP -&gt; php.ini.</p>
<p>Dans ce fichier, deux choses à vérifier. Rechercher <code>memory_limit</code> et vérifier que la valeur est supérieure ou égale à 16M (<code>memory_limit = 16M</code>).</p>
<p>Enfin, si vous utilisez une version inférieure à PHP 5.3, vérifier que les <a href="http://fr2.php.net/magic_quotes" title="Présentation des magic quotes sur le site php.net" rel="noopener noreferrer">magic quotes</a> (guillemets magiques) sont désactivés (<code>magic_quotes_gpc = Off</code>)</p>
<h3 id="modification-de-la-configuration-d-apache">Modification de la configuration d'apache</h3>
<p>Ouvrez maintenant le fichier <code>httpd.conf</code> situé dans le dossier <code>bin/apache/apache2.x.x/conf</code> du répertoire d'installation de WampServer ou encore une fois, accédez-y via l'icône de WampServer situé dans la barre des tâches (Apache -&gt; httpd.conf)</p>
<p>Vérifiez que le module apache de rewriting, <a href="http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html" title="Documentation officiel d&#039;apache: l&#039;extension mod_rewrite" rel="noopener noreferrer">rewrite_module</a> est bien activé (pas de # devant la ligne: <code>LoadModule rewrite_module modules/mod_rewrite.so</code>)</p>
<p>N'oubliez pas de redémarrer le serveur apache afin que les modifications soient prises en compte!</p>
<p>Dernière étape, nous devons permettre au serveur apache d'accéder aux images, feuilles de styles et fichier  javascript fournis par symfony et utilisé notamment par l'admin generator, les pages par défaut et la barre de debug.</p>
<p>La solution la plus optimale est d'utiliser un <a href="http://httpd.apache.org/docs/2.2/mod/mod_alias.html" title="Documentation officielle d&#039;apache: le module alias" rel="noopener noreferrer">alias</a> apache (plus ou moins l'équivalent d'un <a href="http://fr.wikipedia.org/wiki/Lien_symbolique" title="Définition d&#039;un lien symbolique sur Wikipedia" rel="noopener noreferrer">lien symbolique</a> sous unix). Cependant, cette solution dépendant notamment de votre configuration apache existante et de l'utilisation que vous faites de votre serveur (multi-sites, etc) je ne la détaillerai pas dans cet article. Vous trouverez néanmoins un nombre important d'informations dans la <a href="http://www.symfony-project.org/book/1_2/03-Running-Symfony#chapter_03_configuring_the_web_server" title="Documentation officielle de symfony: configurer le serveur web" rel="noopener noreferrer">documentation officielle de symfony</a> ou dans celle d'<a href="http://httpd.apache.org/docs/2.2/mod/mod_alias.html" title="Documentation officielle d&#039;apache: l&#039;extension mod_alias" rel="noopener noreferrer">apache</a>.</p>
<p>Nous allons donc nous contenter d'utiliser la seconde solution. Pour cela, commencez par créer votre projet symfony en suivant les instructions de la <a href="http://www.symfony-project.org/book/1_2/03-Running-Symfony#chapter_03_setting_up_an_application" title="Documentation officielle de symfony: mettre en oeuvre une application" rel="noopener noreferrer">documentation</a>, puis copiez/collez l'ensemble des fichiers situés dans le dossier <code>bin/php/php5.2.6/data/symfony/web/sf</code> du répertoire d'installation de WampServer dans le répertoire web/sf de votre projet symfony. Cette solution fonctionne parfaitement, néanmoins vous serez obligé de refaire cette manipulation pour chaque projet que vous réaliserez sur votre machine mais également à chaque nouvelle version de symfony que vous utiliserez par la suite.</p>]]>
    </content>
    <link href="https://odolbeau.fr/blog/installation-de-symfony-sous-windows/" rel="alternate" type="text/html" />
    <published>2009-08-12T00:00:00+00:00</published>
    <updated>2026-01-05T19:03:41+00:00</updated>
  </entry>
</feed>
