Author image Toby Ovod-Everett


Win32::ASP::DBRecordGroup - an abstract parent class for representing groups of database records



The main purpose of Win32::ASP::DBRecordGroupis to be subclassed. It implements a generic set of default behavior for the purpose of reading a group of records from a table, displaying that group of records in an HTML table, and allowing edits to that group if applicable. All Win32::ASP::DBRecordGroup classes rely upon a Win32::ASP::DBRecord class that implements the underlying record.

Internal Data Structure

The internal data structure of a instance of Win32::ASP::DBRecordGroup consists of the following elements, all optional:


This is a reference to an array of DBRecord objects storing data read from the database.


This is a reference to an array of DBRecord objects storing the currently modified data.

Class Methods

Class methods were used to implement access to class properties. Since Perl doesn't enforce a distinction between class and instance methods, these methods can be called on both class names and on instances of the class, which ends up being incredibly useful. I strongly recommend against ever calling these methods using subroutine notation (i.e. &_DB or &_PRIMARY_KEY). Perl methods execute in the namespace in which they were defined, which means that if you further subclass and define a new implementation of those methods, any methods you don't override that were in the parent class will call the parent class's versions of those methods. That's bad. Always call these methods with the arrow notation and you'll be safe.

Mandatory Class Methods

These class methods will be overridden in every child class.


The _DB method should return the Win32::ASP::DB (or subclass there-of) object that is used for database access. A frequent implementation looks like this:

  sub _DB {
    return $main::TheDB;

The _TYPE method should return the name of the Win32::ASP::DBRecord subclass that implements the underlying records for this DBRecordGroup object.


The _QUERY_METAS method should return a reference to a hash of subroutines that implement more complicated querying behavior. The subroutines will be passed the appropriate query specification and should return legal SQL for inclusion within a query. Of note, for performance reasons the method is usually implemented like so:

  sub _QUERY_METAS {
    return $MyStuff::MyRecordGroup::query_metas;

  $MyStuff::MyRecordGroup::query_metas = {

    Status => sub {
      my($values) = @_;
      $values or return;
      return "Status LIKE '[$values]'";


The above Status query_meta presumes a single character status field and that the passed value indicates a list of desired status values. For instance, the status values might be N for new records, P for in process records, and F for finished records. Using the above query_meta, a user could query for Status=NP, which would indicate they desired new and in process records. They could get the same results by querying for !F.

Note that there is a security hole in the above code - if a user queries for Status=N] GO do_something_ugly_here, they will effectively "jump" out of the LIKE statement and may be able to execute arbitrary SQL code. Defending against this possibility is left as an excercise for the reader.

Optional Class Methods

These class methods can be overriden in a child class.


The _MIN_COUNT method defines the minimum number of records to display when allowing the user to edit a group of records.


The _NEW_COUNT method defines the minimum number of blank records to display when allowing the user to edit a group of records.

Instance Methods


This is a basic new method. It simply creates an anonymous hash and returns a reference. The new method is not responsible for reading data or anything else. Just creating a new record group object. You will probably not need to override this method.


This is the heart of the DBRecordGroup class. The method is passed three parameters: a reference to a hash of constraints, a string specifying how to order the results, and a string specifying a list of columns to retrieve.

The hash of constraints should be indexed on the field name (or query_meta name). If the index references a query_meta, the value will be passed to the query_meta subroutine. If the index doesn't reference a query_meta, the field will be tested for equality with the specified value. The specified value will be formatted by the field's as_sql method before being included in the SQL. All of the constraints will be ANDed together to form the WHERE clause in the SQL.

The order string should be a comma separated list of field names. Bare field names will be sorted in ascending order; field names preceded by a minus sign will be sorted in descending order.

The columns string should be one of three things: empty, an asterisk, or a comma separated list of field names. If the string is absent or an asterisk, the query will retrieve all the columns If a comma separated list is specified, the query will only retrieve those columns. The advantage of this is that queries can be optimized to return only the information that will be displayed to the user. Keep in mind, however, that if the DBRecord object requires specific fields in order to make determinations about viewability or the like, those columns need to be specified in the column list. As a result, query is frequently overriden to automatically append those columns to the column list before call SUPER::query.

After setting up the SQL for the query, query calls exec_sql on the appropriate Win32::ASP::DB object (determined by calling $self->_DB). It then iterates over the result set returned, creating new DBRecord objects of the appropriate class and calling _read on them. The call to _read is wrapped in a try block - if the user doesn't have rights to view the record, _read will throw an exception. That exception will be trapped and the record won't be be appended to the array of DBRecord objects.

Another common modification to query involves adding constraints to all queries to explicitly call query_metas that are responsible for ascertaining viewability. This can greatly improve performance - if the user asks for every record in the system, the query handles the weeding out of those records that are not viewable, rather than reading the data and letting _read throw an exception. Again, this can be easily handled by overriding the method and then calling SUPER::query.

















1 POD Error

The following errors were encountered while parsing the POD:

Around line 176:

=back without =over