Zend Framework : Premiers pas avec Zend_Application

Zend Framework

Date de publication : 10/09/09 , Date de mise à jour : 24/09/09

Par AIT YAHIA Idir (Mon site perso) (Blog)
 Julien Pauli (Tutoriels, conférences PHP) (Blog)
 

Avec l'arrivée de la version 1.8 de Zend Framework, la configuration du bootstraping peut paraître à première vue déroutante pour les novices et même pour les confirmés qui ont l'habitude de travailler avec les versions précédentes du Framework. Dans ce tutoriel nous allons essayer de vous montrer comment configurer une application MVC avec Zend_Application. Vous allez voir que la solution proposée par le Zend Framework permet d'avoir un code clair et organisé qui vous évitera aussi de créer vous-même une solution maison. La testabilité de l'application sera plus élevée, ainsi que le degré de réutilisation des objets. 29 commentaires · Donner une note à l'article (5)

               Version PDF (Miroir)   Version hors-ligne (Miroir)

I. Introduction
I-A. Pré-requis
I-B. Présentation
I-C. Pourquoi utiliser Zend_Application ?
II. Mise en oeuvre d'une application basique
II-A. Création de la structure de votre application
II-B. Configuration de notre application
II-B-1. Fichier de configuration
II-B-2. Index.php
II-B-3. Fichier .htaccess
II-B-4. Bootstrap
II-B-4-a. Théorie
II-B-4-b. Charger des ressources
II-B-4-c. Utiliser les plugins de chargement de ressources
II-B-4-d. Lancer l'application
III. Mise en oeuvre d'une application modulaire
III-A. Fichier de configuration
III-B. Bootstrap
III-B-1. Autoload de modules
III-B-2. Autoload du module par défaut
III-C. Les Controlleurs d'actions
IV. Personnaliser le système
IV-A. Ajouter une ressource
V. Conclusion


I. Introduction


I-A. Pré-requis

Cet article vise un public ayant de très bonnes connaissances en PHP, le concept MVC et ayant déjà utilisé Zend Framework auparavant.
Ce tutoriel a été réalisé avec la version 1.9.2 du Framework et ne fonctionnera avec aucune des versions antérieures à la version 1.8.


I-B. Présentation

#Zend_Application est l'un des nouveaux composants introduits dans la version 1.8 de Zend Framework, son but est de faciliter et de standardiser la mise en place du bootstraping ainsi que la configuration de l'environnement PHP.

Avec #Zend_Application, l'autoloading est mis en avant et le bootstrap se présente sous une forme modulaire où sont initialisées toutes les ressources réutilisables telles que les vues, les connexions aux bases de données, les layout...etc.

Zend_Application
Zend_Application
info L'autoloading plus en détail : fr Atelier Zend Framework : Autochargement de classes et de composants

I-C. Pourquoi utiliser Zend_Application ?

Une application MVC peut très rapidement devenir complexe et faire intervenir énormément d'objets, chacun est configurable d'une manière qui lui est propre, chacun est dépendant éventuellement d'autres objets .... Un casse tête est alors apparu : comment créer, configurer, lier et mémoriser tous ces objets de manière pérenne, et propre ?
Le composant #Zend_Application répond à ces problématiques en proposant une manière simple et efficace de configurer tout l'environnement MVC.


II. Mise en oeuvre d'une application basique


II-A. Création de la structure de votre application

Pour commencer nous allons créer une nouvelle application Zend Framework avec la structure conventionnelle semblable à celle présentée dans l'image ci-dessous. Nous pouvons faire cela manuellement, avec l'IDE Zend Studio (6 ou 7), ou encore en utilisant le composant #Zend_Tool qui va faire le plus gros du travail à notre place.
Cette architecture est non modulaire : elle ne se base pas sur la notion de modules, seul un dossier de contrôleur est utilisé (classique pour les "petits" projets).

info Pour de plus amples détails sur la création d'applications avec #Zend_Tool vous pouvez consulter cet article : fr Créer une application basée sur Zend Framework avec Zend_Tool
structure
Le dossier public contient aussi le fichier htaccess qui n'apparaît pas sur l'arborescence de cette image d'illustration.


