Symfony pour un développement rapide
Enfin, j’émerge, après une semaine de développement où j’ai pu apprécier la puissance du Framework Français (mais totalement anglophone) Symfony (en PHP5), l’un des plus solide et performant, utilisé par 20 millions d’utilisateurs de Yahoo Bookmark’s
Quand on utilise la puissance de ce type d’outil et quand on pense que Ruby on Rail en ai une figure de proue avec une version stable depuis 2005 on se demande ce que Zend était en train de faire pendant ce temps là, en nous proposant encore aujourd’hui une version Beta
Principe de MVC (Model View Controller)
Ce principe permet une séparation entre le Modèle qui permet la manipulation de données, la Vue qui affiche ces données (templates) et le Contrôleur qui indique quoi faire pour une action donnée.
Revenons sur les étapes de création d’un projet sous un framework tel Symfony, je passe volontairement l’aspect installation qui n’est pas évidente et qui peut être longue, mais qui n’a lieu qu’une fois, imaginons alors que nous sommes sur notre deuxième projet, et découvrons les 10 grandes étapes du développement d’un projet jusqu’à sa mise en ligne.
1- Définition de la base de donnée
Le fichier schema.yml peut être très simple et le moteur Propel reconnaît automatiquement :
– les clés primaires id et les clés étrangères nomtable_id,
– les champs created_at et updated_at, qui sont mise à jour automatiquement
propel: rubrique: _attributes: { phpName: Rubrique } id: titre: varchar(255) titre_menu: varchar(50) meta_description: varchar(255) meta_title: varchar(50) url: varchar(255) en_ligne: tinyint classement: tinyint created_at: updated_at: article: _attributes: { phpName: Article } id: rubrique_id: titre: varchar(255) sous_titre: varchar(255) chapo: longvarchar article: longvarchar url: varchar(255) en_ligne: tinyint classement: tinyint created_at: updated_at:
La commande symfony « symfony propel-build-sql » permet ensuite de générer automatiquement le fichier SQL (data/sql/lib.model.schema.sql), qu’on peut lancer en cli avec une commande Mysql ou sous PhpMyAdmin.
2- Préparation de l’insertion de données test
La destruction et la reconstruction (parfois fréquente) des tables rendent l’insertion d’information fastidieuse, c’est pourquoi il est prévu un fichier /data/fixtures/data.yml, qui permet d’insérer automatiquement les informations dans la base.
Rubrique: Rubrique1: titre: Ma Rubrique1 la plus cool titre_menu: Rubrique1 meta_description: Description Ma Rubrique1 la plus cool meta_title: Title Ma Rubrique1 la plus cool url: rubrique1-la-plus-cool en_ligne: 1 Article: Article1: rubrique_id: Rubrique1 titre: Article1 sous_titre: Sous-titre Article1 chapo: Chapo Article1 article: <p>Article1</p> url: article1 en_ligne: 1
Le fichier propel.ini permet de configurer par exemple le type de Base propel.mysql.tableType = InnoDB
ou encore d’émuler les Ondelete Cascade si la base ne les gèrent pas propel.emulateForeignKeyConstraints = true
Dans databases.yml, on configure les accès SQL et l’encodage par défaut :
all: propel: class: sfPropelDatabase param: dsn: mysql://root:@localhost/mabase encoding: utf8 # Default charset for table creation
3- Création d’un projet et d’une application
Symfony considère qu’un projet est composé d’une base, mais qu’il peut y avoir plusieurs applications y travaillant avec une interface différente, ce qui en général le cas avec un /apps/fo/ (front office) et un bo (back office).
Créons alors le projet, qui générera la structure globale (à noter que c’est préalable à l’étape 1) : symfony init-project monprojet
Et l’application, qui générera les fichiers de structure de l’application (fo/templates/layout.php et fichiers de config de l’appli dans fo/config/) symfony init-app fo
4- Création des Classes de manipulation des tables
Pour chacune des tables sont générées des classes de « Base » (/lib/model/om/BaseArticle.php et BaseArticlePeer.php), auquel il ne faudra évidemment pas toucher, et des classes filles vides (/lib/model/Article.php et ArticlePeer.php), qui permettront d’ajouter des fonctions utilisateurs.
Ce qui est très intéressant, c’est que si l’on n’a pas de besoin d’accès direct à la Base et que l’on conserve donc le driver Créole, il n’y a pas de code SQL à réaliser et qu’ainsi il est possible de changer de type de base très facilement en changeant seulement les configurations.
La commande suivante en plus de générer les classes pemets d’ajouter les informations date.yml dans la base : symfony propel-build-all-load fo
5- Création des modules d’affichages
Pour chacune des tables il est possible de créer un module en scaffolding, avec par défaut la possibilité de lister (list), de voir (show), de créer (create->update), d’éditer (edit->update), et de supprimer (delete), soit le traditionnel CRUD (Create, Read, Update, Delete)
Symfony créait pour cela une classe de type action (fo/module/article/template/actions/actions.class.php) avec les fonctions listés précédemment et trois templates correspondants :
– fo/module/article/templates/listSuccess.php
– editSuccess.php (create/edit)
– showSuccess.php.
Dans notre exemple il est intéressant d’avoir la liste des articles en fo : symfony propel-generate-crud fo article Article
6- Modification des actions d’un module
Par défaut la fonction de listing est sans critères :
public function executeList() { $this->articles = ArticlePeer::doSelect(new Criteria()); }
Dans notre cas on peut souhaiter afficher la liste des articles liées à la rubrique courante, en ligne et classé par ordre.
$rubriqueId = $this->getRequestParameter('rubriqueid') $c = new Criteria(); $c->add(ArticlePeer::RUBRIQUE_ID, $rubriqueId); $c->add(ArticlePeer::EN_LIGNE, 1 ); $c->addAscendingOrderByColumn(ArticlePeer::CLASSEMENT); $this->articles = ArticlePeer::doSelect($c);
Joli non ?
7- Modification d’un template d’un module
Par défaut le template est assez simple $this->variable dans une fonction de la classe actions, permet de récupérer directement une variable $variable dans le template lié.
Vous remarquerez aussi la fonction link_to (‘Anchor Text’, ‘monmodule/monaction’), qui permet de créer un lien assez facilement vers les modules et actions, je développerai ce point dans un futur billet spécial sur l’ urlrewriting de Symfony.
List
<?php // auto-generated by sfPropelCrud // date: 2007/05/03 12:20:41 ?>
<h1>article</h1>
<table> <thead> <tr>
<th>Id</th> <th>Rubrique</th> <th>Titre</th> <th>Sous titre</th> <th>Chapo</th> <th>Article</th> <th>Url</th> <th>En ligne</th>
</tr> </thead> <tbody> <?php foreach ($articles as $article): ?> <tr>
<td><?php echo link_to($article->getId(), 'article/show?id='.$article->getId()) ?></td> <td><?php echo $article->getRubriqueId() ?></td> <td><?php echo $article->getTitre() ?></td> <td><?php echo $article->getSousTitre() ?></td> <td><?php echo $article->getChapo() ?></td> <td><?php echo $article->getArticle() ?></td> <td><?php echo $article->getUrl() ?></td> <td><?php echo $article->getEnLigne() ?></td> </tr>
<?php endforeach; ?> </tbody> </table>
<?php echo link_to ('create', 'article/create') ?>
Edit
<<?php // auto-generated by sfPropelCrud // date: 2007/05/03 12:20:41 ?> <?php use_helper('Object') ?> <?php echo form_tag('article/update') ?> <?php echo object_input_hidden_tag($article, 'getId') ?> <table> <tbody> <tr> <th>Titre:</th> <td><?php echo object_input_tag($article, 'getTitre', array ( 'size' => 80, )) ?></td> </tr> <tr> <th>Article:</th> <td><?php echo object_textarea_tag($article, 'getArticle', array ( 'size' => '30x3', ‘rich’=>true )) ?></td>
NB : L’intégration de TinyMCE dans un textarea est très simple puisqu’il suffit d’ajouter une option ‘rich’=>true à la fonction textarea_tag().
8- Vérification de formulaire
Imaginons que les internautes peuvent ajouter des articles, les vérifications de formulaires sont très simplifiées, puisqu’il suffit d’ajouter un fichier fo/module/article/validate/update.yml (nom de fichier correspondant au nom de l’action de mise à jour).
fillin:
enabled: true # Lors d’erreurs permet la réinsertion des informations
fields: titre: required: true msg: Champ requis sfStringValidator: min: 3 min_error: Doit être >= à 3 caractères max: 50 max_error: Doit être <= à 50 caractères
url: required: true msg: Champ requis sfStringValidator: min: 3 min_error: Doit être >= à 3 caractères max: 50 max_error: Doit être <= à 50 caractères sfRegexValidator: match: Yes match_error: Ne doit pas comporter de majuscule ni de caractères spéciaux sauf "-" pattern: /^[a-z0-9-]*$/
A cela doit s’ajoute une fonction indiquant quelles actions à mener en cas d’erreur, qui peut être de cette forme si l’on considère que la modification d’un article est possible.
public function handleErrorUpdate() { if ($this->getRequestParameter('id')) $this->forward('categorie', 'edit'); else $this->forward('categorie', 'create'); }
Et l’affichage des erreurs dans le Template Edit avec l’appel aux fonctions d’aides de validation:
<?php use_helper('Object', 'Validation') ?> ... <th>Url:</th> <td> <?php echo form_error('url') ?> <?php echo object_input_tag($article, 'getUrl, array ( 'size' => 80)
9- Un scaffolding spécial Back-Office
Symfony prévoit par ailleurs la génération d’un CRUD spécial Back-Office, où les templates, les fonctions de recherche et de validations sont déjà générées dans un même fichier generator.yml
Puissant, mais avec évidemment un manque de souplesse certain.
symfony propel-init-admin bo post Post
generator: class: sfPropelAdminGenerator param: model_class: Post theme: default fields: title: { name: Titre } excerpt: { name: Chapô } body: { name: Body } nb_comments: { name: Commentaire } created_at: { name: Creation date } list: title: Liste de Post layout: tabular display: [=title, excerpt, nb_comments, created_at] object_actions: _edit: ~ _delete: ~ max_per_page: 2 filters: [title, body, created_at]
edit: title: Détail du Post fields: title: { type: input_tag, params: size=53 } excerpt: { type: textarea_tag, params: size=50x2 } body: { type: textarea_tag, params: size=50x10 } created_at: { type: input_date_tag, params: rich=on }
10- Mise en ligne
Symfony tourne sur tout type d’hébergement PHP5, pour la mise en ligne il faut désolidariser le projet de l’environnement ce qu’on fait le plus simplement du monde avec la commande : symfony freeze
Une fois l’upload effectué la commande inverse unfreeze permet de revenir en environnement de développement.
A noter tout de même qu’il peut y avoir des particularités selon les hébergeurs, il faut par exemple réalisé un hack pour OVH, qui n’autorise au web que le dossier ‘www’, je ferais un billet spécial à ce sujet, car les nombreuses solutions trouvés n’ont pas été suffisante dans mon cas.
10bis- Des plugins pour un gain de temps encore plus important
En plus de l’intégration (qui consiste en un simple copier-coller) de TinyMCE et de la librairie Javascript Prototype (en attendant la version stable de la librairie Jquery), deux plugins semble souvent indispensable : La bibliothèque
La gestion des utilisateurs et des différents niveaux de permissions
Voir aussi
Le traditionnel bac à sable Symfony avec la création d’un blog en 1h
Bonjour à tous,
Dans databases.yml, cette ligne est effectivement utile:
encoding: utf8
Par contre le commentaire est inapproprié:
# Default charset for table creation
Il s'agit bien du charset pour accès à la base de données.
Les créations de tables utilisent plutôt le fichier propel.ini
Bonjour Heidy,
Je ne serai pas aussi catégorique, dans tous les cas faut voir ca avec les auteurs du livre Symfony 😀
<a href="http://www.symfony-project.com/b… » target= »_blank »>www.symfony-project.com/b…
ROR est d'un simplicité deconcertante, suite à une contrainte d'hébergement, je m'attaque au framework symfony… et je dois bien avouer que je trouve le ruby 100x plus confortable, et que le framework en lui même est plus simple à appréhender.
merci pour cet article.
Une fois qu’on a pris l’habitude de symfony on ne peut plus s’en passer !