Pat's Space http://psparrow.posterous.com Drupal, Lithium, CakePHP, and random snippets of whatever I happen to be working on... posterous.com Fri, 20 Jan 2012 09:20:00 -0800 Using Lithium in Drupal 7 http://psparrow.posterous.com/using-lithium-in-drupal-7 http://psparrow.posterous.com/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);          
}

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/994281/161497_1118241228_8253400_n.jpg http://posterous.com/users/he66dmlBpV66m Patrick Sparrow pjskeptic Patrick Sparrow
Sat, 03 Sep 2011 08:34:00 -0700 Using Traits in PHP 5.4 http://psparrow.posterous.com/using-traits-in-php-54 http://psparrow.posterous.com/using-traits-in-php-54

One interesting new feature of PHP 5.4 is traits.  Traits add horizontal inheritance to the single inheritance language.  Simas Toleikis does a good job of explaining what traits are and how they work in one of his blog posts.  Although traits may be a controversial addition that walks the line of multiple inheritance, I wanted to find a good use for them as I'm sure new frameworks like Lithium will once they provide support for PHP 5.4.

One of my goals for my personal PHP library is to have a good core Object class that can be extended by every other class within my applications.  This class needs to be flexible in order to support the many design patterns that my applications may require.  Here's a simple example of how I started using traits...

First, I created an Object class with a protected constructor.  So, instead of instantiating the class using $obj = new Object(), you do $obj = Object::getInstance();

class Object {
  
  protected function __construct() {
    
  }

  public static function getInstance() {
    $className = get_called_class();
    return new $className();
  }

}

Adding the following two traits will allow subclasses of Object to act as singletons or force static access without writing tons of code:

trait Singleton {

  protected static $_instance;

  public static function getInstance() {
    if(!isset(static::$_instance)) {
      $className = get_called_class();
      static::$_instance = new $className();
    }
    return static::$_instance;
 }

}

trait StaticAccess {
   public static function getInstance() {
      throw new Exception("Instances of this class are not allowed!");
   }
}

Now, subclasses of the Object class can employ the singleton pattern or enforce static-only access with a single line of code in each subclass:

class MySingleton extends Object {
  use Singleton;
}
$x = MySingleton::getInstance();
$x->name = "Pat";

$y = MySingleton::getInstance();
assert("$x->name == $y->name"); // passes

class MyStatic extends Object {
  use StaticAccess;
}

$obj = MyStatic::getInstance(); // throws Exception

If you get a segmentation fault when testing this, you might need to download the latest build of PHP 5.4. You can find it here.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/994281/161497_1118241228_8253400_n.jpg http://posterous.com/users/he66dmlBpV66m Patrick Sparrow pjskeptic Patrick Sparrow
Wed, 03 Aug 2011 08:01:00 -0700 Make your PHP functions more powerful http://psparrow.posterous.com/making-your-php-functions-more-useful http://psparrow.posterous.com/making-your-php-functions-more-useful

Here's a simple trick I've been using lately when working on Drupal functions that I may need to run on multiple nodes.  Instead of writing functions that operate on a single value, allow an array to be passed instead.  You'll find the function more useful in the future.

Take this function as an example.  It returns an array of a node's taxonomy terms from the given vocabulary.

function get_terms($node, $vid) {        
  $tids = array();
  foreach($node->taxonomy as $tid => $term) { 
        if($term->vid == $vid) {
          $tids[$tid] = $term;
        }
  }
  return $tids;
}

Sure, that's a nice function. However, it can definitely be more powerful with a few extra lines of code.  By converting both arguments to arrays and wrapping the original code in a foreach loop, the function now returns the union of all terms belonging to an array of nodes across multiple vocabularies.  Also, any existing calls to the original function will still work fine.

