Perl Beginners - Addressbook Tutorial Step 6 - Writing utility and SQL classes, and using Class::MethodMaker |
In Step 5, we created the start() method for the unrestricted Perl handler. This method is called by the cgi and will display a blank search form. It needs to look in the database to fill the pull-downs for area codes and states. The start method looks to our utility class, Record.pm, for the methods to provide the data to fill the <select> lists in the template file. |
The SQL class |
Before we get into the utility class, let's write the SQL class, SQL.pm: |
|
This doesn't seem too complicated. SQL.pm is simply a container to hold all our SQL queries. Doing this centralizes the SQL to ease future conversion to another database. Additionally, centralizing the SQL means we'll never have to hunt through our code to find a particular query. Notice we're using aliases in the query. I use them to demonstrate one of the most important differences between PostgreSQL and MySQL: aliases do not always seem as they appear to be! Look at this example of a SELECT statement in PostgreSQL: |
|
Notice that, even though the aliases were capitalized, the resulting column headers are lower case. The same query in MySQL yields: |
|
The column headers are capitalized, just as they were declared in the aliases. What does this mean? If we were to use the fetchrow_hashref() method to retrieve the data, and we wanted to print the data directly from our Perl script, we would have to be very careful how we dereferenced the hash: |
|
This will work for MySQL, but not for PostgreSQL. To get it to work with PostgreSQL, we'd have to dereference the hash using the lower case field names: |
|
However, HTML::Template helps us overcome this problem. No matter which case is used, the values are returned as we would expect. We can reference the values in the template file using either upper or lower case: |
|
These template tags will work with either PostgreSQL or MySQL. |
Each method in SQL.pm uses a here document to return the text of each particular query. Using here documents is beneficial for two reasons: when developing, we're able to cut and paste our queries directly into a database command line to test the syntax, and we're free to add punctuation to the queries without having to worry about escaping out certain characters. |
Using Class::MethodMaker |
In the utility class, we'll be using a module called Class::MethodMaker. To demonstrate how Class::MethodMaker works, consider the following code: |
|
Although it may seem complicated, all this code does is provide a class method called tmpldir() that's available to $self after calling the new() constructor. All the tmpldir() method does is return the scalar value, /some/local/directory/. Seems like a lot of work just to provide a scalar to the class, doesn't it? What's more, every time you want to add another class method you'd have to add the corresponding $self->something() line to init(), and then you'd have add something to the qw{} list. Additionally, all the class methods are automatically created, whether you need to use them or not. Too much work and too much overhead. |
What Class::MethodMaker does is automagically create the internal class methods we'll need. Also, we can provide a way to create them only when they're needed. Here's how I would rewrite the above code using Class::MethodMaker: |
|
Class::MethodMaker creates the new() and tmpldir() methods for us, and new() will call init(), so we just need to provide the implementation of init(). |
There are lots of other cool methods provided by Class::MethodMaker. I highly suggest reading more about this class. You can do that by typing perldoc Class::MethodMaker. |
The utility class |
So, let's create our utility class. Remember, the utility class provides methods to the handler for working with the data. Its methods are called by the handler, but not by the cgi. The only thing we'll need the constructor to do is to provide a database handle to the rest of the class. Here's a file, Record.pm to get us started: |
|
After declaring which other modules we'll need and setting the VERSION variable. we tell Class::MethodMaker that we want to create an internal method called dbh. This is the reference to the database handle that's passed in from the constructor in our handler: |
|
Of course, we want the database handle to be defined, so we'll call croak if it isn't. Our handler called two methods in the utility class, select_distinct_area_codes() and select_distinct_states(). So, let's add these methods to the class. You can add them anywhere after the use declarations and before the __END__ token. |
|
Let's analyze the important parts of these methods. |
|
Here we prepare and execute a statement handle, $sth. Notice we're calling one of the query methods we created in SQL.pm. We'll store the query results in an array called @states. |
|
We'll use the fetchrow_hashref() method to retrieve each row of data. This method is considered the slowest of the DBI methods for retrieving data, but we need the data to be stored in a hash to use it in the template file (that's one of the drawbacks of using HTML::Template). |
|
We don't need to call the finish() method, because whenever a statement handle goes out of scope, finish() is called automatically by DBI. I like to call it anyway (call me crazy). Finally, since the template file will be looking for a reference to an array that contains the data hash references, we return a reference to the area_codes array we built when retrieving the data using the fetchrow_hashref() method. |
The select_distinct_states() method is similar to the select_distinct_area_codes() method, so I'll skip its explanation. |
After adding these methods to our utility class, we're finally able to call the cgi. Point your browser to /cgi-bin/handler.cgi, and you should see a form that looks similar to this: |
![]() |
Note that I pulled down the area_code <select> box to demonstrate that it's filled with one area code (I added one sample record). Add a test record or two and reload the cgi. The pull-downs should be filled with the area code and state values you added in your test records. |
Coming next - A summary of what we've done so far. |
Copyright © 2001 by Peace Computer Systems |