ODiN Framework

What is ODiN?

ODiN is an object oriented PHP web develoment framework developed and maintained by SWAP. ODiN is used as the backend for all new applications developed by SWAP.

Key principles and programming philosophies used in ODiN development are:

  • Do Not Repeat Yourself (DRY): Modules need to be reusable and have clear interfaces.
  • Keep It Simple Stupid (KISS): Simple solution works usually best. Avoid bloated classes and massive functions.
  • Fit for purpose: Solution needs to be based on the requirements of the problem. That means no killing flies with a bazooka, and no attempts to cross oceans with a rowing boat.
  • Understandability: Intuitive and logically named interfaces make code mode understandable and maintainable.
  • Security: Prepare for the worst on core level. Avoid any assumptions of the sensibility or good will of the user.

Key Features

Some key features of the ODiN Framework are

  • Powerful base classes for building custom web applications
  • Template engine layer with support of template inheritance
  • NoSql style database access abstraction with support of multiple databases
  • Ready functionality for most common database operations based on prepared queries
  • Native multilingual support
  • Broad centralized configurability
  • Built-in access rights management with multi-site support
  • Support for flexible use of mixed content sources (databases, static files, external servers, ...)
  • A collection of ready-to-use handy tools for site admins.

Where can I get ODiN?

ODiN Framework is intellectual property of the RBINS and is not published to the public domain at the moment. At the moment there are no direct plans for publishing it in near future, but once the codebase becomes more mature, making major releases available under some open source licence might be considered. Customer departments of SWAP may request a snapshot of the current codebase from the OD Nature SVN Server.

How does an ODiN setup look like?

It should be noted at first, that ODiN has been developed and tested only in linux-based environments. There is no guarantee for it to work in a Windows-based environment, altough the developers are not aware of any direct reason why ODiN could not work in Windows if the paths in the Configuration files are set up correctly in windows style.

In principle you can install the ODiN library on any directory on the server, as long as the web server process is able to read from that folder and you set up the configuration files correctly. It is not recommended, however, to place the ODiN library in a folder publicly exposed to the world through the web server.

The ODiN framework makes some assumptions on the directory setup of the web server, however;

  • There must be a public root folder and a resource root folder (or include root folder, if you will).
  • In order to use the Template engine ODiN expects to find under the resource root a template working directory to which it has write access. By default this folder is called 'templates' and it is also included in ODiN's template lookup path.
  • If ODiN is used on a server using virtual hosts, each virtual host's public root is assumed to be located under a common parent path in a folder matching the HostName given to that host.
  • If there are more than one ODiN-based site or application running under the same server or virtual host, each is assumed to have its own public root under the general public root and its own resource root under the general resource root, and the names of these directories are expected to match.

There are further some assumptions made from each Resource folder:

  • ODiN expects to find at least two folders under each resource root, namely a site/application-specific template directory and a client side script directory. By default these are called 'html' and 'js'.
  • If ODiN finds a configuration directory (at the moment hardcoded as 'conf'), it attempts to load any additional configurations from an ini-file matching the identifier of the site (by default same as the root folder name).
  • Additional directories can be specified as per needs of your application, a common directory often used in built-in applications is 'json' which may contain any json-encoded information such as translation strings, static data sources, etc.

How does ODiN work?

The ODiN Framework is divided logically into packages, of which the most important are core and modules. The core contains the most integral classes of ODiN as well as the base classes to build your own applications upon, while the modules contain built-in functionalities which can be enabled at any time. Descriptions of some of the key components of ODiN follow.

ODiN -class

The ODiN class is the central entry point of the entire framework. It is responsible for

  • Loading configurations
  • Loading translations
  • Instantiating built-in modules
  • Making all the above available to other components at any time during a session

The ODiN Class implements the singleton pattern, which means there can be only one active instance of it per session at any one moment. This instance holds references to all configuration objects, translation objects and instances of different modules loaded during that session. The ODiN object cannot be instantiated using the new command. Instead, a call to the static method ODiN::getInstance() takes care of creating a new instance if one is not found at the time it is called. This is to ensure a reliable storage of all loaded objects throughout the session. Normally the user doesn't have to worry about any of this, though, as the odin object is created and made available as $odin during the standard initialisation sequence (odin.init.php) which is included in the package and recommended to be used as a starting point of any ODiN-based application.

Page -class

The Page class is the main part of the Page module. While using the page module is not required to use the ODiN Framework, it is required to use one of the most powerful features of the ODiN Framework; the RequestHandlers. In the Model-View-Controller -paradigm (MVC) the Page class is the controller. It is responsible for

  • Registering request handlers
  • Registering page aliases
  • Registering parameters for the template (which are later assigned as template variables)
  • Resolving any request and passing it on to correct request handler, if registered.
  • Resolving which template to use to view the request.
  • Rendering the template and passing it any variables registered either manually or by the request handlers.
  • Responding to the request (response can also be an error page or custom output)

In the default configuration, ODiN Page module looks from local template and script directories (see below) file names matching the request and returns a 404 not found if it doesn't find a match. It is possible to configure a standard template which will be always rendered if no other match is found, referring the choice of returning an error page to the Request Handlers.

Request handlers

Request handlers are the backbone of any ODiN application, based on the RequestHandler class. In the MVC-paradigm, they are the Views. The RequestHandler base class inherits from the TemplateEngine layer and all Request Handlers are thus capable of rendering content from templates. A RequestHandler is called by a Page object and is responsible for

  • defining which requests are possible (based on name and optionally also the HTTP method used)
  • defining the implementation of any actions that need to be taken for each defined request
  • registering the results of the handling back to the calling Page object as parameters OR calling for exceptional output or error handlers if needed.