II-B. Configuration de notre application

Nous allons maintenant nous intéresser aux fichiers importants pour notre configuration.


II-B-1. Fichier de configuration

Le fichier auquel nous devons accorder une attention particulière est sans doute notre fichier de configuration application.ini, c'est dans ce fichier que seront initialisées toutes les ressources et l'environnement PHP.
Remarquez que nous avons plusieurs sections dans notre application.ini, celles-ci nous permettent d'avoir des configurations distinctes pour chaque mode d'exécution de notre application (production, développement, test...etc.).
application/configs/application.ini
[production]
# initilisation du report d'erreurs pour le mode production 
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
 
#definition de la timezone
phpsettings.date.timezone = "Africa/Algiers"

# Include path
includePaths[] = APPLICATION_PATH "/../library"

# Espaces d'autoload à charger
autoloadernamespaces[] = "Dvp_"
autoloadernamespaces[] = "Foo_"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
Zend_Application reconnaît les mots phpsettings, bootstrap, autoloadernamespaces, et includepaths (la casse n'a pas d'importance). L'option phpSettings nous permet de définir les directives du php.ini. Lorsque cette option est définie, Zend_Application fait appel à la fonction php fr ini_set(), la liste de toutes les directives définissables est disponible sur le site officiel de php Liste des directives du php.ini
La définition des directives se fait de la manière suivante :

phpSettings.nom_directive = valeur
Aussi, Zend_Application récupère automatiquement l'objet Zend_Loader_Autoloader, il active donc l'autochargement des classes et l'option autoloadernamespaces permet d'ajouter à la volée des namespaces à charger.

autoloadernamespaces[] = "Dvp_"
autoloadernamespaces[] = "Foo_"
info Zend_Application fait appel à Zend_Loader_Autoloader::getInstance(), et active donc pour vous l'autoload par défaut des classes Zend_ et ZendX_.
Enfin, souvent utilisé avec les espaces de nom d'autoload, l'include_path de php peut être configuré grâce au mot-clé includepaths.

includepaths[] =  APPLICATION_PATH "/Dvp"
info Rappel : Les constantes PHP sont utilisables dans les fichiers ini, si elles ont été définies avant le chargement du fichier ini en question.
Nous allons parler des options bootstrap et resources que nous n'avons pas encore vues dans quelques instants.


II-B-2. Index.php

Dans le fichier index.php, il faut créer un objet de la classe Zend_Application. Nous allons aussi définir les chemins et les constantes d'exécution dont nous avons besoin.
public/index.php

<?php

// Définit le chemin du repértoire de l'application
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Définit l'environnement de l'application 
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

require_once 'Zend/Application.php';  

// Création de l'application
$application = new Zend_Application(
    APPLICATION_ENV, 
    APPLICATION_PATH . '/configs/application.ini'
);
Analysons de plus près l'instanciation de l'objet Zend_Application:

// Création de l'application
$application = new Zend_Application(
    APPLICATION_ENV, 
    APPLICATION_PATH . '/configs/application.ini'
);
info Important : Instancier un objet Zend_Application revient immédiatement à traiter les directives phpsettings, bootstrap, autoloadernamespaces, et includepaths. Gardez bien ceci en tête.
Le premier paramètre définit la section de la configuration à charger (typiquement 'development', 'production' ...).
Le deuxième paramètre indique la configuration. Il peut être un chemin vers un fichier .ini (un objet Zend_Config_Ini sera alors utilisé pour le lire), vers un fichier .xml (un objet Zend_Config_Xml sera utilisé pour le lire), un tableau php (array) suivant la norme utilisée par Zend_Config, ou encore un fichier php retournant un tableau, qui sera inclus via include().
Il s'agit donc d'une factory qui fabrique l'objet nécessaire à l'analyse de la configuration selon son type. Le plus couramment, un fichier .ini est utilisé.

Nous aurions donc pu écrire, par exemple :

