Quick links: Content - sections - sub sections
EN FR

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.

Classes can be instanciated with jClasses (a kind of class loader), or instanciated as you do in any PHP script, using the autoloading mecanism of PHP.

Using classes with jClasses

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.

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 jClasses class, which avoids to do an include and instantiate yourself.

jClasses gives two static methods, to which you give a selector:

  • createInstance($ClassSelector) (or create($ClassSelector) )
  • getService($ClassSelector)

The first will, for each call, give a new instance. The second will allways 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.

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::inIface('commons~interfaces/IStockUtils');

class stockUtils implements IStockUtils {
    […]
}

Loading classes before the session_start for jClasses

Sometimes, you may want to store some of your business object in sessions. But then your classes should be loaded before the session_start() call, so PHP can unserialize this objects.

Before the autoload system (see below), Jelix provided a way to indicates which classes to load before the session_start(). In the configuration, in the sessions section, you should indicate the selectors of this classes in the loadClasses options:


[sessions]

loadClasses = "mymodule~bar,mymodule~subdir/foo, shop~shoesProduct"

The module name is required in selectors.

  • *Note: this loadClasses parameter is now deprecated. You must declare**
  • *your classes in module.xml files, so they will be auto-loaded.** See below.

Autoloading

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 jClasses. The autoload system of Jelix supports the PSR0 specification and the PSR-4 specification.

Simply declares, in the module.xml file, the classes that will be loaded automatically, with specific tags in the <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" />
        <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>

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:


  <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 include the file autoloadtest/jelixTests/foo/bar/baz.php.

Jelix have an other namespace support, which follow PSR-4 specification (implemented before the release of PSR-4, this is why the tag name is not really good):


   <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" />

Note

Use the autoload system only for classes that are often used, or that share a common namespace. Declaring dozen classes that does not share a namespace, does not have a sens, and will slow the autoload system of PHP, 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, or create classes which share same root namespace.

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 recommand to use this tool (Next version of Jelix 1.6+ will be installable with Composer ;-)).

To integrate Composer packages into your Jelix application, it is very simple:

  • create your composer.json file into your application (see Composer documentation)
  • install packages indicated into your composer.json: php composer.phar install. It generates a vendor/ directory into your application
  • Include the file vendor/autoload.php into application.init.php

require (__DIR__.'/vendor/autoload.php');
require (__DIR__.'/../lib/jelix/init.php');
jApp::initPaths( /* ... */ );
jApp::setTempBasePath( /* ... */ );

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 lib/ directory
  • or into the classes/ directory of a module
  • or into the lib/ directory of a module

Global installation

It is interesting to store a set of classes into the 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 lib/foo/bar.php, do this into your controller or other files of a module:


   require(LIB_PATH.'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.

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