Data sources

Instead of writing lengthy query procedures to the request handlers, it is recommended to also separate the model part of the MVC by creating data source classes. The ODiN framework offers two built-in tools for building efficient interfaces with databases. These are the DbAdapter class which implements a powerful database interface using the PDO layer of PHP, currently supporting MySQL and SQLite3 databases. The DbAdapter offers the following functionalities for the use of your custom datasource classes:

  • Opening and maintaining a connection to the database, as configured
  • Registering known tables
  • Interfaces for performing inserts and deletions by value in any registered table without writing queries.
  • Interfaces for obtaining record(s) based on a single field value from any registered table without writing queries.
  • Interfaces for executing custom simple queries (no user input) and prepared queries (with user input) implemented in a robust manner with the PDO prepared queries and bound variables

If you use the built-in dbModel module to write your database definitions and to register tables to your data source class by feeding an instance of a dbModel object, ODiN can create the data structure automatically for you if it doesn't already exist, making it ideal for easy deployment. Automatic detection of changes to the model and committing changes to the actual database is planned for a future release.

In order to get started with a simple default setup of ODiN:

  1. Make sure you have a web server able to run PHP 5.5 or newer installed on your server machine. Apache 2.4 is recommended, but any web server should do.
  2. Create a resource folder on your server somewhere outside the public root and extract the ODiN library there under a folder called 'lib' and place the odin.init.php to the root folder. In this example we call the folder 'include'
  3. Create a folder called 'templates' under your resource folder and make it writable for the web server.
  4. Create your first main html template and define sections etc. with Smarty3 syntax or use one of the sample templates such as the OD Nature template. We call it in this example 'main.tpl'.
  5. Edit lib/conf/odin_sites.ini and set your 'main.tpl' as the default base template.
  6. Edit the lib/conf/odin_global.ini configuration file to match your setup.
  7. Recommended: If you are likely to have more than one site/application on your server, create a folder for your first application both in the public root and the resource root. In this example we call this 'mywebapp'.
  8. Recommended: Edit the lib/conf/odin_sites.ini and add a section for 'mywebapp'
  9. Create a directory 'mywebapp' under lib/project for the custom classes of your app.
  10. Create a first RequestHandler class for your new webapp, in the example we call it MyHandler.php. It might look like the following.
    <?php
    namespace ODiN/project/mywebapp;
    
    use ODiN/core/RequestHandler;
    
    class MyHandler extends RequestHandler {
    
        public function main() {
        // This is the default action, place your code here.
        }
    }
    It is highly recommended to duly document your code using one of the available standards, while this example doesn't contain any docblocks for simplicity. ODiN uses the Doxygen documentation format for PHP in its standard API documentation.
  11. You need to pass the output of your RequestHandler back to the page that called it. You can do it by finishing your method code with $this->page->addParam('customContent', $result); assuming $result holds whatever custom html your request handling yielded.
  12. Create a file index.php in the public root folder of your site, which should start with require_once('odin.init.php'); For this to work you must have added the path to your 'include' folder to the include path in your php.ini (for apache2 this is likely /etc/php5/apache2/php.ini). Otherwise you will have to give the full path before odin.init.php.
  13. Start building your base app by registering your new 'MyHandler' to a request, we call it 'action' in the below example:
    $page = $odin->getPage(); // This is handy if you perform multiple actions, but not strictly required.
    $page->addHandler('action', '\ODiN\project\mywebapp\MyHandler');
    $page->render(); // This displays the page
    In the above we didn't pass render() any additional parameter, but it is likely that you want to always pass some parameters directly to your template by passing them as an array.
  14. Create template action.tpl for the 'action' request inheriting from the main template created before. Place it under include/mywebapp/html if you previously followed the optional step for multiple apps, or include/html if you didn't. In the following example we assume that your main template defines a section called 'content'. Note that we need to now define a placeholder to receive the 'customContent' parameter which our request handler feeds to the page.
    {extends $conf->base_template}
    {block "content"}
        <h1>This is custom content from my first app:</h1>
        {$customContent}
    {/block}
  15. Optional: If you want to include some javascript, but only for the "page" called 'action', put them into include/mywebapp/js/action.js (leave out '/mywebapp' if you didn't follow the optional step to create it previously). Make sure that your main template has a placeholder called {$scripts} defined in a place where it cannot be overwritten by child templates.
  16. Now all that remains is to tell your web server to route all the incoming requests to index.php. This example is for Apache2, if you have another web server, you may need to consult the server documentation. Create a file called .htaccess in the same folder than your index.php. If you have enabled Apache mod_rewrite globally on your server and you are sure are sure that you're not going to place anything else but index.php on this path, then all you need to put to .htaccess is RewriteRule ^(.*)$ index.php?request=$1 [L,QSA]. The following example, however, assumes that request rewriting is enabled on-folder basis and that apache should first check if a file or folder matching the request exists and only if not, call the index.php
    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f   ## Do not rewrite if request matches a file
        RewriteCond %{REQUEST_FILENAME} !-d   ## Do not rewrite if request matches a directory
        RewriteRule ^(.*)$ index.php?request=$1 [L,QSA] ## Redirect all rest to index.php
    </IfModule>
  17. Browse to [www.yoursiteaddress.org]/mywebapp/action/ and see your code in action (leave out '/mywebapp' if you didn't follow the optional step to this subfolder previously). If something doesn't work the first time off, consult the error log of your web server.

Please note that this is only a simple example using the default setup. Do not forget to consult the extensive API documentation provided in the package.