$application = new Zend_Application(APPLICATION_ENV, array(
    'includepaths' => array(
        APPLICATION_PATH . '/../library',
    ),
    'phpsettings' => array(
        'display_startup_errors' => 1,
        'display_errors' => 0),
    ),
    // etc...
));
Remarquez que passer un tableau ne permet pas de profiter de l'environnement. Une et une seule configuration sera chargée. Dans le cas d'un fichier .ini ou .xml, l'environnement passé sera suivi.


II-B-3. Fichier .htaccess

Notons bien que nous avons fait appel à la variable d'environnement APPLICATION_ENV, celle-ci peut être définie dans notre fichier htaccess ou dans la configuration d'Apache.
public/.htaccess

SetEnv APPLICATION_ENV development

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

II-B-4. Bootstrap


II-B-4-a. Théorie

L'objet Zend_Application ne permet "que" de configurer l'autoloader, l'include_path et des paramètres de PHP.ini.
Pour configurer des objets (appelés "ressources" et notés en anglais "resources" avec un seul "s"), Zend_Application délègue cette responsabilité à Zend_Application_Bootstrap_Bootstrapper (voyez le schéma UML)
Les objets à configurer sont ceux que l'application MVC utilisera dans le futur, on pense donc à frontcontroller, log, db, layout, view, etc...

Il convient donc maintenant d'indiquer le chemin vers un fichier de bootstrap, reprenons notre fichier application.ini:
Suite de application.ini

bootstrap.path  = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
Le fichier représente donc une classe, celle-ci doit implémenter au minimum Zend_Application_Bootstrap_Bootstrapper, mais Zend Framework définit des niveaux plus hauts : Zend_Application_Bootstrap_BootstrapAbstract et Zend_Application_Bootstrap_Bootstrap. Cette dernière classe est la plus pratique pour un projet "classique", mais là encore les nombreux niveaux d'abstraction du Zend Framework offrent des possibilités de personnalisation très avancées.


II-B-4-b. Charger des ressources

warning Rappel : Zend_Application ne sait pas charger des ressources (des objets pour l'application) : c'est le rôle d'un bootstrap, on utilise classiquement Zend_Application_Bootstrap_Bootstrap pour celà.
application/Bootstrap.php
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

    //initilisation de l'autoloader pour une structure sans module
    protected function _initAutoload()
	{
        $moduleLoader = new Zend_Application_Module_Autoloader ( 
        array ('namespace' => '', 'basePath' => APPLICATION_PATH ) );
        return $moduleLoader;
	}

    protected function _initView()
    {
        // Initialisation de la vue
        $view = new Zend_View;
        $view->doctype('XHTML1_STRICT');
        $view->headTitle('Mon application avec Zend_Application');

        // Ajoutons le au ViewRenderer
        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
        $viewRenderer->setView($view);

        // Retournons le, il sera stocké dans un registre par le bootstrap
	    // pour utilisation future
        return $view;
    }
}
Toutes les méthodes protégées commençant par _init vont être lancées lorsqu'on appelle la méthode bootstrap() sur l'objet Bootstrap contenu dans Zend_Application.
suite de index.php
<?php
$application->getBootstrap()->bootstrap();
// ceci est équivalent à 
$application->bootstrap();
Si on ne veut charger qu'une ressource en particulier, il suffit de procéder comme suit :
chargement de la ressource view uniquement
<?php
// Récupérons l'objet bootstrap
$bootstrap = $application->getBootstrap();

// chargeons la ressource view
$bootstrap->bootstrap('view');
Chaque méthode doit retourner l'objet (aussi appelé "ressource") qu'elle a configuré, ceci va permettre de le récupérer via le bootstrap.
récupération de l'objet view

// Récupérons l'objet bootstrap
$bootstrap = $application->getBootstrap();

// charge l'objet view (dans notre cas, exécute _initView() qui retourne bien un objet view)
$bootstrap->bootstrap('view');

// voici l'objet view configuré
$view = $bootstrap->getResource('view');
Le bootstrap possède un système de registre qui stocke l'objet configuré et retourné dans un Zend_Registry (par défaut, c'est changeable) et empêche son chargement plus d'une fois :
bootstrap mémorise les objets configurés

// Récupérons l'objet bootstrap
$bootstrap = $application->getBootstrap();

