Articles taggés "symfony"

Poste par Cedric Sadai, le 14/12/10 - Technologie

Développer un blog avec Symfony est une très belle initiative. Cela permet de se familiariser très rapidement avec tous les concepts classiques rencontrés dans la vaste majorité des projets. C’est même l’application idéale quand on découvre un nouveau langage ou une nouvelle version d’un framework, car c’est assez petit pour être réalisable (2 jours de travail pour mon blog), et vous obtenez un vrai produit fini destiné à être mis en ligne (ce qui vous permet de traiter l’ensemble de la chaîne de construction web, y compris la partie déploiement).

Cependant, bâtir un blog ex-nihilo sur un framework comme Symfony en (bientôt) 2011 est mauvais à long terme. Et c’est même inscrit dans la logique même des framework, puisqu’on nous conseille de ne pas se répéter. Or, pour une application ultra classique comme un blog, tout ce que vous ferez avec un blog custom est d’éternellement vous répéter. Vous répéter et perdre du temps, le tout pour une efficacité moindre, car votre intelligence d’atteindra jamais la somme de l’intelligence collective.

Pour ceux qui voudraient switcher d’un blog développé sous Symfony à un WordPress plus facile à gérer, je vous livre les petits scripts qui m’ont permis très rapidement (après intégration xHTML/CSS du thème, évidemment), de récupérer mes articles et mes commentaires en quelques secondes via l’outil d’import de WP.

Voici la structure de DB de mon ancien blog:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql> show tables;
+---------------------------+
| Tables_in_sadai_blog      |
+---------------------------+
| blog_category             |
| blog_comment              |
| blog_post                 |
| blog_post_images          |
| sf_guard_group            |
| sf_guard_group_permission |
| sf_guard_permission       |
| sf_guard_remember_key     |
| sf_guard_user             |
| sf_guard_user_group       |
| sf_guard_user_permission  |
| sf_tag                    |
| sf_tagging                |
+---------------------------+
13 rows in set (0.00 sec)

 

Voici la description de la table “blog_post”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> show columns from blog_post;
+----------------+--------------+------+-----+---------+----------------+
| Field          | Type         | Null | Key | Default | Extra          |
+----------------+--------------+------+-----+---------+----------------+
| user_id        | int(11)      | YES  | MUL | NULL    |                |
| category_id    | int(11)      | YES  | MUL | NULL    |                |
| title          | varchar(255) | YES  |     | NULL    |                |
| main_pic       | varchar(100) | YES  |     | NULL    |                |
| slug           | varchar(255) | YES  |     | NULL    |                |
| content        | text         | YES  |     | NULL    |                |
| is_published   | int(11)      | YES  |     | NULL    |                |
| allow_comments | int(11)      | YES  |     | NULL    |                |
| created_at     | datetime     | YES  |     | NULL    |                |
| published_at   | date         | YES  |     | NULL    |                |
| id             | int(11)      | NO   | PRI | NULL    | auto_increment |
+----------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)

L’image titre, quand elle est présente, va automatiquement s’ajouter au début du contenu de l’article. Ce mécanisme est repris dans le script d’import.

Les tags, eux, sont simplement des tags, il n’y a pas de version libre et de version normalisée. Enfin, les commentaires se décrivent ainsi:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> show columns from blog_comment;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| post_id      | int(11)      | YES  | MUL | NULL    |                |
| author_name  | varchar(200) | YES  |     | NULL    |                |
| author_email | varchar(255) | YES  |     | NULL    |                |
| author_url   | varchar(255) | YES  |     | NULL    |                |
| ip           | varchar(100) | YES  |     | NULL    |                |
| content      | text         | YES  |     | NULL    |                |
| is_active    | int(11)      | YES  |     | NULL    |                |
| created_at   | datetime     | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)

Nous allons donc générer un XML d’export au format WordPress, qui va nous permettre d’importer l’ensemble de notre blog (articles, tags, commentaires, catégories) en une seule commande.

Côté action, il nous faut donc récupérer l’ensemble des catégories, tags, et articles contenus en DB (on récupérera les commentaires via un accesseur sur le modèle Post). Une execution très simple (sous… Propel! Eh oui, ce blog est vieux) ;)

 

