forked from jelix/jelix-manual-en
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathusing-classes.gtw
205 lines (136 loc) · 9.25 KB
/
using-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
~~LANG:FR@frman:utilisation-classes~~
To respect the MVC pattern, it is recommended to do all business processing and services in classes dedicated to it instead of in controllers.
In this kind of classes, you will manipulate, for example, daos, data from daos or others, do all the processing other than display. The methods of your controllers will thus be lighter and the business processing will be reusable in other actions.
===== Creating a class =====
Business classes and services in Jelix are classic PHP classes which have nothing specific. The only thing that you have to respect is to specify it in a file named @@F@name_of_class.class.php@@ in the @@F@classes/@@ directory of the module, so it can be loaded by @@C@jClasses@@:
<code php>
class StockService {
public function getProductsList(){
$stock = jDAO::get("products");
$list = $stock->findAll();
// here : processing of the list (for example)
return $list;
}
}
</code>
This class must be placed in @@F@classes/StockService.class.php@@.
The difference between a service class and the other classes is that a service class gives... a service. It doesn't need to be instanciated each time we use it because it doesn't have "discriminating" property. Only one instance is enough for all the application.
For example, a "factory" type class, which retrieves sets of data, is a service class. On the other hand, a class representing a product, which thus has identifying fields, is a non service class.
===== Instantiation with jClasses =====
Jelix proposes the @@C@jClasses@@ class, which avoids to do an include and instantiate yourself.
@@C@jClasses@@ gives two static methods, to which you give a selector:
* @@M@createInstance($ClassSelector)@@ (or @@M@create($ClassSelector)@@ )
* @@M@getService($ClassSelector)@@
The first will, for each call, give a new instance. The second will allways give the same instance of the class. @@M@getService@@ will thus be used for the service classes, and @@M@createInstance@@ for the others.
If our @@C@StockService@@ class is in the "shop" module, here is an example in a controller:
<code php>
$stocksrv = jClasses::getService("shop~stockservice");
$rep->body->assign('product_list', $stocksrv->getProductList());
</code>
Notice that you can put classes in sub-directories of the @@F@classes/@@ directory. For example, you can store the @@C@StockService@@ file into @@F@classes/stocks/@@. Then, to call it:
<code php>
$stocksrv = jClasses::getService("shop~stocks/stockservice");
</code>
===== Including classes =====
In some cases, like when the constructor needs parameters, you have to include the class and then instantiate it "manually".
In this case the jClasses class has a static method @@M@inc($ClassSelector)@@. It includes (require_once) the class specified by the selector.
Example:
<code php>
jClasses::inc('shop~shoesProduct');
$shoe = new shoesProduct('43', 'black');
</code>
You can also use the autoload system of PHP, see below.
===== Including interfaces =====
@@C@jClasses@@ provides the static methods @@M@incIFace@@ to include PHP interfaces stored in a @@F@classes@@ directory of a module.
An interface should be store in a @@F@*.iface.php@@ file. To declare a @@C@IStockUtils@@ interface, store this content into the file @@F@classes/interfaces/IStockUtils.iface.php@@:
<code php><?php
interface IStockUtils {
[…]
}
?></code>
Then to include this interface, stored in the module @@commons@@, into file that need it:
<code php><?php
jClasses::inIface('commons~interfaces/IStockUtils');
class stockUtils implements IStockUtils {
[…]
}
?></code>
===== Installing and using vendor classes =====
You have often to use some classes provided by other projects. Of course, you can use it into a Jelix application.
Although you can store this classes where you want (because the require or include statement are not restricted), it's better to store them:
* into the @@F@lib/@@ directory
* or into the @@F@classes/@@ directory of a module
It is interesting to store a set of classes into the @@F@lib/@@ directory, because it's easier to share this classes between projects, and perhaps it will be easier to update them. To include them, you have to use the @@LIB_PATH@@ constant. For example, if you want to include the @@F@lib/foo/bar.php@@, do this into your controller or other files of a module:
<code php>
require(LIB_PATH.'foo/bar.php');
$myclass = new bar();
</code>
You can store the class in a module if it is used only by this module. You can put it into the classes directory:
<code php>
require(jApp::getModulePath('main') . 'classes/bar.php');
$myclass = new bar();
</code>
If the name of the class and the name of the file follow the coding style of jelix (a "bar" class into a bar.class.php file), you can use jClasses of course:
<code php>
$myclass = jClasses::create('bar');
// or if the constructor need arguments
jClasses::inc('bar');
$myclass = new bar('bla');
</code>
===== Loading classes before the session_start =====
Sometimes, you may want to store some of your business object in sessions. But then your classes should be loaded before the @@f@session_start()@@ call, so PHP can unserialize this objects.
Jelix provide a simple way to indicates which classes to load before the @@f@session_start()@@. In the configuration, in the @@sessions@@ section, indicate the selectors of this classes in the @@V@loadClasses@@ options:
<code ini>
[sessions]
loadClasses = "mymodule~bar,mymodule~subdir/foo, shop~shoesProduct"
</code>
Note: the module name is required.
===== Autoload =====
Since Jelix 1.4, it is possible to use an autoload system, based on the autoload function of PHP (the class file is loading automatically when the class is used). It avoids to use @@C@jClasses@@. The autoload system of Jelix supports the [[https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md|spécification PSR0]].
Simply declares, in the @@[email protected]@@ file, the classes that will be loaded automatically, with specific tags in the @@E@<autoload>@@ element.
Here is an example:
<code xml>
<module xmlns="http://jelix.org/ns/module/1.0">
<info id="[email protected]" 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>
As you can see, there are different way to declare a class. Path of file or directory indicated in tags should be relative to the module directory.
To load a specific class, just indicate its name and its file:
<code xml>
<class name="myautoloadedclass" file="autoloadtest/autoloadtestclass.php" />
</code>
To declare several classes that have similar name, you can use a regular expression and indicate the directory where files are. You can indicate also a suffix for the filename:
<code xml>
<!-- load automatically classes that have names beginning by myalclasse -->
<classPattern pattern="/^myalclass/" dir="autoloadtest/withpattern/" suffix=".cl.php" />
</code>
To declare a set of classes that have a specific namespace, indicate the namespace and a directory:
<code xml>
<namespace name="jelixTests\foo" dir="autoloadtest" />
</code>
This behavior follows the PSR0 specification. So the namespace should correspond to a path in the indicated directory.
For example, if Jelix should load the class @@C@jelixTests\foo\bar\baz@@, it will include the file @@F@autoloadtest/jelixTests/foo/bar/baz.php@@.
Jelix have an other namespace support:
<code xml>
<namespacePathMap name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
</code>
The class path does not correspond to the name of the namespace. It indicates that all classes which have the indicated namespace are directly in the given directory. So the class @@C@jelixTests\foo\bar\baz@@ is not in the file @@F@autoloadtest/jelixTests/foo/bar/baz.class.php@@ but in @@F@autoloadtest/bar/baz.class.php@@.
It is possible to indicate a classical include path (like the includePath of PHP): Jelix will search a file which have the same name of the class in this directory.
<code xml>
<includePath dir="autoloadtest/incpath" suffix=".php" />
</code>
Last possibility: it is possible to indicate a file that initialize an other autoloader. It can be useful when you use a vendor libraries which have its own autoloader. This autoloader should use the function [[phpapi:spl_autoload_register|spl_autoload_register]] and its parents.
<code xml>
<autoloader file="autoloadtest/myautoloader.php" />
</code>
==== Note ====
Use the autoload system only for classes that are often used. Declaring all classes does not have a sens. The autoload system could be slow if all modules declares dozen of classes, because it should verify then many name and path. So prefer to use jClasses, for classes used only by the module or used only few times.