// charge l'objet view (dans notre cas, exécute _initView() qui retourne bien un objet view)
$bootstrap->bootstrap('view');

// ici rien ne se passe, la ressource view a déja été chargée, le processus est ignoré
$bootstrap->bootstrap('view');

// voici l'objet view
$view = $bootstrap->getResource('view');
Notez au passage qu'appeler bootstrap() sur l'objet application revient à l'appeler sur l'objet bootstrap. Rappelons aussi qu'appeler bootstrap() sans paramètre permet de lancer la configuration de toutes les ressources (toutes les méthodes _init*() seront lancées à la suite d'un seul coup)

Si une ressource dépend d'une autre, il faut alors la charger au sein même du bootstrap :
gestion des dépendances des objets dans le bootstrap
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

    protected function _initView()
    {
        // Ici nous avons besoin pour une raison quelconque d'un objet Zend_Log

        // chargeons le
        $this->bootstrap('log');

        // récupérons le
        $log = $this->getResource('log');

        // voila, nous avons un objet log
        // ...

        return $view;
    }

    protected function _initLog()
    {
        $log = new Zend_Log();
        // configuration de l'objet Zend_Log
        // ...

        // n'oublions pas de le retourner
        return $log;
    }
}
Si la ressource Log avait elle aussi besoin de la ressource View, une référence circulaire serait apparue, une exception est alors levée.

Chaque ressource peut prétendre chercher des paramètres dans la configuration. Zend_Application passe l'objet de configuration à Zend_Application_Bootstrap_Bootstrap, et la méthode getOption() retourne l'option en question.
récupération de données de configuration

// application.ini
log.file = APPLICATION_PATH "/log/log.log"

// bootstrap
protected function _initLog()
{
    $file = $this->getOption("file");
}
Notez que l'option est précédée dans le fichier .ini du nom de la resource concernée : "log.quelquechose"
La méthode donne bien le nom de la resource : _initLog() et cette ressource se récupère bien avec $bootstrap->getResource('log')


II-B-4-c. Utiliser les plugins de chargement de ressources

Certes les méthodes _init***() sont pratiques, mais elles ont l'inconvénient d'être peu réutilisables. Lorsqu'on possède plusieurs applications, il peut être intéressant d'avoir une manière commune de les configurer en montant des composants par dessus Zend_Application. La solution des méthodes "init" est alors peu recommandée.

Le système de bootstrap n'autorise une autre manière à charger des ressources que via des méthodes : via des objets. Ces objets, appelés "plugins de ressources", implémentent Zend_Application_Resource_Resource et là encore un niveau inférieur plus concret est proposé par le Zend Framework : Zend_Application_Resource_ResourceAbstract

Evidemment, il existe déjà un tas de plugins de ressources dans Zend Framework, il vous reste aussi la possibilité de créer le votre ou d'étendre un existant.
On peut compter sur Zend_Application_Resource_FrontController, mais aussi layout, view, translate, module, db, layout, locale, navigation ...
Chaque plugin de ressource configure la ressource (l'objet) qui lui est associée. Chacun accepte donc des paramètres de configuration qui lui sont propres, ils sont documentés sinon un coup d'oeil dans le code source des classes est toujours rapide et efficace.
Nous allons tout de suite voir quelques exemples. L'important est d'écrire l'option de configuration dans le fichier de configuration de la manière suivante : resources.{plugin}.{option} = valeur
application.ini utilisant des plugins de ressources
<?php
[production]
# initilisation du report d'erreurs pour le mode production 
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
 
#definition de la timezone
phpsettings.date.timezone = "Africa/Algiers"

# Include path
includePaths.library = APPLICATION_PATH "/../library"

# Bootstrap
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"

# Front controller
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

# Layout
resources.layout.layout = "layout"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

# Views
resources.view.encoding = "UTF-8"

# connexion à une base de données
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "aityahia"
resources.db.params.password = "monpasse"
resources.db.params.dbname = "zf-zaProject"
resources.db.isDefaultTableAdapter = true

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

# base de données test
resources.db.params.dbname = "zf-zaProject-test"

# le contrôleur frontal renvoie toute exception capturée
resources.frontController.throwexceptions = true
Alors que le fichier d'index ne bouge toujours pas, le bootstrap en revanche, se simplifie :
bootstrap simplifié grâce aux plugins de ressources
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    // Initialisation d'un autoload pour une application sans module
    protected function _initAutoload()
    {
        new Zend_Application_Module_Autoloader(array(
                'namespace' => '',
                'basePath'  => APPLICATION_PATH,
        ));
    }
}
Plus aucune méthode _init*() concernant une ressource chargée par un plugin n'apparaît. Tout se passe dans la configuration, le bootstrap saura appeler les bons objets plugins en leur passant à chacun la section de la configuration le concernant.