1
2
3
4
5
6
7
8
public function executeExport()
{
  $c = new Criteria();
  $this->blogCats = CategoryPeer::doSelect($c);
  $this->blogTags = TagPeer::doSelect($c);
  $this->posts = PostPeer::doSelect($c);
  $this->setLayout(false);
}

Puis,du côté du template, on effectue toutes les transformations nécessaires afin de satisfaire au format d’export WordPress. Pour en savoir plsu sur ce format, fouillez tout simplement le script d’export de WordPress, et parcourez cette ressource.

Voici la copie exacte du template exportSuccess.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your blog. -->
<!-- It contains information about your blog's posts, comments, and categories. -->
<!-- You may use this file to transfer that content from one site to another. -->
<!-- This file is not intended to serve as a complete backup of your blog. -->

<!-- To import this information into a WordPress blog follow these steps. -->
<!-- 1. Log into that blog as an administrator. -->
<!-- 2. Go to Manage: Import in the blog's admin panels. -->
<!-- 3. Choose "WordPress" from the list. -->
<!-- 4. Upload this file using the form provided on that page. -->
<!-- 5. You will first be asked to map the authors in this export file to users -->
<!--    on the blog.  For each author, you may choose to map to an -->
<!--    existing user on the blog or to create a new user -->
<!-- 6. WordPress will then import each of the posts, comments, and categories -->
<!--    contained in this file into your blog -->

<!-- generator="WordPress/MU" created="2008-05-16 22:49"-->
<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:wp="http://wordpress.org/export/1.0/"
>
<?php $base = 'http://www.sadai.net';?>
<channel>
  <title>Marketing &amp; Technologies | Sadai.net</title>
  <link><?php echo $base;?></link>
  <description>Publicit&acute; digitale, Data, Science, Architectures.</description>
  <pubDate>Fri, 1 Jan 2008 22:46:22 +0000</pubDate>
  <generator>http://wordpress.org/?v=MU</generator>
  <language>fr</language>
  <wp:wxr_version>1.0</wp:wxr_version>
  <wp:base_site_url><?php echo $base;?></wp:base_site_url>
  <wp:base_blog_url><?php echo $base;?></wp:base_blog_url>

  <?php foreach ($blogCats as $blogCat):?>
  <wp:category>
    <wp:category_nicename><?php echo $blogCat->getSlug();?></wp:category_nicename>
    <wp:category_parent></wp:category_parent>
    <wp:cat_name><![CDATA[<?php echo $blogCat->getName()?>]]></wp:cat_name>
  </wp:category>
  <?php endforeach;?>

  <?php foreach ($blogTags as $blogTag):?>
    <wp:tag><wp:tag_slug><?php echo $blogTag->getName();?></wp:tag_slug><wp:tag_name><![CDATA[<?php echo $blogTag->getName();?>]]></wp:tag_name></wp:tag>
  <?php endforeach;?>


  <?php foreach ($posts as $post):?>
    <?php $content = $post->getContent()?>

    <?php if ($post->hasPic()):?>
      <?php $content = '<p>' . '<img src="'.$post->getPicUrl().'" border="0" align="left" style="padding-right:7px" />' . substr($content, 3);?>
    <?php endif;?>
    <item>
    <title><![CDATA[<?php echo $post->getTitle(); ?>]]></title>
    <link><?php $base . '/' . $post->getSlug(); ?></link>
    <pubDate><?php echo date( 'D, d M Y H:i:s +0000', strToTime($post->getCreatedAt())); ?></pubDate>
    <dc:creator><![CDATA[Cedric Sadai]]></dc:creator>

    <category><![CDATA[<?php echo $post->getCategory()->getName()?>]]></category>
    <category domain="category" nicename="<?php echo $post->getCategory()->getSlug()?>"><![CDATA[<?php echo $post->getCategory()->getName()?>]]></category>

    <?php foreach ($post->getTags() as $tag):?>
    <category domain="tag"><![CDATA[<?php echo $tag;?>]]></category>
    <category domain="tag" nicename="<?php echo $tag;?>"><![CDATA[<?php echo $tag;?>]]></category>
    <?php endforeach;?>

    <guid isPermaLink="false"><?php echo $base . '/' .$post->getSlug(); ?></guid>
    <description></description>
    <content:encoded><![CDATA[<?php echo preg_replace('/\/uploads\/assets\//i', '/wp-content/uploads/assets/', $content);?>]]></content:encoded>
    <wp:post_date><?php echo $post->getCreatedAt(); ?></wp:post_date>
    <wp:post_date_gmt><?php echo $post->getCreatedAt(); ?></wp:post_date_gmt>
    <wp:comment_status>open</wp:comment_status>
    <wp:ping_status>open</wp:ping_status>
    <wp:post_name><?php echo $post->getSlug(); ?></wp:post_name>
    <wp:status>publish</wp:status>
    <wp:post_type>post</wp:post_type>
    <wp:post_password></wp:post_password>
    <wp:is_sticky>0</wp:is_sticky>

    <?php foreach ($post->getAllComments() as $comment):?>
    <wp:comment>
    <wp:comment_id><?php echo $comment->getId()?></wp:comment_id>
    <wp:comment_author><![CDATA[<?php echo $comment->getAuthorName()?>]]></wp:comment_author>
    <wp:comment_author_email><?php echo $comment->getAuthorEmail()?></wp:comment_author_email>
    <wp:comment_author_url><?php echo $comment->getAuthorUrl()?></wp:comment_author_url>
    <wp:comment_author_IP><?php echo $comment->getIp()?></wp:comment_author_IP>
    <wp:comment_date><?php echo date( 'D, d M Y H:i:s +0000', strToTime($comment->getCreatedAt())); ?></wp:comment_date>
    <wp:comment_author_IP><?php echo $comment->getIp()?></wp:comment_author_IP>
    <wp:comment_date><?php echo date( 'D, d M Y H:i:s +0000', strToTime($comment->getCreatedAt())); ?></wp:comment_date>
    <wp:comment_date_gmt><?php echo date( 'D, d M Y H:i:s +0000', strToTime($comment->getCreatedAt())); ?></wp:comment_date_gmt>
    <wp:comment_content><![CDATA[<?php echo $comment->getContent()?>]]></wp:comment_content>
    <wp:comment_approved>1</wp:comment_approved>
    <wp:comment_parent>0</wp:comment_parent>
    <wp:comment_user_id>0</wp:comment_user_id>
    <wp:comment_type></wp:comment_type>
    </wp:comment>
    <?php endforeach;?>
  </item>
  <?php endforeach;?>