function get_terms($nodes, $vids) {
  if(!is_array($nodes)) $nodes = array($nodes);
  if(!is_array($vids)) $vids = array($vids);
        
  $tids = array();
  foreach($nodes as $node) {
    foreach($node->taxonomy as $tid => $term) { 
        if(in_array($term->vid, $vids)) {
          $tids[$tid] = $term;
        }
    }
  }
  return $tids;
}

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/994281/161497_1118241228_8253400_n.jpg http://posterous.com/users/he66dmlBpV66m Patrick Sparrow pjskeptic Patrick Sparrow
Tue, 07 Jun 2011 10:11:00 -0700 A Simple HTML5 Fix for IE ··· Nico Hagenburger http://psparrow.posterous.com/a-simple-html5-fix-for-ie-nico-hagenburger http://psparrow.posterous.com/a-simple-html5-fix-for-ie-nico-hagenburger
Check out this website I found at hagenburger.net

Great little javascript snippet to get HTML5 elements to correctly display in IE.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/994281/161497_1118241228_8253400_n.jpg http://posterous.com/users/he66dmlBpV66m Patrick Sparrow pjskeptic Patrick Sparrow
Wed, 25 May 2011 19:21:00 -0700 Nodes and Terms in Drupal 6 http://psparrow.posterous.com/nodes-and-terms-in-drupal-6 http://psparrow.posterous.com/nodes-and-terms-in-drupal-6

I often find myself writing custom chunks of code in Drupal to build taxonomy term arguments consisting of terms shared by the current node.  This is useful in generating blocks of content based on shared tags.  I finally decided to expand that code into a pretty useful function.  The function accepts node(s), term id(s), and some configuration options and returns either an array of term objects or a concatenated string of term ids.  I think it's pretty flexible for such a short amount of code, comments aside.  Maybe someone else will find it useful!

/** * Gets terms for a node filtered by certain vocabularies. * * Example(s): * * Get an array of terms from node 1 from vocabularies 2 and 3: * mymodule_terms(1,array(2,3)); * * Get terms common to nodes 1, 2, and 3 from vocabulary 7 * in a string separated by a '+': * mymodule_terms(array(1,2,3), 7, array('intersection'=>true, 'implode'=>'+')); * * @param mixed $node An array of nids or node objects or a single nid or node objects * @param mixed $vid An array of vids or single vid by which to filter, false to ignore * @param mixed $config Additional configuration parameters including implode and intersection * @return mixed A hash of $tid => $term pairs or a string of tids separated by the contents of $config[implode] */ function mymodule_terms($node, $vid = false, $config = array()) {         $defaults = array('implode' => false, 'intersection' => false);     $config += $defaults;         $tids = array();     $count = array();         /**     * Convert single nodes or vids into arrays     */     if(!is_array($node)) $node = array($node);     if(!is_array($vid) && $vid != false) $vid = array($vid);         foreach($node as $n) {         /**         * Load the node if only a nid is provided         */         if(!is_object($n)) $n = node_load($n);                 /**         * Iterate over all terms, adding any that match the specified vids         * to the result set         */         foreach($n->taxonomy as $tid => $term) {             if(!$vid || in_array($term->vid, $vid)) {                 $tids[$tid] = $term;                 $count[$tid] = (isset($count[$tid])) ? $count[$tid] + 1 : 1;             }         }     }         /**     * If we only want tids that all passed nodes share, strip out     * any that aren't present for each node     */     if(count($node) > 1 && $config['intersection']) {         foreach($tids as $tid=>$term) {             if($count[$tid] != count($node)) unset($tids[$tid]);         }     }         /**     * Return either the array or a string of tids.     */     return ($config['implode'] !== false) ? implode($config['implode'], array_keys($tids)) : $tids; }

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/994281/161497_1118241228_8253400_n.jpg http://posterous.com/users/he66dmlBpV66m Patrick Sparrow pjskeptic Patrick Sparrow
Wed, 25 May 2011 18:23:00 -0700 Drupal-style Path Aliases in Lithium http://psparrow.posterous.com/drupal-style-path-aliases-in-lithium http://psparrow.posterous.com/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); }); ?>

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/994281/161497_1118241228_8253400_n.jpg http://posterous.com/users/he66dmlBpV66m Patrick Sparrow pjskeptic Patrick Sparrow