warning Les méthodes _init*() sont chargées avant les plugins de ressources.
Si une méthode _init*() existe ET un plugin, tous deux concernant la même ressource, alors la méthode sera prioritaire et le plugin ne sera pas exécuté

Vous pouvez cependant charger tout de même le plugin depuis votre méthode, grâce à la méthode getPluginResource(), mais c'est à vous de le demander explicitement.
application.ini chargeant le plugin de ressource db

# dans application.ini, le plugin de ressource "Db" est chargé avec de la configuration

resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
# etc...
un bootstrap demandant lui aussi de charger la ressource db via une méthode _init
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    // une méthode initialisation une resource Db alors qu'un plugin
    // existe et a été déclaré, si on ne touche à rien, cette méthode
    // primera et le plugin ne sera jamais exécuté, il faut le demander
    // explicitement    
	protected function _initDb()
    {
        // charge le plugin de resource db, en lui passant bien sur
        // sa configuration, lue depuis application.ini
		$pluginDb = $this->getPluginResource('db');
		$db = $pluginDb->getDbAdapter();
        $db->setFetchMode ( Zend_Db::FETCH_OBJ );
        
		// N'oublions pas de retourner l'objet configuré afin que le bootstap
		// puisse le stocker dans son registre et nous le retourner à la demande
		return $db;
	}
}
Tous nos objets étant configurés, il ne reste qu'à créer un layout classique comme suit :
application/layouts/scripts/layout.phtml

<?php echo $this->doctype() ?>
<html>
<head>
<?php 
echo $this->headTitle(); 
echo $this->headLink(); 
echo $this->headStyle(); 
echo $this->headScript(); 
?>
</head>
<body>
    <?php echo $this->layout()->content ?>
</body>
</html>
Nous avons terminé avec la configuration de notre application et elle est dès à présent fonctionnelle. Nous pouvons nous attaquer au développement de nos contrôleurs,modèles et vues etc...
Rappelez vous la logique dans application.ini : resources.{NomDuPluginSouventEquivalentAuNomDeLobjetAConfigurer}.{NomDuneOption} = Valeur
Voyons maintenant comment lancer l'application.


II-B-4-d. Lancer l'application

Zend_Application_Bootstrap_Bootstrap est très pratique dans le sens où elle permet directement de lancer l'application.
lancement de l'application, dans index.php
<?php
$application->bootstrap()
            ->run();	
La méthode run() sur application (qui la fait suivre immédiatement à l'objet bootstrap) lance simplement le contrôleur frontal (dispatch()), tout en partageant l'objet de bootstrap dedans, ce qui va permettre par la suite de le récupérer et d'utiliser les objets configurés dedans.
utilisation du bootstrap comme registre principal à objet
<?php
// n'importe  au sein de l'application
$log = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('log');

// N'oublions pas que dans un contrôleur d'action, on peut aussi utiliser le code suivant :
$log = $this->getInvokeArg('bootstrap')->getParam('bootstrap')->getResource('log');
Evidemment avant d'invoquer run(), il faut invoquer bootstrap() pour configurer toutes les ressources.


III. Mise en oeuvre d'une application modulaire

Pour la mise en oeuvre d'une application modulaire, la configuration se corse un petit peu, enfin disons que Zend Framework propose une structure d'héritage des bootstraps vraiment très pratique.

En premier lieu, nous allons remanier notre structure en ajoutant deux modules : default et admin). Chaque module contient sa propre logique applicative : des modèles, des layouts, des contrôleurs et des vues.

