Section: Coder et utiliser des classes
« Un contrôleur pour REST | ^ Développement d'un module |
− Table des matières
Pour respecter le découpage MVC, il est recommandé de réaliser tous ses traitements métiers et services dans des classes dédiées, plutôt que de tout faire dans les contrôleurs.
Dans de telles classes, vous manipulerez par exemple des daos, des données issues de daos ou autre, effectuerez donc des traitements autres que de l'affichage. Aussi les méthodes de vos contrôleurs s'en trouveront allégées et les traitements réutilisables dans d'autres actions.
Elles peuvent être instanciée par jClasses (un "loader" de classes, plus ou moin obsolète), ou instanciée normalement, le chargement de la classe utilisant le mécanisme d'autoload de Jelix.
Utilisation des classes avec jClasses ¶
Création d'une classe utilisable par jClasses ¶
Les classes métiers et services dans Jelix sont des classes PHP classiques qui
n'ont rien de spécifique. La seule chose à respecter est de les stocker dans un
fichier nom_de_la_classe.class.php
dans le répertoire classes/
du
module, pour pouvoir être chargée par jClasses
:
class StockService {
public function getListeProduits(){
$stock = jDAO::get("products");
$liste = $stock->findAll();
// ici traitement sur la liste... par exemple
return $liste;
}
}
Cette classe devra être stockée dans classes/StockService.class.php
.
La différence entre une classe service et les autres classes est qu'une classe service fournit, comme son nom l'indique, un service. Elle n'a pas besoin d'être instanciée à chaque utilisation car elle ne possède pas de propriétés "discriminantes". Une seule instance suffit pour toute l'application.
Par exemple une classe de type "factory", qui permet de récupérer des ensembles de données, est une classe service. Par contre une classe représentant un produit, qui possède donc des champs identifiants, est une classe non service.
Instanciation d'une classe avec jClasses ¶
Jelix propose la classe jClasses
, qui vous évite d'avoir à faire un
include et une instanciation par vous même.
jClasses fournit deux méthodes statiques, auxquelles vous indiquez un sélecteur :
createInstance($selecteurDeClasse)
(oucreate($selecteurDeClasse)
)getService($selecteurDeClasse)
La première créera à chaque appel une nouvelle instance. Par contre la deuxième
renverra toujours une même instance de la classe. getService
sera donc
utilisé sur les classes services, et createInstance
sur les autres.
Si notre classe StockService
se trouve dans le module "shop", voici un
exemple d'appel dans un contrôleur :
$stocksrv = jClasses::getService("shop~stockservice");
$rep->body->assign('liste_produits', $stocksrv->getListeProduits());
Notez que vous pouvez mettre des classes dans des sous-répertoires de
classes/
, ce qui donne, si on place StockService
dans un répertoire
classes/stocks/
:
$stocksrv = jClasses::getService("shop~stocks/stockservice");
Inclure une classe avec jClasses ¶
Dans certains cas, comme celui où le constructeur de la classe métier demande un paramètre, il faut inclure la classe métier puis l'instancier "manuellement".
Dans ce cas la classe jClasses propose la méthode statique
inc($selecteurDeClasse)
. Comme son nom l'indique elle inclut (en fait
effectue un require_once) la classe spécifiée par le sélecteur.
Exemple :
jClasses::inc('shop~shoesProduct');
$shoe = new shoesProduct('43', 'black');
Vous pouvez aussi profiter du système autoload de PHP. Voir plus bas.
Inclure une interface avec jClasses ¶
jClasses
fournit la méthode statique incIFace
pour inclure des
interfaces PHP définies dans le dossier classes
d'un module.
Une interface doit être définie avec la syntaxe PHP (mot clé interface
) dans
un fichier à son nom suivi du suffixe .iface.php
. Par exemple, on peut
déclarer l'interface IStockUtil
dans un fichier
IStockUtil.iface.php
dans un sous dossier interfaces/
du dossier
classes/
:
<?php
interface IStockUtil {
[…]
}
?>
On utilisera ensuite la méthode jClasses::incIface()
dans les fichiers qui
en ont besoin :
jClasses::incIface('commons~interfaces/IStockUtil');
class StockUtil implements IStockUtil {
[…]
}
Charger des classes jClasses avant le session_start ¶
Parfois, vous voulez stockez en session des objets basés sur vos classes
métiers. Mais pour cela, il faut inclure ces classes avant que jelix fasse un
session_start()
, pour que PHP puisse désérialiser correctement l'objet en
session.
Avant le système d'auto-chargement dans les modules (voir la section suivante)
Jelix fournissait un moyen d'indiquer les classes à charger avant ce
start_session()
: il suffisait d'indiquer la liste des sélecteurs de ces
classes dans l'option loadClasses de la section sessions
dans la
configuration :
[sessions]
loadClasses = "mymodule~bar,mymodule~subdir/foo, shop~shoesProduct"
Le nom du module est obligatoire.
- *Note: ce paramètre
loadClasses
est désormais obsolète et vous devez plutôt** - *déclarer les classes dans le fichier module.xml pour qu'elles soient autochargées.** Voir la suite.
Auto-chargement de classes ¶
Depuis Jelix 1.4, il est possible de profiter d'un système d'auto-chargement des
classes reposant sur la fonction autoload de PHP (le fichier de la classe étant
chargé automatiquement au moment de l'instanciation). Cela évite d'utiliser
jClasses
. Le système d'autoload de Jelix supporte la
spécification PSR0 et la
spécification PSR-4.
Il suffit de déclarer dans le fichier module.xml
les classes à charger
automatiquement, avec des balises spécifiques dans une balise <autoload>
.
Voici un exemple :
<module xmlns="http://jelix.org/ns/module/1.0">
<info id="jelix_tests@testapp.jelix.org" name="jelix_tests"> ... </info>
<dependencies> ... </dependencies>
<autoload>
<class name="myautoloadedclass" file="autoloadtest/autoloadtestclass.php" />
<classPattern pattern="/^myalclass/" dir="autoloadtest/withpattern/" suffix=".cl.php" />
<namespace name="jelixTests\foo" dir="autoloadtest" />
<namespacePathMap name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
<includePath dir="autoloadtest/incpath" suffix=".php" />
<autoloader file="autoloadtest/myautoloader.php" />
</autoload>
</module>
Comme vous le voyez, il y a plusieurs façon de déclarer une classe. Les chemins des fichiers ou répertoires à indiquer dans les balises doivent être relatif au répertoire du module.
Pour charger une classe spécifique, en indiquant son nom et son fichier :
<class name="myautoloadedclass" file="autoloadtest/autoloadtestclass.php" />
Pour déclarer plusieurs classes à la fois qui ont des noms ressemblant, on peut utiliser une expression régulière, et indiquer le répertoire où les fichiers se trouvent ainsi qu'un suffixe de fichier :
<!-- chargement automatique des classes dont le nom commençent par myalclasse -->
<classPattern pattern="/^myalclass/" dir="autoloadtest/withpattern/" suffix=".cl.php" />
Pour déclarer un ensemble de classes qui sont dans un namespace particulier, on indique le namespace et le répertoire.
<namespace name="jelixTests\foo" dir="autoloadtest" />
Cette déclaration suit la spécification PSR0. Aussi le namespace de la classe
doit correspondre à un chemin dans le répertoire indiqué. Par exemple, si Jelix
doit charger la classe jelixTests\foo\bar\baz
, il incluera le fichier
autoloadtest/jelixTests/foo/bar/baz.php
.
Jelix possède un autre moyen de prendre en charge les namespaces, qui suit la spécification PSR-4. (implémenté antérieurement à la sortie de la spécification PSR-4, d'où le nom "namespacePathMap" moins parlant).
<namespacePathMap name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
Le chemin du fichier de la classe n'est pas un chemin reprenant le nom du
namespace. Mais ici toute classes correspondant au namespace se trouvent dans
des fichiers directement dans le répertoire indiqué. Ainsi, la classe
jelixTests\foo\bar\baz
n'est pas situé dans
autoloadtest/jelixTests/foo/bar/baz.class.php
mais dans
autoloadtest/bar/baz.class.php
.
Il est possible d'indiquer aussi un répertoire d'include classique : Jelix cherchera un fichier du même nom que la classe dans ce répertoire.
<includePath dir="autoloadtest/incpath" suffix=".php" />
Enfin, on peut indiquer un fichier qui déclarera un autre autoloader. Cela peut être utile quand on utilise une bibliothèque externe qui possède son autoloader. Cet autoloader doit utiliser la fonction spl_autoload_register et ses amis.
<autoloader file="autoloadtest/myautoloader.php" />
Remarque ¶
N'utilisez le système autoload que pour les classes souvent utilisées. Déclarer une multitude de classes qui ne partagent pas le même namespace n'a pas trop de sens, et ralentira le système autoload de PHP (si tout les modules déclarent des dizaines de classes, cela fait au final beaucoup de chemin à vérifier...). Préférez donc l'utilisation de namespaces commun, ou encore de jClasses (surtout pour les classes peu utilisées ou utilisées uniquement par le module).
Installer et Utiliser des classes tierces ¶
Il se peut que vous vouliez réutiliser des classes développées en dehors du projet. Il est bien entendu tout à fait possible de le faire dans Jelix.
Avec Composer ¶
Composer est un gestionnaire de paquets et de dépendance pour PHP, de plus en plus utilisé. Il vous permet d'installer et de mettre à jour facilement des bibliothèques tierces. Nous vous recommandons de l'utiliser si les bibliothèques tierces que vous voulez utiliser peuvent être installées avec Composer. (Les prochaines versions de Jelix > 1.6 seront disponibles par Composer ;-) ).
Pour une intégration de paquets Composer, c'est très simple :
- Créez votre fichier composer.json dans le répertoire de votre application (voir la documentation de Composer)
- installez les paquets indiqués dans composer.json :
php composer.phar install
. Cela génère un répertoirevendor/
dans votre application - Incluez le fichier
vendor/autoload.php
dansapplication.init.php
require (__DIR__.'/vendor/autoload.php');
require (__DIR__.'/../lib/jelix/init.php');
jApp::initPaths( /* ... */ );
jApp::setTempBasePath( /* ... */ );
Ancienne pratique ¶
Si vous ne voulez pas utiliser Composer, voici une autre manière d'intégrer des bibliothèques tierces dans votre application.
Bien que vous puissiez mettre ces classes où bon vous semble et faire un include classique, il y a toutefois trois emplacements préconisés :
- le répertoire
lib/
de Jelix - le répertoire
classes/
d'un module - le répertoire
lib/
d'un module
installation globale ¶
Faire un répertoire spécifique dans lib/
et y placer les fichiers de
classes, est intéressant quand il s'agit de partager ces classes entre plusieurs
modules, voir entre plusieurs projets. Pour faire les includes, vous pouvez
utiliser la constante LIB_PATH
. Par exemple si vous voulez inclure une
classe que vous avez dans lib/foo/bar.php
, vous ferez alors ceci :
require(LIB_PATH.'foo/bar.php');
$maclasse = new bar();
Dans le cas où la bibliothèque que vous installez comporte un fichier d'autoload,
vous pouvez faire l'initialisation (inclusion, appelle d'une fonction spécifique etc,
selon la documentation de cette bibliothèque) dans le fichier application.init.php
de l'application.
installation dans un module ¶
Placer les classes tierces dans le répertoire classes/
ou lib/
du
module est utile si les classes en question ne sont utilisées que par ce module.
Cela permet aussi une réutilisation plus facile : tout est dans le module.
Pour utiliser ces classes, en admettant que vous voulez inclure le fichier
bar.php
que vous avez placé dans le répertoire classes/
du module
main, vous pouvez faire :
require(jApp::getModulePath('main') . 'classes/bar.php');
$maclasse = new bar();
Si la bibliothèque propose un autoloader, déclarez-le dans le
fichier module.xml (voir plus haut). Cela évitera de faire le require
.
Si le nom du fichier de la classe respecte la norme des fichiers de classes pour
jelix (bar.class.php
), et que la classe s'appelle effectivement "bar",
vous pouvez bien entendu utiliser jClasses :
$maclasse = jClasses::create('bar');
// ou si le constructeur attend des arguments
jClasses::inc('bar');
$maclasse = new bar('bla');