Les derniers articles
Introduction au Zend Framework - part II :: MVC
Date :: 2006-03-10
Last Updated :: 2006-04-06
Introduction
Après avoir vu l'utilisation de quelques modules dans la première partie de l'article, nous allons les mettre en oeuvre au travers du design pattern MVC, dont le Zend Framework propose une implémentation.
MVC - Base de départ
Nous allons commencer par créer les fichiers et dossiers indispensables au bon fonctionnement de l'application, à savoir l'index du site.
Arborescence
Commençons par créer l'arborescence de notre projet :
- c/ : Dossier qui va contenir les controller
- m/ : Dossier qui va contenir les modèles
- v/ : Dossier qui va contenir les vues
- Zend/ : Le framework
- .htaccess
- index.php
- Zend.php
Pour des raisons pratiques, tous ces dossiers et fichiers se trouveront dans l'arborescence web du site, mais il est vivement conseillé de les placer dans un répertoire non accessible. Vous n'aurez ensuite qu'à regler l'include_path, grâce à la fonction set_include_path(). Les seuls fichiers qui resteront accessibles seront donc la page index.php et le .htaccess.
Le .htaccess
Le fichier .htaccess est, en l'état, indispensable au bon fonctionnement du modèle MVC implémenté par le framework. Des solutions alternatives sont à l'étude pour les prochaines versions. Il sert uniquement a rediriger les requêtes vers la page index.php.
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
La page index.php
C'est la seule page utilisée directement, elle est chargée de dispatcher les requètes vers le bon controller.
require_once 'Zend.php';
// Chargement du Front Controller
Zend::loadClass('Zend_Controller_Front');
// On récupère une instance du Front Controller
$C = Zend_Controller_Front::getInstance();
// Et on définie le répertoire où sont situés les controllers
$C->setControllerDirectory('c');
// Création d'une nouvelle vue
Zend::loadClass('Zend_View');
$view = new Zend_View;
$view->setScriptPath('v');
// On enregistre la vue
Zend::register('v', $view);
// Appelle le controller adéquate
$C->dispatch();
?>
Le controller de la page d'index
La présence de ce controller est requise obligatoirement.
Il doit être nommé IndexController.
Chaque controller doit comporter au minimum 2 méthodes définies comme abstraites :
- indexAction() : Cette méthode est appelée si aucune action n'est spécifiée
- noRouteAction() : Cette méthode est appelée si l'action demandée n'éxiste pas
Ce fichier nommé IndexController.php sera placé dans le répertoire que nous avons défini dans la page index.php, à savoir c/ .
require_once 'Zend/Controller/Action.php';
/**
* Index controller
*
* @see Zend/Controller/Action.php
*/
class IndexController extends Zend_Controller_Action {
public function indexAction() {
//On récupère l'objet vue que l'on a mis en mémoire sur la page index
$view = Zend::registry('v');
// On appelle la vue par défaut
echo $view->render('default.php');
}
public function noRouteAction() {
// L'action demandée n'éxiste pas, on redirige vers l'index
$this->_redirect('/');
}
}
?>
La vue de la page d'index (default.php)
C'est une simple page au format XHTML qui sera plaçée dans le répertoire contenant les vues (v/). Vous pouvez désormais faire un test, appellez la page http://votre-site.com/ et regardez le résultat.
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
<head>
<title>Classes.ScriptsPHP.org - Index</title>
</head>
<body>
<div>Vous êtes sur la page d'index</div>
<ul>
<li><a href="/">Index</A></li>
<li><a href="/news/">Les News</A></li>
</ul>
</body>
</html>
MVC - Allons plus loin
Ajoutons des news ... Pour se faire, il va falloir créer un controller spécifique associé à une ou plusieurs vues. Nous allons également créer une classe générique permettant de récupérer des news depuis une base de données SQLite, ce sera le modèle.
Les URLs - Comment appeller un controller et une action ?
Les URLs sont de la forme :
- http://votre-site.com/Controller[/action]
- http://votre-site.com/Controller/action[/param/value]
Prenons un exemple :
- http://votre-site.com/news : Le controller de news sera appelé, aucune action n'est spécifiée, l'action index (méthode indexAction()) sera appelée par défaut
- http://votre-site.com/news/index : Même résultat que précédemment ...
- http://votre-site.com/news/get : Le controller news sera appelé, il tentera d'exécuter l'action get (méthode getAction()). Si aucune méthode nommée getAction() n'existe, l'action noRouteAction() sera appelée.
- http://votre-site.com/news/get/limit/10 : Le controller news est appelé avec l'action get. Un paramètre nommé limit est passé au controller, il prendra comme valeur 10.
Les URLs : Comment en récupérer les informations ?
Vous disposez pour se faire de plusieurs méthodes :
- _getControllerName() : Renvoie le nom du controller appelé
- _getActionName() : Renvoie le nom de l'action appelée
- _getParam( Name [, DefaultValue] ) : Renvoie la valeur du paramètre Name. DefaultValue permet de spécifier une valeur par défaut si Name n'existe pas.
- _getAllParams() : Renvoie un tableau associatif contenant tous les paramètres passés et leur valeur.
Le modèle des News
Le modèle suivant va nous permettre de récupérer des informations depuis une base de données SQLite. Nous prendrons comme exemple une base de données nommée base.sql qui contiendra la table suivante :
CREATE TABLE news ( title VARCHAR , content VARCHAR );
INSERT INTO news VALUES ( 'titre 1' , 'contenu 1' );
INSERT INTO news VALUES ( 'titre 2' , 'contenu 2' );
Créons donc, à titre d'exemple, deux méthodes :
- getLastNews( [ $count ] ) : Récupère les $count dernières news, 10 par défaut.
- getNews( $title ) : Récupère le contenu d'une news selon son titre.
/**
* Modèle des news
*
*/
class NewsModel {
/**
* Ressource de connexion
*
* @var void
*/
protected $db = null;
/**
* Constructeur de la classe
* Connexion à la base de données SQLite
*
* @see Zend/Db.php
*/
public function __construct() {
Zend::loadClass('Zend_Db');
$params = array ('dbname' => 'base.sql');
$this->db = Zend_Db::factory('pdoSqlite', $params);
}
/**
* Récupère les dernières news
*
* @param int $count Nombre de news à récupérer
* @return array Résultat
*/
public function getLastNews($count=10) {
return $this->db->fetchAll('SELECT title,content FROM news LIMIT ' . $count);
}
/**
* Récupère une news selon son titre
*
* @param void $title titre de la news
* @return array Résultat
*/
public function getNews($title) {
$q = 'SELECT content FROM news WHERE title = '.$this->db->quote($title);
return $this->db->fetchOne($q);
}
}
?>
Le controller des News
Nous allons définir plusieurs actions dans le controller :
- indexAction() : Aucune action spécifiée, appelle la vue news.php
- lastAction() : Récupération des X dernières news, apelle la vue last-news.php
- pdfAction() : Transformation d'une news au format pdf
- noRouteAction() : Action demandée inconnue : redirection vers l'index des news
require_once 'Zend/Controller/Action.php';
/**
* Controller des news
*
* @see Zend/Controller/Action.php
*/
class NewsController extends Zend_Controller_Action {
/**
* Constructeur de la classe
* Il charge le module Filter et le modèle de news
*
* @see Zend/Filter.php
* @see m/NewsModel.php
*/
public function __construct() {
Zend::loadClass('Zend_Filter');
Zend::loadClass('NewsModel', 'm');
}
/**
* Index Action
* Appelle la vue par défaut
*
* @see v/news.php
*/
public function indexAction() {
echo Zend::registry('v')->render('news.php');
}
/**
* Last Action
* Récupère depuis le modèle les dernières news, et appelle la vue adéquate
*
* @see m/NewsModel.php
*/
public function lastAction() {
$count = Zend_Filter::getInt($this->_getParam('count', 10));
if(!$count || $count > 10) $count = 10;
$model = new NewsModel;
$result = $model->getLastNews($count);
Zend::registry('v')->news = $result;
echo Zend::registry('v')->render('last-news.php');
}
/**
* Pdf Action
* Récupère une news depuis le modèle et le transforme en PDF
*
* @see m/NewsModel.php
* @see Zend/Pdf.php
*/
public function pdfAction() {
$news = urldecode($this->_getParam('title'));
if(!$news) {
throw new Zend_Controller_Action_Exception('no pdf file');
}
$model = new NewsModel;
$n = $model->getNews($news);
Zend::loadClass('Zend_Pdf');
$pdf = new Zend_Pdf();
$font = new Zend_Pdf_Font_Standard(Zend_Pdf_Const::FONT_HELVETICA);
$page = $pdf->newPage(Zend_Pdf_Const::PAGESIZE_A4);
$page->setFont($font, 12);
$height = $page->getHeight();
$page->drawText($news,0,10);
$pdf->pages[] = $page;
header('Content-Type: application/pdf');
$pdf->save('page.pdf');
echo file_get_contents('page.pdf');
unlink('page.pdf');
die();
}
public function noRouteAction() {
$this->_redirect('/news/');
}
}
?>
Les deux vues des news
news.php.
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
<head>
<title>Classes.ScriptsPHP.org - News</title>
</head>
<body>
<div>Vous êtes sur la page des News</div>
<ul>
<li><a href="/">Index</A></li>
<li><a href="/news/">Les News</a>
<ul>
<li><a href="/news/last/">Last News (10)</a></li>
<li><a href="/news/last/count/1">Last News (1)</a></li>
</ul>
</li>
</ul>
</body>
</html>
last-news.php.
...
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
<head>
<title>Classes.ScriptsPHP.org - Dernières News</title>
</head>
<body>
<div>Vous êtes sur la page des dernières news</div>
<ul>
<li><a href="/">Index</A></li>
<li><a href="/news/">Les News</a>
<ul>
<li><a href="/news/last/">Last News (10)</a></li>
<li><a href="/news/last/count/1">Last News (1)</a></li>
</ul>
</li>
</ul>
<?php foreach ($this->news as $n) { ?>
<h2><?php echo $this->escape($n['title']); ?></h2>
<div>
<?php echo $this->escape($n['content']); ?>
</div>
<?php } ?>
</body>
</html>
Liens utiles
Conclusion
Tout en gardant à l'esprit que ce n'est qu'une version 0.1x (à l'heure où est écrit cet article, mais ça avance vite), ça promet, non ? :)
Trackback
Il n'y a pas de trackback recensé pour cet article.
Faire un trackback sur cet article http://classes.scriptsphp.org/Trackbackserver.Introduction-au-Zend-Framework-part-II, récupérer les trackback sur cet article