Structure modulaire

III-A. Fichier de configuration

Pour notre fichier de configuration, nous allons apporter quelques modifications au contrôleur frontal (FrontController).
# Front controller
#initialisation des modules
resources.frontController.moduleDirectory       = APPLICATION_PATH "/modules"

# chargement du plugin Zend_Application_Resource_Modules
resources.modules[] = ""
Après modification notre fichier de configuration complet est donc :
application/configs/application.ini
[production]

# initilisation du report d'erreurs pour le mode production 
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0

#definition de la timezone
phpsettings.date.timezone = "Africa/Algiers"
 
 
# Include path
includePaths.library = APPLICATION_PATH "/../library"

# Bootstrap
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"

#initialisation des modules
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] = ""

# Layout
resources.layout.layout = "layout"
resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

# Views
resources.view.encoding = "UTF-8"

# connexion à une base de données
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "aityahia"
resources.db.params.password = "monpasse"
resources.db.params.dbname = "zf-zaProject"
resources.db.isDefaultTableAdapter = true

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
# initilisation du report d'erreurs pour le mode developpement
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
#base de données test

resources.db.params.dbname = "zf-zaProject-test"

III-B. Bootstrap

Le bootstrap principal de notre application reste inchangé, mais comme nous avons activé Zend_Application_Resource_Modules (nous avons bien écrit resources.modules[] = "" ce qui a pour effet de charger le plugin de ressources appelé "modules"), nous pouvons maintenant créer un bootstrap supplémentaire par module (si nous le souhaitons). Celui-ci contiendra toutes les données de configuration spéciale des objets au sein du module en question (ce qui est souvent nécessaire).
Chaque dossier de module pourra donc contenir à sa racine un fichier de bootstrap dont la classe sera alors nommée "NomDuModule_Bootstrap", celle-ci sera chargée automatiquement lorsque le bootstrap principal est chargé.
application/modules/default/bootstrap.php
<?php
class Default_Bootstrap extends Zend_Application_Module_Bootstrap
{
	     
}
application/modules/admin/bootstrap.php
<?php
class Admin_Bootstrap extends Zend_Application_Module_Bootstrap
{

}

III-B-1. Autoload de modules

Même sans rien écrire dans ces classes de bootstrap des modules, n'oublions pas que nous étendons Zend_Application_Module_Bootstrap (et non pas Zend_Application_Bootstrap_Bootstrap). Cette classe contient la logique de lancement de bootstrap (comme pour le bootstrap principal), mais elle active en plus un Zend_Application_Module_Autoloader qui est un chargeur de ressources en relation avec Zend_Loader_Autoloader.

info Pour plus d'information sur le chargement automatique des classes, consultez fr Atelier Zend Framework : Autochargement de classes et de composants
Il active la structure de chargement suivante pour le module en concerné :
Structure du chargement automatique de classe dans les modules
<?php
    'dbtable' => array(
        'namespace' => 'Model_DbTable',
        'path'      => 'models/DbTable',
    ),
    'form'    => array(
        'namespace' => 'Form',
        'path'      => 'forms',
    ),
    'model'   => array(
        'namespace' => 'Model',
        'path'      => 'models',
    ),
    'plugin'  => array(
        'namespace' => 'Plugin',
        'path'      => 'plugins',
    ),
    'service' => array(
        'namespace' => 'Service',
        'path'      => 'services',
    ),
    'viewhelper' => array(
        'namespace' => 'View_Helper',
        'path'      => 'views/helpers',
    ),
    'viewfilter' => array(
        'namespace' => 'View_Filter',
        'path'      => 'views/filters',
    )
Le chemin du module étant rajouté en basePath, on chargera dans ce module là des classes du type ModuleName_Plugin_MyPlugin, ou encore ModuleName_View_Helper


III-B-2. Autoload du module par défaut

