Using Factory Classes in PHP
A factory class is a special kind of class in PHP. A factory is actually an extension of a well known object oriented design pattern called the 'Factory Method pattern' (http://en.wikipedia.org/wiki/Factory_method_pattern). At its simplest a factory is merely a class that creates other classes. At first this concept seems pretty abstract and useless, but a closer examination of a specific circumstance shows the value of a factory class.
Often times in object oriented programming you quickly identify a specific object, such as a user. This object is singular, and has attributes and methods specific to a single instance. For instance, the user object may have an id property, a name property, and perhaps an update method used to update that specific user (utilizing that instances id). This is a perfectly sound object model. However, where you run into problems is in areas such as an administrative section of a site where you might want to provide a complete list of all the users. You could perhaps create a method inside the user class called get_all() that returned a listing of all the users, but this breaks out of a simple object model. Your object is suddenly performing operations that are beyond its 'singular' scope.
Strictly speaking, collecting a list of users is outside of the user object's realm of responsibility. What is needed is a collection object, an object that can extend the user object, since it will share a lot of similarities, but that can see beyond the scope of a single user. The problem with this model is then you end up writing a collection object for every single object you have in your application. If you have a content object, you're writing a content_collection object as well.
You can quickly begin to notice that all of these 'collections' have something in common - they are merely amassing groups of other objects. Your student collection is a grouping of student objects that you can iterate through, examine, search, and list out.
This is where a factory comes in very handy. By creating a factory we can create a mechanism to create new classes on the fly. Using the dynamic features of PHP we can even abstract to the point where all we need to provide the factory is a name, and it can track down the PHP files it needs to include, the database tables it will require, and the object names it will need to create. Utilizing this level of abstraction requires a bit of discipline in your naming convention, but yields pretty spectacular results.
Consider the following factory class:
1.require_once('../lib/util/db.class.php'); 2.Class Collection extends DB { 3. var $members; 4. function __construct($collection_name) { 5. parent::__construct(); 6. $id = $collection_name . '_id'; 7. $collection_sql = 'select ' . $id . ' from ' . $collection_name; 8. require_once ('lib/obj/' . $collection_name . '.class.php'); 9. $collection = $this->get_all($collection_sql); 10. foreach ($collection as $item) { 11. $this->members[] = new $collection_name($item[$id]); 12. } 13. } 14.}
By looking at this class we can quickly examine a simple factory in action. This factory extends a database connection class. This is useful for connecting the factory to a back end data layer. Line 1 simply includes the required database class.
Line 3 lists a single class variable. This variable actually turns into an array of object that comprise the collection itself (in our user example we'll want this $members variable to become an array of user objects).
Line 4 sets up the construct function in PHP 5 notation, using the reserved __construct(). To create this collection class you'll need to pass in a name.
Line 5 merely instantiates the database class to serve as a back end (using PHP 5's parent notation). Line 6 sets up a variable called $id that actually identifies the primary key for the database table. If we pass 'user' in as the collection_name then user_id becomes the primary key.
Line 7 lists the SQL for querying the database to find all the primary keys for the specified collection. This methodology assumes that when you create new objects you're using a database primary key. For instance, when creating a new user object you must specify the id for that user in order to look up the relevant details concerning the user.
Line 8 actually dynamically includes the PHP file required to build the new classes. For our example this line would look for the file 'lib/obj/user.class.php'. You can quickly see how a strict naming convention is required for this factory to work.
Line 9 simply executes the SQL set up previously and creates an array (using the get_all() function in the parent class DB which merely collects an associative array and returns it) of id's. These id's are then parsed out with the foreach loop on line 10.
Line 11 is the guts of the factory. This line dynamically creates a new object using the collection name passed into the class utilizing the id that is extracted out of the SQL results. In our user example if we queried the database for all the user_id's and got a list of numbers one to five this loop would create a new user object with each of the primary keys and then add that object to the $members class variable.
The ultimate goal is to instantiate the factory and then loop over the factory results. For instance, we could perhaps create our users list with the code:
$user_list = new Collection('user'); foreach ($user_list->members as $listing) { echo "Hello $listing->name"; }
This code assumes that each user object has a $name property, but you get the idea. You can observe how using a rather abstract factory class we can create collections out of all of our objects without having to go through the trouble of either coding specific queries to collect membership lists or coding collection classes specific to each object.