<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>Miximum &#187; symfony</title>
	<atom:link href="http://www.miximum.fr/tag/symfony/feed" rel="self" type="application/rss+xml" />
	<link>http://www.miximum.fr</link>
	<description>Le blog d&#039;un ingénieur web freelance</description>
	<lastBuildDate>Wed, 16 Nov 2011 16:25:14 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>Symfony, c&#8217;est fini…</title>
		<link>http://www.miximum.fr/tranche_vie/715-symfony-cest-fini%e2%80%a6</link>
		<comments>http://www.miximum.fr/tranche_vie/715-symfony-cest-fini%e2%80%a6#comments</comments>
		<pubDate>Mon, 20 Jun 2011 08:00:48 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Tranche de vie]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=715</guid>
		<description><![CDATA[photo credit&#160;: fabiogis50 Et dire que c&#8217;&#233;tait le framework de mon premier amour&#160;! Sous ce titre un peu ridicule[1] se cache un v&#233;ritable cri du c&#339;ur. Ou pourquoi j&#8217;ai d&#233;cid&#233; de ne plus utiliser les technos de Sensio. Un peu d&#8217;histoire Comprendre les propos qui vont suivre n&#233;cessite d&#8217;avoir un aper&#231;u de la vie du [...]]]></description>
			<content:encoded><![CDATA[<div class="postimg alignleft"><a href="http://www.flickr.com/photos/70936800@N00/4887735788/" title="A POSTCARD FROM CAPRI" target="_blank"><img src="http://farm5.static.flickr.com/4118/4887735788_da9546d113_m.jpg" alt="A POSTCARD FROM CAPRI" 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/70936800@N00/4887735788/" title="fabiogis50" target="_blank">fabiogis50</a></small></div>
<p>Et dire que c&#8217;&eacute;tait le framework de mon premier amour<span class="fine">&nbsp;</span>! Sous ce titre un peu ridicule<sup><a href="#footnote-1-715" id="footnote-link-1-715" title="Voir la note.">[1]</a></sup> se cache un v&eacute;ritable cri du c&oelig;ur. Ou <strong>pourquoi j&#8217;ai d&eacute;cid&eacute; de ne plus utiliser les technos de Sensio</strong>.</p>
<h2>Un peu d&#8217;histoire</h2>
<p>Comprendre les propos qui vont suivre n&eacute;cessite d&#8217;avoir <a href="http://en.wikipedia.org/wiki/Symfony#Releases">un aper&ccedil;u de la vie du framework</a>. Si vous n&#8217;aimez pas l&#8217;histoire, vous pouvez passer directement &agrave; la section suivante.</p>
<p>La premi&egrave;re version de symfony est publi&eacute;e en 2005. La version 1.0 voit le jour d&eacute;but 2007. Mi-2008 d&eacute;barque la version 1.1, avec une refonte majeure de l&#8217;architecture interne, et l&#8217;arriv&eacute;e (notamment) du framework du formulaire. La version 1.2 suit rapidement, fin 2008, ajoutant pas mal de fonctionnalit&eacute;s sympa. Enfin, fin 2009 sortent en parall&egrave;le les versions 1.3 et 1.4, avec l&#8217;int&eacute;gration d&#8217;un beau mailer tout neuf, pas mal de travail sur les forms, et encore d&#8217;autres trucs sympa.</p>
<p>Ensuite<span class="fine">&nbsp;</span>? Plus rien&hellip; Sensio a en effet d&eacute;cid&eacute; de ne pas poursuivre le d&eacute;veloppement de symfony en tant que tel, mais de <strong>r&eacute;&eacute;crire le projet de z&eacute;ro pour donner naissance &agrave; Symfony2</strong>. La branche 1.x ne verra donc pas d&#8217;ajout de nouvelles fonctionnalit&eacute;s, et ne sera plus support&eacute;e apr&egrave;s 2012.</p>
<p>La nouvelle version du framework a &eacute;t&eacute; r&eacute;v&eacute;l&eacute;e la premi&egrave;re fois en f&eacute;vrier 2010, mais l&#8217;id&eacute;e de la r&eacute;&eacute;criture date de bien avant. Je me souviens avoir assist&eacute; &agrave; une pr&eacute;sentation Symfony par Fabien Potencier en 2008, et d&eacute;j&agrave;, la r&eacute;&eacute;criture du projet &eacute;tait &eacute;voqu&eacute;e.</p>
<p>Symfony2, au moment de la publication de cet article (juin 2011), est encore en beta, et la stable est attendue pour juillet.</p>
<h2>Awesome framework is awesome<span class="fine">&nbsp;</span>!</h2>
<div class="postimg alignright"><a href="http://www.flickr.com/photos/39070275@N08/4536070757/" title="Thumbs up 35mm" target="_blank"><img src="http://farm3.static.flickr.com/2730/4536070757_07c8c6f68c_m.jpg" alt="Thumbs up 35mm" 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/39070275@N08/4536070757/" title="NICHOLAS-WILLIAMS" target="_blank">NICHOLAS-WILLIAMS</a></small></div>
<p>Contrairement &agrave; ce que mon titre pourrait laisser croire, <strong>je tiens symfony 1.x et ses cr&eacute;ateurs en haute estime</strong>. Malgr&eacute; quelques erreurs de jeunesse et probl&egrave;mes qui apparaissent &agrave; l&#8217;usage, le framework est de haute qualit&eacute;. ORM, architecture MVC, formulaires, outils de tests unitaires et fonctionnels, gestion des caches, g&eacute;n&eacute;rateur d&#8217;admin, documentation abondante et j&#8217;en passe en font une solution compl&egrave;te et robuste.</p>
<p>Mais plus important, <strong>symfony a contribu&eacute; &agrave; diffuser les bonnes pratiques et outils de d&eacute;veloppement &agrave; une grande &eacute;chelle</strong>. Avant symfony, aucun projet dans le monde PHP n&#8217;avait autant insist&eacute; sur l&#8217;importance de la programmation objet, de l&#8217;architecture du code, des tests automatis&eacute;s, des d&eacute;veloppements it&eacute;ratifs, etc.</p>
<p>La plus belle r&eacute;ussite du projet, &agrave; mon humble avis, est d&#8217;avoir <strong>aid&eacute; le monde du d&eacute;veloppement web &agrave; sortir de son amateurisme bon enfant</strong>, pour le hisser vers les standards de comp&eacute;tence qu&#8217;on peut trouver dans d&#8217;autres branches du d&eacute;veloppement logiciel.</p>
<p>Bien entendu, on trouvera toujours une palanqu&eacute;e de zigotos se pr&eacute;tendant d&eacute;veloppeurs web parce qu&#8217;ils ont &eacute;crits trois lignes de PHP. Mais en s&#8217;imposant, symfony a aid&eacute; &agrave; s&eacute;parer le bon grain de l&#8217;ivraie en permettant l&#8217;&eacute;mergence d&#8217;une communaut&eacute; de v&eacute;ritables professionnels.</p>
<p>C&#8217;est gr&acirc;ce &agrave; symfony et &agrave; son &eacute;cosyst&egrave;me que j&#8217;ai d&eacute;couvert le d&eacute;veloppement it&eacute;ratif, les tests automatis&eacute;s, et que j&#8217;ai eu mon premier contact avec les m&eacute;thodes agiles. Et rien que pour &ccedil;a, je tire mon chapeau &agrave; ce framework.</p>
<h2>Vis ma vie de freelance</h2>
<div class="postimg alignleft"><a href="http://www.flickr.com/photos/36887211@N07/4692139101/" title="I'm just here to look cute on the 165th Day" target="_blank"><img src="http://farm5.static.flickr.com/4028/4692139101_5a76e74bcb_m.jpg" alt="I'm just here to look cute on the 165th Day" 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/36887211@N07/4692139101/" title="imNicholas" target="_blank">imNicholas</a></small></div>
<p>Mais alors, pourquoi aller voir ailleurs<span class="fine">&nbsp;</span>? J&#8217;y viens.</p>
<p>Pour ceux qui ne sont pas encore au courant, sachez que je suis d&eacute;veloppeur web freelance. Ce qui signifie que je travaille seul, et que je dois assurer tous les aspects d&#8217;une prestation de d&eacute;veloppement web de A &agrave; Z (prospection, avant-vente, r&eacute;ponse aux appels d&#8217;offre, d&eacute;veloppement, gestion de projet, gestion administrative et commerciale, maintenance, etc.)</p>
<p>Nous vivons dans un monde qui &eacute;volue tr&eacute;s vite. Chaque semaine voit son arriv&eacute;e de nouvelles technos, certaines demeureront, d&#8217;autres disparaitront. Mais <strong>d&eacute;cider d&#8217;apprendre &agrave; utiliser une certaine technologie</strong>, comme un framework, pour pouvoir ensuite l&#8217;inclure dans mes prestations, c&#8217;est un choix difficile, et <strong>un investissement tr&eacute;s lourd</strong>.</p>
<p>C&#8217;est un investissement tr&eacute;s lourd, puisque cela signifie que je vais devoir me former, lire de la doc, faire des tutos, tester sur des petits projets personnels, et <strong>tout cela prends beaucoup de temps</strong>. Et quand on est freelance et qu&#8217;on facture &agrave; la journ&eacute;e, <strong>je peux vous dire qu&#8217;on est tr&eacute;s conscient que le temps, c&#8217;est de l&#8217;argent</strong>.</p>
<p>C&#8217;est un investissement tr&eacute;s lourd, puisque cela signifie que j&#8217;accepte, pendant un temps plus ou moins long, d&#8217;<strong>&ecirc;tre moins efficace dans mon travail</strong>. Une techno donn&eacute;e ne me permettra d&#8217;&ecirc;tre efficient que lorsque je la ma&icirc;triserai suffisamment pour passer plus de temps &agrave; produire qu&#8217;&agrave; chercher de la doc.</p>
<p>C&#8217;est un investissement tr&eacute;s lourd, puisque cela signifie aussi que <strong>le premier projet vendu dans cette techno sera hasardeux et commercialement risqu&eacute;</strong>. Et comme je suis honn&ecirc;te, cela signifie aussi que <strong>je vendrai mon jour de travail moins cher &agrave; mes clients</strong>, parce que je ne peux garantir &agrave; 100% l&#8217;application des bonnes pratiques relatives &agrave; cette techno.</p>
<p>Bref, d&eacute;cider d&#8217;ajouter une techno &agrave; ma trousse &agrave; outil, c&#8217;est un investissement lourd, et <strong>comme pour tout investissement, j&#8217;en attends un b&eacute;n&eacute;fice</strong>. Si je gal&egrave;re pendant 6 ou 9 mois pour redevenir efficient, j&#8217;esp&egrave;re que l&#8217;outil choisi restera valable pendant au moins 4 ou 5 ans.</p>
<h2>Symfony, I am disappoint<span class="fine">&nbsp;</span>!</h2>
<div class="postimg alignright"><a href="http://www.flickr.com/photos/72296542@N00/342947987/" title="death by drowning" target="_blank"><img src="http://farm1.static.flickr.com/152/342947987_76fa321171_m.jpg" alt="death by drowning" 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/72296542@N00/342947987/" title="Meredith_Farmer" target="_blank">Meredith_Farmer</a></small></div>
<p>Mon v&eacute;ritable probl&egrave;me est qu&#8217;avec symfony, j&#8217;ai l&#8217;impression de ne jamais avoir r&eacute;colt&eacute; les b&eacute;n&eacute;fices de mon investissement.</p>
<p>Symfony est une tr&eacute;s bonne techno. C&#8217;est aussi une techno qui est tr&eacute;s loin d&#8217;&ecirc;tre parfaite, et qui aurait n&eacute;cessit&eacute; encore beaucoup d&#8217;ajustements. Sauf que&hellip; Dans le cul Lulu<span class="fine">&nbsp;</span>! Parce qu&#8217;en 2009, boum<span class="fine">&nbsp;</span>! Branche 1.x gel&eacute;e<span class="fine">&nbsp;</span>! Les nouvelles fonctionnalit&eacute;s<span class="fine">&nbsp;</span>? Pas la peine, on est en train de tout r&eacute;&eacute;crire<span class="fine">&nbsp;</span>! Les bugs vraiment bloquants et complexes &agrave; corriger<span class="fine">&nbsp;</span>? Trop dur, c&#8217;est un probl&egrave;me d&#8217;architecture<span class="fine">&nbsp;</span>! Mais pas d&#8217;inqui&egrave;tude, on est en train de tout r&eacute;&eacute;crire, &ccedil;a sera corrig&eacute; dans 2 ans.</p>
<p>Malgr&eacute; tous mes efforts, je ne parviens pas &agrave; me d&eacute;barrasser du sentiment que <strong>les nombreux arguments avanc&eacute;s &agrave; l&#8217;origine pour promouvoir symfony n&#8217;&eacute;taient que de la merde marketing</strong>.</p>
<p>Sensio mettait en avant que symfony &eacute;tait support&eacute; par une entreprise, et pas une communaut&eacute; de hippies pas s&eacute;rieux. Qui a essay&eacute; de soumettre des bugs apr&egrave;s le gel de la branche 1.x sait ce qu&#8217;il en est de ce fameux support. D&#8217;ailleurs, &agrave; ma connaissance, Kris Wallsmith, release manager de la derni&egrave;re version stable, n&#8217;est pas un employ&eacute; de Sensio.</p>
<p>Sensio mettait en avant que symfony &eacute;tait une techno p&eacute;renne, avec des LTS et tout le toutim. Sauf qu&#8217;une LTS ne sert pas &agrave; grand chose si le d&eacute;veloppement du projet s&#8217;arr&ecirc;te derri&egrave;re. Ah, &ccedil;a, mes clients &agrave; qui j&#8217;ai vendu une techno &laquo;&nbsp;&nbsp;LTS&nbsp;&nbsp;&raquo;, ils vont bien rigoler en 2012, quand je leur dirai qu&#8217;ils faut r&eacute;&eacute;crire leur site s&#8217;ils veulent continuer &agrave; b&eacute;n&eacute;ficier de mises &agrave; jour de s&eacute;curit&eacute;.</p>
<p>Sensio mettait en avant que symfony &eacute;tait une techno b&acirc;tie sur l&#8217;exp&eacute;rience, inspir&eacute;e par les meilleurs projets de l&#8217;&eacute;poque. &Agrave; l&#8217;utilisation, on s&#8217;aper&ccedil;oit tr&eacute;s vite (et dans la douleur) que <strong>symfony est surtout bati empiriquement</strong>. Tous ceux qui ont essay&eacute; de cr&eacute;er un site multilingue, ou des formulaires imbriqu&eacute;s, ou d&#8217;utiliser le g&eacute;n&eacute;rateur de backend sur des &laquo;&nbsp;vrais&nbsp;&raquo; projets me rejoindront probablement.</p>
<p>Sensio mettait en avant que symfony &eacute;tait abondamment document&eacute;. Sauf que l&#8217;info, elle peut &ecirc;tre dans le livre d&#8217;introduction, ou dans le tuto de d&eacute;marrage, ou dans le livre pratique de no&euml;l, ou dans le livre de r&eacute;f&eacute;rence, ou dans le tuto sur les formulaires, ou dans le tuto sp&eacute;cifique sur doctrine, ou dans un billet sur le blog, etc. Et attention &agrave; ne pas se gourrer entre la version doctrine ou propel du document. Qui plus est, les m&ecirc;mes docs ne sont pas forc&eacute;ment disponible selon la version du framework, et bien &eacute;videmment, via un moteur de recherche, on ne sait jamais sur quelle version on va tomber. Bref, <strong>la doc de symfony, elle est tr&eacute;s bien sur le papier, mais en pratique, c&#8217;est vraiment casse-bombon</strong>.</p>
<p>D&#8217;ailleurs, c&#8217;est &ccedil;a tout le probl&egrave;me de symfony. C&#8217;est toujours tr&eacute;s bien sur le papier, mais en vrai, &ccedil;a laisse un petit go&ucirc;t bizarre dans la bouche Une petite impression de <em>finalement, c&#8217;est pas si bien que &ccedil;a</em>. <strong>Symfony, c&#8217;est une techno pas finie, pas mature</strong>, et Sensio a choisi de tout reprendre &agrave; z&eacute;ro plut&ocirc;t que de terminer ce qu&#8217;ils avaient commenc&eacute;.</p>
<h2>Et maintenant<span class="fine">&nbsp;</span>?</h2>
<p>En tant que freelance, professionnel du d&eacute;veloppement web, j&#8217;ai maintenant deux possibilit&eacute;s&nbsp;:</p>
<ol>
<li>Je pers&eacute;v&egrave;re, je me mets &agrave; Symfony2, je prends le temps de me former, je paie les pots cass&eacute;s pendant quelques mois, j&#8217;attends que la techno soit stable, j&#8217;attends que les comp&eacute;tences se diffusent, j&#8217;attends que la communaut&eacute; atteigne une taille suffisante, j&#8217;attends que les plugins et extensions apparaissent, j&#8217;attends les premiers retours d&#8217;exp&eacute;rience sur la mise en place de vrais projets, sans garantie que tout ne recommence pas dans 5 ans.</li>
<li>Je vais voir ailleurs si je peux trouver mieux.</li>
</ol>
<div class="postimg alignleft"><a href="http://www.flickr.com/photos/93252788@N00/2532505255/" title="fly away" target="_blank"><img src="http://farm4.static.flickr.com/3234/2532505255_e439b3ef04_m.jpg" alt="fly away" 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/93252788@N00/2532505255/" title="bernat..." target="_blank">bernat&#8230;</a></small></div>
<p>Alors, &agrave; votre avis, je fais quoi<span class="fine">&nbsp;</span>? Et bien, <strong>j&#8217;ai fait ce que tout bon d&eacute;veloppeur web doit savoir faire&nbsp;: j&#8217;ai &eacute;t&eacute; curieux</strong>. J&#8217;ai saut&eacute; une grande marche, et je suis carr&eacute;ment all&eacute; voir dans d&#8217;autres &eacute;cosyst&egrave;mes, d&#8217;autres communaut&eacute;s. J&#8217;ai test&eacute; python, <a href="http://flask.pocoo.org/docs/">j&#8217;ai test&eacute; flask</a>, <a href="https://www.djangoproject.com/">j&#8217;ai test&eacute; django</a>, et ce que j&#8217;ai vu m&#8217;a plu.</p>
<p>Django, ce n&#8217;est bien s&ucirc;r pas parfait, mais c&#8217;est aussi bien en pratique que symfony sur le papier. Ce n&#8217;est pas une techno de frimeurs, on n&#8217;en dit pas plus que ce qu&#8217;on en fait. L&#8217;avenir me dira si c&#8217;est un bon choix.</p>
<p>Pour conclure. Je ne dis pas que Symfony2 ne sera pas une techno g&eacute;niale, et qu&#8217;elle ne doit pas &ecirc;tre utilis&eacute;e. Je ne dis pas que je ne toucherai jamais un projet en Symfony2. Je ne dis pas que prendre la d&eacute;cision de tout r&eacute;&eacute;crire a &eacute;t&eacute; mauvaise. J&#8217;imagine que ce fut un choix tr&eacute;s difficile. <strong>Je dis et r&eacute;p&egrave;te que j&#8217;admire et loue les &eacute;quipes qui travaillent sur symfony</strong>, et que toutes ces remarques sont faites avec humilit&eacute;, en ayant conscience que la critique est ais&eacute;e, mais l&#8217;art est difficile.</p>
<p>Simplement, &agrave; mon niveau, pour mon cas personnel, <strong>je ne peux plus consid&eacute;rer symfony comme une techno viable</strong>.</p>
<p>Et rien ne me dit que dans 5 ans, le m&ecirc;me sch&eacute;ma ne se reproduira pas. Rien ne me dit que dans 5 ans, Fabien Potencier ne va pas p&eacute;ter un boulon pour aller &eacute;lever des ch&egrave;vres dans le d&eacute;sert. Et alors, quel avenir pour symfony<span class="fine">&nbsp;</span>?</p>
<p>Je suis all&eacute; voir ailleurs, et j&#8217;y ai trouv&eacute; mieux. Un petit pas pour l&#8217;humanit&eacute;, mais un grand pas pour moi<span class="fine">&nbsp;</span>?</p>
<h4>Notes&nbsp;:</h4><ol class="footnotes"><li id="footnote-1-715">en encore, vous ne me voyez pas le chanter devant mon &eacute;cran  [<a href="#footnote-link-1-715">retour</a>]</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tranche_vie/715-symfony-cest-fini%e2%80%a6/feed</wfw:commentRss>
		<slash:comments>69</slash:comments>
		</item>
		<item>
		<title>New release of tjSolrDoctrineBehaviorPlugin</title>
		<link>http://www.miximum.fr/actus/558-new-release-of-tjsolrdoctrinebehaviorplugin</link>
		<comments>http://www.miximum.fr/actus/558-new-release-of-tjsolrdoctrinebehaviorplugin#comments</comments>
		<pubDate>Mon, 03 Jan 2011 12:14:19 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Actus]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=558</guid>
		<description><![CDATA[photo credit&#160;: &#160;&#187;grahamblackall Release an open-source software once, and you&#8217;ll be doomed to maintain it for life. This is just a quick message to let you know that I&#8217;ve released a new version of my Symfony Solr plugin&#160;: tjSolrDoctrineBehaviorPlugin. This plugin allows you to easily index and search among your doctrine objects. Wanna see the [...]]]></description>
			<content:encoded><![CDATA[<div class="postimg alignright"><a href="http://www.flickr.com/photos/13086721@N08/3268391472/" title="Mardi Gras Readers (FRONT PAGE #1)" target="_blank"><img src="http://farm4.static.flickr.com/3297/3268391472_08aee98e75_m.jpg" alt="Mardi Gras Readers (FRONT PAGE #1)" 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/13086721@N08/3268391472/" title="&raquo;grahamblackall" target="_blank">&nbsp;&raquo;grahamblackall</a></small></div>
<p>Release an open-source software once, and you&#8217;ll be doomed to maintain it for life.</p>
<p>This is just a quick message to let you know that I&#8217;ve released a new version of my <a href="http://www.symfony-project.org/plugins/tjSolrDoctrineBehaviorPlugin">Symfony Solr plugin&nbsp;: tjSolrDoctrineBehaviorPlugin</a>.</p>
<p>This plugin allows you to easily index and search among your doctrine objects. Wanna see the changelog<span class="fine">&nbsp;</span>?</p>
<ul>
<li>I18n integration</li>
<li>Configure Solr and Jetty logging</li>
<li>Realtime indexing option</li>
<li>DataImportHandler (DIH) configuration</li>
<li>Task to rebuild index</li>
<li>Update documentation</li>
<li>Bug corrections</li>
</ul>
<p>I hope you will find it useful. Happy new year<span class="fine">&nbsp;</span>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/actus/558-new-release-of-tjsolrdoctrinebehaviorplugin/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Comment PHP me rends fou</title>
		<link>http://www.miximum.fr/musee-des-horreurs/473-comment-php-me-rends-fou</link>
		<comments>http://www.miximum.fr/musee-des-horreurs/473-comment-php-me-rends-fou#comments</comments>
		<pubDate>Fri, 12 Mar 2010 18:07:21 +0000</pubDate>
		<dc:creator>thibault</dc:creator>
				<category><![CDATA[Musée des horreurs]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[wtf]]></category>

		<guid isPermaLink="false">http://www.miximum.fr/?p=473</guid>
		<description><![CDATA[Chers amis d&#233;veloppeurs bonsoir. Je vous &#233;cris car je souhaite soumettre &#224; votre sagacit&#233; un probl&#232;me &#233;trange. &#192; d&#233;faut de pouvoir me venir en aide (seule ma dignit&#233; m&#8217;emp&#234;che encore de verser des larmes de frustration), peut-&#234;tre au moins cela vous fera-t-il marrer. Je suis victime d&#8217;une version particuli&#232;rement vicieuse du bug de 17h30 (avec [...]]]></description>
			<content:encoded><![CDATA[<p>Chers amis d&eacute;veloppeurs bonsoir. Je vous &eacute;cris car je souhaite soumettre &agrave; votre sagacit&eacute; un probl&egrave;me &eacute;trange. &Agrave; d&eacute;faut de pouvoir me venir en aide (seule ma dignit&eacute; m&#8217;emp&ecirc;che encore de verser des larmes de frustration), peut-&ecirc;tre au moins cela vous fera-t-il marrer. Je suis victime d&#8217;une version particuli&egrave;rement vicieuse du <a href="http://www.miximum.fr/tranche_vie/62-le-bug-de-17h30">bug de 17h30</a> (avec un combo &laquo;&nbsp;bug du vendredi de fin d&#8217;it&eacute;ration&nbsp;&raquo; compte triple).</p>
<p>Projet symfony. Je code, tout se d&eacute;roule bien, fin de semaine, je balance mon code sur le serveur de recette, et je jette un coup d&#8217;&oelig;il sur l&#8217;environnement de prod. Horreur<span class="fine">&nbsp;</span>! Page blanche<span class="fine">&nbsp;</span>! Je v&eacute;rifie la m&ecirc;me page sous l&#8217;environnement de dev. Elle s&#8217;affiche correctement.</p>
<p>Je jette un coup d&#8217;&oelig;il dans la log apache&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;">child pid <span class="nu0">16813</span> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a> signal Segmentation fault <span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span></pre></div></div>

<p>Un sentiment d&#8217;immense lassitude m&#8217;envahit aussit&ocirc;t.</p>
<p>Apr&egrave;s une longue et p&eacute;nible session profilage / debugage avec xdebug, je finit par circonvenir plus ou moins l&#8217;endroit du probl&egrave;me, et parviens m&ecirc;me &agrave; le reproduire &agrave; l&#8217;envie. Vous allez voir, c&#8217;est surprenant&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="re0">$category</span> <span class="sy0">=</span> Article<span class="sy0">-&gt;</span><span class="me1">getCategory</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// objet de classe 'Category'</span>
<span class="kw1">echo</span> <span class="re0">$category</span><span class="sy0">;</span>  <span class="co1">// Affiche le titre de la catégorie</span>
<a href="http://www.php.net/printf"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st_h">'%s'</span><span class="sy0">,</span> <span class="re0">$category</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// Page blanche, segfault, sacrifice de chatons, etc.</span></pre></div></div>

<p>Amusant, non<span class="fine">&nbsp;</span>? Apr&egrave;s quelques recherches, il semblerait que dans le contexte d&#8217;une fonction *printf, php n&#8217;utilise pas la fonction magique __toString().</p>
<p>Je teste donc&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="kw2">&lt;?php</span>
&nbsp;
<span class="kw2">class</span> Toto
<span class="br0">&#123;</span>
  <span class="kw2">private</span> <span class="re0">$tata</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$tata</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">tata</span> <span class="sy0">=</span> <span class="re0">$tata</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <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> <span class="br0">&#40;</span>string<span class="br0">&#41;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">tata</span> <span class="sy0">.</span> <span class="st0">&quot;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$toto</span> <span class="sy0">=</span> <span class="kw2">new</span> Toto<span class="br0">&#40;</span><span class="st_h">'tutu'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="re0">$toto</span><span class="sy0">;</span>
<a href="http://www.php.net/printf"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st_h">'%s'</span><span class="sy0">,</span> <span class="re0">$toto</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// tutu</span>
<span class="co1">// tutu</span></pre></div></div>

<p>WTF<span class="fine">&nbsp;</span>? Mais &ccedil;a marche tr&eacute;s bien<span class="fine">&nbsp;</span>! C&#8217;est donc un probl&egrave;me sp&eacute;cifique &agrave; mon code<span class="fine">&nbsp;</span>? Je poursuis mon d&eacute;buggage, et j&#8217;arrive ici&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="re0">$category</span> <span class="sy0">=</span> <span class="re0">$article</span><span class="sy0">-&gt;</span><span class="me1">getCategory</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="co1">// $category est de la classe sfOutputEscaperIteratorDecorator</span>
<span class="co1">// qui hérite de sfOutputEscaperObjectDecorator</span>
<span class="co1">// qui définit une fonction __toString()</span>
&nbsp;
<a href="http://www.php.net/printf"><span class="kw3">printf</span></a><span class="br0">&#40;</span><span class="st_h">'%s'</span><span class="sy0">,</span> <span class="re0">$category</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="co1">// Appelle sfOutputEscaperObjectDecorator::__toString</span></pre></div></div>

<p>Bon, allons voir sur place ce qui ne va pas.</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">class</span> sfOutputEscaperObjectDecorator
<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="co1">// Jusqu'ici, tout va bien, mais dés l'instruction return, ça plante</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escape</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escapingMethod</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">value</span><span class="sy0">-&gt;</span>__toString<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co1">// Je modifie donc la fonction pour obtenir ceci :</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="re0">$value</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escape</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escapingMethod</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">value</span><span class="sy0">-&gt;</span>__toString<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <a href="http://www.php.net/var_dump"><span class="kw3">var_dump</span></a><span class="br0">&#40;</span><a href="http://www.php.net/gettype"><span class="kw3">gettype</span></a><span class="br0">&#40;</span><span class="re0">$value</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <a href="http://www.php.net/var_dump"><span class="kw3">var_dump</span></a><span class="br0">&#40;</span><span class="re0">$value</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <a href="http://www.php.net/die"><span class="kw3">die</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">return</span> <span class="re0">$value</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
<span class="co1">// J'obtiens pour affichage :</span>
<span class="co1">// 'String'</span>
<span class="co1">// 'Titre de ma catégorie'</span>
&nbsp;
<span class="co1">// En revanche :</span>
&nbsp;
  <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="re0">$value</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escape</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escapingMethod</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">value</span><span class="sy0">-&gt;</span>__toString<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <a href="http://www.php.net/var_dump"><span class="kw3">var_dump</span></a><span class="br0">&#40;</span><a href="http://www.php.net/gettype"><span class="kw3">gettype</span></a><span class="br0">&#40;</span><span class="re0">$value</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <a href="http://www.php.net/var_dump"><span class="kw3">var_dump</span></a><span class="br0">&#40;</span><span class="re0">$value</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="co1">// die();</span>
    <span class="kw1">return</span> <span class="re0">$value</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
<span class="co1">// Page blanche. Continuons dans l'étrange, avec quelques modifications de la même méthode :</span>
&nbsp;
  <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="re0">$value</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escape</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escapingMethod</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">value</span><span class="sy0">-&gt;</span>__toString<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <a href="http://www.php.net/die"><span class="kw3">die</span></a><span class="br0">&#40;</span><span class="st_h">'ici'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// Affiche 'ici'</span>
    <span class="kw1">return</span> <span class="st_h">'toto'</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <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> <span class="st_h">'toto'</span><span class="sy0">;</span> <span class="co1">// Affiche 'toto'</span>
  <span class="br0">&#125;</span>
&nbsp;
  <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="re0">$value</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escape</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">escapingMethod</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">value</span><span class="sy0">-&gt;</span>__toString<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">return</span> <span class="st_h">'toto'</span><span class="sy0">;</span> <span class="co1">// Page blanche</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div>

<p>Tout &ccedil;a, bien s&ucirc;r, c&#8217;est sur l&#8217;environnement de prod. En dev, tout se passe toujours normalement.</p>
<p>J&#8217;en suis l&agrave;. Si quelqu&#8217;un a une bonne explication. En attendant, je crois que je vais aller me coucher de bonne heure. Bon week-end &agrave; tous.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/musee-des-horreurs/473-comment-php-me-rends-fou/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<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 [...]]]></description>
			<content:encoded><![CDATA[<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>
]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tutos/466-symfony-form-pick-or-create/feed</wfw:commentRss>
		<slash:comments>8</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="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>
]]></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>16</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="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>
]]></content:encoded>
			<wfw:commentRss>http://www.miximum.fr/tutos/435-creer-une-liste-triable-avec-symfony-et-jquery-ui/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

