Section: Coding and using classes
« Doing a redirection | ^ Developing a module |
− Table of content
To respect the MVC pattern, it is recommended to do all business processing and services in dedicated classes 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 business processes will be reusable in other actions.
Classes can be instantiated with jClasses (a kind of class loader), or instantiated as you do in any PHP script, using the autoloading mecanism of PHP.
Autoloading ¶
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 jClasses
(see below). The autoload system of Jelix supports
the PSR0 specification and the
PSR-4 specification.
If you are using Composer, it is possible to declare namespaces in the composer.json file of your application, but modules will be less reusable. Except if you develop a module in its own repository, and if it is installable with Composer.
An alternative way is to declare classes and namespaces in the module.xml
file, in a <autoload>
element.
Here is an example:
<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" suffix=".class.php" />
<includePath dir="autoloadtest/incpath" suffix=".php" />
<autoloader file="autoloadtest/myautoloader.php" />
</autoload>
</module>
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:
<class name="myautoloadedclass" file="autoloadtest/autoloadtestclass.php" />
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:
<!-- load automatically classes that have names beginning by myalclasse -->
<classPattern pattern="/^myalclass/" dir="autoloadtest/withpattern/" suffix=".cl.php" />
To declare a set of classes that have a specific namespace, indicate the
namespace and a directory in a <psr0>
element. You can use also the deprecated element
<namespace>
, they are equivalent.
<psr0 name="jelixTests\foo" dir="autoloadtest" />
<!-- or -->
<namespace name="jelixTests\foo" dir="autoloadtest" />
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 jelixTests\foo\bar\baz
, it will load the file
autoloadtest/jelixTests/foo/bar/baz.php
.
Jelix have an other namespace support, which follow PSR-4 specification. You
should then use the <psr4>
element, or the deprecated element <namespacePathMap>
.
<psr4 name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
<!-- or -->
<namespacePathMap name="jelixTests\bar" dir="autoloadtest/barns" suffix=".class.php" />
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 jelixTests\foo\bar\baz
is not in the file
autoloadtest/jelixTests/foo/bar/baz.class.php
but in
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.
<includePath dir="autoloadtest/incpath" suffix=".php" />
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 spl_autoload_register and its parents.
<autoloader file="autoloadtest/myautoloader.php" />
Using classes with jClasses ¶
jClasses
is a component which is responsible to find, load and instancy
classes provided by your module. It avoids to include and instancy by yourself.
Historically, this component was created into Jelix before namespaces were
appeared into PHP. As jClasses
needs a selector, you don't have to indicate
a path to the class. You can then load easily a class from an other module.
However, since the existence of namespaces and of the auto-loading, the use of jClasses is less relevant. It is still interesting if you need singleton or use a class as a service.
Services classes ¶
The difference between a service class and the other classes is that a service class gives... a service. It doesn't need to be instantiated 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.
Creating a class for jClasses ¶
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 name_of_class.class.php
in the classes/
directory of the
module, so it can be loaded by jClasses
:
class StockService {
public function getProductsList(){
$stock = jDAO::get("products");
$list = $stock->findAll();
// here : processing of the list (for example)
return $list;
}
}
This class must be placed in classes/StockService.class.php
.
Instantiation with jClasses ¶
jClasses
gives two static methods, to which you give a selector:
createInstance($ClassSelector)
(orcreate($ClassSelector)
)getService($ClassSelector)
The first will, for each call, give a new instance. The second will always give
the same instance of the class. getService
will thus be used for the
service classes, and createInstance
for the others.
If our StockService
class is in the "shop" module, here is an example in a
controller:
$stocksrv = jClasses::getService("shop~stockservice");
$rep->body->assign('product_list', $stocksrv->getProductList());
Notice that you can put classes in sub-directories of the classes/
directory. For example, you can store the StockService
file into
classes/stocks/
. Then, to call it:
$stocksrv = jClasses::getService("shop~stocks/stockservice");
Including classes with jClasses ¶
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 inc($ClassSelector)
.
It includes (require_once) the class specified by the selector.
Example:
jClasses::inc('shop~shoesProduct');
$shoe = new shoesProduct('43', 'black');
You can also use the autoload system of PHP, see below.
Note that this method is deprecated. Prefer to configure the auto-loading.
Including interfaces with jClasses ¶
jClasses
provides the static methods incIFace
to include PHP
interfaces stored in a classes
directory of a module.
An interface should be store in a *.iface.php
file. To declare a
IStockUtils
interface, store this content into the file
classes/interfaces/IStockUtils.iface.php
:
interface IStockUtils {
[…]
}
Then to include this interface, stored in the module commons
, into file that
need it:
jClasses::incIface('commons~interfaces/IStockUtils');
class stockUtils implements IStockUtils {
[…]
}
Note that this method is deprecated. Prefer to configure the auto-loading.
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.
With Composer ¶
Composer is a package manager for PHP. You can install easily many vendors libraries. We recommend to use this tool.
To integrate Composer packages into your Jelix application, it is very simple:
- add dependencies into the composer.json file of your application
- launch the installation of packages with the command
composer install
. As the application.init.php includes the scriptvendor/autoload.php
, all classes of the packages are available into your code.
Legacy practice ¶
If you don't want to use Composer, here is an other way to integrate vendor libraries in your 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
classes/
directory of a module - or into the
lib/
directory of a module - or into a
lib/
directory of the application
Installation in a module ¶
You can store the library in a module if it is used only by this module.
You can put it into the classes
or lib/
directory of the module:
require(jApp::getModulePath('main') . 'classes/bar.php');
$myclass = new bar();
If the library has an autoloader, you can declare it in the module.xml file
(see the autoloading section above). The require
will be useless.
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:
$myclass = jClasses::create('bar');
// or if the constructor need arguments
jClasses::inc('bar');
$myclass = new bar('bla');
Global installation ¶
It is interesting to store a set of classes into a lib/
directory at the application level,
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 jApp::appPath()
method.
For example, if you want to include the myapp/lib/foo/bar.php
, do this
into your controller or other files of a module:
require(jApp::appPath('lib/foo/bar.php'));
$myclass = new bar();
If the library provides an autoloader, you can use initialize it into your
application.init.php
file (see the documentation of the library to know
how to use it). So the require
will be useless.