Raccourcis : Contenu - rubriques - sous rubriques
EN FR

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 moins obsolète), ou instanciée normalement, le chargement de la classe utilisant le mécanisme d'autoload de Jelix.

Auto-chargement de classes

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 (voir ci-après). Le système d'autoload de Jelix supporte la spécification PSR0 et la spécification PSR-4.

Si vous utilisez Composer, il est possible de déclarer les espaces de noms dans le fichier composer.json de votre application, mais cela rend votre module moins portable. Toutefois, si le module en question est dans son propre dépôt et destiné à être installé par Composer, le composer.json du module est alors le bon endroit.

Une alternative est de faire les déclarations de classes et namespaces dans le fichier module.xml, 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" />
        <psr0 name="jelixTests\foo" dir="autoloadtest" />
        <psr4 name="jelixTests\bar" dir="autoloadtest/barns"/>
        <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 dans une balise <psr0> (Vous pouvez aussi utiliser l'ancienne balise <namespace>, elles sont équivalentes).


  <psr0 name="jelixTests\foo" dir="autoloadtest" />
  <!-- ou -->
  <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 chargera 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. Il faut utiliser la balise <psr4>. On peut aussi utiliser la balise obsolète <namespacePathMap>.


   <psr4 name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
   <!-- ou -->
   <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" />

Utilisation des classes avec jClasses

jClasses est un composant qui est responsable de trouver, charger et instancier des classes fournies par votre module. Elle vous évite d'avoir à faire un include et une instanciation par vous même.

Historiquement, ce composant a été créé dans Jelix avant l'existence des espaces de noms dans PHP. Comme jClasses demande un "sélecteur", vous n'avez pas à indiquer de chemin vers la classe. Cela est particulièrement pratique quand vous voulez appeler une classe d'un autre module, puisqu'en théorie, vous n'avez pas à savoir où se trouve ce module.

Cependant, depuis l'existance des espaces de noms et de l'auto-loading, l'utilisation de jClasses est moins pertinente. Pour le moment son intérêt est la gestion de singleton d'instances, ou de services.

Classes de services

On distinguera les classes normales, des classes de services. Pour ces dernières une seule instance est autorisée. jClasses permet de s'en assurer.

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. 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, disons normale.

Création d'une classe utilisable par jClasses

Les classes pouvant être instanciées par jClasses 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.

Instanciation d'une classe avec jClasses

jClasses fournit deux méthodes statiques, auxquelles vous indiquez un sélecteur :

  • createInstance($selecteurDeClasse) (ou create($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');

Cette possibilité d'inclusion est toutefois obsolète. Préférez l'auto-loading.

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 {
    […]
}

Note: Cette possibilité d'inclusion d'interface est obsolète. Préférez l'auto-loading.

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 le gestionnaire de paquets et de dépendance pour PHP. 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.

Pour une intégration de paquets Composer, c'est très simple :

  • ajoutez les dépendances dans le fichier composer.json de votre application
  • lancez l'installation des paquets avec la commande composer install. ils sont installé dans le répertoire vendor/ de votre application. Et comme le fichier application.init.php inclus normalement le script vendor/autoload.php, les classes des paquets installés sont utilisables.

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 deux emplacements préconisés :

  • le répertoire classes/ d'un module
  • le répertoire lib/ d'un module
  • un répertoire lib/ dans votre 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 du module : tout est dedans.

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');

installation globale

Faire un répertoire spécifique dans un répertoire lib/ au niveau de l'application et y placer les fichiers de classes, est intéressant quand il s'agit de partager ces classes entre plusieurs modules. Pour faire les includes, vous pouvez utiliser jApp::appPath().

Par exemple si vous voulez inclure une classe que vous avez dans monappli/lib/foo/bar.php, vous ferez alors ceci :


   require(jApp::appPath('lib/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.