</channel>
</rss>

Pas de grosse difficulté ici, à part qu’il faut bien donner un ID à chaque commentaire, sous peine de les voir assimiler à des doublons. Il faut également bien leur passer le statut “approved” à 1, sinon il ne seront pas importés. Enfin, à la ligne 71, je remplace toutes les occurences de /uploads/assets par /wp-content/uploads/assets/, afin que toutes mes images soient conservées au terme d’un simple copier-coller de répertoire au bon endroit.

J’ai hâte à présent de reprendre une activité de blogging plus soutenue!

Poste par Cedric Sadai, le 20/10/09 - Technologie

Plateforme street artCes dernières semaines, nous avons sorti un produit que nous préparons depuis pas mal de temps. Il s’agit de FatCap. Ce site, visible en francais sur www.fatcap.org, et initialement créé en 1998, est un des leaders mondiaux des magazines d’arts urbains en ligne. Pour cette nouvelle version, les objectifs à atteindre étaient multiples:

  • Bâtir une plateforme solide, basée sur le contenu des utilisateurs (photos, vidéos) ET sur le contenu produit par l’équipe éditoriale (vidéos maison, articles, interviews, reportages, mises en avant)
  • Rendre cette plateforme entièrement géolocalisée, afin de pouvoir suivre l’évolution de l’art dans une ville, dans un état américain, dans un pays, ou même dans un continent.
  • Tirer la plus grande valeur de chaque élément de contenu en créant une base intelligente et structurée censée représenter l’art urbain de manière exhaustive: Ainsi, chaque artiste devra avoir sa page, ainsi que chaque groupe d’artiste. De même, chaque photo pourra être multi-taggée avec une quinzaine de critères afin de répondre de manière complète à tout type de demande de la part des internautes.
  • L’ensemble de la plateforme devra être internationalisée, et mieux encore, localisée, c’est à dire que la couche éditoriale devra être différente sur les différents sites locaux. L’actualité de chaque version localisée étant adaptée au pays ciblé par la version, à l’inverse de sites internationalisés au contenu unique, traduit soit à la main soit par un traducteur automatique.

