A Model for Relational Data

Because the topic of this book is database interaction, the rest of this chapter focuses on the Model layer of the MVC methodology. We will show you how to design abstract methods that can be used and reused in a variety of applications. We’ll also show how to weave persistence into your Model with minimal duplication of code, and—for good measure—speed up access by implementation a caching layer.

The Model contains abstractions of all concrete things used within the application. Therefore, a solid Model is an important foundation for the rest of the application.

Luckily for us, designing a Model for a database-driven application is straightforward. That is because the work of discovering the relevant abstractions in a system is done when the database scheme is created, as we described in Chapter 7.

Usually, each table in the database corresponds to one class in the Model. The fields of the tables correspond to the attributes of the class. Relationships between tables can usually be expressed in the following manner:

One to One

If two tables have a one-to-one relationship, their relationship can involve either containment or aggregation , concepts that are familiar to object-oriented programmers. If one contains the other, the contained object should be defined as an attribute of the container class. For instance, if every ISBN corresponds to one and only one title, one of them can be an attribute of the other. If the relationship is one of aggregation, the more specific class should be a subclass of the less-specific class.

One to Many

If two tables have a one-to-many relationship, the “One” class should contain an array of “Many” objects. Thus, a corporation object could contain an array of objects representing products, because most corporations sell multiple products.

Many to Many

If two tables have a many-to-many relationship, each class can contain an array of objects from the other class. That is, a Person table can exist in a many-to-many relationship with an Employer table (with a many-to-many join table in the middle), because a Person usually has had more than one Employer and each Employer has more than one Person. In the Model, the Person class contains an array of Employer objects, and the Employer class contains an array of Person objects. This type of construct can be very challenging to implement, because of the complexities of recursion. When you create a Person, you create all his Employers, each of which then contains the Person, which contains the Employers, etc. Because of this, many designers avoid many-to-many relationships when possible. If they are necessary, however, it is possible to pull off with careful implementation.

Like all classes, a Model class is comprised of attributes and methods. An attribute is simply a variable that describes something about the class. As mentioned above, the attributes of a Model class represent the fields of the underlying table and objects from related tables. But what about the methods?

In object-oriented programming, classes have two kinds of methods: instance and static . Instance methods are only called on actual objects created from the class. Because of this, they have access to the attribute data within the object. Static methods (also known as class methods) are called on the class itself. They have no knowledge of individual objects of that class. For instance, in the first section of this chapter, we saw a static DBI method called connect( ).

Persistence requires each Model class to implement three instance methods, which we’ll call update, remove, and create. These methods parallel the SQL UPDATE, DELETE, and INSERT statements that, along with SELECT, make up the vast majority of SQL statements.

update

This method issues an UPDATE statement to save the current state of the object to the database. When an attribute of a Model object is altered somewhere in the application, the application issues its update method so that the change is reflected in the database and is visible to other applications.

remove

This method issues a DELETE statement to remove the row representing the object from the database. Whenever an object is destroyed by the program, it must make sure to call this method. Destruction can occur through garbage collection or at the termination of the program, as well as through explicit requests. Unfortunately, delete is a keyword in Perl, so we can’t use it is as the method name. We’ll use remove in this chapter. Other common names include destroy, Delete, and deleteObject.

create

This method issues an INSERT statement to create a new row of data in the database. Not all objects in the Model need to be in the database, but anything that you want to have persist beyond a single run of the application needs to be saved through a create method. We chose the name create because “creating” an object is a more logical term than “inserting” an object.

While these methods are the only required ones for a Model class, a common object-oriented practice is to use accessor or getter/setter methods to retrieve and change the values of attributes. If you do this, each attribute of the object should have two instance methods: a get method that retrieves the value of the attribute and a set method that sets the attribute to a new value. They can be named anything, but a common practice is to simply prepend “get” and “set” to the name of the attribute. So an attribute called firstName would have the methods getFirstName and setFirstName.

The instance methods described above cover three of the four basic SQL commands. This leaves SELECT unimplemented. To implement it, we turn to static methods. Unlike the others, the SELECT command does not operate on existing objects. The point of a SELECT query is to retrieve data from the database. In an object-oriented application, you must create new objects to represent data selected from the database. Therefore, it is necessary to use static methods that do not rely on instance data.

Therefore, we’ll write a static method that sends SELECT queries to a database and creates new Model objects from the data returned. Unlike the other methods considered so far, there are often several methods within a Model class that need to select data. This is because there are usually different contexts in which to create new objects. We’ll implement the two methods that almost every application needs: Generic Where and Primary Key. For better reuse of code, we’ll implement Primary Key in terms of Generic Where. Only the latter needs to issue SQL.

Generic Where

This is the most versatile and common select method. An SQL WHERE clause is passed into it as a parameter (or generated from other parameters) and it sends a SELECT query to the database containing this WHERE. Out of the resulting data, an array of Model objects is created. Because of the flexibility of the WHERE clause, this method can be leveraged by more specialized select methods, such as the Primary Key select that follows.

Primary Key

Well-designed relational tables almost always have a primary key. If you know a primary key value, you can retrieve a single row of data from the table. A Model class uses the method to create a single object corresponding to a row of data. We implement this method by creating an SQL WHERE clause containing the value of the primary key and then calling the Generic Where select, previously described, to execute the query. Since we are sending in the value of the primary key, we know we will get an array containing a single row in return. This method then returns this single object.

You might consider Primary Key to be a utility or convenience function built on top of Generic Where.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset