Creating a simple abstract model to reduce boilerplate code

February 28, 2009 – 8:05 pm Tags:

In a usual scenario involving models in PHP, you will have some repeating things: You will usually need getters and setters for the model’s properties, you will need to be able to construct the model from an SQL query or such, and you’ll also need to write SQL queries for the models.

While none of this is very complex, it’s something that you need to repeat all the time, and it’s not really something that actually does anything complex. So why not automate at least some of it?

Getters and setters

In this post we’ll look at creating a simple base model class that can be easily used to reduce the amount of boring code we need to write. The first thing is going to be getters and setters for the properties in a model.

If you use an IDE like Zend Studio or NetBeans, you will have the option of generating the code for this. The code will still exist, though, and you may have to tweak it if you change a name of a variable or such.

We can also use PHP’s magic __call method to automate this completely!

The key idea is that we shouldn’t need to write unnecessary code for this. I think the most optimal solution is using an array of keys and values: Each key is the name of the property, and each value is the default value of the corresponding property.

As such, we will need to define them in the class:

class UserModel extends AbstractModel {
  protected $_magicProperties = array(
    'id' => '',
    'firstName' => '',
    'lastName' => '',
    'password' => ''
  );
}

So there. We’re calling this array _magicProperties because we can also write some getters and setters ourselves – this array will only be a convenience for all the properties that won’t need more complex logic and can simply have very basic auto-generated setters.

Now, let’s look at the AbstractModel class:

abstract class AbstractModel {
  public function __call($method, $parameters) {
    //for this to be a setSomething or getSomething, the name has to have 
    //at least 4 chars as in, setX or getX
    if(strlen($method) < 4) 
      throw new Exception('Method does not exist');
 
    //take first 3 chars to determine if this is a get or set
    $prefix = substr($method, 0, 3);
 
    //take last chars and convert first char to lower to get required property
    $suffix = substr($method, 3);
    $suffix[0] = strtolower($suffix[0]);
 
    if($prefix == 'get') {
      if($this->_hasProperty($suffix) && count($parameters) == 0) 
        return $this->_magicProperties[$suffix];
      else
        throw new Exception('Getter does not exist');
    }
 
    if($prefix == 'set') {
      if($this->_hasProperty($suffix) && count($parameters) == 1)
        $this->_magicProperties[$suffix] = $parameters[0];
      else
        throw new Exception('Setter does not exist');
    }
  }
 
  private function _hasProperty($name) {
    return array_key_exists($name, $this->_magicProperties);
  }
}

So by just adding the __call and _hasProperty methods, we’ve just created code which will allow our models to have getters and setters for properties by just declaring a protected array _magicProperties, and by doing that we’ve greatly reduced the amount of code we need to write.

Using the class is easy, let’s see how:

$u = new UserModel();
 
//these are all provided by the __call method:
$u->setFirstName('Cole');
$u->setLastName('Train');
 
echo 'Name: ' . $u->getFirstName() . ' ' . $u->getLastName();

We could also implement one getter to the UserModel class to get the full name easily:

public function getFullName() {
  return $this->getFirstName() . ' ' . $this->getLastName();
}

We could also write a setter for the full name, but it might be a bit pointless.

Filling models from arrays

Another usual task is filling model properties from arrays. This is common for example when dealing with result rows from an SQL query.

We can add a method for this to the AbstractModel class:

public function fromArray(array $array) {
  foreach($array as $key => $value) {
    //We need to convert first char of key to upper to get the correct 
    //format required in the setter method name
    $property = $key;
    $property[0] = strtoupper($key);
 
    $mtd = 'set' . $property;
    $this->$mtd($value);
  }
}

We may also need the ability to turn an instance of the class into an array:

public function toArray() {
  return $this->_magicProperties;
}

In this case we simply need to return the _magicProperties array, as it already contains the properties of the model as a key-value array. You may also wish to override this method, in case your implemented model provides some more properties that don’t exist in the _magicProperties array.

