PHP: Casting Objects
Working in an MVC environment, one of the things that we do quite often is take a record from a database and send it to the view for display in some form or another. In a strict MVC paradigm the view really shouldn’t know the names of the fields in the database, yet all too often you will find that the code behind most applications is simply loading a row from the database as an object and sending that object - through the controller - to the view and echo-ing out each property of the object as necessary.
One of the major reasons for doing things this way is that too much effort is required to take one object and make it more meaningful to the view. The problem is compounded when you are taking a series of rows - or an array of objects - and sending that to the view.
Now, in practical terms, it really doesn’t matter; the views ultimately get processed by the application language before they get to the client, so you could hardly say that the controller is exposing your data structure (or any other such nonsense). In any case, this relatively small problem has been on my mind for some time and it wasn’t until recently - while working on an unrelated problem - that I found a solution.
The function that I came up with allows you to “cast” an entire object into another object structure using a given format. Take the following object for instance:
$obj = new stdClass;
$obj->name = 'Jeff';
$obj->id = 39;
A very simple object with two properties - name and ID. Now, if we wanted to make this an option in a select-list for example, ideally the view would accept three properties: The value (for the “value” attribute of the tag), the text (which would display in the select-list itself) and the title (attribute, because we like semantics right? :P). What we would really like this object to look like is this:
$obj = new stdClass;
$obj->text = 'Jeff';
$obj->title = 'Jeff';
$obj->value = 39;
In pure PHP, we would normally do this like so:
$obj = new stdClass;
$obj->name = 'Jeff';
$obj->id = 39;
$new_obj = new stdClass;
$new_obj->text = $obj->name;
$new_obj->title = $obj->name;
$new_obj->value = $obj->id;
This is a very raw and relatively unmaintainable solution, especially for objects with plenty of properties - like most database records. What we really need is a function that, give an object and some kind of map, can take the properties from the original object and create another object using the map. Here is just such a function:
/**
* Take one object and cast it to another object structure using a given format
* The '&' symbol can be used in the rules to copy a property.
*
* @access public
* @param object $obj The original object that will be cast
* @param array $rules An associative array of key/value pairs. The key is the name of the current object's property and the value is the new key for the "cast" object.
* @param mixed $blank_value The value given to a property if there is no existing property available in the original object
* @return object
* @author James Angus - ucantblamem.com
* @copyright (c) James Angus 2007. All rights reserved.
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
* @version 1.0
*/
function cast_object($obj, $rules = array(), $blank_value = 0)
{
$obj = (array) $obj;
$fin = array();
foreach($rules as $key => $value) {
if(isset($obj[$key])) {
$fin[$value] = $obj[$key];
} elseif (substr($key, 0, 1) == '&') {
$key = str_replace('&', '', $key);
if(isset($obj[$key])) {
$fin[$value] = $obj[$key];
} else {
$fin[$value] = $blank_value;
}
} else {
$fin[$value] = $blank_value;
}
}
return (object) $fin;
} // cast_object()
With this function we can now rewrite our little example like so:
$obj = new stdClass;
$obj->name = 'Jeff';
$obj->id = 39;
$new_obj = cast_object($obj, array('name' => 'text', '&name' => 'title', 'id' => 'value'));
Note that the second key in our array has an ampersand appended. Because array keys must be unique, I built in a mechanism for copying a property more than once. In the above example, the value of the “name” property will be copied to both the “text” and “title” properties of the new object. Also of note is that you can use multiple ampersands to copy a property as many times as you need.
Lastly, I have allowed you to specify a value for properties that could not be found in the original object. This means, that the object returned contains all the properties specified in the rules.