Win32::ASP::DBRecord - an abstract parent class for representing database records |
Win32::ASP::DBRecord - an abstract parent class for representing database records
The main purpose of Win32::ASP::DBRecord
is to be subclassed. It implements a generic set of
default behavior for the purpose of reading a record from a table, displaying that record in HTML,
allowing edits to it, and writing that record back to the table. It relies heavily upon
Win32::ASP::Field objects, which are used to provide an object-oriented interface to the most
important class data for a Win32::ASP::DBRecord
subclass - the fields possessed by the record
represented by the class.
The internal data structure of a instance of Win32::ASP::DBRecord
consists of the following
elements, all optional:
Win32::ASP::DBRecord
object, not within orig
or edit
. See _CHILDREN
for more
information.
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.
These class methods will be overridden in every child class.
_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; }
_FRIENDLY
method should return a friendly name expressing what sorts of things these
records are. This friendly name may get used in certain error messages (in particular,
Win32::ASP::Error::Field::group_wrapper
). For instance, the _FRIENDLY
method for line items
on an invoice might return ``Line Item''. An error message could then say, ``There was an error in
Line Item 4. The error was . . .''
_READ_SRC
method should return the name of the table or view that should be used to read
records from the database. Frequently a view will be defined on the SQL Server to include
information from various lookup tables.
_WRITE_SRC
method should return the name of the table that should be used to write records
to the database.
_PRIMARY_KEY
method should return a list of the field names in the Primary Key for the
table. Of note, this returns a list, not a reference to an array. The order that the
fields are in _PRIMARY_KEY
is also the order in which the values will be specified for
identifying records for reading them from the database.
_FIELDS
method should return a reference to a hash of Win32::ASP::Field
objects, indexed
on the field names. Of note, for performance reasons the method is usually implemented like so:
sub _FIELDS { return $MyStuff::MyRecord::fields; }
$MyStuff::MyRecord::fields = {
Win32::ASP::Field->new( name => 'RecordID', sec => 'ro', type => 'int', desc => 'Record ID', ),
Win32::ASP::Field->new( name => 'RecordRemarks', sec => 'rw', type => 'text', ),
};
These class methods can be overriden in a child class.
_ACTIONS
method should return a reference to a hash of Win32::ASP::Action
objects,
indexed on the action names. Actions are used to implement things that users do to records. For
instance, a user might want to Edit a record. Some users might not have permissions to edit some
records though, and so it makes sense to implement an object that is responsible for determining
whether a given user is able to execute a given action in a given circumstance. The action is
also responsible for displaying the appropriate HTML for a link that implements the action,
knowing how to warn a user before the action is carried out, etc. For more information, see the
Win32::ASP::Action
class and its various sub-classes.
Of note, for performance reasons the method is usually implemented like so:
sub _ACTIONS { return $MyStuff::MyRecord::actions; }
$MyStuff::MyRecord::actions = {
Win32::ASP::Action::Edit->new,
Win32::ASP::Action::Delete->new,
Win32::ASP::Action->new( name => 'cancel', label => 'Cancel', . . . ),
};
Win32::ASP::DBRecord
class objects, this is implemented
through a reference to a Win32::ASP::DBRecordGroup
class object that contains the child records.
The implementation normally looks something like this:
sub _CHILDREN { return $MyStuff::MyRecord::children; }
$MyStuff::MyRecord::children = {
child_records => { type => 'MyStuff::MyChildRecordGroup', pkext => 'ChildID', },
};
The implication of the above is that MyStuff::MyRecord
objects have a group of associated
MyStuff::MyChildRecord
objects, which are accessed through a MyStuff::MyChildRecordGroup
object. The reference to that object will be stored in $self->{child_records}
. The
primary key of the MyStuff::MyChildRecord
objects will be the primary key for the
MyStuff::MyRecord
objects plus the added field 'ChildID
'. The index on the hash is referred
to hereafter as the 'child group name'.
There is only one of these.
Win32::ASP::DBRecordGroup
objects to add Win32::ASP::Field::dispmeta
objects to the
underlying Win32::ASP::DBRecord
object. Win32::ASP::Field::dispmeta
objects are frequently
used to display more than one field in a column when displaying a table of records (i.e. one field
above the other).
This is a basic new
method. 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
object. You will probably not need to override this method.
This is used for initializing new records prior to being edited. The code in ASP land for throwing up the edit screen when creating a new record looks something like this:
use MyStuff::MyRecord;
$record = MyStuff::MyRecord->new; $record->init; $record->edit; $data = 'edit'; $viewtype = 'edit';
This is then followed by the <FORM> section.
Note that init
modifies orig
, not edit
. Once orig
is modified, the edit
method is
used to place the record in edit
mode.
The read
method is used, coincidentally, to read a record from the database. It should be
passed an array comprised of the primary key values for the record desired.
The read
method is responsible for reading all appropriate values for the record, and for
reading any child records for which the child group name shows up in $self
. The implications
of this are important for providing appropriate behavior when update
is called.
The actual reading in of data from the ADO Recordset object is implemented by _read
. This
is done so that Win32::ASP::DBRecordGroup
object can execute a query and then make calls to
_read
for each record returned.
The _read
method is responsible for reading the data from the ADO Recordset object ($result
)
and entering it into the object. It does this by looping over the fields in _FIELDS
and calling
read
on each of them with the appropriate parameters. Note that _read
accepts the optional
parameter $columns
and passes this along in the call to read
on the Win32::ASP::Field
objects. This is to minimize unneeded value retrieval calls when Win32::ASP::DBRecordGroup
objects are only interested in a few fields. If $columns
is a reference to a hash, it will be
interpreted as a list of the fieldnames of note. However, to allow for more flexibility in
implementation, the decision as to whether or not the field will actually be read is still left
up to the Win32::ASP::Field
object.
In addition, _read
is responsible for calling can_view
on the resultant record object to see
whether the user is allowed to view this record. If can_view
returns false, _read
throws a
Win32::ASP::Error::DBRecord::no_permission
exception
Since the read
method is responsible for reading in all child records for which there is an
entry in $self
, the read_deep
method simply creates an entry in the $self
hash for each
key in the hash returned from _CHILDREN
.
The post
method takes data returned from a POST action and enters it into the
Win32::ASP::DBRecord
object. Of note, post
takes a $row
as a parameter. This is used to
identify which row of a table is of interest when being used for editing
Win32::ASP::DBRecordGroup
objects.
The method simply calls post
on each of the Win32::ASP::Field
objects.
It also posts the data for all of the child records. The presumption is that if the records are
really child records, one would generally edit the whole mess at one time and that they will then
want to be posted. So it creates new child objects of the appropriate
Win32::ASP::DBRecordGroup
classes and calls post
on them.
The insert
method is responsible for taking the data and writing it to the database. If there
are child records associated with object, those are written as well. Everything is wrapped in a
transaction so that a failure to write child records for any reason will roll back the
transaction.
The insert
method is passed a list of fields that should always be written. By default, the
insert
method will only write values that are considered editable (as determined by calling
can_edit
on the field object) <Bor> that show up in the passed list of fields. This enables
one to define certain fields as read only, but still modify them within the context of actions or
other code. Also, values are only written if they are defined in the $self->{edit}
hash.
It is generally considered poor form to write NULL values to the database (especially in SQL
Server 6.5 as this results in a 2K page being allocated for NULL text objects:).
The values are prepared for inserting by calling as_write_pair
on the Win32::ASP::Field
objects. The array of write pairs is then passed to the insert
method on the Win32::DB
object. The return from that call is the ADO Recordset object, which is then passed to
set_inserted
so that auto generated Primary Key values can be retrieved
It then deals with the child record groups as needed. The defined objects have set_prop
used
to propagate the primary key values onto the child objects. The insert
method can then be
called to insert the entire group.
The insert
method returns a list of all write pairs that were inserted. This so that
implementations that override insert
can make use of that information (this is most commonly
done for logging purposes - other records are inserted into logging tables to indicate who did
what when, and having insert
return the information makes that much easier.).
This method is responsible for retrieving the Primary Key values on newly inserted records. Most useful when one of those Primary Key values is an autonumber field.
This is the single largest, ugliest morass of code in Win32::ASP::DBRecord
. Yeach. Think of
it as a slightly uglier insert
, though, and it's a little easier to understand.
First we start a transaction and call can_update
. The can_update
method will call read
in turn (no way to know if we can update a record if we don't know what was in it).
If can_update
returns false, we throw a Win32::ASP::Error::DBRecord::no_permission
exception
and get out of here. Otherwise, we procede to call verify_record
and verify_timestamp
. If
neither of those throw exceptions, we continue on.
The method then creates <C$constraint>, a SQL WHERE
condition suitable for indentifying the
record of interest based on the Primary Key.
It then starts building a list of write pairs. It also adds those pairs to @retvals
, which
will contain a list of fields, new values, and old values for any field that changed. Note that
we only update fields for which can_edit
returns true and that have changed, or that are
mentioned in @ext_fields
, the passed parameter list. Fields updated as a result of being in
@ext_fields
are not mentioned in the list of changed fields that is returned.
Win32::ASP::DBRecord - an abstract parent class for representing database records |