Generating some SQL queries

By now we’ve reduced the amount of code we need to write to deal with typical model cases. There’s one more example we’ll look at: generating SQL queries for the models.

This is a very common task, and it can be automated easily, assuming your database structure is similar to the naming of your model properties.

SQL queries are code that shouldn’t necessarily live inside the model. It’s good practice to have a separate class which deals with database actions related to a specific model, but designing such is probably worth another blogpost so we’ll just look at automating the query generation part for now.

Generating SQL from the models is quite simple. We will just employ some code to convert the property names into column names, and column names back into property names. In essence, we are converting camel-case into underscore separated and converting underscore separated into camel-case. This is because database column naming usually uses underscores, unlike property naming.

The following code can be used to convert underscore separated into camel-case:

public function underscoreToCamelCase($row) {
  $convertedRow = array();
  //foo_bar -> fooBar
  foreach($row as $key => $value) {
    $parts = explode('_', $key);
    for($i = 1, $partCount = count($parts); $i < $partCount; $i++) {
      $parts[$i][0] = strtoupper($parts[$i][0]);
    }
 
    $fixedKey = implode('', $parts);
    $convertedRow[$fixedKey] = $value;
  }
 
  return $convertedRow;
}

And the following to convert camel-case into underscore separated:

public function camelCaseToUnderscore($row) {
  $convertedRow = array();
  foreach($row as $key => $value) {
    $newKey = strtolower(preg_replace('/([a-z])([A-Z])/','$1_$2',$key));
    $convertedRow[$newKey] = $value;
  }
 
  return $convertedRow;
}

So now, when you need to write an SQL query for a model, you can simply utilize these functions to save you from writing all the possible properties in the model.

You may also wish to look at Zend Framework’s Zend_Filter_Inflector classes. They are ready to use filters for converting strings to/from various formats, including the cases shown above.

Conclusion

By utilizing PHP’s “magic” features we can easily reduce the amount of code we need to write. I think this is something that sets PHP apart from some other languages without this kind of features.

What do you think of the approach shown in this post? Personally I think it’s quite useful, but it may be a bit confusing for people not familiar with the concept, as it looks like the classes have methods that actually don’t exist at all… (which is kind of true)

If you want ideas on how to use models like this with a database, have a look at my post about the Data Access Object pattern in PHP, which shows a good way to do this. You may also be interested in reading how to use models as criteria objects in database queries.

Share this:

RSS feed Subscribe to my RSS feed

About the author