La conception

Nous sommes donc partis pour quatre mois de conception dès mi-2007, où il s’agissait d’abord de faire un état des lieux des technologies en présence. En effet, il fallait contourner par la technologie les problèmes suivants:

- Géolocalisation: Il était bien sûr hors de question de se baser sur des champs texte, qui laissent la part belle aux fautes en tout genre. Il fallait se baser sur une API (et donc sur des ID), et proposer un système souple et intelligent qui permettrait aux utilisateurs de renseigner les villes de manière intuitive. Côté API, elle devait être pérenne, et n’avoir aucun risque de cessation d’activité à court ou moyen terme. Nous avons opté pour les bases de Yahoo! Geoplanet (qui, hasard ou chance, sont sorties en plein milieu de notre phase de conception), car ce sont celles utilisées par Flickr et celles qui de loin présentent les fonctionnalités les plus puissantes et les meilleures garanties.

- Ce dernier point fut en outre à l’origine d’un problème plus global: ayant un contenu méta-taggée de manière très riche, il convenait d’adopter un système de tagging très simple à utiliser, très loin donc, des éléments de formulaires mis à notre disposition par le simple fruit de l’HTML (menu déroulants, boutons radio, etc..). Le JavaScript allait donc être intensivement utilisé sur certaines pages. En effet, plus vous rendez une information compliquée à rentrer, plus vous risquez d’avoir des informations lacunaires.

La solution à ces deux problèmes peuvent être illustrés au travers de l’exemple du tagging d’une ville. Voici à quoi ressemble le formulaire d’inscription, par exemple:

fig. 1

fig. 2

fig. 3

Comme vous le voyez sur le drapeau, on préselectionne automatiquement le pays dans lequel l’IP du visiteur se situe. S’il le souhaite, il peut évidemment changer ce pays. Cela nous permet de restreindre notre recherche de ville au pays sélectionné, évitant ainsi les multiples doublons constatés à l’échelle mondiale. Ensuite, la puissance de l’interactivité avec l’API se déploie lorsque la ville est inconnue en base (fig. 2). La, alors que l’utilisateur va taper entièrement le texte de sa ville, nous ne sommes pas encore en mesure de la relier à une ville issue de la base de données des villes Flickr, disponible à travers Yahoo! Geoplanet. Alors, une fois le texte rentré, nous effectuons par Ajax la bonne requete YQL, et sommes capables, s’il y a confusion, de proposer des options à sélectionner (fig. 3), ou, si nous avons un résultat unique, de simplement qualifier le texte tapé en une puce signalant la validation du contenu.

- Ensuite, il y a la spécificité d’un site au contenu localisé. Un tel site ne saurait abriter toutes ses différentes version sous un seul et même serveur. Au contraire, le contenu doit être servi au plus près des internautes, ce qui sous entend d’héberger chaque version dans le pays des visiteurs qu’il cible. Cela pose donc entre autres le problème de l’appel aux images, puisque des images sont chargées sur des serveurs distants de 7000 km, et que nous ne souhaitions pas utiliser de technologie de synchronisation des fichiers (même si unison a longuement été étudié), car technologie sujette aux lags, à la consommation frénétique de bande passante (qui n’est pas chère en France mais hors de prix aux Etats Unis), et sous-optimale en terme d’optimisation de l’espace disque.