Certes une classe Admin_Model_DbTable_Membres est nommée correctement, pour le module par défaut, Default_Form_Articles_Ajouter peut paraître un peu lourd.
Comme le module par défaut contient aussi un bootstrap étendant Zend_Application_Module_Bootstrap, un autoloader rajoutant son nom (Default_) est chargé automatiquement.
Si nous voulons supprimer ce nom (comme c'est le cas dans le cas du chargement des contrôleurs), nous avons plusieurs solutions :

  1. Supprimer le bootstrap du module par défaut : Default_Bootstrap
  2. Modifier l'autoloader du module par défaut afin qu'il n'ajoute pas le namespace "Default_"
Supprimer le bootstrap du module n'est pas très gênant car les ressources dans ce module sont sensées être configurées entièrement dans le bootstrap principal à la racine de l'application.
Cependant en supprimant la classe Default_Bootstrap, nous perdons toute manière d'auto-charger des classes dans ce module. Il faut donc réactiver un autoload pour le module par défaut, ceci s'effectue tout à fait logiquement dans le bootstrap principal :
Réactivation du chargement automatique pour le module par défaut depuis le bootstrap principal
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initDefaultNamespace()
    {
        $this->bootstrap('frontcontroller');
        $fc = $this->getResource('frontcontroller');
        $defaultModule = $fc->getControllerDirectory($fc->getDefaultModule());
        
		new Zend_Application_Module_Autoloader(array(
                'namespace' => '', // pas de namespace
                'basePath'  => APPLICATION_PATH . $defaultModule,
            ));
    }
}
On chargera donc dans le module par défaut des objets de type Model_DbTable_Articles, par exemple.

Une autre solution pourrait consister à proposer son propre chargeur de classes pour ce module, en étendant Zend_Application_Module_Bootstrap ou en fouillant en documentation (ou dans le code source des classes) pour s'apercevoir qu'une option resourceloader permet de faire cela simplement.


III-C. Les Controlleurs d'actions

Pour pouvoir essayer notre application modulaire, nous allons définir les contrôleurs "index" de nos modules.
application/modules/default/IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
    public function init()
    {
       $this->view->headTitle('Mon application avec Zend_Application (Utilisateur par défaut)');
    }

    public function indexAction()
    {
        $this->_response->appendBody('Bievenue');
    }
}
application/modules/admin/IndexController.php
<?php
class Admin_IndexController extends Zend_Controller_Action
{
    public function init()
    {
       	$this->view->headTitle('Mon application avec Zend_Application (Administrateur)');
    }

    public function indexAction()
    {
       $this->_response->appendBody('Bievenue Admin'); 
    }
}
warning Les contrôleurs n'ont rien à voir avec les autoloads des modules chargés par leurs bootstraps. Les contrôleurs obéissent à une règle (par défaut) propre que vous devez certainement déjà connaître : {NomDuModule}_{NomDuControleur}Controller sauf pour les contrôleurs du module par défaut dans lesquels un simple {NomDuContrôleur}Controller suffit.
Une fois encore : ceci n'a rien à voir avec les autoloaders ou les bootstraps. Souvenez vous que nous avons d'ailleurs nous mêmes configuré l'autoloader du module par défaut afin que les objets à y charger respectent cette même logique de nommage des contrôleurs.
Notre application modulaire est à présent opérationnelle, essayons maintenant de tester que tout fonctionne en tapant ces URLs http://localhost/zf-zaProject/public et http://localhost/zf-zaProject/public/admin dans le navigateur.


IV. Personnaliser le système

Comme d'habitude dans les technologies objets orientées patterns : plus il y a de classes, de classes abstraites et d'interfaces, plus le système est personnalisable et modulable.
En mettant les mains dans le code source et les schémas UML, la puissance de Zend Framework ressort une fois encore : tout est absolument personnalisable, jusqu'à des solutions très complexes (répondant donc à des problèmes complexes).
Sans être trop pointus dans cet article, nous allons voir comment ajouter des plugins de chargement de ressources, ou en redéfinir certains à notre guise.


IV-A. Ajouter une ressource

