forked from jelix/jelix-manuel-fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utilisation-classes.gtw
256 lines (182 loc) · 10.6 KB
/
utilisation-classes.gtw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
~~LANG:EN@enman:using-classes~~
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.
===== Création d'une classe =====
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 @@F@nom_de_la_classe.class.php@@ dans le répertoire @@F@classes/@@ du
module, pour pouvoir être chargée par @@C@jClasses@@:
<code php>
class StockService {
public function getListeProduits(){
$stock = jDAO::get("products");
$liste = $stock->findAll();
// ici traitement sur la liste... par exemple
return $liste;
}
}
</code>
Cette classe devra être stockée dans @@F@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 @@C@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 :
* @@M@createInstance($selecteurDeClasse)@@ (ou @@M@create($selecteurDeClasse)@@ )
* @@M@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. @@M@getService@@ sera donc
utilisé sur les classes services, et @@M@createInstance@@ sur les autres.
Si notre classe @@C@StockService@@ se trouve dans le module "shop", voici un
exemple d'appel dans un contrôleur :
<code php>
$stocksrv = jClasses::getService("shop~stockservice");
$rep->body->assign('liste_produits', $stocksrv->getListeProduits());
</code>
Notez que vous pouvez mettre des classes dans des sous-répertoires de
@@F@classes/@@, ce qui donne, si on place @@C@StockService@@ dans un répertoire
@@F@classes/stocks/@@ :
<code php>
$stocksrv = jClasses::getService("shop~stocks/stockservice");
</code>
===== Inclure simplement une classe =====
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
@@M@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 :
<code php>
jClasses::inc('shop~shoesProduct');
$shoe = new shoesProduct('43', 'black');
</code>
Vous pouvez aussi profiter du système autoload de PHP. Voir plus bas.
===== Inclure une interfaces définies dans un module =====
@@C@jClasses@@ fournit la méthode statique @@M@incIFace@@ pour inclure des
interfaces PHP définies dans le dossier @@F@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 @@C@@IStockUtil@@ dans un fichier
@@F@IStockUtil.iface.php@@ dans un sous dossier @@F@interfaces/@@ du dossier
@@F@classes/@@ :
<code php><?php
interface IStockUtil {
[…]
}
?></code>
On utilisera ensuite la méthode @@M@jClasses::incIface()@@ dans les fichiers qui
en ont besoin :
<code php><?php
jClasses::incIface('commons~interfaces/IStockUtil');
class StockUtil implements IStockUtil {
[…]
}
?></code>
===== 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.
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 @@F@lib/@@
* le répertoire @@F@classes/@@ d'un module
Faire un répertoire spécifique dans @@F@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 @@F@lib/foo/bar.php@@, vous ferez alors ceci :
<code php>
require(LIB_PATH.'foo/bar.php');
$maclasse = new bar();
</code>
Placer les classes tierces dans le répertoire @@F@classes/@@ 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
@@F@bar.php@@ que vous avez placé dans le répertoire @@F@classes/@@ du module
main, vous pouvez faire :
<code php>
require(jApp::getModulePath('main') . 'classes/bar.php');
$maclasse = new bar();
</code>
Si le nom du fichier de la classe respecte la norme des fichiers de classes pour
jelix (@@F@bar.class.php@@), et que la classe s'appelle effectivement "bar",
vous pouvez bien entendu utiliser jClasses :
<code php>
$maclasse = jClasses::create('bar');
// ou si le constructeur attend des arguments
jClasses::inc('bar');
$maclasse = new bar('bla');
</code>
===== Charger des classes 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
@@f@session_start()@@, pour que PHP puisse désérialiser correctement l'objet en
session.
Jelix fournit un moyen simple d'indiquer les classes à charger avant ce
@@f@start_session()@@ : il suffit d'indiquer la liste des sélecteurs de ces
classes dans l'option @@V@loadClasses@@ de la section @@sessions@@ dans la
configuration :
<code ini>
[sessions]
loadClasses = "mymodule~bar,mymodule~subdir/foo, shop~shoesProduct"
</code>
Note: le nom du module est obligatoire.
===== Autoload =====
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 @@C@jClasses@@. Le système d'autoload de Jelix supporte la [[https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md|spécification PSR0]].
Il suffit de déclarer dans le fichier @@F@module.xml@@ les classes à charger automatiquement, avec des balises spécifiques dans une balise @@E@<autoload>@@.
Voici un exemple :
<code xml>
<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>
</code>
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 :
<code xml>
<class name="myautoloadedclass" file="autoloadtest/autoloadtestclass.php" />
</code>
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 :
<code xml>
<!-- chargement automatique des classes dont le nom commençent par myalclasse -->
<classPattern pattern="/^myalclass/" dir="autoloadtest/withpattern/" suffix=".cl.php" />
</code>
Pour déclarer un ensemble de classes qui sont dans un namespace particulier, on indique le namespace et le répertoire.
<code xml>
<namespace name="jelixTests\foo" dir="autoloadtest" />
</code>
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 @@C@jelixTests\foo\bar\baz@@, il incluera le fichier @@F@autoloadtest/jelixTests/foo/bar/baz.php@@.
Jelix possède un autre moyen de prendre en charge les namespaces, différent de PSR0.
<code xml>
<namespacePathMap name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
</code>
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 @@C@jelixTests\foo\bar\baz@@ n'est pas situé dans @@F@autoloadtest/jelixTests/foo/bar/baz.class.php@@ mais dans @@F@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.
<code xml>
<includePath dir="autoloadtest/incpath" suffix=".php" />
</code>
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 [[phpapi:spl_autoload_register|spl_autoload_register]] et ses amis.
<code xml>
<autoloader file="autoloadtest/myautoloader.php" />
</code>
==== Remarque ====
N'utilisez le système autoload que pour les classes souvent utilisées. Déclarer toutes les classes n'a pas trop de sens, et ralentira le système autoload (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 jClasses, surtout pour les classes peu utilisées ou utilisées uniquement par le module.