<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Miximum</title>
	
	<link>http://www.miximum.fr</link>
	<description>Le blog d'un ingénieur web freelance</description>
	<lastBuildDate>Tue, 02 Feb 2010 14:40:29 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/LeBlogDunDeveloppeur" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="leblogdundeveloppeur" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Symfony form : pick or create</title>
		<link>http://www.miximum.fr/tutos/466-symfony-form-pick-or-create</link>
		<comments>http://www.miximum.fr/tutos/466-symfony-form-pick-or-create#comments</comments>
		<pubDate>Tue, 02 Feb 2010 14:40:29 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Tutos]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=466</guid>
		<description><![CDATA[Here&#8217;s an other tutorial about the Symfony form framework. Last time, we managed to make embedded i18n forms optionals. In today&#8217;s howto, we will talk about embedded forms again.
In the last tutorials, we created a form to edit news. Today we will create another form to write interviews. An interview is just an article about [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F466-symfony-form-pick-or-create"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F466-symfony-form-pick-or-create" height="61" width="51" /></a></div><p>Here&#8217;s an other tutorial about the Symfony form framework. Last time, <a href="http://www.miximum.fr/tutos/449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine">we managed to make embedded i18n forms optionals</a>. In today&#8217;s howto, we will talk about embedded forms again.</p>
<p>In the last tutorials, we created a form to edit news. Today we will create another form to write interviews. An interview is just an article about a celebrity. So our Interview class will inherit from our Article one, so we can reuse our last time work.</p>
<p>Here&#8217;s the complete schema we will work with&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">Article:
  actAs:
    Timestampable: ~
    I18n:
      fields: [ title, body ]
      actAs:
        Sluggable: { fields: [ title ], uniqueBy: [ lang, title ] }
&nbsp;
  columns:
    title: { type: string(255), notnull: true }
    body: { type: clob, notnull: true }
    author: { type: string(255), notnull: false }
&nbsp;
News:
  inheritance:
    extends: Article
    type: concrete
&nbsp;
Celebrity:
  actAs:
    Sluggable:
      fields: [ firstname, lastname ]
  columns:
    firstname: { type: string(255), notnull: true }
    lastname: { type: string(255), notnull: true }
&nbsp;
Interview:
  inheritance:
    extends: Article
    type: concrete
&nbsp;
  columns:
    celebrity_id: { type: integer, notnull: true }
&nbsp;
  relations:
&nbsp;
    Celebrity:
      local: celebrity_id
      foreign: id
      type: one
      foreignType: one
      foreignAlias: Interviews</pre></div></div>

<p>And the fixtures&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">Interview:
  i1:
    Author: 'Thibault J.'
    Celebrity:
      firstname: Thibault
      lastname: Jouannic
    Translation:
      fr:
        title: Auto-interview
        body: |
          Bla bla bla question et réponses</pre></div></div>

<p>As usual&hellip;</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">php symfony build <span class="re5">--all</span> <span class="re5">--and-load</span>
php symfony doctrine:generate-admin backend Interview</pre></div></div>

<h2>Before we start</h2>
<p>Before we start, let&#8217;s stop and think a few seconds. How do we want to manage our celebrity relation into our form<span class="fine">&nbsp;</span>? Take a look at the default form, without any modification&nbsp;:</p>
<div class="postimg"><a href="http://www.flickr.com/photos/24469297@N05/4324656195/" title="01_celebrity" target="_blank"><img src="http://farm3.static.flickr.com/2687/4324656195_1f90822fc6.jpg" alt="01_celebrity" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> </small></div>
<p>Quite bad, isn&#8217;t it<span class="fine">&nbsp;</span>? We could make a little better by adding a toString method.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/model/doctrine/Celebrity.class.php</span>
&nbsp;
<span class="kw2">class</span> Celebrity <span class="kw2">extends</span> BaseCelebrity
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> __toString<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <a href="http://www.php.net/sprintf"><span class="kw3">sprintf</span></a><span class="br0">&#40;</span><span class="st_h">'%s %s'</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getFirstname</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getLastname</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<div class="postimg"><a href="http://www.flickr.com/photos/24469297@N05/4325392592/" title="02_celebrity" target="_blank"><img src="http://farm5.static.flickr.com/4066/4325392592_dc60d3e13d.jpg" alt="02_celebrity" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a></small></div>
<p>That&#8217;s a bit better. The problem with this form is that you cannot add a new celebrity. If you want to create a new interview, you&#8217;ll have to chose among the existing celebrities. Of course, you could create an other admin module to manage celebrities, but a good programmer is a lazy one. And that would be great if we could create a new celebrity directly in the interview form.</p>
<p>In fact, that&#8217;s quite easy, <a href="http://prendreuncafe.com/blog/post/2009/11/29/Embedding-Relations-in-Forms-with-Symfony-1.3-and-Doctrine">using the embedRelation function</a>.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/InterviewForm.class.php</span>
<span class="kw2">class</span> InterviewForm <span class="kw2">extends</span> BaseInterviewForm
<span class="br0">&#123;</span>
  <span class="co4">/**
   * @see ArticleForm
   */</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span><span class="me2">configure</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embedRelation</span><span class="br0">&#40;</span><span class="st_h">'Celebrity'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<div class="postimg"><a href="http://www.flickr.com/photos/24469297@N05/4325392638/" title="03_celebrity" target="_blank"><img src="http://farm5.static.flickr.com/4015/4325392638_ecf4e66bb4.jpg" alt="03_celebrity" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a></small></div>
<p>Here&#8217;s the result, which is quite relevant to what we wanted. But<span class="fine">&nbsp;</span>!? Wait<span class="fine">&nbsp;</span>! There is a regression. What if we want to choose among existing celebrities, the way we used to do<span class="fine">&nbsp;</span>? What we really need is a way to combine both methods actually. We need to be able to choose an existing celebrity OR to create a new one.</p>
<h2>Pick or create</h2>
<p>We will have to use an intermediate form.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/InterviewForm.class.php</span>
<span class="kw2">class</span> InterviewForm <span class="kw2">extends</span> BaseInterviewForm
<span class="br0">&#123;</span>
  <span class="co4">/**
   * @see ArticleForm
   */</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span><span class="me2">configure</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$celebrityId</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">isNew</span><span class="br0">&#40;</span><span class="br0">&#41;</span> ? <span class="kw4">NULL</span> <span class="sy0">:</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getCelebrity</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$form</span> <span class="sy0">=</span> <span class="kw2">new</span> InterviewCelebrityForm<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'celebrity_id'</span> <span class="sy0">=&gt;</span> <span class="re0">$celebrityId</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embedForm</span><span class="br0">&#40;</span><span class="st_h">'Celebrity'</span><span class="sy0">,</span> <span class="re0">$form</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// lib/form/doctrine/InterviewCelebrityForm.class.php</span>
<span class="kw2">class</span> InterviewCelebrityForm <span class="kw2">extends</span> sfForm
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">widgetSchema</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw2">new</span> sfWidgetFormDoctrineChoice<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'model'</span> <span class="sy0">=&gt;</span> <span class="st_h">'Celebrity'</span><span class="sy0">,</span>
      <span class="st_h">'add_empty'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">setDefault</span><span class="br0">&#40;</span><span class="st_h">'celebrity_id'</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getOption</span><span class="br0">&#40;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$form</span> <span class="sy0">=</span> <span class="kw2">new</span> CelebrityForm<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embedForm</span><span class="br0">&#40;</span><span class="st_h">'new_celebrity'</span><span class="sy0">,</span> <span class="re0">$form</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">widgetSchema</span><span class="sy0">-&gt;</span><span class="me1">setLabel</span><span class="br0">&#40;</span><span class="st_h">'celebrity_id'</span><span class="sy0">,</span> <span class="st_h">'Choose one…'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">widgetSchema</span><span class="sy0">-&gt;</span><span class="me1">setLabel</span><span class="br0">&#40;</span><span class="st_h">'new_celebrity'</span><span class="sy0">,</span> <span class="st_h">'or create a new.'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<div class="postimg"><a href="http://www.flickr.com/photos/24469297@N05/4325392696/" title="04_celebrity" target="_blank"><img src="http://farm3.static.flickr.com/2702/4325392696_aa940a7fb8.jpg" alt="04_celebrity" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a></small></div>
<p>Try to edit the existing interview, and look at the nice result. However, if you try to save the form, you&#8217;ll have a lot of validations errors. There is still some work to do before we can get rid of all those red alerts.</p>
<div class="postimg"><a href="http://www.flickr.com/photos/24469297@N05/4324656371/" title="05_celebrity" target="_blank"><img src="http://farm5.static.flickr.com/4034/4324656371_64d6c5eb2f.jpg" alt="05_celebrity" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a></small></div>
<h2>Form validation</h2>
<p>The form validation strategy is quite simple. If an existing celebrity is chosen with the drop down list, the firstname and lastname should be ignored. If the list is empty, we must validate the firstname and lastname. At last, we must throw an error if all the fields are empty.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/InterviewCelebrityForm.class.php</span>
&nbsp;
    <span class="co1">// add this  at the end of the configure() function</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">validatorSchema</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw2">new</span> sfValidatorPass<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$celebrityValidatorSchema</span> <span class="sy0">=</span> clone <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">validatorSchema</span><span class="br0">&#91;</span><span class="st_h">'new_celebrity'</span><span class="br0">&#93;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">validatorSchema</span><span class="br0">&#91;</span><span class="st_h">'new_celebrity'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="kw2">new</span> sfValidatorPass<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">validatorSchema</span><span class="sy0">-&gt;</span><span class="me1">setPostValidator</span><span class="br0">&#40;</span>
      <span class="kw2">new</span> sfValidatorAnd<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
        <span class="kw2">new</span> sfValidatorOr<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
          <span class="kw2">new</span> sfValidatorSchemaFilter<span class="br0">&#40;</span><span class="st_h">'celebrity_id'</span><span class="sy0">,</span> <span class="kw2">new</span> sfValidatorDoctrineChoice<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
            <span class="st_h">'model'</span> <span class="sy0">=&gt;</span> <span class="st_h">'Celebrity'</span><span class="sy0">,</span>
            <span class="st_h">'required'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span>
          <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span>
          <span class="kw2">new</span> sfValidatorSchemaFilter<span class="br0">&#40;</span><span class="st_h">'new_celebrity'</span><span class="sy0">,</span> <span class="re0">$celebrityValidatorSchema</span><span class="br0">&#41;</span>
        <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span>
        <span class="kw2">new</span> sfValidatorCallback<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
          <span class="st_h">'callback'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">,</span> <span class="st_h">'checkCelebrity'</span><span class="br0">&#41;</span>
        <span class="br0">&#41;</span><span class="br0">&#41;</span>
      <span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="co1">// add this function into the class</span>
  <span class="co4">/**
   * Either who choose an existing celebrity, either we create a new one
   *
   * Unset values according to the choice we made
   **/</span>
  <span class="kw2">public</span> <span class="kw2">function</span> checkCelebrity<span class="br0">&#40;</span><span class="re0">$validator</span><span class="sy0">,</span> <span class="re0">$values</span><span class="sy0">,</span> <span class="re0">$argument</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">if</span><span class="br0">&#40;</span><span class="sy0">!</span><a href="http://www.php.net/empty"><span class="kw3">empty</span></a><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span>
        <span class="re0">$this</span><span class="br0">&#91;</span><span class="st_h">'new_celebrity'</span><span class="br0">&#93;</span><span class="sy0">,</span>
        <span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'new_celebrity'</span><span class="br0">&#93;</span>
      <span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">else</span>
    <span class="br0">&#123;</span>
      <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span>
        <span class="re0">$this</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span><span class="sy0">,</span>
        <span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span>
      <span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="kw1">return</span> <span class="re0">$values</span><span class="sy0">;</span>
  <span class="br0">&#125;</span></pre></div></div>

<p>Try differents combinations to submit your form. The validation should behave correctly now.</p>
<h2>Saving your form</h2>
<p>You may have noticed that our interview isn&#8217;t updated correctly after we submit the form. We will have to override the doUpdateObject method in order to update the object correctly on form validation.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/InterviewForm.class.php</span>
  <span class="kw2">public</span> <span class="kw2">function</span> doUpdateObject<span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">if</span><span class="br0">&#40;</span><span class="sy0">!</span><a href="http://www.php.net/empty"><span class="kw3">empty</span></a><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'Celebrity'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setCelebrityId</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'Celebrity'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'celebrity_id'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">else</span>
    <span class="br0">&#123;</span>
      <span class="re0">$celebrity</span> <span class="sy0">=</span> <span class="kw2">new</span> Celebrity<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">fromArray</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'Celebrity'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st_h">'new_celebrity'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getObject</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">Celebrity</span> <span class="sy0">=</span> <span class="re0">$celebrity</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span>
      <span class="re0">$values</span><span class="br0">&#91;</span><span class="st_h">'Celebrity'</span><span class="br0">&#93;</span><span class="sy0">,</span>
      <span class="re0">$this</span><span class="br0">&#91;</span><span class="st_h">'Celebrity'</span><span class="br0">&#93;</span>
    <span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    parent<span class="sy0">::</span><span class="me2">doUpdateObject</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span></pre></div></div>

<p>That&#8217;s it<span class="fine">&nbsp;</span>! Everything should be alright now. But just to make sure, here&#8217;s a few tests.</p>
<h2>Test your forms</h2>
<p>We are reusing some code from the last day, so I just will output the new part here.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">// test/function/backend/interviewActions.class.php
&nbsp;
<span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><a href="http://www.php.net/dirname"><span class="kw3">dirname</span></a><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../bootstrap/functional.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$browser</span> <span class="sy0">=</span> <span class="kw2">new</span> sfBackendTestFunctional<span class="br0">&#40;</span><span class="kw2">new</span> sfBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$browser</span><span class="sy0">-&gt;</span><span class="me1">loadData</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$browser</span><span class="sy0">-&gt;</span><span class="me1">setTester</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="sy0">,</span> <span class="st_h">'sfTesterDoctrine'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$interview</span> <span class="sy0">=</span> Doctrine<span class="sy0">::</span><span class="me2">getTable</span><span class="br0">&#40;</span><span class="st_h">'Interview'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">createQuery</span><span class="br0">&#40;</span><span class="st_h">'i'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">leftJoin</span><span class="br0">&#40;</span><span class="st_h">'i.Translation t'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">andWhere</span><span class="br0">&#40;</span><span class="st_h">'t.lang = ?'</span><span class="sy0">,</span> <span class="st_h">'fr'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">orderBy</span><span class="br0">&#40;</span><span class="st_h">'i.id'</span><span class="sy0">,</span> <span class="st_h">'asc'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">fetchOne</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$celebrity</span> <span class="sy0">=</span> <span class="kw2">new</span> Celebrity<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">setFirstname</span><span class="br0">&#40;</span><span class="st_h">'toto'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">setLastname</span><span class="br0">&#40;</span><span class="st_h">'tutu'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$editUrl</span> <span class="sy0">=</span> <a href="http://www.php.net/sprintf"><span class="kw3">sprintf</span></a><span class="br0">&#40;</span><span class="st_h">'/interview/%s/edit'</span><span class="sy0">,</span> <span class="re0">$interview</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$browser</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/interview'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'1 - Interview list'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkElement</span><span class="br0">&#40;</span><span class="st_h">'h1'</span><span class="sy0">,</span> <span class="st_h">'/Interview List/'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkElement</span><span class="br0">&#40;</span><span class="st_h">'body'</span><span class="sy0">,</span> <span class="st_h">'/Thibault Jouannic/'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
<span class="co1">// …</span>
&nbsp;
   info<span class="br0">&#40;</span><span class="st_h">'  2.8 - The current celebrity is already selected'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkElement</span><span class="br0">&#40;</span><span class="st_h">'#interview_Celebrity_celebrity_id option[selected=&quot;selected&quot;]'</span><span class="sy0">,</span> <a href="http://www.php.net/sprintf"><span class="kw3">sprintf</span></a><span class="br0">&#40;</span><span class="st_h">'/%s/'</span><span class="sy0">,</span> <span class="re0">$interview</span><span class="sy0">-&gt;</span><span class="me1">getCelebrity</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.9 - The current celebrity can be updated'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'interview'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'Celebrity'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'celebrity_id'</span> <span class="sy0">=&gt;</span> <span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
      <span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'Interview'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="re0">$interview</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
      <span class="st_h">'celebrity_id'</span> <span class="sy0">=&gt;</span> <span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.10 - Updating the celebrity does not create empty records'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'Celebrity'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">4</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.11 - A new celebrity can be created'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'interview'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'Celebrity'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'celebrity_id'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span>
      <span class="st_h">'NewCelebrity'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
        <span class="st_h">'firstname'</span> <span class="sy0">=&gt;</span> <span class="st_h">'oncle'</span><span class="sy0">,</span>
        <span class="st_h">'lastname'</span> <span class="sy0">=&gt;</span> <span class="st_h">'picsou'</span>
      <span class="br0">&#41;</span>
    <span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'Celebrity'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">5</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'Interview'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="re0">$interview</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
      <span class="st_h">'celebrity_id'</span> <span class="sy0">=&gt;</span> <span class="re0">$celebrity</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="nu0">1</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="sy0">;</span></pre></div></div>

<p>That&#8217;s all folks<span class="fine">&nbsp;</span>! See you soon.</p>
<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F466-symfony-form-pick-or-create"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F466-symfony-form-pick-or-create" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tutos/466-symfony-form-pick-or-create/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>L’automobiliste du vendredi soir</title>
		<link>http://www.miximum.fr/tranche_vie/456-lautomobiliste-du-vendredi-soir</link>
		<comments>http://www.miximum.fr/tranche_vie/456-lautomobiliste-du-vendredi-soir#comments</comments>
		<pubDate>Fri, 29 Jan 2010 19:25:57 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Tranche de vie]]></category>
		<category><![CDATA[divers]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=456</guid>
		<description><![CDATA[ photo credit&#160;: ルーク.チャン.チャン
Il m&#8217;arrive d&#8217;&#234;tre stupide, parfois. Si si, &#231;a m&#8217;arrive. Tenez, aujourd&#8217;hui par exemple, j&#8217;ai &#233;t&#233; assez b&#234;te pour aller faire des courses en centre ville, un vendredi, &#224; 17h30. Moi qui suis freelance, et travaille &#224; domicile, avouez, faut quand m&#234;me pas &#234;tre fut&#233;.
Parce qu&#8217;&#224; 17h30, &#224; Montpellier, dans le cente-ville, il [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F456-lautomobiliste-du-vendredi-soir"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F456-lautomobiliste-du-vendredi-soir" height="61" width="51" /></a></div><div class="postimg alignleft"><a href="http://www.flickr.com/photos/82327812@N00/3909305503/" title="on the way back home" target="_blank"><img src="http://farm3.static.flickr.com/2487/3909305503_17aa1c7e0e_m.jpg" alt="on the way back home" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc-sa/2.0/" title="Attribution-NonCommercial-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/82327812@N00/3909305503/" title="ルーク.チャン.チャン" target="_blank">ルーク.チャン.チャン</a></small></div>
<p>Il m&#8217;arrive d&#8217;&ecirc;tre stupide, parfois. Si si, &ccedil;a m&#8217;arrive. Tenez, aujourd&#8217;hui par exemple, j&#8217;ai &eacute;t&eacute; assez b&ecirc;te pour aller faire des courses en centre ville, un vendredi, &agrave; 17h30. Moi qui suis freelance, et travaille &agrave; domicile, avouez, faut quand m&ecirc;me pas &ecirc;tre fut&eacute;.</p>
<p>Parce qu&#8217;&agrave; 17h30, &agrave; Montpellier, dans le cente-ville, il y a beaucoup de voitures immobiles. &Agrave; la limite, dans la mesure ou je suis cycliste, ces voitures ne me d&eacute;rangeraient pas plus que &ccedil;a, si chacune d&#8217;entre elle ne contenait un petit automobiliste aigri et hargneux.</p>
<p>Car l&#8217;automobiliste du vendredi soir, ext&eacute;nu&eacute; par sa semaine de travail, et press&eacute; de rentrer chez lui s&#8217;affaler devant sa t&eacute;l&eacute;, a tout de la version bip&egrave;de du bichon. Mais si, ce petit monstre au poil blanc, version animale de la brosse &agrave; chiottes, &agrave; l&#8217;aboiement strident, et dont il est impossible de distinguer l&#8217;avant du derri&egrave;re.</p>
<p>C&#8217;est amusant l&#8217;&eacute;volution. Prenons le cas des chats, par exemple. &Agrave; l&#8217;instar d&#8217;un pigeon, un chat peut objectivement &ecirc;tre consid&eacute;r&eacute; comme un animal &agrave; faible valeur ajout&eacute;e. En gros, un chat, &ccedil;a bouffe, &ccedil;a dort, &ccedil;a casse deux ou trois trucs de temps en temps, mais &agrave; part &ccedil;a, &ccedil;a ne sert &agrave; rien</p>
<p> Seulement voil&agrave;, l&#8217;&eacute;volution a conf&eacute;r&eacute; au chat, notamment dans sa version juv&eacute;nile, un minois si adorable qu&#8217;il est tout bonnement impossible pour un &ecirc;tre humain normalement constitu&eacute; de ne pas s&#8217;y attacher. Et c&#8217;est ainsi que l&#8217;esp&egrave;ce du chat, si&egrave;cle apr&egrave;s si&egrave;cle, continue de se perp&eacute;tuer, comptant sur sa miraculeuses facult&eacute; &agrave; se faire offrir &agrave; bouffer. </p>
<p>Quand aux bichons, honn&ecirc;tement, je ne comprends pas comment ils ont r&eacute;ussi &agrave; ne pas tous se faire transformer en saucisses. Allons, je ne d&eacute;sesp&egrave;re pas.</p>
<div class="postimg alignright"><a href="http://www.flickr.com/photos/70757073@N00/131100622/" title="peque&ntilde;ita" target="_blank"><img src="http://farm1.static.flickr.com/51/131100622_f6c985bf36_m.jpg" alt="peque&ntilde;ita" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc-nd/2.0/" title="Attribution-NonCommercial-NoDerivs License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/70757073@N00/131100622/" title="Difusa" target="_blank">Difusa</a></small></div>
<p>Mais je digresse, je divague, et je m&#8217;&eacute;loigne de mon sujet<sup><a href="#footnote-1-456" id="footnote-link-1-456" title="Voir la note.">[1]</a></sup>. Sujet qui est, je le rappelle pour les moins attentifs d&#8217;entres vous, l&#8217;aggressivit&eacute; hargneuses de l&#8217;automobiliste du vendredi soir. Car si le bichon aboie, l&#8217;automobiliste lui klaxonne rageusement, convaincu que gr&acirc;ce &agrave; un myst&eacute;rieux ph&eacute;nom&egrave;ne physique, plus il fera de bruit, plus vite il sera chez lui.</p>
<p>Les geeks le savent, le vendredi est le jour du troll. Le jour ou il est tol&eacute;r&eacute;, dans les limites impos&eacute;es par le bon sens, de prendre quelques libert&eacute;s avec la netiquette. Sans doute inspir&eacute;s par cette charmante coutume, les automobilistes, eux aussi, d&eacute;cident parfois de prendre quelques libert&eacute;s avec la &laquo;&nbsp;&nbsp;netiquette de la route&nbsp;&nbsp;&raquo;.</p>
<p>Seulement voil&agrave;, contrairement &agrave; la netiquette du web, qu&#8217;il est recommand&eacute; de suivre pour le confort de tous, la n&eacute;tiquette de l&#8217;automobiliste, plus connue sous le nom de &laquo;&nbsp;&nbsp;code de la route&nbsp;&nbsp;&raquo;, n&#8217;est pas facultative. En fait, le code de la route est m&ecirc;me un texte l&eacute;gislatif, un truc quand m&ecirc;me un peu s&eacute;rieux, du genre qu&#8217;on n&#8217;est pas cens&eacute; trop faire le guignol avec.</p>
<p>Alors, puisque j&#8217;ai la chance de disposer ici d&#8217;un espace d&#8217;expression libre, et que peut-&ecirc;tre, il reste un ou deux automobilistes &agrave; lire ces lignes, voici quelques r&egrave;gles que je souhaite rappeler &agrave; leur attention&nbsp;:</p>
<ol>
<li>En agglom&eacute;ration, l&#8217;usage de l&#8217;avertisseur sonore n&#8217;est autoris&eacute; qu&#8217;en cas de danger imm&eacute;diat. (art. R416-1 du code de la route). Je tiens &agrave; pr&eacute;ciser que l&#8217;hypoth&eacute;tique possibilit&eacute; de pouvoir esp&eacute;rer faire avancer sa voiture de quelques centim&egrave;tres ne constitue pas un danger imm&eacute;diat&nbsp;<span class="fine">&nbsp;</span>;</li>
<li>L&#8217;arr&ecirc;t ou le stationnement d&#8217;un v&eacute;hicule empi&eacute;tant sur un passage pr&eacute;vu &agrave; l&#8217;intention des pi&eacute;tons est interdit. (art. Article R417-5 du code de la route)&nbsp;<span class="fine">&nbsp;</span>;</li>
</ol>
<p>Bref<span class="fine">&nbsp;</span>!&nbsp;Tout ce que je viens de tartiner pourrait en fait &ecirc;tre r&eacute;sum&eacute; de mani&egrave;re assez concise&nbsp;: <strong>Si vous &ecirc;tes un automobiliste, que vous klaxonnez en agglom&eacute;ration juste parce que vous &ecirc;tes fatigu&eacute;, et/ou stationnez sur les passages pi&eacute;tons (m&ecirc;me quelques secondes), ne vous cherchez pas d&#8217;excuses&nbsp;: vous &ecirc;tes un con et vous me faites chier</strong>.</p>
<p>C&#8217;&eacute;tait vendredi, merci de votre attention, et bon week-end &agrave; vous.</p>
<h4>Notes&nbsp;:</h4><ol class="footnotes"><li id="footnote-1-456">Mais bon, pour une fois que je peux illustrer mon blog avec des photos de chats.  [<a href="#footnote-link-1-456">retour</a>]</li></ol><div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F456-lautomobiliste-du-vendredi-soir"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F456-lautomobiliste-du-vendredi-soir" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tranche_vie/456-lautomobiliste-du-vendredi-soir/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Optional translation form for I18n objects with Symfony and Doctrine</title>
		<link>http://www.miximum.fr/tutos/449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine</link>
		<comments>http://www.miximum.fr/tutos/449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine#comments</comments>
		<pubDate>Fri, 29 Jan 2010 14:23:20 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Tutos]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[I18n]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=449</guid>
		<description><![CDATA[ photo credit&#160;: kevindooley
There are people around here claiming that the Symfony form framework is a gift from the gods. One might say that this statement is a bit exaggerated, but it&#8217;s true that the release of the 1.3/1.4 version of the Symfony brought many improvements, and yes, the form framework can save you a [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine" height="61" width="51" /></a></div><div class="postimg alignleft"><a href="http://www.flickr.com/photos/12836528@N00/2201791390/" title="Scaffolding: Not just for construction workers anymore" target="_blank"><img src="http://farm3.static.flickr.com/2037/2201791390_7bc614e27c_m.jpg" alt="Scaffolding: Not just for construction workers anymore" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by/2.0/" title="Attribution License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/12836528@N00/2201791390/" title="kevindooley" target="_blank">kevindooley</a></small></div>
<p>There are people around here claiming that the <a href="http://www.symfony-project.org/forms/1_4/">Symfony form framework</a> is a gift from the gods. One might say that this statement is a bit exaggerated, but it&#8217;s true that the release of the 1.3/1.4 version of the Symfony brought many improvements, and yes, the form framework can save you a lot of time.</p>
<p>In one of my current project, I had to developp some basic CMS-like features. In a back-office, an admin should be able to writes different kind of articles (news, interviews&hellip;). Hey<span class="fine">&nbsp;</span>! <a href="http://www.symfony-project.org/more-with-symfony/1_4/en/09-Doctrine-Form-Inheritance">That&#8217;s a job for inheritance<span class="fine">&nbsp;</span>!</a> But wait, there&#8217;s more. Articles can be translated in several languages. <a href="http://www.doctrine-project.org/documentation/manual/1_2/en/behaviors#core-behaviors:i18n">Easy, with the Doctrine I18n behavior</a>.</p>
<p>One last requirement&nbsp;: article translations are optional. One might publish an article in english, an other in french, and a third in both languages. This is a little more tricky, so let&#8217;s see how to do this.</p>
<h2>Some references</h2>
<p>Before we start, here are the wonderful ressources I read to get my way out&nbsp;:</p>
<p><a href="http://prendreuncafe.com/blog/post/2009/11/29/Embedding-Relations-in-Forms-with-Symfony-1.3-and-Doctrine"> http://prendreuncafe.com/blog/post/2009/11/29/Embedding-Relations-in-Forms-with-Symfony-1.3-and-Doctrine</a><br />
<a href="http://symfonyguide.wordpress.com/2009/09/28/symfony-forms-saving-process/"> http://symfonyguide.wordpress.com/2009/09/28/symfony-forms-saving-process/</a><br />
<a href="http://vousavezchoisi.com/img/formsdiagram.png"> http://vousavezchoisi.com/img/formsdiagram.png</a><br />
<a href="http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms#chapter_06_ignoring_embedded_forms"> http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms#chapter_06_ignoring_embedded_forms</a><br />
<a href="http://www.ze-technology.com/2009/06/05/tests-fonctionnels-avec-symfony-gerer-lidentification/"> http://www.ze-technology.com/2009/06/05/tests-fonctionnels-avec-symfony-gerer-lidentification/</a></p>
<h2>Let&#8217;s start with the schema</h2>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">Article:
  actAs:
    Timestampable: ~
    I18n:
      fields: [ title, body ]
      actAs:
        Sluggable: { fields: [ title ], uniqueBy: [ lang, title ] }
&nbsp;
  columns:
    title: { type: string(255), notnull: true }
    body: { type: clob, notnull: true }
    author: { type: string(255), notnull: false }
&nbsp;
News:
  inheritance:
    extends: Article
    type: concrete</pre></div></div>

<p>As it&#8217;s friday, I will also give you some fixtures&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">News:
  n1:
    author: 'Fantomas'
    Translation:
      fr:
        title: 'OK, l''Ipad est sorti. Vous allez me foutre la paix maintentant ?'
        body: |
          Vous commencez serieusement à me gonfler avec vos articles et vos tweets sur
          une techno inutile et bardée de DRM.
&nbsp;
  n2:
    author: 'Garcin Fony'
    Translation:
      fr:
        title: 'Sinon, à part ça, ça va ?'
        body: |
          Allez, pour me calmer, je vais me refaire un p'tit café</pre></div></div>

<p>Let&#8217;s build the whole thing, and generate the admin in the same time.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">php symfony doctrine:build <span class="re5">--all</span> <span class="re5">--and-load</span>
php symfony generate:app backend
php symfony doctrine:generate-admin backend News</pre></div></div>

<p>Check the module admin we&#8217;ve just build. Click on the first &laquo;&nbsp;&nbsp;edit&nbsp;&nbsp;&raquo; button. Wait a minute<span class="fine">&nbsp;</span>? The I18n fields are just missing, how are we gonna edit our articles<span class="fine">&nbsp;</span>? If you&#8217;re not familiar with the I18n behavior, you must know that the content is split into two tables. The new table, &laquo;&nbsp;&nbsp;article_translation&nbsp;&nbsp;&raquo;, holds all the I18n fields.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">mysql<span class="sy0">&gt;</span> <span class="kw1">SELECT</span> <span class="sy0">*</span> <span class="kw1">FROM</span> news;
<span class="sy0">+</span><span class="co1">----+-------------+---------------------+---------------------+</span>
<span class="sy0">|</span> id <span class="sy0">|</span> author      <span class="sy0">|</span> created_at          <span class="sy0">|</span> updated_at          <span class="sy0">|</span>
<span class="sy0">+</span><span class="co1">----+-------------+---------------------+---------------------+</span>
<span class="sy0">|</span>  <span class="nu0">1</span> <span class="sy0">|</span> Fantomas    <span class="sy0">|</span> <span class="nu0">2010</span><span class="sy0">-</span>01<span class="sy0">-</span><span class="nu0">29</span> <span class="nu0">12</span>:<span class="nu0">14</span>:<span class="nu0">46</span> <span class="sy0">|</span> <span class="nu0">2010</span><span class="sy0">-</span>01<span class="sy0">-</span><span class="nu0">29</span> <span class="nu0">12</span>:<span class="nu0">14</span>:<span class="nu0">46</span> <span class="sy0">|</span> 
<span class="sy0">|</span>  <span class="nu0">2</span> <span class="sy0">|</span> Garcin Fony <span class="sy0">|</span> <span class="nu0">2010</span><span class="sy0">-</span>01<span class="sy0">-</span><span class="nu0">29</span> <span class="nu0">12</span>:<span class="nu0">14</span>:<span class="nu0">46</span> <span class="sy0">|</span> <span class="nu0">2010</span><span class="sy0">-</span>01<span class="sy0">-</span><span class="nu0">29</span> <span class="nu0">12</span>:<span class="nu0">14</span>:<span class="nu0">46</span> <span class="sy0">|</span> 
<span class="sy0">+</span><span class="co1">----+-------------+---------------------+---------------------+</span>
&nbsp;
mysql<span class="sy0">&gt;</span> <span class="kw1">SELECT</span> id<span class="sy0">,</span> lang<span class="sy0">,</span> title <span class="kw1">FROM</span> news_translation;
<span class="sy0">+</span><span class="co1">----+------+------------------------------------------------------------------+</span>
<span class="sy0">|</span> id <span class="sy0">|</span> lang <span class="sy0">|</span> title                                                            <span class="sy0">|</span>
<span class="sy0">+</span><span class="co1">----+------+------------------------------------------------------------------+</span>
<span class="sy0">|</span>  <span class="nu0">1</span> <span class="sy0">|</span> fr   <span class="sy0">|</span> OK<span class="sy0">,</span> l<span class="st0">'Ipad est sorti. Vous allez me foutre la paix maintentant ? | 
|  2 | fr   | Sinon, à part ça, ça va ?                                     | 
+----+------+------------------------------------------------------------------+</span></pre></div></div>

<p>So, to be able to edit our translations, you have to embed the I18n forms, using the embedI18 function. Let&#8217;s do that.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/NewsForm.class.php</span>
<span class="kw2">class</span> NewsForm <span class="kw2">extends</span> BaseNewsForm
<span class="br0">&#123;</span>
  <span class="co4">/**
   * @see ArticleForm
   */</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span><span class="me2">configure</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embedI18n</span><span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'fr'</span><span class="sy0">,</span> <span class="st_h">'en'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<p>Reload the edit page. Now you can edit your news in french and english. Pretty cool, isn&#8217;t it<span class="fine">&nbsp;</span>?</p>
<h2>Let&#8217;s refactor this mess</h2>
<p>Since now, it&#8217;s been pretty easy. However, the code we&#8217;ve written is bad. It&#8217;s bad, for the following reasons&nbsp;:</p>
<ol>
<li>Each time we will add a new article type (e.g. interview, etc.), we will have to update it&#8217;s configure method&nbsp;<span class="fine">&nbsp;</span>;</li>
<li>Each time we will change the available languages (to add a new one, or disable an existing one), we will have to edit every forms&nbsp;<span class="fine">&nbsp;</span>;</li>
</ol>
<p>Fortunately, since Symfony 1.3, <a href="http://www.symfony-project.org/tutorial/1_4/en/whats-new#chapter_a2fae23c9403b0e9ec99806fccf6b53e_sub_form_classes_inheritance">the form inheritance follow the model structure</a>. Notice that NewsForm extends BaseNewsForm, which extends ArticleForm.</p>
<p>Let&#8217;s refactor our code&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"># config/app.yml
all:
  cultures:
    enabled:
      fr: Français
      en: English</pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/NewsForm.class.php</span>
&nbsp;
<span class="co1">// Revert the changes we added there</span>
<span class="kw2">class</span> NewsForm <span class="kw2">extends</span> BaseNewsForm
<span class="br0">&#123;</span>
  <span class="co4">/**
   * @see ArticleForm
   */</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span><span class="me2">configure</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// lib/form/doctrine/ArticleForm.class.php</span>
<span class="kw2">class</span> ArticleForm <span class="kw2">extends</span> BaseArticleForm
<span class="br0">&#123;</span>
  <span class="co4">/**
   * Available languages
   *
   * @var array $languages
   **/</span>
  protected <span class="re0">$langages</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span> <span class="sy0">=</span> sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'app_cultures_enabled'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$langs</span> <span class="sy0">=</span> <a href="http://www.php.net/array_keys"><span class="kw3">array_keys</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embedI18n</span><span class="br0">&#40;</span><span class="re0">$langs</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span> <span class="kw1">as</span> <span class="re0">$lang</span> <span class="sy0">=&gt;</span> <span class="re0">$label</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">widgetSchema</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="sy0">-&gt;</span><span class="me1">setLabel</span><span class="br0">&#40;</span><span class="re0">$label</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<p>Reload the edit form. Now, no matter how many articles sub-types you add, you still can configure available languages from one place.</p>
<h2>Translation edition</h2>
<p>Let&#8217;s try our brand new admin module by editing a news. If you used the fixtures I provided, you should have a french article, with no english translation. Try to update the french version, and click on the &#8216;Save&#8217; button.</p>
<p>This is a miserable failure. We can&#8217;t save the form, because the english fields are required. We&#8217;re stuck, we have to fill every translation for an article before we can save it. Wouldn&#8217;t it be nice if we could make a translation form optional<span class="fine">&nbsp;</span>? Let&#8217;s add a requirement&nbsp;: if every fields in a translation form are empty, it just should be ignored.</p>
<p>To achieve this goal, we will update the doBind method. Here&#8217;s the new ArticleForm class.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/ArticleForm.class.php</span>
<span class="kw2">class</span> ArticleForm <span class="kw2">extends</span> BaseArticleForm
<span class="br0">&#123;</span>
  <span class="co4">/**
   * Available languages
   *
   * @var array $languages
   **/</span>
  protected <span class="re0">$langages</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span> <span class="sy0">=</span> sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'app_cultures_enabled'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$langs</span> <span class="sy0">=</span> <a href="http://www.php.net/array_keys"><span class="kw3">array_keys</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embedI18n</span><span class="br0">&#40;</span><span class="re0">$langs</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span> <span class="kw1">as</span> <span class="re0">$lang</span> <span class="sy0">=&gt;</span> <span class="re0">$label</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">widgetSchema</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="sy0">-&gt;</span><span class="me1">setLabel</span><span class="br0">&#40;</span><span class="re0">$label</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co4">/**
   * Cleans and binds values to the current form
   *
   * Ignore i18n forms when all their fields are empty
   *
   * @see sfForm::doBind
   **/</span>
  protected <span class="kw2">function</span> doBind<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="re0">$values</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span> <span class="kw1">as</span> <span class="re0">$lang</span> <span class="sy0">=&gt;</span> <span class="re0">$label</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="kw1">if</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embeddedI18nFormIsEmpty</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
      <span class="br0">&#123;</span>
        <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span>
          <span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="sy0">,</span>
          <span class="re0">$this</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span>
        <span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
&nbsp;
    parent<span class="sy0">::</span><span class="me2">doBind</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co4">/**
   * Check if every fields, except for id and lang, are empty
   **/</span>
  protected <span class="kw2">function</span> embeddedI18nFormIsEmpty<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="re0">$values</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$values</span> <span class="kw1">as</span> <span class="re0">$key</span> <span class="sy0">=&gt;</span> <span class="re0">$value</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="kw1">if</span><span class="br0">&#40;</span><a href="http://www.php.net/in_array"><span class="kw3">in_array</span></a><span class="br0">&#40;</span><span class="re0">$key</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="sy0">,</span> <span class="st_h">'lang'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
        <span class="kw1">continue</span><span class="sy0">;</span>
&nbsp;
      <span class="kw1">if</span><span class="br0">&#40;</span><span class="st_h">''</span> <span class="sy0">!==</span> <a href="http://www.php.net/trim"><span class="kw3">trim</span></a><span class="br0">&#40;</span><span class="re0">$value</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
      <span class="br0">&#123;</span>
        <span class="kw1">return</span> <span class="kw4">false</span><span class="sy0">;</span>
      <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    <span class="kw1">return</span> <span class="kw4">true</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<p>In the overidden doBind method, we check, for every I18n form, if some values has been submited. If not, we just unset the corresponding fields.</p>
<p>Reload the edit form, update the french translation, leave the english field empty, and click on &#8216;Save&#8217;. Hurray, the forms is saved correctly.</p>
<p>However, you should keep reading, because you&#8217;re gonna have a surprise. Let&#8217;s look into the database.</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">mysql<span class="sy0">&gt;</span> <span class="kw1">SELECT</span> id<span class="sy0">,</span> lang<span class="sy0">,</span> slug <span class="kw1">FROM</span> news_translation;
<span class="sy0">+</span><span class="co1">----+------+--------------------------------------------------------------+</span>
<span class="sy0">|</span> id <span class="sy0">|</span> lang <span class="sy0">|</span> slug                                                         <span class="sy0">|</span>
<span class="sy0">+</span><span class="co1">----+------+--------------------------------------------------------------+</span>
<span class="sy0">|</span>  <span class="nu0">1</span> <span class="sy0">|</span> en   <span class="sy0">|</span>                                                              <span class="sy0">|</span> 
<span class="sy0">|</span>  <span class="nu0">1</span> <span class="sy0">|</span> fr   <span class="sy0">|</span> ok<span class="sy0">-</span>l<span class="sy0">-</span>ipad<span class="sy0">-</span>est<span class="sy0">-</span>sorti<span class="sy0">-</span>vous<span class="sy0">-</span>allez<span class="sy0">-</span>me<span class="sy0">-</span>foutre<span class="sy0">-</span>la<span class="sy0">-</span>paix<span class="sy0">-</span>maintentant <span class="sy0">|</span> 
<span class="sy0">|</span>  <span class="nu0">2</span> <span class="sy0">|</span> fr   <span class="sy0">|</span> sinon<span class="sy0">-</span>a<span class="sy0">-</span>part<span class="sy0">-</span>ca<span class="sy0">-</span>ca<span class="sy0">-</span>va                                        <span class="sy0">|</span> 
<span class="sy0">+</span><span class="co1">----+------+--------------------------------------------------------------</span></pre></div></div>

<p>Something went wrong<span class="fine">&nbsp;</span>! Somewhere in the process, Symfony created an empty translation object for our article. Notice the void &laquo;&nbsp;&nbsp;slug&nbsp;&nbsp;&raquo; field<span class="fine">&nbsp;</span>? If you try to edit another news, you will receive a duplicate content error. WTF<span class="fine">&nbsp;</span>?</p>
<p>I must admit that I don&#8217;t fully understand why this happens (sometimes I think I do, and a few minutes later, I realize I don&#8217;t). However, here&#8217;s the solution to overcome this problem.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/ArticleForm.class.php</span>
&nbsp;
  <span class="co1">// Add this at the beginnig of the class:</span>
  <span class="co4">/**
   * I18n ignored forms
   **/</span>
  protected <span class="re0">$I18nFormsIgnored</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="co1">// update the doBind method:</span>
  <span class="co4">/**
   * Unset i18n forms values when every field is empty
   **/</span>
  protected <span class="kw2">function</span> doBind<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="re0">$values</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">languages</span> <span class="kw1">as</span> <span class="re0">$lang</span> <span class="sy0">=&gt;</span> <span class="re0">$label</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="kw1">if</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">embeddedI18nFormEmpty</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
      <span class="br0">&#123;</span>
        <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">I18nFormsIgnored</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$lang</span><span class="sy0">;</span>
        <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span>
          <span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="sy0">,</span>
          <span class="re0">$this</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span>
        <span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
&nbsp;
    parent<span class="sy0">::</span><span class="me2">doBind</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co1">// And override the doUpdateObject method:</span>
  <span class="co4">/**
   * Updates the values of the object with the cleaned up values.
   *
   * @param  array $values An array of values
   *
   * @see sfFormDoctrine::doUpdateObject()
   */</span>
  protected <span class="kw2">function</span> doUpdateObject<span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span><span class="me2">doUpdateObject</span><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">I18nFormsIgnored</span> <span class="kw1">as</span> <span class="re0">$lang</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">object</span><span class="sy0">-&gt;</span><span class="me1">Translation</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span><span class="re0">$values</span><span class="br0">&#91;</span><span class="re0">$lang</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span></pre></div></div>

<p>Delete the useless line in the mysql database, so we can start on a clean base. Once more, reload the edit form, leave the english translation empty, save the form, et voil&agrave;<span class="fine">&nbsp;</span>! The form is saved, the translation is updated, and no empty line is added in the table.</p>
<h2>Adding and deleting a translation</h2>
<p>Just to be sure, add some data in the english translation title, and try to save the form. You should get a &laquo;&nbsp;&nbsp;body is required&nbsp;&nbsp;&raquo; error, which is the expected behavior. Try to fill correctly the english translation, and save again. Check that the english translation is correctly added in the database. Erase all french fields, save one more time, and check that the french translation is definitly removed from the DB (no dummy entry).</p>
<p>Well, it seems that our form is pretty functional. One last thing&nbsp;: there is still a &laquo;&nbsp;&nbsp;slug&nbsp;&nbsp;&raquo; field in each translation form, that you might want to unset. Easy, just edit the ArticleTranslationForm.class.php file&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/ArticleTranslationForm.class.php</span>
&nbsp;
<span class="kw2">class</span> ArticleTranslationForm <span class="kw2">extends</span> BaseArticleTranslationForm
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <a href="http://www.php.net/unset"><span class="kw3">unset</span></a><span class="br0">&#40;</span><span class="re0">$this</span><span class="br0">&#91;</span><span class="st_h">'slug'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<p>Edit a news. If you&#8217;re using Symfony 1.{3,4}.1, you&#8217;ll see that the damn slug field is still there. It&#8217;s a Symfony bug. Indeed, <a href="http://trac.symfony-project.org/ticket/8199">the translations forms don&#8217;t follow the inheritance schema</a>. Notice that BaseNewsTranslationForm extends BaseFormDoctrine instead of ArticleNewsTranslation.</p>
<p>You&#8217;ll have to manually change that, however, this will be overidden each time you rebuild your forms.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/form/doctrine/base/BaseNewsTranslationForm.class.php</span>
<span class="co1">// Replace</span>
abstract <span class="kw2">class</span> BaseNewsTranslationForm <span class="kw2">extends</span> BaseFormDoctrine
&nbsp;
<span class="co1">// with</span>
abstract <span class="kw2">class</span> BaseNewsTranslationForm <span class="kw2">extends</span> ArticleTranslationForm
&nbsp;
<span class="co1">// lib/form/doctrine/NewsTranslationForm.class.php</span>
<span class="co1">// Edit the configure method</span>
<span class="kw2">class</span> NewsTranslationForm <span class="kw2">extends</span> BaseNewsTranslationForm
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span><span class="me2">configure</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<p>Now, the slug field should disapear. At the present moment, the bug is still open.</p>
<h2>Let&#8217;s test everything</h2>
<p>As it&#8217;s friday, I feel generous, so as a bonus, I will give you the functional tests to cover our magical form. Enjoy.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/sfBackendTestFunctional.class.php</span>
<span class="co4">/**
 * This class is used to run functional tests in a secured backend
 *
 * Takes care of the login action, and the fixtures loading
 */</span>
<span class="kw2">class</span> sfBackendTestFunctional <span class="kw2">extends</span> sfTestFunctional
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$browser</span><span class="sy0">,</span> <span class="re0">$lime</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="re0">$testers</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    parent<span class="sy0">::</span>__construct<span class="br0">&#40;</span><span class="re0">$browser</span><span class="sy0">,</span> <span class="re0">$lime</span><span class="sy0">,</span> <span class="re0">$testers</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co4">/**
   * Perform user authentication
   *
   * @param   array of String         $user_data
   * @return  sfGuardTestFunctional   $this
   */</span>
  <span class="kw2">public</span> <span class="kw2">function</span> signin<span class="br0">&#40;</span><span class="re0">$user_data</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
   <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">info</span><span class="br0">&#40;</span><a href="http://www.php.net/sprintf"><span class="kw3">sprintf</span></a><span class="br0">&#40;</span><span class="st_h">'Login as &quot;%s&quot;'</span><span class="sy0">,</span> <span class="re0">$user_data</span><span class="br0">&#91;</span><span class="st_h">'username'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
     <span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'/login'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
     <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'sign in'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'signin'</span> <span class="sy0">=&gt;</span> <span class="re0">$user_data</span><span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
     <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
     <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
     <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'user'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <span class="me1">isAuthenticated</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
     <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
     <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'request'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <span class="me1">isParameter</span><span class="br0">&#40;</span><span class="st_h">'module'</span><span class="sy0">,</span> <span class="st_h">'sfGuardAuth'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <span class="me1">isParameter</span><span class="br0">&#40;</span><span class="st_h">'action'</span><span class="sy0">,</span> <span class="st_h">'signin'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
     <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
     <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <span class="me1">isRedirected</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <span class="me1">followRedirect</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
       <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span>
    <span class="sy0">;</span>
  <span class="br0">&#125;</span>
  <span class="co4">/**
   * Load project fixtures
   **/</span>
  <span class="kw2">public</span> <span class="kw2">function</span> loadData<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    Doctrine<span class="sy0">::</span><span class="me2">loadData</span><span class="br0">&#40;</span>sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'sf_data_dir'</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/fixtures'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// test/functional/backend/newsActionsTest.php</span>
&nbsp;
<span class="kw1">include</span><span class="br0">&#40;</span><a href="http://www.php.net/dirname"><span class="kw3">dirname</span></a><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../../bootstrap/functional.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$browser</span> <span class="sy0">=</span> <span class="kw2">new</span> sfBackendTestFunctional<span class="br0">&#40;</span><span class="kw2">new</span> sfBrowser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$browser</span><span class="sy0">-&gt;</span><span class="me1">loadData</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$browser</span><span class="sy0">-&gt;</span><span class="me1">setTester</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="sy0">,</span> <span class="st_h">'sfTesterDoctrine'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$languages</span> <span class="sy0">=</span> sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'app_cultures_enabled'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$news</span> <span class="sy0">=</span> Doctrine<span class="sy0">::</span><span class="me2">getTable</span><span class="br0">&#40;</span><span class="st_h">'News'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">createQuery</span><span class="br0">&#40;</span><span class="st_h">'n'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">leftJoin</span><span class="br0">&#40;</span><span class="st_h">'n.Translation t'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">andWhere</span><span class="br0">&#40;</span><span class="st_h">'t.lang = ?'</span><span class="sy0">,</span> <span class="st_h">'fr'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">orderBy</span><span class="br0">&#40;</span><span class="st_h">'n.id'</span><span class="sy0">,</span> <span class="st_h">'asc'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">fetchOne</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$editUrl</span> <span class="sy0">=</span> <a href="http://www.php.net/sprintf"><span class="kw3">sprintf</span></a><span class="br0">&#40;</span><span class="st_h">'/news/%s/edit'</span><span class="sy0">,</span> <span class="re0">$news</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$browser</span><span class="sy0">-&gt;</span>
  <span class="co1">// Uncomment this if your backend is protected by a login</span>
  <span class="co1">//signin(array(</span>
  <span class="co1">//  'username' =&gt; 'admin',</span>
  <span class="co1">//  'password' =&gt; 'admin'</span>
  <span class="co1">//)))-&gt;</span>
  get<span class="br0">&#40;</span><span class="st_h">'/news'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'1 - News list'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkElement</span><span class="br0">&#40;</span><span class="st_h">'h1'</span><span class="sy0">,</span> <span class="st_h">'/News List/'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkElement</span><span class="br0">&#40;</span><span class="st_h">'body'</span><span class="sy0">,</span> <span class="st_h">'/Fantomas/'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'2 - News edit'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.1 - I18n forms for all languages are embedded'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'response'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isStatusCode</span><span class="br0">&#40;</span><span class="nu0">200</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">checkElement</span><span class="br0">&#40;</span><span class="st_h">'input[name$=&quot;[title]&quot;]'</span><span class="sy0">,</span> <a href="http://www.php.net/count"><span class="kw3">count</span></a><span class="br0">&#40;</span><span class="re0">$languages</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.2 - I18n forms are validated'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'news'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'en'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">'toto'</span><span class="sy0">,</span>
      <span class="st_h">'body'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">isError</span><span class="br0">&#40;</span><span class="st_h">'en[body]'</span><span class="sy0">,</span> <span class="st_h">'required'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.3 - Empty forms are validated'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'news'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'en'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span>
      <span class="st_h">'body'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.4 - Translations are updated'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'news'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'fr'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">'toto tutu tata'</span><span class="sy0">,</span>
      <span class="st_h">'body'</span> <span class="sy0">=&gt;</span> <span class="st_h">'riri fifi loulou'</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'NewsTranslation'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="re0">$news</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
      <span class="st_h">'lang'</span> <span class="sy0">=&gt;</span> <span class="st_h">'fr'</span><span class="sy0">,</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">'toto tutu tata'</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.5 - No empty translation is created'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'NewsTranslation'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="re0">$news</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
      <span class="st_h">'lang'</span> <span class="sy0">=&gt;</span> <span class="st_h">'en'</span><span class="sy0">,</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.6 - New translations can be added'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'news'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'en'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">'toto tutu tata'</span><span class="sy0">,</span>
      <span class="st_h">'body'</span> <span class="sy0">=&gt;</span> <span class="st_h">'riri fifi loulou'</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'NewsTranslation'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="re0">$news</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
      <span class="st_h">'lang'</span> <span class="sy0">=&gt;</span> <span class="st_h">'en'</span><span class="sy0">,</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">'toto tutu tata'</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">info</span><span class="br0">&#40;</span><span class="st_h">'  2.7 - Existing translations can be deleted'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">get</span><span class="br0">&#40;</span><span class="re0">$editUrl</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">click</span><span class="br0">&#40;</span><span class="st_h">'Save'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'news'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
    <span class="st_h">'fr'</span> <span class="sy0">=&gt;</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'title'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span><span class="sy0">,</span>
      <span class="st_h">'body'</span> <span class="sy0">=&gt;</span> <span class="st_h">''</span>
    <span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'_with_csrf'</span> <span class="sy0">=&gt;</span> <span class="kw4">true</span><span class="br0">&#41;</span>
  <span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'form'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">hasErrors</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
&nbsp;
  <span class="me1">with</span><span class="br0">&#40;</span><span class="st_h">'doctrine'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
    <span class="me1">check</span><span class="br0">&#40;</span><span class="st_h">'NewsTranslation'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span>
      <span class="st_h">'id'</span> <span class="sy0">=&gt;</span> <span class="re0">$news</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span>
      <span class="st_h">'lang'</span> <span class="sy0">=&gt;</span> <span class="st_h">'fr'</span><span class="sy0">,</span>
    <span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span>
  <a href="http://www.php.net/end"><span class="kw3">end</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="sy0">;</span></pre></div></div>

<p>Here we are. Hope you find this helpful. I thought some kind of requirement would be pretty common, however, I didn&#8217;t find any directly related howto&#8217;s. If you can think of any other way to achieve this, please, let me know.</p>
<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tutos/449-optional-translation-form-for-i18n-objects-with-symfony-and-doctrine/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Créer une liste triable avec Symfony et jquery ui</title>
		<link>http://www.miximum.fr/tutos/435-creer-une-liste-triable-avec-symfony-et-jquery-ui</link>
		<comments>http://www.miximum.fr/tutos/435-creer-une-liste-triable-avec-symfony-et-jquery-ui#comments</comments>
		<pubDate>Sat, 23 Jan 2010 12:23:57 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Tutos]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=435</guid>
		<description><![CDATA[ 
Lors de mes d&#233;veloppements de ces derniers jours, je suis tomb&#233; sur un besoin qui me semblait relativement simple. Je voulais &#234;tre capable de trier rapidement une liste d&#8217;&#233;l&#233;ments. Et comme, malgr&#233; tout, j&#8217;ai un peu gal&#233;r&#233; &#224; trouver des tutoriaux &#224; jour, je vous livre ici le fruit de mes travaux.
J&#8217;utiliserai pour ce [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F435-creer-une-liste-triable-avec-symfony-et-jquery-ui"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F435-creer-une-liste-triable-avec-symfony-et-jquery-ui" height="61" width="51" /></a></div><div class="postimg alignleft"><a href="http://www.flickr.com/photos/24469297@N05/4297558881/" title="Symfony backlog" target="_blank"><img src="http://farm5.static.flickr.com/4068/4297558881_f8b9f64056_m.jpg" alt="Symfony backlog" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> </small></div>
<p>Lors de mes d&eacute;veloppements de ces derniers jours, je suis tomb&eacute; sur un besoin qui me semblait relativement simple. Je voulais &ecirc;tre capable de trier rapidement une liste d&#8217;&eacute;l&eacute;ments. Et comme, malgr&eacute; tout, j&#8217;ai un peu gal&eacute;r&eacute; &agrave; trouver des tutoriaux &agrave; jour, je vous livre ici le fruit de mes travaux.</p>
<p>J&#8217;utiliserai pour ce faire la <a href="http://jqueryui.com/">librairie jquery UI</a>, elle m&ecirc;me bas&eacute;e sur Jquery.</p>
<p>Attention, hein, c&#8217;est samedi matin, je suis mal r&eacute;veill&eacute;, &minus; edit&nbsp;: tellement mal r&eacute;veill&eacute; que j&#8217;ai publi&eacute; l&#8217;article avant de l&#8217;avoir termin&eacute;. fail&hellip; &minus; alors je raccourcirai au maximum. Si je ne suis pas clair, laissez moi des commentaires.</p>
<h2>Comme d&#8217;hab, commen&ccedil;ons par le mod&egrave;le</h2>
<p>Je voulais trouver un outil capable de g&eacute;rer un backlog de produit &agrave; la scrum, histoire de communiquer avec mes clients &agrave; distance. Cependant, malgr&eacute; l&#8217;existence d&#8217;<a href="http://www.icescrum.org/">excellents outils</a> d&eacute;di&eacute;s &agrave; cette <a href="http://www.aubryconseil.com/">sympathique m&eacute;thodologie agile</a>, je n&#8217;ai pas r&eacute;ussi &agrave; trouver quelque chose de vraiment simple et r&eacute;pondant &agrave; mes besoins. Alors, en bon geek, j&#8217;ai choisi de coder le mien.</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">Project:
  actAs:
    Sluggable:
      fields: [ title ]
      unique: true
&nbsp;
  columns:
    id: { type: integer, primary: true, autoincrement: true }
    title: { type: string(255), notnull: true }
&nbsp;
Story:
  actAs:
    Timestampable: ~
&nbsp;
  columns:
    id: { type: integer, primary: true, autoincrement: true }
    project_id: { type: integer, notnull: true }
    description: { type: clob, notnull: true }
    priority: { type: integer(2), default: 0 }
    effort: { type: integer(2), default: 0 }
&nbsp;
  relations:
    Project:
      foreignAlias: Stories
      local: project_id
      foreign: id
      type: one
      foreignType: many</pre></div></div>

<p>Pour les paresseux, je vous colle aussi les fixtures&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">&nbsp;
Project:
  demo:
    title: 'Demo project'
&nbsp;
Story:
  s1:
    description: 'En tant qu''anonyme, je peux m''inscrire sur le site pour devenir membre'
    priority: 1
    effort: 5
    Project: demo
&nbsp;
  s2:
    description: 'En tant que membre, je dispose d''une page d''accueil pour éditer mon profil et voir l''activité de mon réseau'
    priority: 3
    effort: 13
    Project: demo
&nbsp;
  s3:
    description: 'En tant que rédacteur, je peux écrire des articles de type magazine et les publier dans des catégories'
    priority: 2
    effort: 25
    Project: demo</pre></div></div>

<p>Voil&agrave; pour le mod&egrave;le. Je vous laisse construire tout &ccedil;a, vous connaissez le topo. Cr&eacute;ez ensuite un module &laquo;&nbsp;&nbsp;project&nbsp;&nbsp;&raquo;, avec une action &laquo;&nbsp;&nbsp;show&nbsp;&nbsp;&raquo; qui affichera une liste de users stories. Je vous passe le code de l&#8217;action qui n&#8217;a rien de sp&eacute;cial. Dans le template &laquo;&nbsp;&nbsp;showSuccess.php&nbsp;&nbsp;&raquo; du module project, ajoutez&nbsp;:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">// bla bla bla
&lt;div id=&quot;stories&quot;&gt;
<span class="kw2">&lt;?php</span> include_partial<span class="br0">&#40;</span><span class="st_h">'story/list'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'stories'</span> <span class="sy0">=&gt;</span> <span class="re0">$project</span><span class="sy0">-&gt;</span><span class="me1">getStories</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&lt;/div&gt;</pre></div></div>

<p>Vous aurez bien entendu surcharg&eacute; la fonction &laquo;&nbsp;&nbsp;getStories&nbsp;&nbsp;&raquo; pour trier les histoires par priorit&eacute;.</p>
<p>Cr&eacute;ez ensuite un module &laquo;&nbsp;&nbsp;story&nbsp;&nbsp;&raquo;, et ajoutez-y un template &laquo;&nbsp;&nbsp;_list.php&nbsp;&nbsp;&raquo; &hellip;</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">&lt;table class=&quot;stories&quot;&gt;
&lt;tbody&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$stories</span> <span class="kw1">as</span> <span class="re0">$story</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
    &lt;tr class=&quot;story&quot; id=&quot;story_<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&quot;&gt;
      <span class="kw2">&lt;?php</span> include_partial<span class="br0">&#40;</span><span class="st_h">'story/detail'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">'story'</span> <span class="sy0">=&gt;</span> <span class="re0">$story</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
    &lt;/tr&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">endforeach</span> <span class="sy1">?&gt;</span>
&lt;/tbody&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;#id&lt;/th&gt;
    &lt;th&gt;Description&lt;/th&gt;
    &lt;th&gt;Effort&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;</pre></div></div>

<p>Ainsi qu&#8217;un autre template &laquo;&nbsp;&nbsp;_detail.php&nbsp;&nbsp;&raquo;</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">&lt;td&gt;
  &lt;a href=&quot;#&quot; class=&quot;sort-button fg-button fg-button-icon-left ui-state-default ui-corner-all&quot;&gt;
    &lt;span class=&quot;ui-icon ui-icon-arrowthick-2-n-s&quot;&gt;&lt;/span&gt;
    <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  &lt;a&gt;
&lt;/td&gt;
&lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">getDescription</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
&lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">getEffort</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;</pre></div></div>

<p>Vous voil&agrave; donc avec une belle liste de users stories, affich&eacute;es par ordre de priorit&eacute;, et que vous souhaiteriez pouvoir r&eacute;ordonner par drag&#8217;n'drop.</p>
<h2>Des p&#8217;tits tris, des p&#8217;tits tris, encore des p&#8217;tits tris&hellip;</h2>
<p>Dans le r&eacute;pertoire web, ajoutez dans votre fichier js maison (cr&eacute;ez le s&#8217;il n&#8217;existe pas) le code suivant.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">$<span class="br0">&#40;</span>document<span class="br0">&#41;</span>.<span class="me1">ready</span><span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  $<span class="br0">&#40;</span><span class="st0">&quot;#stories table tbody&quot;</span><span class="br0">&#41;</span>.<span class="me1">sortable</span><span class="br0">&#40;</span><span class="br0">&#123;</span>
    <span class="co1">// limitons les déplacements sur l'axe des ordonnées, ce sera plus propre</span>
    axis<span class="sy0">:</span> <span class="st0">'y'</span><span class="sy0">,</span>
&nbsp;
    <span class="co1">// Il faut cliquer sur cet élément pour pouvoir initier le drag'n'drop</span>
    handle<span class="sy0">:</span> <span class="st0">'.sort-button'</span><span class="sy0">,</span>
&nbsp;
    <span class="co1">// Créons un joli trou stylé lors des déplacements</span>
    placeholder<span class="sy0">:</span> <span class="st0">'ui-state-highlight'</span><span class="sy0">,</span>
    forcePlaceholderSize<span class="sy0">:</span> <span class="kw2">true</span><span class="sy0">,</span>
&nbsp;
    <span class="co1">// Cette fonction permet à notre ligne de conserver son formatage lors du déplacement</span>
    <span class="co1">// Pas vraiment utile, mais plus agréable à l'œil</span>
    helper<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>e<span class="sy0">,</span> tr<span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="kw2">var</span> $originals <span class="sy0">=</span> tr.<span class="me1">children</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="kw2">var</span> $helper <span class="sy0">=</span> tr.<span class="me1">clone</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      $helper.<span class="me1">children</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">each</span><span class="br0">&#40;</span><span class="kw2">function</span><span class="br0">&#40;</span>index<span class="br0">&#41;</span>
      <span class="br0">&#123;</span>
        <span class="co1">// Set helper cell sizes to match the original sizes</span>
        $<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">width</span><span class="br0">&#40;</span>$originals.<span class="me1">eq</span><span class="br0">&#40;</span>index<span class="br0">&#41;</span>.<span class="me1">width</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
      <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="kw1">return</span> $helper<span class="sy0">;</span>
    <span class="br0">&#125;</span><span class="sy0">,</span>
&nbsp;
    <span class="co1">// La fonction appelée quand un élément change de position</span>
    <span class="co1">// C'est le code vraiment utile, en fait</span>
    update<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span>event<span class="sy0">,</span> ui<span class="br0">&#41;</span><span class="br0">&#123;</span>
      <span class="co1">// Construit un tableau des ids des stories</span>
      serial <span class="sy0">=</span> $<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span>.<span class="me1">sortable</span><span class="br0">&#40;</span><span class="st0">'serialize'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
      <span class="co1">// Appelle une action en ajax</span>
      $.<span class="me1">ajax</span><span class="br0">&#40;</span><span class="br0">&#123;</span>
        url<span class="sy0">:</span> updateorderurl<span class="sy0">,</span> <span class="co1">// set in layout.php</span>
        type<span class="sy0">:</span> <span class="st0">&quot;post&quot;</span><span class="sy0">,</span>
        data<span class="sy0">:</span> serial<span class="sy0">,</span>
        error<span class="sy0">:</span> <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span>
          <span class="kw3">alert</span><span class="br0">&#40;</span><span class="st0">&quot;Error ! Order not updated&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
      <span class="br0">&#125;</span><span class="br0">&#41;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div>

<p>Remarquez que la variable &laquo;&nbsp;&nbsp;updateorderurl&nbsp;&nbsp;&raquo; contient l&#8217;url de l&#8217;action qui va r&eacute;aliser la r&eacute;affectation des priorit&eacute;s. Comme cette url est g&eacute;n&eacute;r&eacute;e par Symfony, elle est d&eacute;finie dans le contr&ocirc;leur, puis affect&eacute;e &agrave; une variable javascript  dans la layout gr&acirc;ce &agrave; un slot. &Ccedil;a vaut ce que &ccedil;a vaut.</p>
<p>Normalement, vous devriez maintenant &ecirc;tre capable de changer l&#8217;ordre des stories c&ocirc;t&eacute; frontend. Bien entendu, le code m&eacute;tier charg&eacute; de g&eacute;rer le r&eacute;ordonnancement n&#8217;existe pas encore.</p>
<h2>Au c&oelig;ur du m&eacute;tier</h2>
<p>Cr&eacute;ons donc une nouvelle action dans le module &laquo;&nbsp;&nbsp;story&nbsp;&nbsp;&raquo;.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;">  <span class="kw2">public</span> <span class="kw2">function</span> executeUpdateOrder<span class="br0">&#40;</span>sfWebRequest <span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// Il nous faut un moyen de récupérer le projet en question</span>
    <span class="re0">$project</span> <span class="sy0">=</span> Doctrine<span class="sy0">::</span><span class="me2">getTable</span><span class="br0">&#40;</span><span class="st_h">'project'</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">find</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'project_id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">forward404Unless</span><span class="br0">&#40;</span><span class="re0">$project</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// Correspond à la variable 'serial' dans le js, vous vous souvenez ?</span>
    <span class="co1">// C'est un simple tableau d'ids</span>
    <span class="re0">$order</span> <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'story'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$project</span><span class="sy0">-&gt;</span><span class="me1">updateStoriesOrder</span><span class="br0">&#40;</span><span class="re0">$order</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">HEADER_ONLY</span><span class="sy0">;</span>
  <span class="br0">&#125;</span></pre></div></div>

<p>Nous revoil&agrave; repartis dans le mod&egrave;le. &Eacute;ditons notre classe Project.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;color: #ccc; font: 12px Consolas, Lucida Console, Monaco, monospace;"><span class="co1">// lib/model/doctrine/Project.class.php</span>
<span class="co1">// …</span>
  <span class="co4">/**
   * Update the stories order
   *
   * @param array $order An array with the stories ids, sorted by priority
   **/</span>
  <span class="kw2">public</span> <span class="kw2">function</span> updateStoriesOrder<span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="re0">$order</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">foreach</span><span class="br0">&#40;</span><span class="re0">$order</span> <span class="kw1">as</span> <span class="re0">$priority</span> <span class="sy0">=&gt;</span> <span class="re0">$storyId</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$story</span> <span class="sy0">=</span> Doctrine<span class="sy0">::</span><span class="me2">getTable</span><span class="br0">&#40;</span><span class="st_h">'story'</span><span class="br0">&#41;</span>
        <span class="sy0">-&gt;</span><span class="me1">find</span><span class="br0">&#40;</span><span class="re0">$storyId</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
      <span class="kw1">if</span><span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$story</span> <span class="sy0">||</span> <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">getProjectId</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">!=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getId</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
        throw <span class="kw2">new</span> Exception<span class="br0">&#40;</span><span class="st_h">'moo'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
      <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">setPriority</span><span class="br0">&#40;</span><span class="re0">$priority</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="re0">$story</span><span class="sy0">-&gt;</span><span class="me1">save</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span></pre></div></div>

<p>Tadaaaaaam<span class="fine">&nbsp;</span>! &Ccedil;a devrait fonctionner. Voil&agrave;, c&#8217;est tout. Tiens, au moment o&ugrave; je finis d&#8217;&eacute;crire ces lignes, je m&#8217;aper&ccedil;ois qu&#8217;un <a href="http://www.symfony-project.org/plugins/sfDoctrineJQueryUISortablePlugin">plugin cens&eacute; faire exactement la m&ecirc;me chose</a> vient de sortir. Frustration. Bon, tant pis, bon week-end quand m&ecirc;me.</p>
<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F435-creer-une-liste-triable-avec-symfony-et-jquery-ui"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftutos%2F435-creer-une-liste-triable-avec-symfony-et-jquery-ui" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tutos/435-creer-une-liste-triable-avec-symfony-et-jquery-ui/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Colombo 2.0</title>
		<link>http://www.miximum.fr/humour/420-colombo-2-0</link>
		<comments>http://www.miximum.fr/humour/420-colombo-2-0#comments</comments>
		<pubDate>Tue, 05 Jan 2010 11:43:21 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Humour]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[client]]></category>
		<category><![CDATA[freelance]]></category>
		<category><![CDATA[newbie]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[vim]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=420</guid>
		<description><![CDATA[ photo credit&#160;: Pieter Musterd
D&#233;s qu&#8217;elle est entr&#233;e dans mon bureau, j&#8217;ai su que l&#8217;affaire ne sentait pas bon. On ne fait pas carri&#232;re dans ma branche si on ne d&#233;veloppe pas un solide sixi&#232;me sens pour flairer les probl&#232;mes. Et manifestement, mon embrouillom&#232;tre me criait que je ferais mieux de me planquer sous mon [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Fhumour%2F420-colombo-2-0"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Fhumour%2F420-colombo-2-0" height="61" width="51" /></a></div><div class="postimg alignleft"><a href="http://www.flickr.com/photos/90794078@N00/3796655530/" title="Night" target="_blank"><img src="http://farm3.static.flickr.com/2468/3796655530_d4efa6d5ea_m.jpg" alt="Night" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc-nd/2.0/" title="Attribution-NonCommercial-NoDerivs License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/90794078@N00/3796655530/" title="Pieter Musterd" target="_blank">Pieter Musterd</a></small></div>
<p>D&eacute;s qu&#8217;elle est entr&eacute;e dans mon bureau, j&#8217;ai su que l&#8217;affaire ne sentait pas bon. On ne fait pas carri&egrave;re dans ma branche si on ne d&eacute;veloppe pas un solide sixi&egrave;me sens pour flairer les probl&egrave;mes. Et manifestement, mon embrouillom&egrave;tre me criait que je ferais mieux de me planquer sous mon bureau la t&ecirc;te cach&eacute;e dans une des poches de mon imper.</p>
<p>Malheureusement, mon vieux fauteuil donnait des signes de fatigue, la machine &agrave; caf&eacute; agonisait, et j&#8217;avais un besoin urgent de renflouer mes caisses. Je n&#8217;avais pas les moyens de faire le difficile.</p>
<p>&mdash; Thibault Jouannic, d&eacute;tective TMA priv&eacute;<span class="fine">&nbsp;</span>?<br />
&mdash; Lui m&ecirc;me, &agrave; votre service m&#8217;dame. Entez, je vous en prie.</p>
<p>D&#8217;un signe de main, je l&#8217;invitai &agrave; s&#8217;asseoir. Pendant qu&#8217;elle s&#8217;ex&eacute;cutait, je l&#8217;examinais &agrave; la d&eacute;rob&eacute;e. Mon boulot m&#8217;amenait plut&ocirc;t &agrave; c&ocirc;toyer de gros barbus aux dents jaunies par la clope et le caf&eacute;, et portants des tee-shirt rigolos. Aussi, cette belle brune &eacute;lanc&eacute;e en tailleur &eacute;l&eacute;gant avait imm&eacute;diatement &eacute;veill&eacute; mon attention. D&#8217;un point de vue professionnel, j&#8217;entends.</p>
<p>&mdash; Alors m&#8217;dame, que puis-je faire pour vous &ecirc;tre utile<span class="fine">&nbsp;</span>?</p>
<p>J&#8217;ai toujours pens&eacute; qu&#8217;un bon d&eacute;tective &eacute;tait comme un m&eacute;decin. Pas de bon diagnostic sans conna&icirc;tre les sympt&ocirc;mes. Le probl&egrave;me, c&#8217;est que mes clients me prenaient souvent pour une esp&egrave;ce de devin vaudou, et j&#8217;&eacute;tais oblig&eacute; de les soumettre &agrave; un interrogatoire digne d&#8217;un inquisiteur espagnol pour esp&eacute;rer obtenir le minimum d&#8217;indices.</p>
<p>&mdash; Je suis Lucie S., webmaster d&#8217;un site de petites annonces agricoles, et depuis quelques temps il semble que les performances ne soient plus au rendez-vous.<br />
&mdash; Vous voulez dire que le site est lent<span class="fine">&nbsp;</span>?<br />
&mdash; En fait, ce site n&#8217;a jamais &eacute;t&eacute; une foudre de guerre, mais depuis deux jours, la situation est devenue catastrophique. La moindre requete n&eacute;cessite 45 secondes avant d&#8217;aboutir. Quand on ne r&eacute;cup&egrave;re pas une page blanche. Et le moteur de recherche, le c&oelig;ur du site, est compl&egrave;tement inutilisable.</p>
<div class="postimg alignright"><a href="http://www.flickr.com/photos/71447254@N00/415929172/" title="Dr Cog" target="_blank"><img src="http://farm1.static.flickr.com/182/415929172_ca443ab75d_m.jpg" alt="Dr Cog" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc/2.0/" title="Attribution-NonCommercial License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/71447254@N00/415929172/" title="Balakov" target="_blank">Balakov</a></small></div>
<p>Les performances<span class="fine">&nbsp;</span>! Un grand classique. 90% de mes affaires concernaient des sites qui se trainaient comme des escargots neurasth&eacute;niques. Parfois, rajouter quelques caches rendait le probl&egrave;me supportable, mais la plupart du temps, la conception de base &eacute;tait tellement m&eacute;diocre qu&#8217;il n&#8217;y avait rien &agrave; faire. Allez expliquer &ccedil;a &agrave; un client d&eacute;sesp&eacute;r&eacute;.</p>
<p>Bref<span class="fine">&nbsp;</span>! Cela s&#8217;annon&ccedil;ait comme une petite enqu&ecirc;te de routine. Alors pourquoi mon instinct me criait-il qu&#8217;il y avait anguille sous roche<span class="fine">&nbsp;</span>?</p>
<p>&mdash; Vous dites que depuis deux jours, la situation a empir&eacute;. Que s&#8217;est-il pass&eacute; de sp&eacute;cial &agrave; ce moment l&agrave;<span class="fine">&nbsp;</span>?<br />
&mdash; Mais justement<span class="fine">&nbsp;</span>! Rien, rien du tout.</p>
<p>C&#8217;&eacute;tait louche. Soit elle me mentait, soit elle ignorait quelque chose, mais son histoire n&#8217;&eacute;tait pas cr&eacute;dible, et elle m&ecirc;me ne semblait pas convaincue. Je d&eacute;cidai de la titiller.</p>
<p>&mdash; Aucune erreur dans vos tests unitaires ou fonctionnels<span class="fine">&nbsp;</span>? Que dit votre plate-forme d&#8217;int&eacute;gration continue<span class="fine">&nbsp;</span>?<br />
&mdash; Euh&hellip;</p>
<p>J&#8217;avais touch&eacute; juste. Je posais la question pour la forme, j&#8217;aurais &eacute;t&eacute; bien surpris d&#8217;apprendre l&#8217;existence de tests automatis&eacute;s. La plupart du temps, je devais m&#8217;estimer heureux si des conventions de codages &eacute;taient d&eacute;finies et respect&eacute;es. Mais bon, la question avait le don de rendre mes clients mal &agrave; l&#8217;aise, et un client qui n&#8217;est pas droit dans ses chausettes est moins enclin &agrave; discuter mes tarifs. Et puis, &ccedil;a me distrait.</p>
<p>&mdash; &Eacute;coutez, je vais voir ce que je peux faire. Mais il me faudra un acc&egrave;s au serveur de production, un compte sur le gestionnaire de source, et un dump r&eacute;cent de la base de donn&eacute;es.<br />
&mdash; Je vous communiquerai ces informations dans la journ&eacute;e. Il y a autre chose, me dit-elle l&#8217;air embarass&eacute;e.<br />
&mdash; Oui<span class="fine">&nbsp;</span>?</p>
<p>Je masquai un petit sourire. Quand un client ne rechigne pas &agrave; vous donner l&#8217;acc&egrave;s &agrave; la prod, c&#8217;est qu&#8217;il y a toujours autre chose.</p>
<p>&mdash; Un grand salon d&#8217;agriculture a lieu dans deux jours. Une pr&eacute;sentation publique du site est pr&eacute;vue. C&#8217;est un site subventionn&eacute; par le minist&egrave;re, vous comprenez. Et si la situation n&#8217;est pas revenue &agrave; la normale d&#8217;ici l&agrave;, nous risquons de nous retrouver dans une situation d&eacute;licate.</p>
<p>Ben voyons<span class="fine">&nbsp;</span>! J&#8217;aimais travailler dans le stress du temps qui passe. Corriger un bug qui semblait insolvable est gratifiant, mais aucune sensation n&#8217;est plus exaltante que celle de le faire dans un d&eacute;lai aussi court. Et puis, &ccedil;a me permet de gonfler mon tarif.</p>
<p>&mdash; Je prends l&#8217;affaire en main m&#8217;dame. Envoyez moi les donn&eacute;es dont j&#8217;ai besoin, et je commence sur le champ.</p>
<div class="postimg alignleft"><a href="http://www.flickr.com/photos/7626777@N02/2975010771/" title="Smoke &#038; Smokey" target="_blank"><img src="http://farm4.static.flickr.com/3237/2975010771_ca17f3a713_m.jpg" alt="Smoke &#038; Smokey" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc-nd/2.0/" title="Attribution-NonCommercial-NoDerivs License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/7626777@N02/2975010771/" title="Anders Adermark" target="_blank">Anders Adermark</a></small></div>
<p>Une fois qu&#8217;elle fut sortie, je m&#8217;abandonnais &agrave; une m&eacute;ditation contemplative. R&eacute;fl&eacute;chir repr&eacute;sente une part importante de mon temps de travail. Je dirais m&ecirc;me que c&#8217;est la part la plus importante. Il m&#8217;est m&ecirc;me arriv&eacute; de r&eacute;soudre des affaires sans poser les mains sur mon clavier b&eacute;po.</p>
<p>L&#8217;ennui, avec la r&eacute;flexion, c&#8217;est qu&#8217;elle passe facilement pour de l&#8217;oisivet&eacute; pour les non-init&eacute;s. C&#8217;est pour &ccedil;a que je prends toujours l&#8217;air affair&eacute; et soucieux quand j&#8217;attends un client. Pour avoir l&#8217;air comp&eacute;tent et efficace.</p>
<p>C&#8217;est important, les apparences, dans mon m&eacute;tier. Pourquoi croyez-vous que je laisse tra&icirc;ner toutes ces piles de dossiers poussi&eacute;reux<span class="fine">&nbsp;</span>? Pas pour le travail, tout est num&eacute;ris&eacute;. Non, c&#8217;est simplement parce qu&#8217;un d&eacute;tective sans imper et qui ne pue pas le tabac froid. &Ccedil;a ne fait pas s&eacute;rieux.</p>
<p>Je suis bien rest&eacute; deux heures, les pieds sur mon bureau couvert de paperasse bidon, sirotant mon infusion &agrave; la verveine (que je ne bois qu&#8217;en cachette, un vrai d&eacute;tective est cens&eacute; carburer au caf&eacute; noir d&eacute;gueulasse), avant de parvenir &agrave; la conclusion suivante&nbsp;: j&#8217;&eacute;tais dans une impasse. J&#8217;avais le crime, mais pas le mobile.</p>
<p>Quelqu&#8217;un aurait-il sabot&eacute; volontairement le site pour plomber la pr&eacute;sentation publique<span class="fine">&nbsp;</span>? Un hacker<span class="fine">&nbsp;</span>? Un script-kiddie<span class="fine">&nbsp;</span>? Un concurrent<span class="fine">&nbsp;</span>?  Je n&#8217;arrivais pas &agrave; m&#8217;en persuader. Les gens regardent trop la t&eacute;l&eacute;. Ils s&#8217;imaginent que nous autres, d&eacute;tectives TMA, passont notre temps &agrave; combattre le crime virtuel organis&eacute;, sauver des demoiselles en d&eacute;tresse et emp&ecirc;cher <i>in-extremis</i> des fus&eacute;es d&#8217;exploser. Tout &ccedil;a, c&#8217;est la faute &agrave; Colombo.</p>
<p>La r&eacute;alit&eacute; est toute  autre, et le quotidien d&#8217;un d&eacute;tective TMA est finalement assez banal. Point de grand bantitisme. Point de course poursuite effr&eacute;n&eacute;e dans les limbes du web. Simplement quelques corrections orthographiques par ci, un petit bug css (damn IE!) par l&agrave;. Et puis, m&eacute;tro, popote, dodo. Comme tout le monde, finalement.</p>
<p>C&#8217;est justement pour cette raison que je ne parvenais pas &agrave; accepter l&#8217;id&eacute;e d&#8217;un acte de piraterie. R&egrave;gle n&deg;1 du d&eacute;tective TMA&nbsp;: &laquo;&nbsp;&nbsp;N&#8217;attribue jamais &agrave; la malveillance ce qui peut s&#8217;expliquer par l&#8217;incomp&eacute;tence&nbsp;&nbsp;&raquo;. Quelqu&#8217;un avait merd&eacute; dans cette histoire, et il fallait trouver qui.</p>
<div class="postimg alignleft"><a href="http://www.flickr.com/photos/63894760@N00/419776073/" title="Mala Strana" target="_blank"><img src="http://farm1.static.flickr.com/186/419776073_24039d39ab_m.jpg" alt="Mala Strana" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc-nd/2.0/" title="Attribution-NonCommercial-NoDerivs License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/63894760@N00/419776073/" title="Pensiero" target="_blank">Pensiero</a></small></div>
<p>C&#8217;est maintenant que les choses s&eacute;rieuses allaient commencer. Je commen&ccedil;ai par v&eacute;rifier la justesse des informations envoy&eacute;es par ma cliente, et r&eacute;cup&eacute;rais le trunk du site. Haha<span class="fine">&nbsp;</span>! Quelqu&#8217;un avait commit&eacute; un &laquo;&nbsp;&nbsp;Thumbs.db&nbsp;&nbsp;&raquo; dans le dossier &laquo;&nbsp;&nbsp;img&nbsp;&nbsp;&raquo;. Quelle bande de newbies<span class="fine">&nbsp;</span>! Mais<span class="fine">&nbsp;</span>!? Ils avaient &eacute;galement commit&eacute; leur r&eacute;pertoire de backup, rempli de dumps sql<span class="fine">&nbsp;</span>?! Seigneur<span class="fine">&nbsp;</span>! Je sentis mes yeux tenter de jaillir hors de leurs orbites.</p>
<p>Pris d&#8217;un doute, je r&eacute;cup&eacute;rai &eacute;galement en local la version de production du site. Autant vous dire qu&#8217;&agrave; ce moment, je m&#8217;attendais &agrave; tout. Pour le plaisir, j&#8217;ouvris quelques fichiers au hasard gr&acirc;ce &agrave; mon &eacute;diteur de geek pr&eacute;f&eacute;r&eacute; (vim, car tout le monde sait que les vrais pros n&#8217;utilisent jamais emacs). La syntaxe hasardeuse, l&#8217;indentation erratique, et les caract&egrave;res de saut de lignes al&eacute;atoires me firent vite tourner la t&ecirc;te, le c&oelig;ur au bord des l&egrave;vres.</p>
<p>&mdash; &laquo;&nbsp;&nbsp;Fiston, dans la vie, on ne m&eacute;lange jamais les espaces et les tabulations&nbsp;&nbsp;&raquo;, me disait souvent ma grand-m&egrave;re, de son air sage et p&eacute;n&eacute;trant de v&eacute;t&eacute;ran qui avait connu le Cobol. Il faut croire que tout le monde n&#8217;a pas eu la chance d&#8217;avoir une grand m&egrave;re comme la mienne.</p>
<p>En dernier recours, je d&eacute;cidai de jeter un coup d&#8217;&oelig;il &agrave; l&#8217;historique svn. En parcourant rapidement les derniers messages de commits, quelque chose attira mon regard. Serait-il possible que&hellip; Quelques diffs plus tard, j&#8217;&eacute;tais fix&eacute;. Les mains derri&egrave;re la t&ecirc;te, je me laissai aller en arri&egrave;re, un sourire sur les l&egrave;vres. Voil&agrave; une affaire rondement men&eacute;e.</p>
<p>Le c&oelig;ur l&eacute;ger, je d&eacute;crochai le t&eacute;l&eacute;phone, et appelai ma cliente.</p>
<p>&mdash; Allo<span class="fine">&nbsp;</span>?<br />
&mdash; Thibault Jouannic, d&eacute;tective TMA &agrave; l&#8217;appareil. J&#8217;ai quelque chose qui peut vous int&eacute;resser.<br />
&mdash; Vous avez trouv&eacute;<span class="fine">&nbsp;</span>?<br />
&mdash; Pas par t&eacute;l&eacute;phone. &Eacute;coutez, je veux que vous r&eacute;unissiez l&#8217;&eacute;quipe de d&eacute;veloppement au complet, dans vos locaux, demain 9h. Assurez-vous que tout le monde soit pr&eacute;sent. Et amenez des croissants.</p>
<div class="postimg alignright"><a href="http://www.flickr.com/photos/14326506@N08/3294156681/" title="Van Williams &#038; Bruce Lee" target="_blank"><img src="http://farm4.static.flickr.com/3557/3294156681_5f68e7dce4_m.jpg" alt="Van Williams &#038; Bruce Lee" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by/2.0/" title="Attribution License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/14326506@N08/3294156681/" title="Elmo Alves" target="_blank">Elmo Alves</a></small></div>
<p>Sans attendre sa r&eacute;ponse, je raccrochai. Pour ma peine, je d&eacute;cidai de me pr&eacute;parer une autre infusion. Demain promettait d&#8217;&ecirc;tre une belle journ&eacute;e.</p>
<p>Le lendemain, je me rendis au si&egrave;ge de S&hellip; S.A., dans ma vielle guibarde fumante. J&#8217;ai l&#8217;habitude de me d&eacute;placer en v&eacute;lo, mais pour les rencontres avec mes clients, &ccedil;a me donne un genre.</p>
<p>Quand j&#8217;arrivai &agrave; la r&eacute;union, tout le monde &eacute;tait d&eacute;j&agrave; pr&eacute;sent, et &agrave; l&#8217;heure, preuve s&#8217;il en est de la gravit&eacute; de la situation. Les croissants &eacute;taient chauds, le caf&eacute; finissait de glouglouter dans le percolateur, les choses s&#8217;annon&ccedil;aient bien.</p>
<p>Je m&#8217;emparai d&#8217;une viennoiserie, et demandai &agrave; chacun de se pr&eacute;senter.</p>
<p>&mdash; Fr&eacute;d&eacute;ric B., chef de projet<br />
&mdash; Solange C., d&eacute;veloppeuse<br />
&mdash; Alfred D., designer / int&eacute;grateur<br />
&mdash; Anatole P., d&eacute;veloppeur stagiaire<br />
&mdash; Pierre Z, d&eacute;veloppeur<br />
&mdash; Lucie S., webmaster</p>
<p>Apr&egrave;s ce petit tour de table, je leur tournai le dos, et m&#8217;approchai de la fen&ecirc;tre, faignant de contempler le paysage b&eacute;tonneux des alentours d&#8217;un air inspir&eacute;. Dans mon m&eacute;tier, il faut savoir m&eacute;nager ses effets. Lorsque je jugeai la tension nerveuse suffisante, je me retournai, et commen&ccedil;ai mon num&eacute;ro.</p>
<p>&mdash; Comme vous le savez, j&#8217;ai &eacute;t&eacute; contact&eacute; hier par Lucie S., ici pr&eacute;sente, pour &eacute;lucider un cas myst&eacute;rieux. Depuis 2 jours, le site www.&hellip;.com semble rencontrer des probl&egrave;mes de lenteur anormale. Est-ce exact<span class="fine">&nbsp;</span>?<br />
&mdash; Tout &agrave; fait.<br />
&mdash; Cet &eacute;venement survient juste avant une importante pr&eacute;sentation publique du site. Si l&#8217;anomalie n&#8217;est pas r&eacute;solue, sa r&eacute;putation sera ruin&eacute;e, et par exension celle de l&#8217;agence web qui l&#8217;a con&ccedil;u et r&eacute;alis&eacute;, n&#8217;est-ce-pas<span class="fine">&nbsp;</span>?<br />
&mdash; En effet.<br />
&mdash; Comme vous, j&#8217;ai pens&eacute; &agrave; un acte malveillant. La vile d&eacute;marche d&#8217;un concurrent amer et jaloux d&#8217;avoir &eacute;t&eacute; &eacute;conduit lors de l&#8217;appel d&#8217;offre. Aussi ai-je analys&eacute; en d&eacute;tails les diff&eacute;rents logs de votre serveur de production. J&#8217;ai vite d&eacute;couvert que je faisais fausse route. Je n&#8217;ai rien remarqu&eacute; d&#8217;anormal. En revanche, je peux vous dire une chose.</p>
<p>Je les parcourus tous de mon regard de d&eacute;tective p&eacute;n&eacute;trant. La tension &eacute;tait &agrave; son comble, chacun &eacute;tait pendu &agrave; mes l&egrave;vres, retenant son souffle.</p>
<p>&mdash; Mesdames, messieurs, il s&#8217;agit d&#8217;un acte interne. Qui plus est, j&#8217;affirme que le coupable se trouve dans cette pi&egrave;ce<span class="fine">&nbsp;</span>!</p>
<p>L&#8217;annonce fit son effet. Des exclamations d&#8217;indignation &eacute;clat&egrave;rent, des caf&eacute;s furent renvers&eacute;s, certains manqu&egrave;rent s&#8217;&eacute;touffer avec leur croissant. J&#8217;attendis, inflexible, que le calme revienne, et imposai le silence de mon &oelig;il de lynx (la bestiole, pas le navigateur).</p>
<p>&mdash; Lucie S., vous m&#8217;avez annonc&eacute; lors de notre premier entretien qu&#8217;aucun changement n&#8217;&eacute;tais survenu sur l&#8217;application au moment du d&eacute;but des anomalies. Et bien, madame S., vous vous &ecirc;tes tromp&eacute;e. Il y a bel et bien eu une livraison ce jour l&agrave;.</p>
<p>Le chef de projet bondit alors de sa chaise, en renversant son caf&eacute;, hurlant presque.</p>
<p>&mdash; Mensonge<span class="fine">&nbsp;</span>! Calomnies<span class="fine">&nbsp;</span>! Aucune livraison n&#8217;a &eacute;t&eacute; d&eacute;ploy&eacute;e en production depuis 10 jours. V&eacute;rifiez le svn, le dernier tag date de plus d&#8217;une semaine. Et aucun ticket n&#8217;est pass&eacute; en &eacute;tat &laquo;&nbsp;&nbsp;clotur&eacute;&nbsp;&nbsp;&raquo; durant cette p&eacute;riode<span class="fine">&nbsp;</span>! Vous mentez<span class="fine">&nbsp;</span>!<br />
&mdash; Croyez-moi bien, j&#8217;ai v&eacute;rifi&eacute; ces &eacute;l&eacute;ments. Et comme vous le dite, aucune livraison n&#8217;a &eacute;t&eacute; d&eacute;ploy&eacute;e en utilisant votre proc&eacute;dure de livraison <em>officielle</em>. &mdash; J&#8217;appuyais sur le terme &mdash; En revanche, il y a bien eu une livraison <em>sans passer par votre proc&eacute;dure de d&eacute;ploiement<span class="fine">&nbsp;</span>!</em><br />
&mdash; C&#8217;en est trop, rugit le chef de projet. Comment pouvez-vous l&#8217;affirmer<span class="fine">&nbsp;</span>?<br />
&mdash; C&#8217;est simple, j&#8217;ai eu l&#8217;occasion de comparer la version svn des sources, avec ce qu&#8217;on peut trouver sur vos serveurs de production et pr&eacute;production. Le r&eacute;sultat fut tr&eacute;s instructif, je dois dire.</p>
<div class="postimg alignleft"><a href="http://www.flickr.com/photos/14838182@N00/4163064826/" title="Pens in light" target="_blank"><img src="http://farm3.static.flickr.com/2754/4163064826_1b5d350a02_m.jpg" alt="Pens in light" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc-nd/2.0/" title="Attribution-NonCommercial-NoDerivs License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/14838182@N00/4163064826/" title="maistora" target="_blank">maistora</a></small></div>
<p>Je les observai d&#8217;un air amus&eacute;, avant de reprendre.</p>
<p>&mdash; J&#8217;ai en effet pu constater que ces trois environnements n&#8217;&eacute;taient pas synchronis&eacute;s. Il ne peut y avoir qu&#8217;une seule explication. Quelqu&#8217;un n&#8217;a pas respect&eacute; votre proc&eacute;dure de d&eacute;ploiement.</p>
<p>L&#8217;assistance resta muette. Des murmures incr&eacute;dules se firent entendre. Le chef de projet, hagard, bafouilla.</p>
<p>&mdash; Mais&hellip; Qui<span class="fine">&nbsp;</span>? Qui pourrait faire une chose pareille<span class="fine">&nbsp;</span>?<br />
&mdash; Voyez-vous, j&#8217;ai eu l&#8217;occasion d&#8217;analyser les logs svn de votre application. Quelque chose a attir&eacute; mon attention. Quelque chose qui n&#8217;aurait pas du se trouver l&agrave;.</p>
<p>Suspense. J&#8217;observai une pause th&eacute;&acirc;trale, avant de m&#8217;&eacute;crier&nbsp;:</p>
<p>&mdash; Votre log svn est en effet truff&eacute; de messages de commits vides<span class="fine">&nbsp;</span>!</p>
<p>Stupeur dans l&#8217;auditoire. Le chef de projet, le souffle coup&eacute;, retomba sur sa chaise, effondr&eacute;. Je m&#8217;emparai d&#8217;un croissant, leur laissant le temps de reprendre leurs esprits avant de continuer.</p>
<p>&mdash; Plus int&eacute;ressant. J&#8217;ai &eacute;galement remarqu&eacute; que ces commits f&eacute;lons se sont multipli&eacute;s depuis quelque temps.</p>
<p>Je pointai alors un doigt vengeur vers l&#8217;un de mes auditeurs.</p>
<p>&mdash; Monsieur Anatole P. Pourriez vous nous indiquer &agrave; quel moment vous avez commenc&eacute; &agrave; travailler sur ce projet<span class="fine">&nbsp;</span>?!</p>
<p>Le pauvre stagiaire, au comble de l&#8217;embarras, parvint &agrave; peine &agrave; bredouiller une r&eacute;ponse intelligible.</p>
<p>&mdash; Une&hellip; une semaine environ. Mon stage a d&eacute;but&eacute; au d&eacute;but du mois.<br />
&mdash; Monsieur Anatole P. N&#8217;est-il-pas vrai que vous avez, au cours de cette semaine, r&eacute;alis&eacute; des d&eacute;veloppements sur le site dont nous parlons<span class="fine">&nbsp;</span>?<br />
&mdash; Oui, mais&hellip;<br />
&mdash; Et n&#8217;est-il-pas vrai qu&#8217;au cours de ces travaux, vous avez modifi&eacute; l&#8217;un des fichiers de configuration de l&#8217;application<span class="fine">&nbsp;</span>?<br />
&mdash; Peut-&ecirc;tre bien que oui.<br />
&mdash; Et bien monsieur Anatole P., lorsque vous avez livr&eacute; vos travaux directement sur le serveur de production, vous avez &eacute;cras&eacute; un fichier de configuration avec les param&egrave;tres de la plate-forme d&#8217;int&eacute;gration. Cela a eu pour action de <em>d&eacute;sactiver tous les caches de la production<span class="fine">&nbsp;</span>!</em></p>
<div class="postimg alignleft"><a href="http://www.flickr.com/photos/37996583025@N01/2821633690/" title="Not getting Involved" target="_blank"><img src="http://farm4.static.flickr.com/3120/2821633690_e0cb9b6bbb_m.jpg" alt="Not getting Involved" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-nc/2.0/" title="Attribution-NonCommercial License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/37996583025@N01/2821633690/" title="TarikB" target="_blank">TarikB</a></small></div>
<p>Dans la folie qui s&#8217;ensuivit, le chef de projet, rouge de col&egrave;re, tonna.</p>
<p>&mdash; Anatole<span class="fine">&nbsp;</span>! Je vous jure que vous allez&hellip;<br />
&mdash; Un instant<span class="fine">&nbsp;</span>! &mdash; J&#8217;avais us&eacute; de toute mon autorit&eacute; pour l&#8217;interrompre &mdash; J&#8217;affirme qu&#8217;il n&#8217;y a qu&#8217;un seul coupable dans cette pi&egrave;ce. Et ce coupable n&#8217;est pas Anatole.</p>
<p>Stup&eacute;faction.</p>
<p>&mdash; Monsieur Fr&eacute;d&eacute;ric B., chef de projet du site www.&hellip;.com, je vous accuse d&#8217;avoir confi&eacute; les acc&egrave;s de votre plate-forme de production &agrave; un stagiaire inexp&eacute;riment&eacute;. Je vous accuse de l&#8217;avoir laiss&eacute; travailler sur un projet sans l&#8217;avoir form&eacute; &agrave; vos m&eacute;thodes. Je vous accuse de ne pas avoir mis en place de proc&eacute;dure syst&eacute;matique de v&eacute;rification ni de validation de son travail par ses pairs plus exp&eacute;riment&eacute;s. Enfin, au nom d&#8217;Ada, sainte patronne des geeks et codeurs, je vous accuse de contribuer &agrave; la m&eacute;diocrit&eacute; logicielle g&eacute;n&eacute;rale, en omettant de mettre en place et de promouvoir des bonnes pratiques de d&eacute;veloppements. Arr&ecirc;tez-le<span class="fine">&nbsp;</span>!</p>
<p>Deux gorilles de la s&eacute;curit&eacute; se saisirent du f&eacute;lon, et l&#8217;emmen&eacute;rent tandis que ses cris de rage et menaces de vengeances raisonnaient dans les couloirs.</p>
<p>Quand le silence revint, je savourai les regards admiratifs qui se posaient sur moi. Ma cliente ne put s&#8217;emp&ecirc;cher de me demander&nbsp;:</p>
<p>&mdash; Mais comment avez-vous su<span class="fine">&nbsp;</span>? Pour la livraison en production<span class="fine">&nbsp;</span>?<br />
&mdash; C&#8217;est simple, ma ch&egrave;re. Lorsque j&#8217;ai r&eacute;alis&eacute; que certains fichiers n&#8217;&eacute;taient pas synchrones entre les diff&eacute;rents environnements, j&#8217;ai fouill&eacute; pour en d&eacute;couvrir l&#8217;origine. J&#8217;ai remarqu&eacute; que certaines modifications apport&eacute;es r&eacute;cemments sur le svn se trouvaient bien sur la pr&eacute;production, mais jamais sur la production. En revanche, d&#8217;autres commits avaient &eacute;t&eacute; directement report&eacute;s sur la production, mais &eacute;taient absent de la production. Et il se trouve que ces commits anormaux avaient tous un m&ecirc;me auteur&nbsp;: Monsieur Anatole P., ici pr&eacute;sent.</p>
<p>En remontant dans l&#8217;historique, j&#8217;ai tout de suite vu que ses premi&egrave;res contributions &eacute;taient tr&eacute;s r&eacute;centes. J&#8217;ai &eacute;videmment pens&eacute; &agrave; un stagiaire.<br />
&mdash; Votre analyse est limpide.<br />
&mdash; &Eacute;l&eacute;mentaire, m&ecirc;me. Cette affaire prouve une fois de plus l&#8217;une des Grandes Lois Immuables des TIC.<br />
&mdash; Laquelle<span class="fine">&nbsp;</span>?<br />
&mdash; Il n&#8217;existe pas d&#8217;anomalie, quelle qu&#8217;elle soit, dans laquelle aucun stagiaire ne soit impliqu&eacute;.<br />
&mdash; &Eacute;coutez, monsieur Jouannic. Vous nous avez d&eacute;montr&eacute; que vous &ecirc;tes un homme capable. Nous avons une place de chef de projet &agrave; pourvoir. Seriez-vous disponible<span class="fine">&nbsp;</span>?<br />
&mdash; D&eacute;sol&eacute;, m&#8217;dame, mais &ccedil;a ne m&#8217;int&eacute;resse pas. Ind&eacute;pendant je suis, ind&eacute;pendant je reste. Vous savez, d&eacute;tective freelance, ce n&#8217;est pas un m&eacute;tier, c&#8217;est un mode de vie.<br />
&mdash; Bon. Si vous changez d&#8217;avis, n&#8217;h&eacute;sitez pas &agrave; me contacter.<br />
&mdash; J&#8217;y penserai. Je vous enverrai ma facture d&#8217;ici demain.</p>
<p>Tout en m&#8217;installant derri&egrave;re le volant ma fid&egrave;le guibarde, je sortis mon agenda et v&eacute;rifiai mon emploi du temps du reste de la semaine. Rien. Parfait, j&#8217;allais pouvoir partir en vacances quelques jours. Je l&#8217;avais bien m&eacute;rit&eacute;.</p>
<div style="text-align: center">&#8211; FIN &#8211;</div>
<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Fhumour%2F420-colombo-2-0"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Fhumour%2F420-colombo-2-0" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/humour/420-colombo-2-0/feed</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>Bonne année 2010 !</title>
		<link>http://www.miximum.fr/tranche_vie/417-bonne-annee-2010</link>
		<comments>http://www.miximum.fr/tranche_vie/417-bonne-annee-2010#comments</comments>
		<pubDate>Thu, 31 Dec 2009 22:00:37 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Tranche de vie]]></category>
		<category><![CDATA[divers]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=417</guid>
		<description><![CDATA[ photo credit&#160;: teeboo2734
Ch&#232;res lectrices, chers lecteurs,
Merci de votre fid&#233;lit&#233; et de votre int&#233;r&#234;t. Je vous souhaite &#224; toutes et &#224; tous une excellente ann&#233;e 2010 pleine de r&#234;ves, de projets, d&#8217;accomplissements, de sant&#233;, de bonheur, d&#8217;amour, de sexe, de php, de web, et de tout un tas d&#8217;autres trucs plaisants qu&#8217;il serait fastidieux de [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F417-bonne-annee-2010"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F417-bonne-annee-2010" height="61" width="51" /></a></div><div class="postimg alignleft"><a href="http://www.flickr.com/photos/24469297@N05/4228072817/" title="Christmas coder" target="_blank"><img src="http://farm5.static.flickr.com/4061/4228072817_840fd5300a_m.jpg" alt="Christmas coder" border="0" /></a><br /><small><a href="http://creativecommons.org/licenses/by-sa/2.0/" title="Attribution-ShareAlike License" target="_blank"><img src="http://www.miximum.fr/wp-content/plugins/photo-dropper/images/cc.png" alt="Creative Commons License" border="0" width="16" height="16" align="absmiddle" /></a> <a href="http://www.photodropper.com/photos/" target="_blank">photo</a> credit&nbsp;: <a href="http://www.flickr.com/photos/24469297@N05/4228072817/" title="teeboo2734" target="_blank">teeboo2734</a></small></div>
<p>Ch&egrave;res lectrices, chers lecteurs,</p>
<p>Merci de votre fid&eacute;lit&eacute; et de votre int&eacute;r&ecirc;t. Je vous souhaite &agrave; toutes et &agrave; tous une excellente ann&eacute;e 2010 pleine de r&ecirc;ves, de projets, d&#8217;accomplissements, de sant&eacute;, de bonheur, d&#8217;amour, de sexe, de php, de web, et de tout un tas d&#8217;autres trucs plaisants qu&#8217;il serait fastidieux de lister ici.</p>
<p>Comme disait je ne sais plus qui&nbsp;: &laquo;&nbsp;Que vos m&eacute;moires vivent, et que vos disques durent&nbsp;&raquo;.</p>
<p>Sur ce, je retourne faire la f&ecirc;te (je plaisante, c&#8217;est un billet programm&eacute;. Je suis geek mais quand m&ecirc;me).</p>
<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F417-bonne-annee-2010"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.miximum.fr%2Ftranche_vie%2F417-bonne-annee-2010" height="61" width="51" /></a></div>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tranche_vie/417-bonne-annee-2010/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
