Filed under: lithium

Using Lithium in Drupal 7

Let me start off by saying that the Lithium framework is awesome.  It allows the programmer to write very elegant code, something that cannot be said about many of the other PHP frameworks out there.  Also, it's very loosely-coupled, and many of it's components can be used separately and integrated with other applications.

Lithium prides iteself upon being easy to integrate into other systems.  Let's put that to the test in Drupal 7.  I am planning on a "full blown" Lithium module for Drupal that will allow for other modules to easily hook into the bootstrap process.  The code below is meant to show how easy it is to use Lithium within a Drupal 7 application.

First, download the lithium core library and place it in /sites/all/libraries.

Then, create a custom module and include the following lines at the top of it:

define('LITHIUM_LIBRARY_PATH', './sites/all/libraries');
require_once(LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php');

Next, implement hook_init() and bootstrap the Lithium core library and your custom module library:

function mymodule_init() {
   \lithium\core\Libraries::add('lithium');
   \lithium\core\Libraries::add('mymodule', array('path' => __DIR__));
   // Now, you can add a database connection or any other bootstrap code. 
}

That's it!  You can now use classes from the Lithium core library and your own custom library in your custom modules and themes.  Here's a simple example:

function mymodule_node_view($node, $view_mode, $langcode) {
   echo \lithium\util\String::insert('Now viewing node {:nid}', array('nid' => $node->nid)); 
   // If you setup a connection and created a model at /sites/all/modules/mymodule/models/Foo.php, you can do this:
   $foo = \mymodule\models\Foo::findById(1);          
}

Drupal-style Path Aliases in Lithium

In Drupal, node urls are formatted as node/nid where nid is the unique numeric id of the node. The path module provides the very useful ability of creating custom aliases for system paths (e.g. my-custom-page maps to node/7).  Other modules extend this this functionality to automatically generate clean aliases based on node content.

In one of my late night Lithium R&D sessions, I decided to see how similar functionality could be developed using Lithium, one of the first frameworks to take advantage of PHP 5.3.  I decided to package the functionality into a library so it can easily be added to my other Lithium apps.  However, you can place this code directly within your app.

The first step is to create a paths collection in MongoDB.  If you're using an SQL database, create a paths table with system and alias varchar fields.

The Path model provides a method to set or delete an alias.  It requires the system path (i.e. controller/action/args) and has optional parameters for alias and delete.  If the alias is empty or the delete flag is present, any existing alias for the system path is deleted.  If the alias is not empty, a new Path containing the system path and alias is created.

The Path model also provides a method to resolve an alias to its corresponding system path.  If a path exists for the given alias, it's corresponding system path is returned.  If no path is found, the alias is returned.

li3_paths/models/Path.php:

<?php

namespace li3_paths\models;

/**
 * `Path` is a model that allows system paths to be aliased and accessed by
 * "clean" urls.  A path consists of an alias and its corresponding system path.
 */
class Path extends \lithium\data\Model {
    
    /**
     * Resolves a url to it's corresponding system path
     *
     * @param string $alias The path to be resolved (e.g. my-custom-url)
     * @return string The system path if one was found, the original path if not
     */
    public static function resolve($alias) {
        return ($path = static::findByAlias($alias)) ? $path->system : $alias;
    }
    
    /**
     * Removes and/or creates a new path alias
     *
     * @param string $system The system path to be (un)aliased (e.g. posts/view/1)
     * @param string $alias The clean url to be used to access the system url
     * @param boolean $delete True to remove any existing aliases to the given system path
     * @return boolean The result of the operation
     */
    public static function alias($system, $alias = '', $delete = true) {
        
        $system = trim($system);
        $alias = trim($alias);
        
        if($delete || empty($alias)) {
            $path = static::findBySystem($system);
            if($path) {
                static::delete($path);
            }
        }

        if(!empty($alias)) {
            $path = static::create(compact('alias', 'system'));
            return static::save($path);
        } else {
            return true;
        }
    }
}

?>

The model is complete.  We now have enough functionality to get and set path aliases and determine system paths from aliases.

Now, it's time for some Lithium magic!  Without hacking the core, we can filter the Dispatcher::run() method and to resolve the custom alias before the url is processed...

In li3_paths/config/bootstrap.php:

<?php


use \lithium\action\Dispatcher;
use \li3_paths\models\Path;


Dispatcher::applyFilter('run', function($self, $params, $chain) {
    $params['request']->url = Path::resolve($params['request']->url);
    return $chain->next($self, $params, $chain);
});


?>