Les ressources que nous avons ajoutées jusque-là sont un peu particulières car elles ont des plugins prédéfinis dans #Zend_Application et elles répondent à un schéma de configuration bien déterminé. La liste de ces plugins ainsi que leurs options sont disponibles en documentation ou dans la source du framework.
Pour un exemple d'ajout de ressource via un plugin écrit par nos soins, nous allons doter notre application d'un Log qui va écrire vers la console de firebug en mode développement et vers un fichier en mode production.

Le plugin sera logé dans le dossier "library", qui est prévu pour accueillir nos objets personnels étendant ceux du Zend Framework. Comme nous avons déclaré un namespace d'autoload "Dvp_", nous devrons respecter cette structure.
Le plugin s'appellera donc Dvp_Application_Resource_Log.
Ensuite, lorsqu'on écrit "resources.log" dans le fichier de configuration, Zend Framework s'attend à un objet Zend_Application_Resource_Log. Pour indiquer qu'il s'agit du notre, il faut préciser l'option pluginpaths.
application/configs/application.ini
[production]
# ... ...
#Ajout du path vers nos plugins
pluginpaths.Dvp_Application_Resource = APPLICATION_PATH "/../library/Dvp/Application/Resource"

# initilisation du log
resources.log.writer.type = stream
resources.log.writer.file = APPLICATION_PATH "/logs/application.log"

...

[development : production]
resources.log.writer.type = firebug
Il n'y a plus qu'à écrire la classe de plugin qui gérera la ressource log (un Zend_Log). Cette classe doit implémenter Zend_Application_Resource_Resource, mais il est plus pratique de se baser sur un étage plus bas : la classe Zend_Application_Resource_ResourceAbstract qui nous oblige à définir une méthode init().
library/Dvp/Application/Resource/Log.php
<?php
class Dvp_Application_Resource_Log extends Zend_Application_Resource_ResourceAbstract
{
	public function init()
    {
        // récupère les paramètres de notre ressource, le bootstrap va nous
        // passer tout ce qui se trouve dans "resources.log" automatiquement
		$options = $this->getOptions();

        // récupération type de notre log (stream ou firebug)
        $type = $options['writer']['type'];
        //defini le writer de notre log selon le type.
        switch ($type) {
            case 'stream':
                $writer = new Zend_Log_Writer_Stream($options['writer']['file']);
				break;
            case 'firebug':
                $writer = new Zend_Log_Writer_Firebug();
                break;
		}
        $logger = new Zend_Log($writer);
        return $logger;
        }
		
    }
}
Bien entendu, dans une application réelle, quelques précautions et vérifications d'options seraient nécessaires, mais le principe est là.

Afin de vérifier que notre log fonctionne bien, nous allons rajouter quelques instructions d'écriture dans nos contrôleurs.
application/modules/default/IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
    public function init()
    {
        $this->view->headTitle('Mon application avec Zend_Application (Utilisateur par défaut)');
    }

    public function indexAction()
    {
        $this->_response->appendBody('Bienvenue'); 
        $bootstrap = $this->getInvokeArg('bootstrap'); //Récupère l'instance de notre bootstrap

         //vérifie si la ressource logs existe
        if ($bootstrap->hasResource('log')) {
            $log = $bootstrap->getResource('log'); //Récupère l'instance de la ressource enregistrée
            // s'écrit aussi $log = $bootstrap->getContainer()->log
            
            $log->info('une information');
            $log->warn('un avertissement');
            $log->err('une erreur');
        }
    }
}

V. Conclusion

#Zend_Application est une très forte valeur ajoutée à Zend Framework. C'est le composant qu'il manquait à sa structure MVC. Capable de proposer une interface claire et commune pour la configuration de ses objets, il propose également un registre unique pour le stockage de ceux-ci, ainsi qu'une structure très modulaire permettant une personnalisation extrême s'adaptant ainsi à tout type de projet Zend Framework.
en Zend_Application : Documentation officielle



               Version PDF (Miroir)   Version hors-ligne (Miroir)

Valid XHTML 1.0 TransitionalValid CSS!

Copyright © 2007 Julien Pauli. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.

Vos questions techniques : forum d'entraide Zend Framework - Publiez vos articles, tutoriels et cours
et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones
Nous contacter - Hébergement - Participez - Copyright © 2000-2010 www.developpez.com - Legal informations.