Nous avons donc développé un système de gestion intelligent des assets, qui est capable de déterminer sur quel serveur du cluster une image a été chargée, et de l’afficher en conséquence, ainsi que de faire une copie permanente des assets sur le service de stockage tiers Amazon S3. Une fois une image répliquée (dans les 10mn de sa validation), c’est le chemin physique le plus court jusqu’au serveur qui est choisi, dans le but d’optimiser la vitesse et les couts.

Le design

Printemps – été 2008: Une fois les documents établis (specs fonctionnelles et techniques, wireframes détaillées, etc.), l’élaboration du design fut assez “simple”. Déja, parce que nous travaillions avec une des meilleures agences de Paris (CubeDesigners), mais aussi parce que nous avions une idée très claire de ce que nous souhaitions, facilitant les allers retours. Ainsi, élaborer une page clé complexe ne prenait pas plus d’une journée, sans que nous n’ayons, jusqu’au dernier jour, besoin d’y retoucher le moindre pixel.

Le défi ergonomique de ce design était de bâtir des listes filtrables, sur un système d’entonnoir. Ainsi, une liste de photo peut être celle d’un artiste, comme C215, mais aussi la liste des photos de Paris, celle de tous les “personnages” apparaissant dans une oeuvre, celle de France, de Californie, etc… C’est là la puissance du site: structurer à l’extrême un contenu par ailleurs désorganisé, rendu simple par une utilisation de repères identiques à travers la visite.

Un bon exemple de ce souci de facilitation du parcours de visite est la page de vue d’une photo. Sur cette page finale (rien après ce niveau de navigation), il convenait de proposer aux visiteurs d’autres contenus reliés. Etant donnés nos multiples niveaux de classification d’une photo, nous avons opté pour un coverflow, grâce auquel on peut continuer sa visite sur des frises, toutes classées selon un des critères clés choisis à travers le site (timeline, artiste, ville, type, style, support).

Le développement

Beaucoup de travail nous attendait chez SeedWeb. Nous avons passé l’été 2008 à concevoir le design, et l’automne fut l’occasion pour notre équipe d’intégrateurs HTML d’intégrer toutes ces maquettes en XHTML selons nos critères très stricts, dans le but d’améliorer le référencement naturel, et en parallèle pour notre équipe de développeurs front, de développer la couche Javascript (avec jQuery), en particulier les interfaces de tagging. Le développement lourd a donc commencé à l’hiver 2008-2009, ce qui concordait avec la sortie de Symfony 1.2, qui intégrait enfin Doctrine comme un ORM de choix, ce qui tombait bien car vue la complexité de certaines requêtes du site et la puissance du DQL, nous aurions difficilement pu nous en passer.

De cette base Symfony, nous avons presque tout étendu: les classes de base bien sûr, le moteur lui même, puisque nous lui avons adjoint la puissance des composants Zend Framework, notamment pour la gestion des services tiers (FatCap est un site ouvert, interfacé avec Twitter, Youtube, Dailymotion, Vimeo, Flickr, etc..), nous avons également étendu les actions de manière à ce qu’elles héritent d’une classe mère par application, qui hérite elle même d’une action commune à l’ensemble du projet, afin de factoriser au maximum le code. Idem pour les composants. Toute notre gestion des images (redimensionnement, intégration à notre système de réplication) est le fruit d’une nouvelle classe de traitement de l’upload des fichiers (qui étend sfValidatedFile), et qui se base sur des fichiers de configuration puissants afin de déterminer la liste des miniatures à effectuer, des emplacements de stockage, etc… L’ensemble des modèles de données est étendu grâce à des templates doctrine, comme par exemple le choix du serveur pour afficher une image, ou encore la réplication S3, le tout en minimisant les requêtes et le temps d’affichage, et en rendant toutes les actions longues asynchrones.

L’ensemble de cette belle machine est surveillée par des milliers de tests fonctionnels et unitaires, et servie par un cluster de serveurs répartis dans le monde, avec des systèmes de réplications de bases de données complets, une utilisation intensive de memcache, et un accent fort mis sur la sécurité. La base de données est constamment “purifiée” par une cinquantaine de démons qui tournent à longueur de journées et de nuits, et qui s’appuient sur un logging statistique étendu pour garder une base de données propre, qualifiée, et à jour (qui sert aussi à produire les différents graphiques sur le site).

