The magic column pattern

Ever since I changed jobs at the start of the year, I've been far more cavalier with trying new techniques and seeing how they pan out. I don't know where this sudden devil-may-care attitude came from, but I suspect it's lifting the burden of old systems from my shoulders and a mirror of the approach I have taken with Fusion. The old systems were so ingrained in me that they almost became a force of nature unto themselves that couldn't be changed by a mere mortal code jockey like myself. And Fusion has required me to forego good practice and just do what works, since my attempts to make Dovetail correctly from the start is responsible for that project being shelved.

With all of that in mind, I've begun to experiment with a pattern that I'm sure has been fine tuned and named by better programmers than myself, but I would like to talk about it nonetheless. I have named this pattern the magic column pattern - an extension of the classic MVC models pattern, combined with lazy load and magic get methods. The essence of the pattern is to provide a set of hooks within a model that can return data of any complexity, but through lazy load to reduce the footprint of the object except when this additional data is called.

In a classic model pattern, a model object is bound to a database table through naming conventions. An example of this is the Kohana 2.3 models that introduced me to the concept a few years ago. You create an instance of a model by specifying the name and primary key, and it connects up to the database and retrieves the information relevant to the object. In this pattern, there are two kinds of member properties for this model - columns and related.

A column is a straight one-to-one binding to a field in the database. When you load a model, the columns come with it by default since they are so lightweight and easy to pull with the initial data. When you retrieve a column, you are given a one-dimensional piece of data such as an integer, string or boolean.

A related item binds the current model to another model in a one-to-one, one-to-multi or multi-to-multi relationship. It leverages foreign keys to look up associated data and return it in a usable format. One example of a usable format is to return an implementation of Array_Iterator so the resulting data can be processed by a foreach loop.

A magic column is a lazy loaded field that is similar to a related item, but infinitely flexible. The first part of a magic column relies in the Model class that all models extend. In PHP, the magic __get method is leveraged and magic columns are added into the sequence, along with columns and relateds.

class Model {
  public function __get($name) {
    $function = 'get_' . $name;
    if(method_exists($function, $this)) {
      return $this->$function(); // process magic column
    } else if (array_key_exists($name, $this->columns)) {
      return $this->columns[$name]; // process column
    } else if (array_key_exists($name, $this->related)) {
      return $this->related($name); // process related
    } else {
      return null; // unknown field
    }
  }
}

Whenever a member method is accessed, this magic __get method attempts to load magic columns by checking to see if a member method exists that matches the name. If so, it returns the result of that function. This allows any model using the magic column method to define a function and return anything it needs to return.

class User_Model extends Model {
  public function get_full_name() {
    return $this->first_name . ' ' . $this->last_name;
  }
}

You can see in this example that the user model has a magic column called "full_name" that returns a concatenation of the user's first and last name (both of which are simple fields in the database). This can be rendered in a template in a way that is indistinguishable from a regular column.

  // echo a magic column and a column side by side
  echo $user->full_name . ' : ' . $user->email;

Best of all, it is lazy loaded, since this function is never called unless necessary. This allows you to create a relatively heavy magic column without bogging down pages that don't make use of it. Magic columns can even reference other magic columns for even more flexibility.

class User_Model extends Model {
  public function get_email_header() {
    // full_name is a magic column, email is a column
    return $this->full_name . ' <' . $this->email . '>';
  }
 
  public function get_full_name() {
    return $this->first_name . ' ' . $this->last_name;
  }
}

As an added bonus, the magic columns can easily be cached inside an object by storing the resulting data inside a member property inside the magic __get method. This reduces overhead of repeated calls tremendously.

class Model {
  public function __get($name) {
    $function = 'get_' . $name;
    if(array_key_exists($name, $this->magic_columns)) {
      return $this->magic_columns[$name]; // return cache
    } else if(method_exists($function, $this)) {
      $value =  $this->$function(); // process magic column
      $this->magic_columns[$name]; // cache value
      return $value;
    } else if (array_key_exists($name, $this->columns)) {
      return $this->columns[$name]; // process column
    } else if (array_key_exists($name, $this->related)) {
      return $this->related($name); // process related
    } else {
      return null; // unknown field
    }
  }
}

I have been using this system for a few months now, and the only drawback I have seen is that it can create excessively large models that have a lot of magic columns. But the advantages have outweighed the drawbacks. If the magic columns are defined in small, discreet chunks without violating the inappropriate intimacy bad smell that Martin Fowler spoke about, they can be enhanced and reused in many ways. A simple call to return the value of a component can easily be reused in many ways to return the value of a subassembly, assembly or even the entire cost of completing a build order.

As I stated at the beginning of this article, I'm certain that I did not pioneer this pattern. Unfortunately, I have not actually found the proper name for this pattern and so I continue to call it the magic column method for now. It has served me very well and it is probably the best thing to come out of the new approach to development I have enjoyed since the start of the year.

Comments

<p>Trackback from <a href="http://www.fatdux.com/en/Blog/2011/06/15/undercover-boss-service-design-bitch-slapping-for-clueless-ceos">http://www.fatdux.com/en/Blog/2011/06/15/undercover-boss-service-design-bitch-slapping-for-clueless-ceos</a>.</p><p>The magic column pattern | Steve Phillips</p>