Recently published

Practice notes

Home arrow Blog arrow Peer classes in Symfony and Propel

Peer classes in Symfony and Propel

There are a number (perhaps too many) of good PHP frameworks. Symfony is one of them. I like to study their features to see what I can adopt in my own work. Partly because the best ideas are worth copying and partly because it is a pity to create conflicting standards where there is already a good one available.

The Aliro CMS is, like all substantial CMS, a framework in its own right. As its original author, I'm aware that it can be further developed, and one important area is to improve database abstraction. There is also interesting work on this topic in the Drupal project and doubtless many other places too. But the notion of "peer classes" troubled me, and in the PHP world it seems to be largely confined to Symfony and the database object mapping it uses, Propel.

My immediate concern is that peer classes seem to be the very same thing as helper classes, whose drawbacks I wrote about earlier. Let's outline the context for peer classes. The general idea is to have a class that corresponds (in a way yet to be defined in detail) with a database table, so that the objects of that class are equivalent to rows in the table. Classes of this kind derive a lot of their behaviour by subclassing a standard class provided by the framework (Symfony/Propel). In addition, each database table is also given a "peer class" consisting of static methods.

The methods of the peer class do things like providing metadata about the table and creating sets of objects that are instances of the ordinary class to which the peer class is related. Given all the drawbacks of classes that consist entirely of static methods, it seems worth reviewing how the peer classes can be fitted into an object model.

One relevant question is to ask which of the methods could equally well be made static methods of the main class instead of being in the peer class at all. If PHP treated classes as first class objects, then we would regard "static" classes simply as being class methods. It seems that methods to create sets of objects could, logically, be class methods. They may also count as helper methods, which are, unlike helper classes, a perfectly legitimate element in an OO system. There is a major drawback to generalizing this scheme too much, and that is the lack of inheritance of static methods. Any static methods have to exist in the class for which they are invoked, so we cannot build them in a standard class and then inherit.

Other methods are to do with metadata. Where the results are simple values, it might be acceptable for them to be static methods of the main class. It could be regarded as reasonable for the class to know what fields are in the relevant table, and what are their properties. But it doesn't make good sense to include methods for manipulating metadata as static methods either in the main class or the peer class. The simple fact that such methods could easily be generalized means that inheritance is highly desirable, and hence static methods are unsuitable.

This brings us to what ought to be a critical question in an OO system, which is to ask exactly what is being modelled by our classes. Digressing for a moment, every member (so far as I know) of the Mambo family has a standard class that can be subclassed to create objects, each of which corresponds to a row in a database table. The standard class was originally named mosDBTable, giving the impression that the class is a model of a database table.

This seems to me a mistake. The objects of these classes, and of the database table classes in Propel and Symfony, model the rows of a table. Collectively, the class models the contents of a table. It does not, however, follow that this exhausts the properties and behaviours of a table. An actual database table can be operated on in ways that go beyond what can be done by SQL operations on the set of rows. Significantly, the metadata can be altered to add or subtract fields, or change their properties. It is not a good idea to build such operations as PHP class methods, since these would be static and therefore not inheritable. From this, it follows that given the object resources in PHP, it does not make sense for the class of table rows to be thought of as a complete model of a database table.

If we need a model of the table, seen as a whole and with its metadata handling behaviours, then we need another genuine (not full of static methods) class, in addition to the class whose objects correspond to rows. An instance of the new class would model one database table, not one table row.

There is not actually such a class in Aliro (or any Mambo derived CMS that I know of), leading to another question. Do we need a class to model database tables?

At the moment, my inclination is to answer "no". The reason for this is that I cannot see any interesting behaviours that need to belong to an object that models a table. Certainly there are properties of a table, but that in itself does not justify anything more than a data structure; it is not enough to justify a class. And, so far at least, it seems that the behaviours we need to manipulate tables can quite naturally fall within the scope of the object that models a database - in the sense of a specific collection of tables, not in the sense of a database system that is capable of managing multiple databases.

The database object can (and does in Aliro) hold data structures (cached) that detail the metadata relating to the tables in the database. The methods of the database object are able to operate on that information, and change the database itself where appropriate.

Also, given that a pragmatic approach suggests that when we want to create sets of objects it is usually as a result of a single SQL operation (albeit possibly a complex one), then it also makes sense for methods to do exactly that should count as a behaviour of the database object.

I'm very interested to hear other views, either supporting or opposing what has been said here. And where I've made mistakes, corrections are welcome!

#128005 • 08/25/2008 12:33pm by Martin Brampton • Vote: Up votes (749) Down votes (509)

blog comments powered by Disqus