En bref, un très long projet, et une belle prouesse technologique pour SeedWeb, qui synthétise l’ensemble de nos champs d’expertise, et qui correspond à notre positionnement ambitieux de s’employer à produire des sites de très haute qualité, facilement extensibles et utilisant un éventail de technologies matures et professionnelles, en utilisant notre expertise en marketing internet et en ergonomie pour maximiser l’expérience utilisateur, et diminuer les couts d’acquisition.

Poste par Cedric Sadai, le 27/04/09 - Technologie

Lorsqu’on réalise un projet web d’envergure, il convient de prendre en compte les éléments annexes au développement: le référencement naturel (c’est le cas pour de plus en plus de développeur), et, chose moins commune, le déploiement et l’évolutivité de l’hébergement.

Si vous êtes sur financement, cette question pourra être rapidement éludée en passant par des prestataires à obligation de résultats, qui se chargeront pour vous de monter la meilleure architecture possible, quel qu’en soit le prix.

En revanche si vous êtes en auto-financement, les solutions techniques vous permettant de construire une architecture évolutive à moindre coût vous intéresseront. A notre sens, il convient ainsi de canaliser les goulets d’étranglement du serveur (généralement, la RAM, le processeur, et les écritures simultanées sur le disque dur, hors swap) en séparant les tâches sur des serveurs différents, ce qui permet de monter des système extensibles et légers.

Ainsi, un site avec du PHP extensif, comme une application sous symfony sera réparti ainsi:

- Un serveur avec pas mal de RAM pour servir le frontend. Sur ce serveur, une partie de la RAM doit être allouée à un accélérateur d’opcode type APC (512 Mo dédié parait pas mal). Cela permet de garder en cache une version précompilée de vos fichiers PHP, très utile quand on utilise un framework, puisqu’un framework procède à des inclusions en série de milliers de fichiers PHP, ce qui créé une brèche de rapidité automatiquement.

- Un serveur avec pas mal de RAM/proc pour la base de données. Idéalement, cette base de données ne remplit qu’un seul des deux rôles clés: soit elle lit, soit elle écrit. Si elle écrit, elle doit être couplée à une base “esclave”, elle même sur un serveur à part, avec encore moins de RAM. Si vous optez pour un tel système, sachez que cela implique pas mal de changement côté applicatif, puisque vous devez signaler à chaque action persistante la base de données à utiliser. Dans la théorie, des ORM avancés comme Doctrine et son fameux système de listeners permettent de faire cela. Dans la pratique, on a beaucoup de surprises.

- Un + qui fera toute la différence: si vous avez assez de RAM, et si vos serveurs de DB sont connectés à votre serveur de front en VLAN, profitez-en pour installer Memcache sur ce serveur, ouvrir le port adéquat, et stockez-y vos sessions utilisateurs. Memcache est infiniment plus rapide qu’un stockage sur disque dur, car les couples clés/valeurs sont stockés directement dans la mémoire vive.  Autre avantage, le fait d’avoir vos sessions stockées directement sur un serveur dédié à cette tâche rend votre serveur d’application encore plus indépendant, ce qui facilite, si besoin est, son extension par quelque méthode que ce soit (load balancing, failover heartbeat, round robin, etc…).

- Ensuite, nous vous recommandons de vous débarasser de la gestion des “assets” (éléments persistants uploadés par les administrateurs ou les utilisateurs, typiquement des images). En plus d’encombrer votre bande passante et de démultiplier vos lectures/écritures sur disque dur (ce qui augmente considérablement les risques de casse et pose le souci de la sauvegarde), vous rendez votre cluster de front plus difficile à mettre en place. En effet, pour que celui-ci soit efficace, il faut que chaque serveur contienne exactement la même chose. S’il existe des solutions de synchronisation comme rsync, ou mieux, unison, l’idéal est à notre sens de passer par un système de stockage tiers, comme Amazon S3 ou Mosso. Certes, l’implémentation côté applicatif sera plus complexe à gérer, mais vous vous débarrasserez des problèmes de sauvegarde, de sécurité, et surtout, vous allégerez considérablement votre/vos serveur(s)  applicatif(s).

