|
DB::Objects - Perl extension to ease creation of database-bound objects |
DB::Objects - Perl extension to ease creation of database-bound objects
This module is intended to provide an object-oriented framework for accessing data sources. The source of the data is completely abstract, allowing for complete flexibility for the data back-end. This module is NOT intended to provide a persistance layer - please use another module (like TANGRAMS) if you require object persistance.
I'm really not sure how to go about documenting this library, so let me start by explaining the history of why it was written
I developed this module when I began to notice that most of my web-applications followed a very similar format - there was a data back end and web methods which could interoperate with them. When I started to need helper applications to work with the web-apps, I started porting all of my applications to use 2 layers. The lower layer was an object framework which contained the Perl code needed to work with the database. This way, I could be sure that all of the helper applications, and of course the web application, all used the same access methods to get to the database to eleminate the possibility that something was getting f#$%ed up in the database by a faulty query somewhere in the big mess of code. (The upper layer was the ``business logic'' layer, which was the web or helper application.)
Then, I noticed that all of these database access objects were very similar: they all had access methods for each member of the class, which represented a single field in the database and had select/insert/update/delete routines.
I'd also developed a ``dynamic object'' at this point, where I'd have a huge variable-length field in the database which conatained many fields. This way I could change the object without worrying about compatibility in the back end database if I added/changed/removed fields. (We'll get back to this later.)
Beyond that, there were different ways of embedding objects (for example, a person object might have a phone number object embedded in it as part of an address-book application). (We'll get back to this later, too).
So there were different ways of logically grouping different sets of data, but the objects all shared a unified way of accessing the data. Thus was DB::Objects born - it provided a framework which would reallly guarantee that the objects would really function in a logically similar way - similar to the way that most GUI applications work in logically similar ways (they all have that File menu with Open , Save, Exit... The Help menu with Help topics, an optional upgrade, etc). So I guess you could call this library an API for developing database bound objects.
For more information, see http://www.beamartyr.net/YAPC/
The most basic type of object that can be used with this library simply get tied directly to fields in the database.
new() method should be done here. DO NOT DECLARE YOUR OWN new()
FUNCTION! This function gets a bless()ed $self passed as the first parameter,
followed by the argument list to the new() call. The constructor is expected
to call the internal _blank() function described below, and should call _primary(),
if applicable.
In addition to being called by the constructor, this function is also called every time an empty (blank) instance of the object is needed.
A sample structure for the blank() method is included for reference:
sub blank { my $self=shift; $self->_register; $self->_blank(``FOO'', ``BAR'', ... , ``LAST''); $self->_primary(1); # Optional - Marks as containing primary key }
refresh() and should contain code to sync the data
structure with the back end database. Note that the $package variable is optional,
but should be checked for (META: Remove code that makes this necessary). The
subroutine should either refresh the data from the database and call _validate() or,
if no suitable data is found, should call blank() [NOT _blank()]. In either case,
it should return $self. A sample structure for the _refresh() method is included here
for reference:
sub _refresh {
my $self=shift; # Shift $self
my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || ref($self); # Detect $package
my $sth=$dbh->prepare_cached('SELECT FIRSTNAME, LASTNAME FROM PEOPLE WHERE (ID=?)'); # Set SQL
$sth->execute(@_) or return $self->blank; # Run SQL or return blank() object
if ($sth->rows!=1) { # A good SQL statement should always return EXACTLY one row
$self->blank; # Bad SQL - return blank()
} else { # Good SQL
my $res=$sth->fetchrow_hashref; # Fetch results
$self->{FIRSTNAME}=$res->{FIRSTNAME}; # Each element from the result set
$self->{LASTNAME}=$res->{LASTNAME}; # gets passed to exactly one hash element
$self->_validate; # And call _validate() to mark it as clean and in-sync with the DB
}
$sth->finish; # Finished with SQL
return $self; # ...and return $self
}
Beyond storing simple values, you can also magically construct objects using the primary key of the target objects. Using the above example, we spoke of a phone book catalog, which has a person object and a phone number object. Would it be nice, if instead of writing:
my $phone_number=Phone_Number->new($person->phone_number); print ``Number is ''.$phone_number->thenumber;
... you could simply write ...
print ``Number is ''.$person->phone_number->thenumber;
The advanced objects API lets you do just that, with almost no extra work on your behalf.
blank() constructor. It is used to mark access
method $var as an embedded object of type $package. If $package is not passed
as an argument, it will default to the current package (eg, Your::Object). To
use the feature, simply store the data you want passed to the object's constructor
the same way you'd store any other data for a normal access method, and when
you _validate the object, the data will be appropriately stored. The object will be
constructed on the first call to the access method.
blank() constructor. It is used to mark access method
$var as an embedded array of objects of type $package. If $package is not passed
as an argument, it will default to the current package (eg, Your::Object). The
functionality is similar to that of _object except that we are dealing with arrays.
As such, care must be taken to properly initialize the array. To use it, store an
array (not a reference) in the _refresh function. When _validate is called on the
object, the array will be stored and objects will be called on subsequent access
via the method call $var. The method call will return the number of objects in
the array if called in a scalar context, and the array of objects if called in a
list context. The objects will be constructed upon access.
Note that this functionality is still under construction.
_blank(@vars)blank() constructor. The arguments should be all of the
access methods provided by this class. It should *not* include inherited access
methods, as they will automatically be discovered by AUTOLOAD. This will register
all of the access methods in the registry under the module's namespace, so that
AUTOLOAD can auto-load the module to refresh or update the database for it.
_validate()
Copyright (c) 2003 Issac Goldstand <margol@beamartyr.net> - All rights reserved.
This library is free software. It can be redistributed and/or modified under the same terms as Perl itself.
|
DB::Objects - Perl extension to ease creation of database-bound objects |