Skip to main content

Drupal 7 Design Patterns

Drupal PHP

** This is an old write up I did several years ago and posted to an internal blog system we had when I worked at VML. I am reposting it. One day I will create a Drupal 8 version! **

I decided to do research on how Drupal 7 implements popular Design patterns and was pleasantly surprised. In this post I will go over those I found.

Observer Pattern & Visitor Pattern

http://www.oodesign.com/observer-pattern.html
http://www.oodesign.com/visitor-pattern.html

Before understanding the Observer and Visitor patterns in Drupal you must have a understanding of the hook system. Drupal uses a hook system where developers can register and alter data in Drupal. This allows for creating and altering functionality without altering core and contributed code.

The observer pattern has an object (subject) that maintains a list of other objects (observers) and notifies them whenever changes occur. An example of an Observer pattern in Drupal is implementing a hook when a user logs in using hook_user_login(). The example below registers our observer with Drupal and receives a notification after a user logs in.

/**
 * Implements hook_user_login().
 *
 * @param $edit
 * @param $account
 */
function MYMODULE_user_login(&$edit, $account)
{
  if(in_array('administrator', $account->roles)){
   // Do some sweet stuff because the user is an administrator
  }	  	
}

The Visitor Pattern is similar to the Observer pattern but is used for extending or modifying the behavior of the subject rather than responding to events. In Drupal there are several hooks that implement this pattern such as hook_user_presave() and hook_node_presave(). The example below uses hook_node_presave() which can modify a node object before inserting into the database.

/**
 * Implements hook_node_presave().
 * 
 * @param $node
 */
function MYMODULE_node_presave($node) {
  if ($node->type == 'article') {
    // Do some mind blowing code now that we know its an article node.
  }
}

Decorator Pattern

http://www.oodesign.com/decorator-pattern.html

The Decorator Pattern allows for extended functionality to an individual object. The Drupal Entity contrib module shows use of this pattern with the EntityMetadataWrapper that wraps an entity object (node, user, taxonomy). The example shows wrapping a node object in a EntityMetadataWrapper and its functionality.

function MYMODULE_wrapper_example($node)
{
  $node_wrapper = entity_metadata_wrapper('node', $node);

  // Example changing a value using our Decorator Pattern.
  $node_wrapper->field_my_field->set(1);

  // Example using plain old node.
  $node->field_my_field[LANGUAGE_NONE][0]['value'] = 1;
}

The above seems like a small example but the functionality of the wrapper allows for managing entities of different types in a unified way.

Dependency Injection

Dependency Injection is a fancy name for giving an object(or function) its instance variables. The following example shows an example of not using Dependency Injection in Drupal.

/**
 * Non dependency injection example
 * 
 * @return mixed
 */
function MYMODULE_injection_example()
{
  $node = node_load(123);

  return $node->title;
}

This does not work well with testing. Instead we can use Drupal’s hook_menu to pass in our Dependency Injection to create a custom Drupal page.

/**
 * Implements hook_menu().
 * 
 * @return mixed
 */
function MYMODULE_hook_menu()
{
  $items['user/%node/profile'] = array(
    'title' => 'Profile',
    'page callback' => 'MYMODULE_page_callback',
    'page arguments' => array(1),
    'access arguments' => array('access content'),
    'type' => MENU_LOCAL_TASK,
  );
  
  return $items;
}

/**
 * My Dependency Injection Callback
 * 
 * @param $node
 * @return mixed
 */
function MYMODULE_page_callback($node)
{
  return $node->title;
}

Chain of Responsibility

http://www.oodesign.com/chain-of-responsibility-pattern.html

The Chain of Responsibility Pattern allows for delegation of handling requests to a set of candidates. An example in Drupal is using the core theme system in which the theme layer attempts to find a template file based on this pattern. Consider the following chain of responsibility:

node--nodeid.tpl.php
node--type.tpl.php
node.tpl.php

The following allows for using specific node templates and if they exist then they are used, if not it is passed down to the next template until finding the appropriate one being used.

Adapter Pattern

http://www.oodesign.com/adapter-pattern.html

The Adapter Pattern allows for interfacing between two incompatable things. Drupal has some built in Adapter patterns for interfacing with MYSQL and extending Drupal's functionality into using other databases such as Microsoft SQL Server, SQL Lite, and Mongo DB.

Many of these do not use patterns in the traditional Object Oriented sense but alike in theory.