- Pour finir, quelque chose de plus classique, débarrassez vous de la gestion des emails. Cela monopolise beaucoup de ressources (les AV et autres antispam sont TRES gourmands en RAM), et cela requiert énormément de maintenance pour conserver un système efficace. Optez pour une solution payante (vous transférez les MX et ne vous occupez plus de rien), ou optez au “pire” pour Google Apps qui vous permet également de faire ce genre de délocalisation, souvent pour le bonheur de vos utilisateurs.

Voilà, en ayant ce réflexe délocalisation, vous pouvez bâtir un serveur applicatif ultra light, extensible à l’infini, sécurisé à tous les niveaux (lancez un petit cron sur les serveurs de DB pour faire un dump et l’envoyer sur S3, ca prend 10 mn et 20mn pour faire le travail inverse), et très rapide à mettre en place. Côté installation, nous vous recommandons de travailler sur des box identiques, montées par exemple avec un Ubuntu LTS vide, et bloqués sur tous les ports qu’on utilisera pas avec iptable, ce qui rendra le tout encore plus sécurisé.

En conclusion, faire un système scalable, c’est également penser serveurs, et penser serveurs impose bien souvent de développer des spécificités au sein même de la couche applicative. Dans un prochain article, nous étudierons d’autres moyens d’accélérer l’exécution de son application.

Poste par Cedric Sadai, le 19/02/09 - Business 2.0

C’est officiel, Dailymotion a récemment fait évoluer sa plateforme technique en la redéveloppant sous symfony. Même si la version utilisée semble un hybride “customisé” pour l’occasion, l’essentiel de l’application semble reposer sur le même framework que celui publié à ce jour.

Après Yahoo! Answers, Delicious et beaucoup d’autres, le passage d’un autre grand nom du web sera certainement bénéfique au framework, qui continue doucement mais sûrement sa route vers une large adoption auprès du secteur professionnel.

Poste par Cedric Sadai, le 21/12/08 - Technologie

Jobeet est un tutorial en 24 parties, destiné à couvrir l’ensemble des fonctions offertes par le framework symfony dans le contexte de réalisation d’un projet réel. Alors que la série touche à sa fin, on peut faire quelques observations:

#1 L’ordre des cours

L’équipe symfony a choisi de partir du cahier des charges (sous formes de stories, respectant les principes de l’XP), pour ouvrir les hostilités par le design, juste après la conception du modèle de données, avant de s’attaquer en profondeur à la couche métier.

C’est précisément l’ordre par lequel nous avons également eu le moins de surprises. Juste après la conception de données, nous nous attardons même sur le conception visuelle de l’ensemble des parties où l’utilisateur peut entrer des données. Généralement, donc, le back office. En plus du design des formulaires (très utile pour repasser ensuite aux formulaires objets, faire les bons empilements de formulaires et développer les bons widgets), on peut également designer les différentes listes proposées à l’administrateur, ainsi que les interactions qu’il devrait être en mesure de faire à partir de là (et les implémenter basiquement, y compris l’éventuelle couche javascript).

Il s’agit en fait d’un bon moyen pour se rendre compte des éventuelles incohérences de conception, puisqu’en partant de toutes les possibilités d’entrées de données, il est facile d’adapter le modèle de données au mieux, l’affichage en front n’étant très souvent qu’une simple exploitation de données entrées par ailleurs (ou alors, si ce n’est pas le cas, il convient dinclure ces interactions utilisateurs en front dans ce prototypage graphique du début). Commencer par l’aspect visuel permet aussi de rapidement traduire vos scénarii d’utilisation en modules, actions, et routes correspondantes.

#2 L’importance des plugins

Nous avons beaucoup aimé le retour appuyé sur l’utilité des plugins. En effet, les plugins ne sont pas uniquement des packages publics prets à l’emploi. Ils sont aussi peut être le meilleur moyen de développer la véritable intelligence de votre projet. En effet, leur réutilisation est extrêmement simple, et se résume souvent à copier/coller un répertoire, qui lui reprend l’ensemble de votre travail.

