<?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>Vincent Mazenod, aka mazenovi, aka voisin de gennetines &#187; dev</title>
	<atom:link href="http://blog.mazenod.fr/category/dev/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.mazenod.fr</link>
	<description>#symfony #ispcp #iphone #php #cornemuse</description>
	<lastBuildDate>Mon, 05 Apr 2010 17:35:26 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>note sur les suffixes dans les routes symfony &gt;= 1.2</title>
		<link>http://blog.mazenod.fr/2010/04/note-sur-les-suffixes-dans-les-routes-symfony-1-2/</link>
		<comments>http://blog.mazenod.fr/2010/04/note-sur-les-suffixes-dans-les-routes-symfony-1-2/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 15:41:18 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=375</guid>
		<description><![CDATA[le système de routage
Si comme moi vous avez tenté d&#8217;utiliser la directive .suffix dans le routing.yml

prod:
  routing:
    param:
      suffix: .html

et que vous vous attendiez à ce que vorte suffixe .html soit ajouté automatiquement à tous les liens générés via link_to et gen_url &#8230; c&#8217;est que vous [...]]]></description>
			<content:encoded><![CDATA[<h3>le système de routage</h3>
<p>Si comme moi vous avez tenté d&#8217;utiliser la directive .suffix dans le routing.yml</p>
<pre class="yml:nogutter" name="code">
prod:
  routing:
    param:
      suffix: .html
</pre>
<p>et que vous vous attendiez à ce que vorte suffixe .html soit ajouté automatiquement à tous les liens générés via link_to et gen_url &#8230; c&#8217;est que vous ne savez pas que <a href="http://groups.google.com/group/symfony-users/browse_thread/thread/b8991d26a8bfc82f">par défaut symfony génère des urls aussi courtes que possible</a>.</p>
<p>Il vous faudra donc changer ce comportement dans factories.yml</p>
<pre class="yml:nogutter" name="code">all:
  routing:
    class: sfPatternRouting
    param:
      generate_shortest_url: false
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/04/note-sur-les-suffixes-dans-les-routes-symfony-1-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>outils de modélisation pour symfony</title>
		<link>http://blog.mazenod.fr/2010/03/outils-de-modelisation-pour-symfony/</link>
		<comments>http://blog.mazenod.fr/2010/03/outils-de-modelisation-pour-symfony/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 10:14:18 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[gratuit]]></category>
		<category><![CDATA[modélisation]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=818</guid>
		<description><![CDATA[Symfony à radicalement changer mon approche du développement PHP.
En effet l&#8217;approche RAD proposée par ce framework rend d&#8217;autant plus crucial le schéma de base de données, sur lequel est basé l&#8217;application. Ce schéma matérialisé par le fichier schema.yml (ou .xml si vous utilisez propel) est en quelque sorte la feuille de route du développeur agile.
Il [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.symfony-project.org/">Symfony</a> à radicalement changer mon approche du développement PHP.</p>
<p>En effet l&#8217;approche <a href="http://fr.wikipedia.org/wiki/D%C3%A9veloppement_rapide_d%27applications">RAD</a> proposée par ce framework rend d&#8217;autant plus crucial le schéma de base de données, sur lequel est basé l&#8217;application. Ce schéma matérialisé par le fichier schema.yml (ou .xml si vous utilisez propel) est en quelque sorte la feuille de route du développeur agile.</p>
<p>Il est intéressant de remarquer qu&#8217;une modélisation graphique de ce schéma donne un point de vue (partiel certes) sur le système d&#8217;information que l&#8217;applicaiton sert &#8230;</p>
<p>Il ya déjà quelques temps déjà j&#8217;avais cherché à automatiser la modification du schéma géré par <a href="http://www.symfony-project.org/">symfony</a> via des outils graphiques (<a href="http://blog.mazenod.fr/2007/11/utiliser-dbdesigner-avec-l-i18n-de-symfony/">Utiliser DBDesigner avec l&#8217;i18n de symfony 1.0</a>)</p>
<ul></ul>
<p>Aujourd&#8217;hui je vous propose un petit tour d&#8217;horizon des solutions à votre disposition selon vos outils et votre orm préférés</p>
<h2>DbDesigner</h2>
<p>si vous utilisez propel</p>
<ul>
<li>Le plugin <a href="http://www.symfony-project.org/plugins/sfDB4toPropelPlugin/1_0_3">sfDB4toPropelPlugin</a> automatisera le processus de conversion du schéma sauvé par DbDesigner en un schema.yml (à la manière des scritps cités précédemment)</li>
</ul>
<p>si vous utilisez Doctrine</p>
<p>le plugin <a href="http://www.symfony-project.org/plugins/sfDbDesignerPlugin">sfDbDesignerPlugin</a> ne s&#8217;installe pas correctement via la commande symfony. En revanche en l&#8217;installant manuellement, i.e. en téléchargeant l&#8217;archive directement sur le site et en la décompressant dans /plugins/sfDbDesignerPlugin puis en activant le plugin dans /config/ProjectConfiguration.class.php</p>
<pre class="php:nogutter:nocontrols" name="code">
&lt;?php

require_once dirname(__FILE__).'/..\lib\vendor\symfony\lib/autoload/sfCoreAutoload.class.php';
sfCoreAutoload::register();

class ProjectConfiguration extends sfProjectConfiguration
{
&nbsp;&nbsp;public function setup()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;enablePlugins('sfDoctrinePlugin');
&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;enablePlugins('sfDbDesignerPlugin');
&nbsp;&nbsp;}
}
</pre>
<p>vous aurez accès à la task</p>
<pre class="bash:nogutter:nocontrols" name="code">
php symfony dbdesigner:convert-doctrine doc/database.xml
</pre>
<p>Où  doc/database.xml est le path vers le fichier DBDesigner.<br />
Si vous n&#8217;avez pas une application nommée frontend il faudra la passer ne paramètre</p>
<pre class="bash:nogutter:nocontrols" name="code">
php symfony dbdesigner:convert-doctrine --application=myapp doc/database.xml
</pre>
<p>vous obtiendrez alors un fichier &laquo;&nbsp;ready to build&nbsp;&raquo; du schéma modélisé avec DBDesigner dans config/doctrine/schema.yml</p>
<h2>MySQLWorkBench</h2>
<p>Il est à noter que <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> est un fork de <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner4</a>, mais il ne peut pas remplacer ce dernier. En effet si <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> sait parfaitement importer un fichier xml produit par <a href="http://www.fabforce.net/dbdesigner4/">DbDesigner4</a>, il est en revanche incapable d&#8217;exporter ce schéma au format xml de <a href="http://www.fabforce.net/dbdesigner4/">DbDesigner4</a>.  <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> ne permet que d&#8217;enregistrer au format mwb.</p>
<p>Ce format est en fait un zip, qui une fois décompresser donne un fichier xml, mais qui n&#8217;a rien à voir avec le xml généré par <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner4</a>, le fichier xml issu du .wmb est donc inutilisatble avec les deux plugins ci dessus.</p>
<p><a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> bénéficie en revanche d&#8217;<a href="http://forums.mysql.com/read.php?52,216856,216856">une bibliothèque de plugins</a> qui permettent de l&#8217;utiliser en remplaçant de <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner</a>.</p>
<p>Pour installer un plugin dans<a href="http://www.mysql.fr/products/workbench/"> MySQLWorkbench</a> il suffit de copier le fichier .lua qui contient le code du plugin (du Python Like) dans le répertoire &laquo;&nbsp;modules&nbsp;&raquo; du répertoire d&#8217;installation de <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a></p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/03/plugin_mwb.png" rel="lightbox[818]"><img class="aligncenter size-full wp-image-855" title="plugin_mwb" src="http://blog.mazenod.fr/wp-content/uploads/2010/03/plugin_mwb.png" alt="" width="553" height="245" /></a></p>
<ul>
<li>Si vous utilisez Doctrine <a href="http://code.google.com/p/mysql-workbench-doctrine-plugin/">mysql-workbench-doctrine-plugin<sup>1</sup> </a>fonctionne plutôt bien</li>
<li>Si vous utilisez Propel <a href="http://www.diloc.de/blog/2009/10/14/mysql-workbench-propel-export-plugin-v05/">mysql-workbench-propel-export-plugin<sup>2</sup></a> de générer le contenu de schema.xml, alors que <a href="http://trac.symfony-project.org/wiki/SymfonyYamlMyqlWorkbenchPlugin">SymfonyYamlMysqlWorkbenchPlugin<sup>3</sup></a> vous permettra de générer le contenu de schema.yml</li>
</ul>
<h2>Dia</h2>
<p>Si vous utilisez propel, il est possible de modéliser également votre schéma avec <a href="http://projects.gnome.org/dia/">Dia</a> grâce au plugin <a href="http://www.symfony-project.org/plugins/diaToPropelPlugin">diaToPropelPlugin</a>, en utilisant la boite à outil &laquo;&nbsp;Database&nbsp;&raquo; ça va sans dire.</p>
<p>Si vous utilisez Doctrine le plugin <a href="http://trac.symfony-project.org/wiki/ConvertPropelSchemaToDoctrineSchema">ConvertPropelSchemaToDoctrineSchema</a> réalisera la conversion d&#8217;un schéma propel en schéma doctrine.</p>
<h2>ArgoUML</h2>
<p>En graltant un pei plus j&#8217;ai trouvé également le plugin<a href="http://sourceforge.net/projects/uml2symfony"> uml2symfony</a>, qui semble avoir l&#8217;ambition de traduire non pas un schéma de base de données, mais un schéma UML généré avec <a href="http://en.wikipedia.org/wiki/ArgoUML">ArgoUML</a>. Je vous laisse aller plus loin car j&#8217;ai personnellement trouvé l&#8217;<a href="http://www.symfonylab.com/uml2symfony/">idée séduisante</a> mais la documentation très pauvre, voir inexistante!</p>
<h2>Conclusion</h2>
<p>Mon choix se portera sur le doublette gagnante <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a>, <a href="http://code.google.com/p/mysql-workbench-doctrine-plugin/">mysql-workbench-doctrine-plugin</a> car:</p>
<ul>
<li><a href="http://www.fabforce.net/dbdesigner4/">DBDesigner </a>n&#8217;est plus maintenu depuis belle lurette (je me rends compte en écrivant ce post que <a href="http://sourceforge.net/projects/dbdesigner-fork/">DBDesigner fork</a> qui a dormi pendant prêt de 3 ans semble revenir à la vie depuis quelques jours)</li>
<li>j&#8217;utilise maintenant Doctrine</li>
<li>ce plugin permet une gestion fine des <a href="http://code.google.com/p/mysql-workbench-doctrine-plugin/wiki/HowToAddDoctrineBehavioursToTheWorkbenchModel">Behaviors Doctrine</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/03/outils-de-modelisation-pour-symfony/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>installation de symfony via PEAR avec UwAmp</title>
		<link>http://blog.mazenod.fr/2010/01/installation-de-symfony-via-pear-avec-uwamp/</link>
		<comments>http://blog.mazenod.fr/2010/01/installation-de-symfony-via-pear-avec-uwamp/#comments</comments>
		<pubDate>Sun, 31 Jan 2010 19:43:41 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[gratuit]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=827</guid>
		<description><![CDATA[sorti en fin d&#8217;année 2009 UwAmp est un environnement de développement Apache MySQL PHP portable pour windows. Son installation est on ne peut plus simple puisqu&#8217;il suffit de le télécharger et de le dézipper (dans z:\Apps\UwAmp par exemple) pour l&#8217;utiliser.
C&#8217;est sans aucun doute le plus sexy de tous les environnements de développement PHP pour windows [...]]]></description>
			<content:encoded><![CDATA[<p>sorti en fin d&#8217;année 2009 UwAmp est un environnement de développement Apache MySQL PHP portable pour windows. Son installation est on ne peut plus simple puisqu&#8217;il suffit de le télécharger et de le dézipper (dans z:\Apps\UwAmp par exemple) pour l&#8217;utiliser.</p>
<p>C&#8217;est sans aucun doute le plus sexy de tous les environnements de développement PHP pour windows (<a href="http://www.wampserver.com/">wampserver2</a>, successeur du défunt Wamp5, <a href="http://www.apachefriends.org/en/xampp.html">xampp</a> , et <a href="http://www.easyphp.org/">easyPHP</a>), en effet son interface de maintenance est riche et ergonomique</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.2.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-829" title="UwAmp" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.2-268x300.jpg" alt="" width="268" height="300" /></a></p>
<p>Elle permet notamment de passer d&#8217;une version à une autre de PHP en 1 clic, ce qui n&#8217;est pas négligeable.</p>
<p>Elle propose des interfaces de gestion simplifiées pour la configuration de PHP (extensions et variables du fichier  php.ini)</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.4.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-830" title="config extension php" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.4-300x234.jpg" alt="" width="300" height="234" /></a></p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.4.jpg" rel="lightbox[827]"></a><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.5.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-831" title="config php.ini" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.5-300x234.jpg" alt="" width="300" height="234" /></a></p>
<p>ainsi que pour celle d&#8217;apache (configuration de virtual hosts &amp; modules apache)</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.6.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-832" title="screenshot.6" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.6-300x265.jpg" alt="" width="300" height="265" /></a></p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.7.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-833" title="screenshot.7" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.7-300x265.jpg" alt="" width="300" height="265" /></a></p>
<p>Pour les barbus, les fichiers php.ini et httpd.conf sont toujours accessibles via un bouton, et il est même possible de paramétrer son éditeur de texte  préféré (paths portables acceptés!) en cliquant sur le bouton &laquo;&nbsp;préférences&nbsp;&raquo;.</p>
<p>Dans la séries des raccourcis vous pourrez accéder en un clic</p>
<ul>
<li>à la page d&#8217;accueil de votre répertoire racine &#8211; http://localhost/</li>
<li>au répertoire contenant les virtual hosts &#8211; z:\Apps\UwAmp\www\</li>
<li>à phpMyAdmin &#8211; http://localhost/mysql/ (à noter que le mot de passe root de MySQL par défaut est root)</li>
<li>à un phpinfo() &#8211; http://localhost/uwamp/phpinfo.php</li>
<li>à SQLite Database Browser qui vous permettra de gérer vos bases de données SQLite</li>
</ul>
<p>et en cadeau bonus</p>
<ul>
<li>Les logs access du serveur apache</li>
<li>Les logs error du serveur apache</li>
<li>Les logs du serveur MySQL</li>
<li>à la suppression des fichiers de log sus cités</li>
<li>à la suppression des fichiers de sessions PHP</li>
</ul>
<p>&#8230;</p>
<p>Le petit bémol que je mettrais par rapport à l&#8217;installation de <a href="../2009/11/environnement-de-developpement-portable-pour-symfony-framakey/">xampp portable</a>, que j&#8217;ai déjà documentée, est que PEAR n&#8217;est pas préinstallé.</p>
<p>Pour utiliser les commandes PHP et mysql sans se préoccupper de leur path, suivez <a href="http://blog.mazenod.fr/2009/11/environnement-de-developpement-portable-pour-symfony-framakey/#path">les explications suivantes</a> en remplaçant ;\Apps\xampp\php\;\Apps\xampp\mysql\bin\ par  ;\Apps\UwAmp\apache\php_5.3.1;\Apps\UwAmpp\mysql\bin\. N&#8217;oubliez pas de fermer et de rouvrir votre session pour prendre en compte le nouveau Path (n&#8217;oubliez pas non plus de laisser un commentaire si vous avez une meilleure solution).</p>
<p>ouvrez ensuite un prompt de commande et tapez</p>
<pre class="php:nogutter" name="code">
cd z:\Apps\UwAmp\apache\php_5.31
go-pear.bat
</pre>
<p>si vous ne vous mettez pas dans le dossier  vous allez obtenir un message d&#8217;erreur du genre</p>
<p>Could not open input file: PEAR\go-pear.phar<br />
Appuyez sur une touche pour continuer&#8230;</p>
<p>Sinon PEAR vous demandera si vous souhaitez faire une installation système ou locale. La différence réside essentiellement dans la localisation du fichier pear.ini. Si vous voulez comme moi jouer avec plusieurs installations de PEAR choisissez local. En effet l&#8217;installation système installe le fichier pear.ini dans c:\windows, ce qui le rendra commun à toutes  vos installations de PEAR. L&#8217;installation local permet de ranger le pear.ini dans le répertoire de la commande php auquel CETTE commande PEAR est associée &#8230;</p>
<p>Une fois le choix fait vous pouvez choisir les options par défaut lors du process d&#8217;installation</p>
<p>ensuite pour installer symfony 1.0</p>
<pre class="php:nogutter" name="code">
pear upgrade PEAR
pear channel-discover pear.symfony-project.com
pear install symfony/symfony-1.0.21
symfony -V
</pre>
<p>Vous êtes alors prêt pour <a href="http://www.symfony-project.org/askeet/1_0/en/">askeet</a>.</p>
<p>Pour installer la dernière version courante de symfony1.4.1</p>
<pre class="php:nogutter" name="code">
pear install symfony/symfony
</pre>
<p>Vous êtes alors prêt pour <a href="http://www.symfony-project.org/jobeet/1_4/Doctrine/en/">jobeet</a>.</pre>
<p>L'avantage c'est que vous disposez d'une installation de PEAR par version de PHP, ce qui est idéale pour avoir plusieurs version de symfony sur votre plateforme de développement!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/01/installation-de-symfony-via-pear-avec-uwamp/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Design Pattern MVC &#8211; zoom sur la couche modèle : DAL / DAO / ORM / CRUD</title>
		<link>http://blog.mazenod.fr/2010/01/design-pattern-mvc-zoom-sur-la-couche-modele-dal-dao-orm-crud/</link>
		<comments>http://blog.mazenod.fr/2010/01/design-pattern-mvc-zoom-sur-la-couche-modele-dal-dao-orm-crud/#comments</comments>
		<pubDate>Sun, 17 Jan 2010 14:30:41 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[design pattern]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=739</guid>
		<description><![CDATA[Design Pattern MVC
Voilà deux ans que je fais un cours, suivi d’un petit projet traitant du design Pattern MVC.  Le projet est à écrire en PHP5, avec une base de données MySQL comme support de stockage des données, l’architecture MVC est donc à implémenter dans un contexte purement web. Le paradigme objet est tout indiqué [...]]]></description>
			<content:encoded><![CDATA[<h2>Design Pattern MVC</h2>
<p>Voilà deux ans que je fais un cours, suivi d’un petit projet traitant du design Pattern MVC.  Le projet est à écrire en PHP5, avec une base de données MySQL comme support de stockage des données, l’architecture MVC est donc à implémenter dans un contexte purement web. Le paradigme objet est tout indiqué quand il s&#8217;agit d&#8217;écrire et d&#8217;agencer des composants logiciels, c&#8217;est donc celui qui sera adopté dans toute la suite.</p>
<p>Il y a bien entendu beaucoup de projets existants implémentant le design Pattern MVC en PHP, mais le propos du cours est plutôt de réaliser un cas pratique d’implémentation afin de bien saisir les bien faits du design pattern  MVC.</p>
<p>En effet l’utilisation d’un design pattern architectural en PHP, s’oppose à la pratique empirique de ce langage qui consiste à mélanger les connexions et accès à la base de données, avec le traitement des données et leur affichage. Dans ce cas il est alors commun d’avoir dans un seul script: du PHP, du SQL, du Javascript et du CSS dans les attributs des balises HTML soit pas moins de 5 langages distincts.</p>
<p>Au même titre qu’il est conseillé dans la présentation de séparer l’HTML, qui structure le document, du CSS, qui l’habille, il est conseillé d’<a href="http://www.do-as-i-say.com/notes/2009/09/design-patterns-symfony-explique-a-ma-maman-2/">utiliser un design pattern architectural pour structurer une application web</a>.</p>
<p>Pour mémoire cette architecture permet d’organiser une application en 3 couches distinctes à savoir :</p>
<ul>
<li>le modèle, qui contient la logique métier;</li>
<li>la vue, qui regroupe tout ce qui a trait à la présentation (des données / comme des interactions utilisateur);</li>
<li>le contrôleur, qui répond à des interactions utilisateurs en provenance de la vue, en appelant des traitements mis à disposition sous forme de méthode par le modèle, afin de nourrir la vue associée au traitement demandé par l&#8217;utilisateur.</li>
</ul>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/MVC_final.png" rel="lightbox[739]"><img class="aligncenter" title="MVC_final" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/MVC_final.png" alt="" width="585" height="315" /></a></p>
<p>Si vous pratiquez PHP et n&#8217;avez jamais utilisé le design pattern MVC, je vous recommande la lecture de : &laquo;&nbsp;<a href="http://trac.symfony-project.org/wiki/Documentation/fr_FR/book/1.0/trunk/02-Exploring-Symfony-s-Code">comment convertir une application PHP standard en une application basée sur l’architecture MVC</a>&laquo;&nbsp;.</p>
<p>Le propos de ce billet n’est pas de vanter <a href="http://www.do-as-i-say.com/notes/2010/01/symfony-explique-a-ma-maman-4eme-partie-le-mvc/">les mérites du design pattern MVC</a>, car ils sont en général bien compris par mes étudiants. C&#8217;est plutôt d’en détailler la couche modèle dont la structure est assez dure à disséquer. J’ai moi-même pas mal lu et beaucoup débattu, avec le camarade <a href="http://willdurand.fr">will durand</a> notamment, avant d&#8217;arriver à isoler chaque composant de cette couche.</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/Zoom_modele.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-741" title="Zoom_modele" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/Zoom_modele.png" alt="" width="568" height="268" /></a></p>
<h2>le Modèle</h2>
<p>Sous sa forme la plus brute, la couche modèle peut être vu comme les « données ». Par données on entend tout ce qui est persistant, c&#8217;est-à-dire tout ce qu’on pourra lire à partir d’une source, et modifier pour le relire plus tard si besoin est. Dans une logique de découplage, il est de bon ton d’essayer de s’affranchir le plus possible de la forme brute des données. C’est ce que va faire le modèle en transformant des données brutes en objets structurés, utilisables simplement par la couche inférieure : le contrôleur.</p>
<p>Pour réaliser ce découplage le modèle utilise 3 couches d’abstraction :</p>
<ul>
<li>La DAL (Data Access Layer) : couche abstraction de données</li>
<li>Le DAO (Data Access Object) : objet d’accès aux données</li>
<li>L’ORM (Object / Relation Mapping) : Mapping objet / relationnel</li>
</ul>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/DAL_final.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-766" title="DAL_final" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/DAL_final.png" alt="" width="555" height="323" /></a></p>
<p>Pour l&#8217;exemple je vous propose de partir du diagramme UML suivant</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/uml.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-754" title="uml" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/uml.png" alt="" width="315" height="152" /></a></p>
<p>qui donne le MLD (schéma de base de données) suivant</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/mld.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-755" title="mld" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/mld.png" alt="" width="529" height="470" /></a></p>
<h3>La DAL</h3>
<p>La DAL Permet de s’abstraire du support des données. Pour se faire elle met à disposition des méthodes génériques permettant d’accomplir des actions de maintenances sur les données. Les actions les plus communes sont regroupées sous l’acronyme CRUD (Create Read Update Delete). Basiquement la DAL va donc mettre à disposition des méthodes permettant d’ajouter, mettre à jour, lire, supprimer un enregistrement, et ce quelque soit le support de stockage des donnéees.</p>
<p>La généricité par rapport au stockage est en général matérialisée par un paramètre permettant de spécifier la nature du support (on appelle ça des drivers). Ainsi les méthodes CRUD associées au support de stockage sont utilisées de manière transparente par le développeur. Concrètement il n’y a donc en théorie qu’un paramètre à changer pour qu’une application utilisant une DAL puisse changer de support.</p>
<p>D’un point de vue strictement théorique la DAL devrait offrir la possibilité de maintenir des données dans n&#8217;importe quelle base de données, dans des fichiers texte, dans des fichiers xml …</p>
<p>D’un point de vue pratique, en PHP, une DAL utilise toujours un SGBD. Son rôle est donc de rendre l’applicatif qui l’utilise (la DAL) portable par rapport au SGBD utilisé.</p>
<p>Les DAL PHP que je connais sont <a href="http://pear.php.net/package/DB">Pear DB</a> (la première que j&#8217;ai utilisé), <a href="http://creole.phpdb.org/trac/wiki/Documentation/CreoleGuide">Creole</a>, et <a href="http://php.net/manual/fr/book.pdo.php">PDO</a> qui tend à devenir le standard en PHP, puisque disponible sous forme d&#8217;extension PHP.</p>
<h3>Le DAO</h3>
<p>Le Dao a pour but de transformer les données contenues dans une bases de données en objets et inversement</p>
<p>Pour se faire il va faire correspondre (de manière bijective &#8211; ca veut dire qu&#8217;on peut rajouter &laquo;&nbsp;et inversement&nbsp;&raquo; à la fin de chacun des points suivants)</p>
<ul>
<li>une table (appelée aussi relation) à une liste d’objets</li>
<li>une ligne d&#8217;une table (appelée aussi tuple) à un objet</li>
<li>un champs de base de données à un attribut d’objet</li>
<li>une valeur d’un champs à une valeur d’attribut d’un objet</li>
</ul>
<p>Dans notre exemple les classes issues du DAO seront au minimum</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/UML_DAO.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-794" title="UML_DAO" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/UML_DAO.png" alt="" width="479" height="511" /></a></p>
<p>je dis au minimum, car ces classes pourraient avoir un peu plus de &#8230; classe! avec des getters et des setters.</p>
<p>Techniquement la DAO interroge le SGBD via la DAL sur la structure des tables afin de maintenir la correspondance entre les champs des tables de la base de données et les attributs des objets. Il y a au moins deux façons de réaliser celà:</p>
<ul>
<li>soit en générant un code PHP minimale fonction de la structure de la base de données (il faudra alors regénérer à chaque modification du schéma de base de données).</li>
<li>soit en utilisant les méthodes magiques PHP (comme <a href="http://www.php.net/manual/en/language.oop5.overloading.php">__get, __set ou __call et ___callStatic</a>) et le <a href="http://php.net/manual/fr/language.oop5.late-static-bindings.php">late static binding de PHP 5.3</a></li>
</ul>
<p>Notez qu&#8217;à ce niveau là le seul code logique que possède les objets construits à partir d’un DAO sont les méthodes CRUD qui vont permettre d’aller le lire, le modifier, le supprimer en base. En cela les objets issus d’un DAO pourraient être appelés des POPO  (rigolez pas! c&#8217;est pas moi qui l&#8217;ai dit le premier) par analogie aux <a href="http://fr.wikipedia.org/wiki/Plain_Old_Java_Object">POJO Java (Plain Old Java Object)</a></p>
<p>Notez également que dans le schéma de présentation des couches d&#8217;abstraction du modèle,  le DAO utilise la DAL, mais que ce n’est pas une vraie obligation : un DAO hardcodé en mysql resterait un DAO … simplement non portable au niveau du SGBD.</p>
<p>Notez enfin que chaque classe est isolée, c&#8217;est à dire qu&#8217;il n&#8217;existe pas de code logique qui permette une quelconque interaction entre elles.</p>
<h3>L’ORM</h3>
<p>L’ORM a pour but de transformer les relations entre les tables d’ une base de données en relations entre objets et inversement</p>
<p>Elle va typiquement se préoccuper de matérialiser les clés étrangères par des dépendances entre objets<a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/UML_ORM.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-777" title="UML_ORM" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/UML_ORM.png" alt="" /></a>L&#8217;intérêt réside dans le fait que les méthodes de la couche ORM, renvoient ou prennent en paramètres des listes d&#8217;objets. Par exemple $post-&gt;getComments() renverra une liste d&#8217;objets de classe Comment. Ces objets Comment seront sous forme de POPOS, c&#8217;est en celà que l&#8217;ORM utilise le DAO.</p>
<p>Techniquement l’ORM utilise soit les contraintes d’intégrité référentielle, soit une certaine logique de nommage, pour déterminer les clés étrangères. Dans les deux cas elle passe par la DAL.</p>
<p>Comme pour le DAO, les stratégies de génération de code ou d&#8217;utilisation des méthodes magiques peuvent être adoptées.</p>
<p>Comme pour le DAO également, l&#8217;utilisation d&#8217;une DAL est conseillée mais pas obligatoire.</p>
<p><a href="http://propel.phpdb.org">propel</a> et <a href="http://www.doctrine-project.org/">doctrine</a> sont deux <a href="http://trac.symfony-project.org/wiki/ComparingPropelAndDoctrine">ORM PHP typiques</a>.</p>
<h3>La couche métier</h3>
<p>Le but des couhes précédentes est de soulager la couche métier. Si la couche métier peut hériter des méthodes de la DAO et de l&#8217;ORM, nous aurons des objets présentant toutes les méthodes pour les gérer en base.</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/UML_METIER.png" rel="lightbox[739]"><img class="aligncenter size-full wp-image-778" title="UML_METIER" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/UML_METIER.png" alt="" /></a></p>
<p>La couche métier est sensée ne contenir que la logique métier, c&#8217;est à dire propre à l&#8217;objet qu&#8217;elle représente. Ce sera effectivement le cas dans la class Post par exemple, où l&#8217;on pourra n&#8217;avoir qu&#8217;une seule méthode Post::getArchives(), qui retourne une liste de liste contenant les archives du type</p>
<pre class="php:nogutter" name="code">Array(
'2009' =&gt; Array('janvier' =&gt; 1),
'2008' =&gt; Array('septembre' =&gt; 7, 'octobre' =&gt; 2, 'novembre' =&gt; 1, 'décembre' =&gt; 1)
)</pre>
<p>Cette méthode fait bien parti de la couche métier puisqu&#8217;elle est propre au concept de post (ou billet) de blog.</p>
<p>En appelant cette méthode le contrôleur pourra nourrir une vue qui n&#8217;aura alors qu&#8217;un double foreach à faire pour présenter les archives dans le menu de droite.</p>
<h2>conclusion</h2>
<p>Les concepts que je viens de détailler sont souvent amalgamés, aussi il est souvent difficile de les cerner précisément. J&#8217;ai essayé d&#8217;illustrer le rôle de chacun par un exemple simpliste, mais je ne prétends détenir aucune vérité sur le sujet et les commentaires sont ouverts pour accueillir vos questions, suggestions et corrections</p>
<p>le mld a été généré avec <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner4</a> et tout les diagrammes uml ont été générés avec <a href="http://yuml.me/diagram/scruffy/class/draw">yUML</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/01/design-pattern-mvc-zoom-sur-la-couche-modele-dal-dao-orm-crud/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>alimenter facebook &amp; twitter via un filet RSS</title>
		<link>http://blog.mazenod.fr/2009/12/alimenter-facebook-twitter-via-un-filet-rss/</link>
		<comments>http://blog.mazenod.fr/2009/12/alimenter-facebook-twitter-via-un-filet-rss/#comments</comments>
		<pubDate>Mon, 21 Dec 2009 14:10:20 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[Non classé]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[réalisations]]></category>
		<category><![CDATA[free]]></category>
		<category><![CDATA[réseaux sociaux]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=714</guid>
		<description><![CDATA[<a href="../wp-content/uploads/2009/12/twitterfeed.png"><img class="alignleft" style="margin: 10px;" title="twitterfeed" src="../wp-content/uploads/2009/12/twitterfeed.png" alt="" width="142" height="134" /></a>je cherchais à réaliser cette opération pour les <a href="http://www.voyage-grand-coeur.org/">voyageurs au grand coeur</a>, pour qui je viens juste de terminer une nouvelle version de leur site web. Le but est de pouvoir suivre les différents voyages organisés pour les dons du sang sur les réseaux sociaux, en particulier sur facebook et twitter.

J'ai donc créée un <a href="http://www.voyage-grand-coeur.org/feed">filet RSS "mixant" les derniers comptes rendus d'étapes et les dernières news</a>. Ce qui m'a préoccupé ensuite c'est comment brancher ce flux à un compte twitter et à compte facebook.]]></description>
			<content:encoded><![CDATA[<p>je cherchais à réaliser cette opération pour les <a href="http://www.voyage-grand-coeur.org/">voyageurs au grand coeur</a>, pour qui je viens juste de terminer une nouvelle version de leur site web. Le but est de pouvoir suivre les différents voyages organisés pour les dons du sang sur les réseaux sociaux, en particulier sur facebook et twitter.</p>
<ul>
<li>www: <a href="http://www.voyage-grand-coeur.org/">http://www.voyage-grand-coeur.org/</a></li>
<li>rss: <a href="http://www.voyage-grand-coeur.org/">http://www.voyage-grand-coeur.org/feed</a></li>
<li><a href="http://www.facebook.com/pages/Les-voyageurs-au-grand-coeur-tour-du-monde-a-velo-pour-le-don-du-sang/208897501614?v=wall">fan page facebook</a></li>
<li>twitter : <a href="http://twitter.com/vagc63">http://twitter.com/vagc63</a></li>
</ul>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2009/12/vagc.png" rel="lightbox[714]"><img class="aligncenter size-medium wp-image-721" title="vagc" src="http://blog.mazenod.fr/wp-content/uploads/2009/12/vagc.png" alt="voyageurs au grand coeur" width="404" /></a></p>
<p>Il est évident que les voyageurs n&#8217;auront ni le temps, ni les moyens techniques de mettre à jour leur statu en temps réel. En revanche il vont faire à intervalle régulier des &laquo;&nbsp;comptes rendus d&#8217;étape&nbsp;&raquo;. Le but est de diffuser automatiquement ces comptes rendus dès qu&#8217;un voyage est en cours et de diffuser de manière tout aussi automatique les news du site entre deux voyages.</p>
<p>J&#8217;ai donc créée un <a href="http://www.voyage-grand-coeur.org/feed">filet RSS &laquo;&nbsp;mixant&nbsp;&raquo; les derniers comptes rendus d&#8217;étapes et les dernières news</a>. Ce qui m&#8217;a préoccupé ensuite c&#8217;est comment brancher ce flux à un compte twitter et à compte facebook.</p>
<p>Je suis rapidement tombé sur une <a href="http://socialmediarockstar.com/update-twitter-facebook-fan-pages-automatically">solution utilisant twitterfeed</a>. Cette solution utilise l&#8217;application <a href="http://apps.facebook.com/selectivetwitter/">selective tweet statu</a> (l&#8217;une des <a href="http://blog.mazenod.fr/2009/09/applications-facebook-pour-twitter/">applications facebook pour twitter</a>) qui est capable d&#8217;alimenter le statu d&#8217;une fan page facebook avec un compte twitter. Le problème de cette application, et donc de cette solution, est que chaque tweet doit être suffixé avec le hashtag #fb, ce qui ne fait pas de très beaux tweets &#8230;</p>
<p>Heureusement en regardant un peu plus dans le détails twitterfeed, j&#8217;ai remarqué qu&#8217;il était possible de configuré non seulement un compte twitter pour recevoir un flux RSS, mais aussi un compte facebook. Voici comment faire en quelques étapes</p>
<p>conifgurer le RSS à diffuser</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2009/12/step1.png" rel="lightbox[714]"><img class="size-full wp-image-716 aligncenter" title="step1" src="http://blog.mazenod.fr/wp-content/uploads/2009/12/step1.png" alt="" width="500" /></a></p>
<p>configurer le compte twitter sur lequel le RSS sera publié</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2009/12/step2.png" rel="lightbox[714]"><img class="size-full wp-image-716 aligncenter" title="step1" src="http://blog.mazenod.fr/wp-content/uploads/2009/12/step2.png" alt="" width="500" /></a></p>
<p>configurer le compte facebook de l&#8217;administrateur de la fan page sur laquelle le RSS sera publié</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2009/12/step3.png" rel="lightbox[714]"><img class="size-full wp-image-716 aligncenter" title="step1" src="http://blog.mazenod.fr/wp-content/uploads/2009/12/step3.png" alt="" width="500" /></a></p>
<p>aperçu du dashboard</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2009/12/step4.png" rel="lightbox[714]"><img class="size-full wp-image-716 aligncenter" title="step1" src="http://blog.mazenod.fr/wp-content/uploads/2009/12/step4.png" alt="" width="500" /></a></p>
<p>Notez que vous pouvez aussi alimenter ping.fm, hellotxt ou laconica, et qu&#8217;il est possible de paramétrer un compte bit.ly pour tracker les clics sur vos liens &#8230; Bref twitterfeed est un point d&#8217;entrée automatique idéal pour les pipelines sociaux<br />
pour en savoir plus</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/12/alimenter-facebook-twitter-via-un-filet-rss/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>environnement de développement portable pour symfony #FramaKey</title>
		<link>http://blog.mazenod.fr/2009/11/environnement-de-developpement-portable-pour-symfony-framakey/</link>
		<comments>http://blog.mazenod.fr/2009/11/environnement-de-developpement-portable-pour-symfony-framakey/#comments</comments>
		<pubDate>Mon, 16 Nov 2009 09:14:46 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[bricolage]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[gratuit]]></category>
		<category><![CDATA[install]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=519</guid>
		<description><![CDATA[     Le propos de cet article est de disposer d&#8217;un environnement de développement complet et portable pour le framework PHP symfony. Les paths sont ceux utilisés par la Framakey Unbuntu Remix, dont j&#8217;ai déjà parlé &#8211; je suppose dans toute la suite de ce post que vous voyez votre clé sur [...]]]></description>
			<content:encoded><![CDATA[<p>     <span>Le propos de cet article est de disposer d&#8217;un environnement de développement complet et portable pour le framework PHP <a href="http://www.symfony-project.org/">symfony</a>. Les paths sont ceux utilisés par la <a href="http://blog.mazenod.fr/2009/10/framakey-ubuntu-remix-100-portable-100-libre-et-multiplateforme-aussi-un-peu/">Framakey Unbuntu Remix</a>, dont j&#8217;ai déjà parlé &#8211; je suppose dans toute la suite de ce post que vous voyez votre clé sur le lecteur <em>z:\</em> dans votre poste de travail. <a href="http://www.symfony-project.org/">symfony</a> sera installé via la commande PEAR, comme dans <a href="http://www.symfony-project.org/askeet/1_0/en/">askeet</a> et non en stand-alone comme dans <a href="http://www.symfony-project.org/jobeet/1_2/Doctrine/en/"><span> </span>jobeet</a>.</span></p>
<h2><span>installation et configuraiton de xampp portable<br />
</span></h2>
<p><a href="http://www.apachefriends.org/fr/xampp.html">xampp</a> est l&#8217;environnement Apache / MySQL / PHP portable le plus stable que j&#8217;ai trouvé à l&#8217;heure actuelle. Je garde toutefois un œil sur le tout jeune, mais néanmoins prometteur, <a href="http://www.uwamp.com/">uWamp</a> (soumis par le camarade <a href="http://www.willdurand.fr/">@couac</a>).<br />
il faudra télécharger la <a href="http://www.apachefriends.org/en/xampp-windows.html#641">version zip de xampp</a> et la dézipper dans <em>z:\Apps\xampp</em> par exemple</p>
<p>Dans un premier temps il faut exécuter le batch <em>z:\Apps\xampp\setup_xampp.bat</em> (il faudra le faire à chaque fois que le path change)</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_1.jpg" rel="lightbox[519]"><img class="size-medium wp-image-671 aligncenter" title="setup_xampp_1" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_1-300x151.jpg" alt="setup_xampp_1" width="300" height="151" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_2.jpg" rel="lightbox[519]"><img class="size-medium wp-image-672 aligncenter" title="setup_xampp_2" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_2-300x151.jpg" alt="setup_xampp_2" width="300" height="151" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_3.jpg" rel="lightbox[519]"><img class="size-medium wp-image-673 aligncenter" title="setup_xampp_3" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_3-300x151.jpg" alt="setup_xampp_3" width="300" height="151" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_4.jpg" rel="lightbox[519]"><img class="size-medium wp-image-674 aligncenter" title="setup_xampp_4" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_4-300x151.jpg" alt="setup_xampp_4" width="300" height="151" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_5.jpg" rel="lightbox[519]"><img class="size-medium wp-image-675 aligncenter" title="setup_xampp_5" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_5-300x151.jpg" alt="setup_xampp_5" width="300" height="151" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_6.jpg" rel="lightbox[519]"><img class="size-medium wp-image-676 aligncenter" title="setup_xampp_6" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/setup_xampp_6-300x151.jpg" alt="setup_xampp_6" width="300" height="151" /></a></p>
<p>normalement si vous taper <a href="http://localhost/xampp/">http://localhost/xampp/</a> dans votre navigateur  vous devriez arriver sur la page suivante</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/localhost_xampp.png" rel="lightbox[519]"><img class="aligncenter size-full wp-image-678" title="localhost_xampp" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/localhost_xampp.png" alt="localhost_xampp" width="300" height="141" /></a></p>
<p>Ca y est! vous avez installé des services portables et fonctionnels &#8230;</p>
<p><a name="path"></a>Maintenant passons à la ligne de commande. En effet pour être tout à fait à l&#8217;aise, l&#8217;idéal est de pouvoir appeler l&#8217;interpréteur php (ainsi que la commande pear et les commandes mysql) sans avoir à se préoccupper du chemin pour y accéder. Pour cela il va falloir ajouter deux chemins supplémentaires dans la variable d&#8217;environnement PATH de windows.</p>
<p>Voici comment procéder sur windows 7 (la démarche pour Vista peut être adaptée à partir de ces <a href="http://notes.mazenod.fr/symfony-on-wamp-pret-pour-l-utilisation-d-eclipse-pdt-avec-plugins-sfdt.html">explications dans la section &laquo;&nbsp;mise à jour des variables d&#8217;environnement&nbsp;&raquo;</a>).</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_1.jpg" rel="lightbox[519]"><img class="size-medium wp-image-679 aligncenter" title="environnement_1" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_1-300x226.jpg" alt="environnement_1" width="300" height="226" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_2.jpg" rel="lightbox[519]"><img class="size-medium wp-image-680 aligncenter" title="environnement_2" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_2-300x226.jpg" alt="environnement_2" width="300" height="226" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_3.jpg" rel="lightbox[519]"><img class="size-medium wp-image-681 aligncenter" title="environnement_3" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_3-259x300.jpg" alt="environnement_3" width="259" height="300" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_4.jpg" rel="lightbox[519]"><img class="size-medium wp-image-682 aligncenter" title="environnement_4" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_4-271x300.jpg" alt="environnement_4" width="271" height="300" /></a><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_5.jpg" rel="lightbox[519]"><img class="size-medium wp-image-683 aligncenter" title="environnement_5" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/environnement_5-300x115.jpg" alt="environnement_5" width="300" height="115" /></a></p>
<p>Notez les que nous n&#8217;avons pas spécifié de lettre dans les path que nous venons d&#8217;ajouter (;\Apps\xampp\php\;\Apps\xampp\mysql\bin\) &#8230;<br />
cela présente un avantage majeur : le path n&#8217;aura pas à être changé si la clé change de lettre;<br />
Mais aussi un inconvénient : la commande php ne peut être utilisée qu&#8217;à partir du lecteur de la clé USB (pour l&#8217;utilisation de la commande php pour symfony cela ne pose aucun problème).</p>
<p>Il faut savoir que les variables d&#8217;environnements dans un système d&#8217;exploitation sont initialisées à l&#8217;ouverture de la session d&#8217;un utilisateur. Dans la plupart des systèmes on peut forcer la relecture de ces variables, mais je n&#8217;ai jamais trouvé comment faire sous windows (les suggestions sont les bienvenues) &#8230; donc pour appliquer les changements qui viennent d&#8217;être effectués il faut fermer votre session et la rouvrir (ou de manière plus brutale redémarrer).</p>
<p>Une fois reconnecté, ouvrez un ligne de commande, positionnez vous dans le répertoire <em>z:\Apps\xampp</em> et tapez</p>
<pre class="bash:nogutter" name="code">php -v</pre>
<p>si vous voyez apparaître quelque chose du genre</p>
<pre class="bash:nogutter" name="code">PHP 5.3.0 (cli) (built: Jul  2 2009 21:08:11)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies</pre>
<p>c&#8217;est gagné!<br />
Il suffit maintenant de procéder à l&#8217;installation de symfony via PEAR. Ca tombe bien , la commande est déjà installé et accessible via la ligne de commande, il n&#8217;y a donc qu&#8217;à taper</p>
<pre class="bash:nogutter" name="code">pear upgrade PEAR
pear channel-discover pear.symfony-project.com
pear install symfony/symfony-1.0.21
symfony -V</pre>
<p>devrait maintenant afficher</p>
<pre class="bash:nogutter" name="code">symfony version 1.0.21</pre>
<p><strong>N.B.</strong> pour installer la version stable courante de symfony il suffit de lancer la commande</p>
<pre class="bash:nogutter" name="code">pear install symfony/symfony</pre>
<p>sans spécifier de numéro de version</p>
<p>Félicitation votre environnement est près &#8230;</p>
<h3>configuration des virtual hosts</h3>
<p>Pour utiliser les virtual hosts il vous faudra décommenter cette ligne</p>
<pre class="bash:nogutter" name="code">NameVirtualHost *:80</pre>
<p>Les vhosts et les alias peuvent s&#8217;écrire sans lettre de lecteur dans <em>z:\Apps\xampp\apache\conf\extra\httpd-vhosts</em></p>
<pre>#préserver le vhost localhost pour avoir accès à la page d'admin de xampp
&lt;virtualhost *:80&gt;
  ServerName localhost
  DocumentRoot /apps/xampp/htdocs
&lt;/virtualhost&gt;
#création d'un vhost dédié pour un projet symfony
&lt;virtualhost *:80&gt;
  ServerName vdm
  DocumentRoot /apps/xampp/htdocs/vdm/web
  &lt;directory "/Apps/xampp/htdocs/vdm/web/"&gt;
    AllowOverride All
  &lt;/directory&gt;
  #alias permettant d'accéder aux js / css / images symfony installés dans PEAR
  Alias /sf /apps/xampp/pear/data/symfony/web/sf
  #directive permettant de faire fonctionner l'alias précédent sous windows
  &lt;directory "/Apps/xampp/pear/data/symfony/web/"&gt;
    AllowOverride All
    Allow from All
  &lt;/directory&gt;
&lt;/virtuahHost&gt;
</pre>
<p>Dernière étape, la moins fun,  il vous faudra ajouter le nom du vhost au fichier <em>c:\windows\system32\drivers\etc\hosts</em> comme suit</p>
<pre class="bash:nogutter" name="code">127.0.0.1   vdm</pre>
<p>vous pouvez a présent configurer autant de virtual host que vous voulez et bénéficier sans limite des bienfaits du routing symfony</p>
<h2>Portabiliser NetBeans</h2>
<p>source: <a href="http://www.portablefreeware.com/forums/viewtopic.php?f=4&amp;t=5301&amp;hilit=netbeans">http://www.portablefreeware.com/forums/viewtopic.php?f=4&amp;t=5301&amp;hilit=netbeans</a></p>
<p>En résumé pour une <a href="../2009/09/framakey-ubuntu-remix-100-portable-100-libre-et-multiplateforme-aussi-un-peu/">FramaKey</a> (sur le lecteur <em>z:\</em> par exemple), sachant que ce qui m&#8217;intéresse c&#8217;est netbeans pour PHP :</p>
<p>Sur <a href="http://www.netbeans.org/downloads/index.html">http://www.netbeans.org/downloads/index.html</a> télécharger la version que vous voulez en sélectionnant dans plateforme &laquo;&nbsp;OS Independent Zip&nbsp;&raquo;, et décompressez l&#8217;archive dans <em>z:\Apps\portableNetbeans\</em></p>
<p>Sur <a href="https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u16-oth-JPR@CDS-CDS_Developer">https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u16-oth-JPR@CDS-CDS_Developer</a>,  téléchargez la version &laquo;&nbsp;windows&nbsp;&raquo;, installez la et copiez le contenu de <em>c:\program files\java\jdk1.6.0_16\ dans z:\Apps\portableJava\</em></p>
<p>Créez le répertoire <em>z:\Data\Netbeans</em></p>
<p>Editez le fichier <em>z:\Apps\portableNetbeans\etc\netbeans.conf</em> et modifiez le comme suit</p>
<pre class="bash:nogutter" name="code"># ${HOME} will be replaced by JVM user.home system property
# netbeans_default_userdir="${HOME}/.netbeans/6.7"
netbeans_default_userdir="/Data/Netbeans"

# Options used by NetBeans launcher by default, can be overridden by explicit
# command line switches:
netbeans_default_options="-J-client -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Xverify:none -J-Dapple.laf.useScreenMenuBar=true -J-Dsun.java2d.noddraw=true"
# Note that a default -Xmx is selected for you automatically.
# You can find this value in var/log/messages.log file in your userdir.
# The automatically selected value can be overridden by specifying -J-Xmx here
# or on the command line.

# If you specify the heap size (-Xmx) explicitely, you may also want to enable
# Concurrent Mark &amp; Sweep garbage collector. In such case add the following
# options to the netbeans_default_options:
# -J-XX:+UseConcMarkSweepGC -J-XX:+CMSClassUnloadingEnabled -J-XX:+CMSPermGenSweepingEnabled
# (see http://wiki.netbeans.org/wiki/view/FaqGCPauses)

# Default location of JDK, can be overridden by using --jdkhome &lt;dir&gt;:
netbeans_jdkhome="/Apps/portableJava"

# Additional module clusters, using ${path.separator} (';' on Windows or ':' on Unix):
#netbeans_extraclusters="/absolute/path/to/cluster1:/absolute/path/to/cluster2"

# If you have some problems with detect of proxy settings, you may want to enable
# detect the proxy settings provided by JDK5 or higher.
# In such case add -J-Djava.net.useSystemProxies=true to the netbeans_default_options.</pre>
<p>et le tour est joué.</p>
<p>Notez que cette astuce marche également avec le support symfony fraîchement disponible sur <a href="http://blogs.sun.com/netbeansphp/entry/symfony_support_finished">http://blogs.sun.com/netbeansphp/entry/symfony_support_finished</a></p>
<h2>Portabiliser PHPEdit</h2>
<p>Si vous êtes l&#8217;heureux possesseur d&#8217;une licence PHPedit, cet IDE est également portabilisable</p>
<p>source : <a href="http://doc.waterproof.fr/faq/phpedit/how_do_i_install_phpedit_on_a_usb_stick_phpedit_2_10_and_up">http://doc.waterproof.fr/faq/phpedit/how_do_i_install_phpedit_on_a_usb_stick_phpedit_2_10_and_up</a></p>
<p>Il suffit de copier le répertoire <em>c:\Program Files\WaterProof\PHPEdit\3.4.2</em> sur votre clé USB par exemple dans <em>z:\Apps\PHPedit</em><br />
créez le répertoire <em>z:\Data\PHPEdit</em> destiné à stocker l&#8217;espace de travail<br />
Ensuite Deux solutions s&#8217;offrent à vous :<br />
créer un fichier texte PHPEdit.PHPEditSettings dans le répertoire contenant PHPEdit.exe<br />
enregistrer le contenu suivant dans <em>z:\Apps\PHPedit\</em><em>PHPEdit.PHPEditSettings</em></p>
<pre class="bash:nogutter" name="code">      ..\..\Data\PHPedit

      ..\..\Data\PHPedit</pre>
<p>Sinon Rendez-vous dans &laquo;&nbsp;Outils&nbsp;&raquo; -&gt; &laquo;&nbsp;Editer les préférences&nbsp;&raquo; (ou directement en tapant F1O)</p>
<ul>
<li><span>cliquez juste en dessous de la barre des titres là où il y a les deux flèches (1)</span></li>
<li><span>cliquez ensuite sur &laquo;&nbsp;paramètres locaux&nbsp;&raquo; en haut à gauche (2)</span></li>
<li><span>remplir le champs &laquo;&nbsp;Dossier de stockage des données de l&#8217;application&nbsp;&raquo; avec un path relatif (ici ..\..\Data\PHPEdit) (3)</span></li>
</ul>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/PHPedit.jpg" rel="lightbox[519]"><img class="size-medium wp-image-689 aligncenter" title="PHPedit" src="http://blog.mazenod.fr/wp-content/uploads/2009/10/PHPedit-300x217.jpg" alt="PHPedit" width="300" height="217" /></a></p>
<p>Notez que je fais l&#8217;impasse sur eclipse que je trouve définitivement trop gourmand en ressource!</p>
<h2>En guise de conclusion</h2>
<p><span>J&#8217;ajoute également quelques outils portables supplémentaires que j&#8217;aime bien avoir à portée de clic:<br />
</span></p>
<ul>
<li><span><a href="http://sourceforge.net/projects/console/">console 2</a> :  un projet de console un peu plus fun que la ligne de commande de base. Au programme paramètrage du dossier d&#8217;ouverture par défaut, redimensionnement, choix des polices et de leur taille, consoles virtuelles et transparence &#8230; voici <a href="http://blog.mazenod.fr/wp-content/uploads/2009/10/console.xml">mon fichier de conf</a> à copier / coller dans<em> z:\Apps\Console2\bin\release</em></span></li>
<li><span> <a href="http://winmerge.org/">winmerge</a> : permet de fusionner facilement du code. C&#8217;est en gros une version graphique de la commande UNIX diff</span></li>
<li><span> <a href="http://fabforce.net/dbdesigner4">DBDesigner4</a> ou <a href="http://www.mysql.fr/products/workbench/">MySQL Workbench</a> : designers graphique de base de données.</span></li>
</ul>
<div><span><img src="http://img.zemanta.com/pixy.gif?x-id=2365d92c-67c6-8d33-b209-77e459db8965" alt="" width="1.5" height="1.5" /></span></div>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/11/environnement-de-developpement-portable-pour-symfony-framakey/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>un captcha avec sfCryptographPlugin</title>
		<link>http://blog.mazenod.fr/2009/04/un-captcha-avec-sfcryptographplugin/</link>
		<comments>http://blog.mazenod.fr/2009/04/un-captcha-avec-sfcryptographplugin/#comments</comments>
		<pubDate>Tue, 28 Apr 2009 18:47:09 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=249</guid>
		<description><![CDATA[après avoir mis en place sfPropelActAsCommentableBehaviorPlugin, j&#8217;y ai ajouté quelques personnalisations
Intégration de FCK
j&#8217;ai réécrit le template _commentForm.php pour y mettre un éditeur &#171;&#160;riche&#160;&#187; : FCKeditor (il doit bien entendu être configuré au préalable dans le /apps/myapp/config/settings.yml) en modifiant
&#60;?php echo textarea_tag('sf_comment', '', array('cols' =&#62; 40, 'rows' =&#62; 8)) ?&#62;
en
&#60;?php echo textarea_tag('sf_comment', '', 'tool=Basic rich=fck size=40x8 disabled=false')?&#62;
J&#8217;ai [...]]]></description>
			<content:encoded><![CDATA[<p>après avoir mis en place <a href="http://www.symfony-project.org/plugins/sfPropelActAsCommentableBehaviorPlugin">sfPropelActAsCommentableBehaviorPlugin</a>, j&#8217;y ai ajouté quelques personnalisations</p>
<h2>Intégration de FCK</h2>
<p>j&#8217;ai réécrit le template _commentForm.php pour y mettre un éditeur &laquo;&nbsp;riche&nbsp;&raquo; : FCKeditor (il doit bien entendu être configuré au préalable dans le /apps/myapp/config/settings.yml) en modifiant</p>
<pre name="code" class="php:nogutter">&lt;?php echo textarea_tag('sf_comment', '', array('cols' =&gt; 40, 'rows' =&gt; 8)) ?&gt;</pre>
<p>en</p>
<pre name="code" class="php:nogutter">&lt;?php echo textarea_tag('sf_comment', '', 'tool=Basic rich=fck size=40x8 disabled=false')?&gt;</pre>
<p>J&#8217;ai ensuite stocké le fichier modifié au niveau de l&#8217;appli /apps/myapp/modules/sfComment/templates/_commentForm.php afin de ne pas toucher au code du plugin</p>
<p>FCK se sent obligé de mettre une ligne vide en valeur par défaut ce qui fait que la directive required ne suffit pas, puisque ligne vide n&#8217;est pas vide.<br />
J&#8217;ai donc réécrit la validation en tant qu&#8217;anonyme en y ajoutant</p>
<pre name="code" class="yml:nogutter">sf_comment:
  required:
    msg:           The message is required
  # blank from FCK
  sfCallbackValidator:
    callback:      strip_tags
    invalid_error: The message is required
</pre>
<p>J&#8217;ai stocké le fichier modifié au niveau de l&#8217;appli /apps/myapp/modules/sfComment/validate/anonymousComment.yml</p>
<h2>Liens en nofollow</h2>
<p>Mettre un lien en nofollow fait que ce lien n&#8217;est pas suivi par les moteurs de recherche, ce qui évite la transmission du PR &#8230; <a href="http://www.dofollow.fr/">y en a que ça énerve d&#8217;ailleurs le nofollow</a> <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /><br />
Pour commencer par supprimer l&#8217;intérêt de laisser un lien dans un commenatire, je me suis fait une fonction noFollow() dans /lib/myTools.php, qui prend en entrée du code html et réécrit en sortie tous les liens avec l&#8217;attribut rel=&nbsp;&raquo;nofollow&nbsp;&raquo;.</p>
<pre name="code" class="php:nogutter">
class myTools
{
  public static function noFollow ($str) {
    $str = stripslashes($str);
    $preg = "/&lt;a href=\"(.*)\"&gt;(.*?)&lt;\/a&gt;/U";
    preg_match_all($preg, $str, $match);
    if(count($match[1]))
    {
        foreach ($match[1] as $key=&gt;$val)
        {
            $pattern[] = '/'.preg_quote($match[0][$key],'/').'/';
            $replace[] = "&lt;a href=\"".$val."\" rel=\"nofollow\"&gt;".$match[2][$key]."&lt;/a&gt;";
        }
        return preg_replace($pattern, $replace, $str);
    }
    else
    {
       return $str;
    }
  }
}
</pre>
<p>J&#8217;ai ensuite réécrit l&#8217;affichage des commentaires en changeant ce passage à la fin de _commentView.php</p>
<pre name="code" class="php:nogutter">
&lt;?php
  echo __('&lt;span&gt;%1%&lt;/span&gt;, &lt;a href="#sf_comment_%2%"&gt;%3%&lt;/a&gt;',
    array(
      '%1%' =&gt; myTools::noFollow($author),
      '%2%' =&gt; $comment['Id'],
      '%3%' =&gt; $date
    )
  )
?&gt;
&lt;div&gt;
  &lt;?php echo myTools::noFollow($comment['Text']); ?&gt;
&lt;/div&gt;
</pre>
<p>En stockant ensuite ce fichier au niveau de mon application /apps/myapp/modules/sfComment/templates/_commentView.yml</p>
<p>Après ça je n&#8217;ai toujours pas réglé réellement le problème du spam &#8230; les bots des spamers ayant une approche pas très fine, ils ne prendront pas la peine de vérifier si leurs liens sont bien suivables par les moteurs de recherche dans les commentaires qu&#8217;ils ont laissés &#8230; en revanche ils vont repasser à intervalles réguliers ce qui est très désagréable!!</p>
<h2>formulaire javascript</h2>
<p>J&#8217;ai donc testé <a href="http://spamcleaner.org/fr/misc/blog-spam.html">une des solutions js proposées sur cette page</a> en remplacant les balise de formulaires dans /apps/myapp/modules/sfComment/templates/_commentForm.php par</p>
<pre name="code" class="js:nogutter">
...
&lt;script language="JavaScript" type="text/javascript"&gt;
    document.write('&lt;?echo form_tag('sfComment/'.$action, $options);?&gt;');
&lt;/script&gt;
...
&lt;script language="JavaScript" type="text/javascript"&gt;
    document.write('&lt;/form&gt;');
&lt;/script&gt;
...
</pre>
<p>Naïf et rapide mais pas suffisant :-/</p>
<h2>Captcha</h2>
<p>Pour régler définitivement le problème j&#8217;ai mis en place  <a href="http://raw.trac.symfony-project.org/wiki/sfCryptographpPlugin">sfCryptographpPlugin</a>.<br />
Comme la doc laisse un peu à désirer, voici une version traduite et adaptée de <a href="http://symfoniac.wordpress.com/2007/05/03/howto-do-a-captcha-in-symfony-with-plugins/">Howto do a Captcha in Symfony</a></p>
<h3>installation</h3>
<pre name="code" class="bash:nogutter">symfony plugin-install http://plugins.symfony-project.com/sfCaptchaPlugin</pre>
<p>activer le module dans le settings.yml de l&#8217;application</p>
<pre name="code" class="yml:nogutter">
all:
  .settings:
    enabled_modules: [default, sfCaptcha]
</pre>
<p>Personnellement je n&#8217;avais ni jpgraph_antispam-digits.class.php, ni jpgraph.class.php dans le répertoire lib/ du plugin je l&#8217;ai donc téléchargé ici <a href="https://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.phphttps://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.phphttps://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.php">https://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.php</a> &#8230; en le sauvant dans lib/jpgraph_antispam-digits.class.php du plugin et changé:</p>
<pre name="code" class="php:nogutter">
require_once 'jpgraph_antispam.class.php';
</pre>
<p>en</p>
<pre name="code" class="php:nogutter">require_once 'jpgraph_antispam-digits.class.php'</pre>
<p>L&#8217;idée c&#8217;est que dans l&#8217;action qui doit afficher le captcha on le sauve (le captcha) dans une session pour ensuite l&#8217;afficher dans le tempate. J&#8217;ai donc ajouté le code suivant dans les actions qui affichent des posts de blog ou des pages. Vu que le captacha est sauvé session il sera forcément le même pour tous les posts lors d&#8217;un affichage par liste :-/</p>
<pre name="code" class="php:nogutter">
$g = new Captcha();
$this-&gt;getUser()-&gt;setAttribute('captcha', $g-&gt;generate());
&lt;span style="font-family: verdana;"&gt;...&lt;/span&gt;
</pre>
<p>il suffit maintenant d&#8217;afficher le captcha dans le template d&#8217;affichage de formulaire de commentaires que nous avons déjà modifié tout à l&#8217;heure, en ajoutant</p>
<pre name="code" class="php:nogutter">
&lt;div class="required"&gt;
  &lt;img src="&lt;?php echo url_for('sfCaptcha/index'); ?&gt;" alt="captcha" /&gt;
  &amp;nbsp; &amp;nbsp; &amp;nbsp;
  &lt;?php echo form_error('captcha'); ?&gt;
  &lt;?php echo input_tag('captcha',''); ?&gt;
&lt;/div&gt;
</pre>
<p>Il reste à modifier le validateur anonymousComment.yml en ajoutant le contrôle du captcha</p>
<pre name="code" class="php:nogutter">
captcha:
  required:
    msg: Please enter the numbers in the captcha image
  captchaValidator:
    error: Incorrect code</pre>
</pre>
<p>reste à effacer le cache du projet </p>
<pre name="code" class="bash:nogutter">symfony cc</pre>
<p>et le captcha devrait fonctionner</p>
<p>Au passage un petit tips qui sert bien quand on joue avec les plugins dans symfony  : <a href="http://symfoniac.wordpress.com/2007/05/28/how-to-fix-a-manually-deleted-plugin-directory/">How to fix a manually deleted plugin directory</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/un-captcha-avec-sfcryptographplugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>internationalisation sans révolution avec symfony (pareil mais mieux!)</title>
		<link>http://blog.mazenod.fr/2009/04/internationalisation-sans-revolution-avec-symfony-pareil-mais-mieux/</link>
		<comments>http://blog.mazenod.fr/2009/04/internationalisation-sans-revolution-avec-symfony-pareil-mais-mieux/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 12:47:10 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=234</guid>
		<description><![CDATA[imaginons qu&#8217;on ait un mld de ce genre

Le propos de ce tuto est le suivant
optimiser la navigation multilingue et le référencement avec symfony en ne touchant pas aux urls des link_to existants, sachant que le site n&#8217;est que partiellement traduit, que l&#8217;internaute doit être notifié dans sa langue en cas de contenu non traduit, et [...]]]></description>
			<content:encoded><![CDATA[<p>imaginons qu&#8217;on ait un mld de ce genre<br />
<a href="http://blog.mazenod.fr/wp-content/uploads/2009/09/mcd.gif" rel="lightbox[234]"><img class="size-full wp-image-235 aligncenter" title="mcd" src="http://blog.mazenod.fr/wp-content/uploads/2009/09/mcd.gif" alt="mcd" width="513" height="307" /></a><br />
Le propos de ce tuto est le suivant</p>
<h3>optimiser la navigation multilingue et le référencement avec symfony en ne touchant pas aux urls des link_to existants, sachant que le site n&#8217;est que partiellement traduit, que l&#8217;internaute doit être notifié dans sa langue en cas de contenu non traduit, et que tous les objets du site ne sont pas forcément multilingues</h3>
<h4>référencement et internationalisation</h4>
<p>Plusieurs paramètres permettent à Google de présager de la langue par défaut d&#8217;un site<br />
1 &#8211; L&#8217;extension pays (.fr -&gt; français, .it italien, etc &#8230;)<br />
2 &#8211; Et en cas d&#8217;extension générique (.com, .org, .net, etc &#8230;) la géolocalisation de l&#8217;IP<br />
Ce dernier point est à surveiller avec vigilance: un hébergeur comme <a href="http://1and1.fr/">http://1and1.fr</a> qui possède une IP allemande est susceptible de vous attribuer une IP allemande pour un site a contenu exclusivement français &#8230; ce qui n&#8217;est pas conseillé par <a href="http://seminaires.ranking-metrics.fr/">les tueurs du référencement qui m&#8217;ont fait la formation référencement naturelle</a> <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /><br />
Le meilleur moyen de veiller au grain est encore d&#8217;installer <a href="https://addons.mozilla.org/fr/firefox/addon/5791">FlagFox</a>, une <a href="http://blog.mazenod.fr/extensions-firefox-les-indispensables.html">extension firefox indispensable</a> qui  affiche le drapeau du pays où est hébergé une page dans la barre d&#8217;url de votre navigateur (ainsi que beaucoup plus de détails via un simple clic sur le sus dit drapeau).</p>
<p>Bref une fois que la langue par défaut, le nom de domaine et éventuellement la géolocalisation sont en accord, il s&#8217;agit de gérer les autres langues disponibles de manière claire.<br />
En gros j&#8217;ai retenu qu&#8217;il y avait deux écoles:<br />
1 &#8211; Un sous domaine par langue comme dans ce post &#8211; <a href="http://prendreuncafe.com/blog/post/2008/03/14/Symfonians-en-VF">symfonians en VF</a><br />
2 &#8211; Un répertoire dans l&#8217;url par langue comme je vais l&#8217;exposer dans ce billet<br />
D&#8217;après ce que j&#8217;ai compris on pourrait aussi faire figurer la langue dans le stripped_title des url réécrites par exemple</p>
<p>Pour toute la suite on travaille sur un site en .fr (donc google s&#8217;attend par défaut à du français), une application nommée back, et les trois langues gérées sont le français l&#8217;anglais et le chinois</p>
<p>N.B le stripped_title est généré via la fonction stripText suivante <strong>/lib/myTools.php</strong></p>
<pre name="code" class="php:nogutter">public static function stripText($text)
{
   $text = preg_replace('/\ +/', '-', $text);
   $text = preg_replace('/\-$/', '', $text);
   $text = preg_replace('/^\-/', '', $text);
   return $text;
}</pre>
<h4>faire apparaitre la culture dans l&#8217;url</h4>
<p>il s&#8217;agit ici de ne pas utiliser la variable d&#8217;url sf_culture qui courcircuiterait la gestion de la culture et compliquerait pas mal les chose &#8230;<br />
Je propose plutot une routing rule par culture <strong>/apps/back/config/routing.yml</strong></p>
<pre name="code" class="yml:nogutter">sf_cms_content_fr:
  url:   /:stripped_title.html
  param: { module: sfCmsContent, action: show }
sf_cms_content_en:
  url:   /en/:stripped_title.html
  param: { module: sfCmsContent, action: show }
sf_cms_content_zh:
  url:   /zh/:stripped_title.html
param: { module: sfCmsContent, action: show }</pre>
<p>les link_to s&#8217;écrivent alors comme suit</p>
<pre name="code" class="php:nogutter">&lt;?php link_to('link to content', '@sf_cms_content_'.$sfCmsContent-&gt;getCulture().'?stripped_title='.$sfCmsContent-&gt;getStrippedTitle());?&gt;</pre>
<p>L&#8217;idée est donc que l&#8217;on a ou non une langue par défaut qui ne fait pas l&#8217;objet d&#8217;un &laquo;&nbsp;préfixe culture&nbsp;&raquo; dans l&#8217;ur<br />
On peut stocker la cultutre éventuelle par défaut dans <strong>/config/app.yml</strong></p>
<pre name="code" class="php:nogutter">lang:
  default: fr</pre>
<h4>navigation multilingue</h4>
<p>le changement de culture se fait via une action associé à l&#8217;utilisateur <strong>/apps/back/modules/sfCmsUser/actions/actions.class.php</strong></p>
<pre name="code" class="php:nogutter">class sfCmsUserActions extends autosfCmsUserActions

{
   public function executeLanguage() {
      $this-&gt;getUser()-&gt;setCulture($this-&gt;getRequestParameter('culture'));
      $url = $this-&gt;getRequest()-&gt;getReferer() != '' ? $this-&gt;getRequest()-&gt;getReferer() : '@homepage';
      $this-&gt;redirect($url);
   }
}</pre>
<p>L&#8217;affichage des drapeaux se fait comme suit /apps/back/modules/sfCmsLang/tmeplates/_default.php</p>
<pre name="code" class="php:nogutter">&lt;?php if( count( sfConfig::get('app_lang_back') ) &gt; 1):?&gt;
   &lt;?php foreach(sfConfig::get('app_lang_back') AS $lg =&gt; $lang):?&gt;
      &lt;?php if($lg==$sf_user-&gt;getCulture()):?&gt;
         &lt;?php echo image_tag('lang/'.$lg.'_shadow',array('alt'=&gt; $lang, 'title'=&gt;$lang))?&gt;
      &lt;?php else:?&gt;
         &lt;?php echo link_to(image_tag('lang/'.$lg,array('alt'=&gt; $lang, 'title'=&gt;$lang)), 'sfCmsUser/language?culture='.$lg)?&gt;
      &lt;?php endif?&gt;
   &lt;?php endforeach?&gt;
&lt;?php endif?&gt;</pre>
<p>Notez la soumission par méthode get qui permet aux moteur de recherche d&#8217;indexer le contenu multilingue<br />
Enfin j&#8217;ai retouché la classe SfCmsContent comme suit dans /lib/model/SfCmsContent.php</p>
<pre name="code" class="php:nogutter">class SfCmsContent extends BaseSfCmsContent
{
  public function hydrate(ResultSet $rs, $startcol = 1)
  {
    parent::hydrate($rs, $startcol);
    $this-&gt;setCulture(sfContext::getInstance()-&gt;getUser()-&gt;getCulture());
    $lang = array_keys(sfConfig::get('app_lang_'.sfConfig::get('sf_app')));
    $lg = array_pop($lang);
    while($this-&gt;getTitle()=='' &amp;&amp; $lg )
    {
      $this-&gt;setCulture($lg);
      $lg = array_pop($lang);
    }
  }

  public function isTranslated($lg)
  {
    $c = new Criteria();
    $c-&gt;add(SfCmsContentI18nPeer::CULTURE, $lg);
    $c-&gt;addAnd(SfCmsContentI18nPeer::SF_CMS_CONTENT_ID, $this-&gt;getId());
    $sfCmsContentI18n = SfCmsContentI18nPeer::doSelectOne($c);
    if($sfCmsContentI18n &amp;&amp; $sfCmsContentI18n-&gt;getTitle() &amp;&amp; $sfCmsContentI18n-&gt;getStrippedTitle())
    {
      return $sfCmsContentI18n;
    }
    else
    {
      return false;
    }
  }
}</pre>
<p>La réécriture de la méthode <strong>hydrate</strong> permet d&#8217;avoir l&#8217;objet sfCmsContent dans la culture la plus adaptée à celle de l&#8217;internaute (notez que l&#8217;ordre des cultures à un sens)<br />
La méthode <strong>isTranslated</strong> permet de connaître la disponibilité d&#8217;un objet sfCmsContent pour une culture &#8230;</p>
<h4>gérer la cohérence de la langue dans l&#8217;url / éviter le duplicate content</h4>
<p>Maintenant le problème est de gérer la cohérence des urls, en utilisant correctement les routing rules énoncées plus haut, il y a une bonne partie du travail de fait &#8230; le seul problème qui se pose est celui de la culture par défaut qui n&#8217;a pas de préfixe. La conséquence est que les urls des cultures autres que par défaut répondent sur toute leur routing rule &#8230; et même sion a bien soigné la génération des liens en amont, mieux vaut se préserver du duplicate content et éviter les blagues. Voici donc un validateur pour l&#8217;action show qui va vérifier</p>
<ul>
<li>que l&#8217;url contient bien un stripped_title et non pas un simple identifiant</li>
<li>la disponibilité du contenu dans la culture de l&#8217;utilisateur (en le notifiant si le contenu n&#8217;est pas disponible)</li>
<li>la cohérence entre la culture du stripped_title du contenu et celle de l&#8217;utilisateur</li>
<li>la cohérence entre la culture du préfixe et celle du stripped_title</li>
</ul>
<pre name="code" class="php:nogutter">public function validateShow()
{

  // l'url contient bien un stripped_title et non pas un simple identifiant
  if($this-&gt;getRequestParameter('id'))
  {
    $sfCmsContent = SfCmsContentPeer::retrieveByPk($this-&gt;getRequestParameter('id'));
    $this-&gt;redirect('@sf_cms_content_'.$this-&gt;getUser()-&gt;getCulture().'?stripped_title='.$sfCmsContent-&gt;getStrippedTitle());
  }
  elseif($this-&gt;getRequestParameter('stripped_title'))
  {
    $sfCmsContent = SfCmsContentPeer::retrieveByStrippedTitle($this-&gt;getRequestParameter('stripped_title'));
    if($this-&gt;getUser()-&gt;can($sfCmsContent, 'show'))
    {
      $c = new Criteria();
      $c-&gt;add(SfCmsContentI18nPeer::STRIPPED_TITLE, $this-&gt;getRequestParameter('stripped_title'));
      $sfCmsContentI18nFromStrippedTitle = SfCmsContentI18nPeer::doSelectOne($c);

      // disponibilité du contenu dans la culture de l'utilisateur (en le notifiant si le contenu n'est pas disponible)
      if($sfCmsContent-&gt;isTranslated($this-&gt;getUser()-&gt;getCulture()))
      {
        $this-&gt;getUser()-&gt;setAttribute('translation_missing', false);
      }
      else
      {
        $this-&gt;getUser()-&gt;setAttribute('translation_missing', true);
      }

      // cohérence entre la culture du stripped_title du contenu et celle de l'utilisateur
      if($sfCmsContentI18nFromStrippedTitle-&gt;getCulture()!=$this-&gt;getUser()-&gt;getCulture())
      {
        if($sfCmsContent-&gt;isTranslated($this-&gt;getUser()-&gt;getCulture()))
      	 {
      	   $c = new Criteria();
      	   $c-&gt;add(SfCmsContentI18nPeer::SF_CMS_CONTENT_ID, $sfCmsContentI18nFromStrippedTitle-&gt;getSfCmsContentId());
      	   $c-&gt;addAnd(SfCmsContentI18nPeer::CULTURE, $this-&gt;getUser()-&gt;getCulture());
      	   $sfCmsContentUserCulture = SfCmsContentI18nPeer::doSelectOne($c);
      	   $this-&gt;redirect('@sf_cms_content_'.$this-&gt;getUser()-&gt;getCulture().'?stripped_title='.$sfCmsContentUserCulture-&gt;getStrippedTitle());
      	  }
       }
       // cohérence entre la culture du préfixe et celle du stripped_title
      	$uri = split('/',$this-&gt;getRequest()-&gt;getPathinfo());
    	if(strlen($uri[1])==2)
    	{
	  $uri_language = $uri[1];
    	}
    	elseif(sfConfig::get('app_lang_default'))
    	{
	  $uri_language = sfConfig::get('app_lang_default');
    	}
    	if(sfConfig::has('app_lang_default') &amp;&amp; $uri_language != $sfCmsContentI18nFromStrippedTitle-&gt;getCulture())
    	{
	  $this-&gt;redirect('@sf_cms_content_'.$sfCmsContentI18nFromStrippedTitle-&gt;getCulture().'?stripped_title='.$sfCmsContentI18nFromStrippedTitle-&gt;getStrippedTitle());
    	}
     }
     return true;
   }
   else
   {
     $this-&gt;forward404();
   }
 }
}</pre>
<p>Comme on a pris soin de mettre une notification si le contenu n&#8217;est pas disponible dans la culture souhaité, il ne reste plus qu&#8217;à l&#8217;afficher, avec un petit effet scriptaculous histoire d&#8217;attirer l&#8217;attention de l&#8217;internaute<br />
<strong>/apps/back/modules/sfCmsContent/templates/showSuccess.php</strong></p>
<pre name="code" class="php:nogutter">
&lt;?php if($sf_user-&gt;getAttribute('translation_missing')):?&gt;
   &lt;p id="missing_translation"&gt;
      &lt;?php echo __('The content you are looking for is missing for your language')?&gt;
   &lt;/p&gt;
   &lt;?php echo javascript_tag(
      visual_effect('highlight', 'missing_translation',
         array(
            'duration' =&gt; 2
         )
      )
   )?&gt;
&lt;?php endif?&gt;
&lt;?php echo $sfCmsContent->getHtmlBody();?&gt;
</pre>
<p>N.B pour chaque nouvel objet multilingue il faudra</p>
<ul>
<li>ajouter une routing rules par culture</li>
<li>réécrire les méthode hydrate et isTranslated</li>
<li>adapter le validateur validateShow</li>
<li>modififer le template pour faire apparaître la notification</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/internationalisation-sans-revolution-avec-symfony-pareil-mais-mieux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>googlebot notifier</title>
		<link>http://blog.mazenod.fr/2009/04/googlebot-notifier/</link>
		<comments>http://blog.mazenod.fr/2009/04/googlebot-notifier/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 22:21:48 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[bricolage]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=225</guid>
		<description><![CDATA[En lisant ce post, &#171;&#160;Être notifié du passage de Google sur son site&#160;&#187; de William DURAND, je me suis fendu d&#8217;une adaptation symfony à base d&#8217;un petit filtre &#8230;
Et je crois que Googlebot est justement en train de passer  

&#60;?php class sfGooglebotNotifierFilter extends sfFilter
{
   public function execute($filterChain)
   {
   [...]]]></description>
			<content:encoded><![CDATA[<p>En lisant ce post, <a href="http://www.willdurand.fr/posts/11/etre-notifie-du-passage-de-google-sur-son-site.html">&laquo;&nbsp;Être notifié du passage de Google sur son site&nbsp;&raquo; de William DURAND</a>, je me suis fendu d&#8217;une adaptation symfony à base d&#8217;un petit filtre &#8230;<br />
Et je crois que Googlebot est justement en train de passer <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<pre name="code" class="php:nogutter:nocontrols">
&lt;?php class sfGooglebotNotifierFilter extends sfFilter
{
   public function execute($filterChain)
   {
      if (preg_match('#Googlebot#i', $this-&gt;getContext()-&gt;getRequest()-&gt;getHttpHeader('User-Agent')))
      {
         $mail = new sfMail();
         $mail-&gt;initialize();
         $mail-&gt;setMailer('sendmail');
         $mail-&gt;setCharset('utf-8');
         $mail-&gt;setSender('mailer@monsite.com', 'mailer de monsite.com');
         $mail-&gt;setFrom('mailer@monsite.com', 'mailer de monsite.com');
         $mail-&gt;addAddress('your@mail.com');
         $mail-&gt;setSubject('Google bot visit');
         $mail-&gt;setBody($this-&gt;getContext()-&gt;getRequest()-&gt;getHttpHeader('User-Agent').' has visited '.$this-&gt;getContext()-&gt;getRequest()-&gt;getUri());
         $mail-&gt;send();
      }
      $filterChain-&gt;execute();
   }
}
 ?&gt;
</pre>
<p>N.B. Les passages du googlebots sont aussi disponibles dans les rapports de Googel Analytics ( visiteurs, capacités du navigateur, navigateurs).<br />
à intégrer comme suit dans /apps/myapp/config/filters.yml</p>
<pre name="code" class="yml:nogutter:nocontrols">
rendering: ~
web_debug: ~
sf_googlebot_notifier:
  class: sfGooglebotNotifierFilter
security:  ~
# generally, you will want to insert your own filters here
cache:     ~
common:    ~
flash:     ~
execution: ~
</pre>
<p>J&#8217;allais oublier un gand merci au même William Durand pour son autre post <a href="http://www.willdurand.fr/posts/10/syntaxhighlighter-tinymce-et-yml.html">SyntaxHighlighter, TinyMCE et YML</a>, qui m&#8217;a permis d&#8217;intégrer la coloration syntaxique YAML à syntaxHighlighter2 via la brush qu&#8217;il a conçue <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>autre ressource: <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes:Custom">http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes:Custom</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/googlebot-notifier/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>symfony fête son 500ème pulgin</title>
		<link>http://blog.mazenod.fr/2009/04/symfony-fete-son-500eme-pulgin/</link>
		<comments>http://blog.mazenod.fr/2009/04/symfony-fete-son-500eme-pulgin/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 13:55:19 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=212</guid>
		<description><![CDATA[Le système de plugin est un atout majeur du framework PHP symfony. Il permet de ne pas réinventer la roue à chaque projet : De la gestion d&#8217;utilisateur, au comportement taggable, en passant par le moteur de recherche interne basé sur Lucene, l&#8217;éventail s&#8217;élargit tous les jours (cf la courbe ci dssous)

C&#8217;est pourquoi il est [...]]]></description>
			<content:encoded><![CDATA[<p>Le <a href="http://www.symfony-project.org/plugins/">système de plugin</a> est un atout majeur du <strong>framework PHP symfony</strong>. Il permet de ne pas réinventer la roue à chaque projet : De la gestion d&#8217;utilisateur, au comportement taggable, en passant par le moteur de recherche interne basé sur Lucene, l&#8217;éventail s&#8217;élargit tous les jours (cf la courbe ci dssous)</p>
<p style="text-align: center;"><a href="http://www.symfony-project.org/blog/2009/04/07/tell-us-the-plugins-you-use"><img class="size-full wp-image-213 aligncenter" title="plugin_creation_rate" src="http://blog.mazenod.fr/wp-content/uploads/2009/09/plugin_creation_rate.png" alt="plugin_creation_rate" width="450" height="278" /></a></p>
<div style="text-align: left;">C&#8217;est pourquoi il est maintenant possible de connaître la <a href="http://www.symfony-project.org/plugins/popular/">popularité d&#8217;un plugin symfony</a> avant de l&#8217;intégrer à son projet <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>La liste de mes préférés:</p></div>
<ul>
<li><a href="http://www.symfony-project.org/plugins/sfLucenePlugin">sfLucenePlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfPropelActAsTaggableBehaviorPlugin">sfPropelActAsTaggableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfPropelActAsRatableBehaviorPlugin">sfPropelActAsRatableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfThumbnailPlugin">sfThumbnailPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfTagtoolsPlugin">sfTagtoolsPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfFeed2Plugin">sfFeed2Plugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/">sfPropelActAsCommentableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfPropelVersionableBehaviorPlugin">sfPropelVersionableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfSimpleForumPlugin">sfSimpleForumPlugin</a></li>
</ul>
<p>N.B. ce sont mes plugins préférés pour <strong>symfony 1.0</strong> &#8230; je ne désepère pas de trouver 24h00 pour me fader <a href="http://www.symfony-project.org/jobeet/1_2/">jobeet</a> et passer à la 1.2:-/</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/symfony-fete-son-500eme-pulgin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