Jani is a 15 year veteran of the software industry. He's currently available for consulting

  1. 18 Responses to “Creating a simple abstract model to reduce boilerplate code”

  2. The two biggest downsides are that IDE’s obviously don’t respond well to “magic methods” (not the biggest problem in the world).
    The second is more of a semantic and taste issue…I prefer my models to be clean in the sense that they take care only of the responsibility of their domain logic. Converting to an array, or back from an array seems more in the domain of the data access layer. Therefore hydrating your business objects or serializing them to put them into a database should be handled by an outside class. If that’s the case, the simple improvement here would be to have a class which acts in your data access layer to handle these things.

    By Avi on Mar 1, 2009

  3. @Avi, NetBeans as well as Eclipse PDT 2 (not sure about the latter) handle magic methods quite well if you help them a little bit. That means adding a few phpdoc comments to the respective class. The phpdoc comments are @method, @property, @property-read and @property-write. More details:

    http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.property.pkg.html
    http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.method.pkg.html

    @Jani, any reason for which you decided to implement __call instead of __get/__set. In my opinion properties yield a nicer API.

    By Ionut G. Stan on Mar 1, 2009

  4. Avi, well that depends on the more precise ways you look at models. You could think of turning the model’s values into a simpler data structure a part of the business logic, since the model itself knows best which values it contains that need to be used.

    Ionut G. Stan, no particular reason. It’s just that I’m used to having get/set methods.

    By Jani Hartikainen on Mar 1, 2009

  5. Doesnt this completey defeat the purpose of getters and setters? I though they were there for vaidation and business logic purposes, not just to dump information back…

    By DangerMouse on Mar 2, 2009

  6. Since not all props require more than simple get/set logic, this saves you some typing. If you write a custom setter for one of the magic properties, it will override the __call one and you can add your custom logic there if you like, and still benefit from the automatic getter.

    By Jani Hartikainen on Mar 2, 2009

  7. Yea, if you simply want to cut out the getter/setter logic PHP has __get/__set for this purpose.

    By Thomas on Mar 2, 2009

  8. Thank you very much of this post. I was thinking about a similar solution for one of my projects when I stumbled upon this. Very helpful.

    By Jacob Kiers on Mar 2, 2009

  9. Nice post, Jani. The only suggest I have is not to create a super model class. This typically ties a model to a particular IO method (database in this case). Maybe a series of reusable abstract classes (ado, web services, file, network, memory, etc) for IO. That way, if a model needs access to IO operations, they’re there.

    By Will Fitch on Mar 2, 2009

  10. The idea is that the model class itself does not know about specific storage related things.

    For storage, you would have a separate table gateway, data access object, or whatever you like to call them. That then handles storing instances of the model class and converting data from the storage mechanism back into instances.

    By Jani Hartikainen on Mar 3, 2009

  11. I agree with you. I like ZF’s approach. Abstract database classes available for that purpose. It isn’t a super class specifically for a model, only abstract database functionality.

    Your domain logic may require initiating a workflow via web services, for example. In this scenario, their may be no specific data elements, only a workflow generated/initiated.

    Your idea of abstracting a model even further is good, however. Not arguing against that. If your application requires specific “fields” be returned from multiple sources, an abstract class to provide that information without additional work across multiple models is nice.

    By Will Fitch on Mar 3, 2009

  12. I do agree that this approach might look useful and you save some code typing. But the downside is that magic methods are numerous times slower than regular getters and setters. So I would keep this in mind when considering using it.

    By Andy on Mar 3, 2009

  13. I’m so curious why PHP doesn’t have real properties, like C# for example, or even JavaScript.

    Maybe because Java doesn’t have either… oh well…

    By Ionut G. Stan on Mar 3, 2009

  14. @Ionut What? Do you know anything about PHP?

    PHP
    class SomeClass
    {
    public $property;
    }

    C#
    class SomeClass
    {
    public string property;
    }

    How does PHP not have “real properties”?

    By Will Fitch on Mar 3, 2009

  15. I believe you are mistaking public fields with properties ;)

    MSDN: Properties (C#)

    By Jani Hartikainen on Mar 3, 2009

  16. Overloaded properties would have been a better description, which PHP does offer some functionality, more so than Java.

    [PHP]
    __get
    __set

    C# certainly does have a more elegant solution to this than any other language, but to say PHP doesn’t offer at least some solution is wrong.

    By Will Fitch on Mar 3, 2009

  17. @Will, I didn’t say anything wrong about __get/__set. In fact I like them, but it would have been better to have both of them in the language, i.e. properties as in C# and overloaded properties (as you said).

    I’m sorry that my C skills are almost none, otherwise I’d have hacked the internals of PHP.

    By Ionut G. Stan on Mar 3, 2009

  18. I feel your pain, Ionut. Adding C# style overloaded properties would be ideal, but looking at zend_language_parser.y and implementing that would be a challenge. Even worse, you’d have to convince the internals to buy in. I’ve been trying to convince them for type-hinting method returns for two years.

    By Will Fitch on Mar 3, 2009

  19. What actually you want to do?
    PHP != (Java || C#).
    you can do more shorter if no classes :P .
    My current application dam complex but still php make it’s very simple to do.
    VB and Vb.net and C# just make you code a lot more :P

    By hafizan on Mar 5, 2009

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)