Ainsi, dans les traitements de la couche modèle que nous faisons, nous travaillons par itérations succsessives:

1/ définition du comportement souhaité (exemple, un modèle Article doit etre taggé par un modèle Tag)

2/ qualification selon le critère “besoin ponctuel” ou “fonctionnalité réutilisable”. Si la fonctionnalité doit être réutilisée, soit sur un autre modèle du même projet (ex: modèle Video), soit sur un autre projet, nous pensons “plugin”.

3/ Comme nous travaillons désormais uniquement sur Doctrine, nous avons le bonheur d’utiliser très régulièrement les Templates, natifs à l’ORM. Pour chaque template que nous créons, nous créons un plugin correspondant.

4/ Si des interactions utilisateurs sont propres à cette fonctionnalité, nous les ajoutons au plugin. Par exemple dans notre cas, nous pouvons y intégrer une interface d’administration pour voir la liste des tags et en supprimer, ou encore un widget spécial qui étend une fonctionnalité jQuery qui va puiser dans notre liste de tags pour auto-alimenter un champs text avec suggestions AJAX (qui lui même pourrait étendre ce widget déja existant).

Ainsi, l’application pure de notre système se résume à définir le comportement dans le modèle de données (actAs: Taggable), puis de définir notre nouveau widget dans notre formulaire (sfForm::setWidget(‘field’ => myWidgetFormInputWhatever)). En bref, une fois que le plugin est développé, deux minutes de travail réutilisables sur tous nos modèles.

#3 L’ouverture sur le monde

Le move stratégique le plus intéressant de cette série de tutoriaux est d’avoir inscrit clairement symfony non comme une solution s’opposant aux autres offres du marché, mais comme une solution complémentaire, et en particulier un produit complémentaire du framework Zend.

C’est également une pratique que nous apprécions. En effet, de si nombreuses librairies ayant été développées pour Zend, qu’il serait contraire au principe DRY de vouloir réinventer la roue. De plus, symfony permet d’intégrer facilement n’importe quelle librairie externe. Zend_Rest, Zend_Gdata, Zend_Amf, Zend_Search_Lucene, et bien d’autres encore sont des librairies magnifiques tant elles vous font gagner du temps de développement.

En bref, ces tutoriaux nous ont fait découvrir le symfony que nous pratiquons et que nous aimons: une plateforme complète et fiable qui s’intègre avec toute l’intelligence créée par ailleurs, et qu’on peut vraiment développer de manière modulaire et incrémentale en réutilisant systématiquement chaque entité développée.

C’est en programmant de cette manière (avec des tests systématiques et complets) que l’ont peut beaucoup gagner en productivité, à mesure que l’expérience s’accumule.

Poste par Cedric Sadai, le 01/12/08 - Technologie

Si vous maîtrisez bien le PHP5, comprenez la programmation orientée objet, et souhaitez en apprendre plus sur le framework symfony, cette news est pour vous. L’équipe symfony va mettre en place 24 tutoriaux successifs visant à développer de zéro une application entière, en s’arrêtant sur tous les points stratégiques qui vous permettront de bien maîtriser le fonctionnement du framework pour rapidement le prendre en main pour vos propres projets.

Symfony est un framework MVC (le traitement de votre couche métier est séparée du traitement de la requête, elle même séparée de l’affichage, via les templates et les fragments). Son but est de traiter la couche basse de votre application, celle qui est indispensable (et donc, récurrente) à chaque projet. Le projet symfony est entièrement découplé et organisé par grands ensembles. Gestion de vos URLs (routing), de vos formulaires, de l’accès à vos données modelisées (via un ORM, comme Doctrine ou Propel), sont autant de briques indépendantes qui, après les avoir apprivoisées, vous feront gagner du temps dans votre développement. Côté “magique”, un générateur d’administration rendra heureux tous ceux qui souhaitent réaliser un prototypage rapide ou qui aiment voir un réusltat visuel immédiat.

Nous vous tiendrons informés ici-même de l’avancée de ces tutoriaux, et reviendrons en profondeur sur certaines briques qui nous paraissent importantes.

Lien >> Le tutorial Jobeet