Object Relational Mapping
ClearPress comes with a built-in, basic ORM layer in the ClearPress::model superclass. Like views, data model objects should be systematically named to avoid adding complexity (though it is possible to override everything if you really want to!). Usually a class maps to a table and an instance of a class (yes, an object) usually maps to an individual row. Tables are usually named in the singular form (e.g. 'child' rather than 'children') and the class name-suffix has the same name as the table, e.g. application::model::child.
Table fields / data members
Data model objects at the bare minimum carry the following information - the package name and an array of fields which map to the database table the class represents.
Fields can be reflected in automatic get/set accessors courtesy of
Class::Accessor. Most data models come with this near their head:
__PACKAGE__->mk_accessors(fields())
The ClearPress ORM does not (currently) perform database inspection
to build attributes and relationships automatically (at run- or
compile- time). It relies on the list of fields always reflecting
what's in the database. For example:
sub fields {
return qw(id_child name dob id_family);
}
Creating stuff
Here's our data model:
package Grandkids::model::kid;
use strict;
use warnings;
use base qw(ClearPress::model);
__PACKAGE__->mk_accessors(fields());
sub fields {
return qw(id_kid name dob id_family);
}
1;
create table kid(
id_kid bigint unsigned primary key auto_increment not null,
name char(128) default '' not null,
dob datetime not null,
id_family bigint unsigned
);
use Grandkids::model::kid;
my $oKid = Grandkids::model::kid->new();
$oKid->name('Bob');
$oKid->id_family(1);
$oKid->dob('2009-02-10 10:00:00');
$oKid->create or die;
my $oKid = Grandkids::model::kid->new({
name => 'Bob',
id_family => 1,
dob => '2009-02-10 10:00:00',
});
$oKid->create or die;
Reading stuff
Read access from the database comes in a couple of flavours - lists and individuals. The latter (read) requires an entity's id. Listings are fractionally simpler as the basics are implemented in the superclass.
List
To activate the basic list method (list everything) we add this to
the top of the class __PACKAGE__->has_all()
my $oRootKid = Grandkids::model::kid->new(); # note no id required
my $arKids = $oRootKid->kids();
for my $oKid (@{$arKids}) {
printf q[%d %s %s %d\n],
$oKid->id_kid,
$oKid->name,
$oKid->dob,
$oKid->id_family;
}
Read
To read a specific entity given its id (primary key) we can specify
it in the constructor:
my $oKid = Grandkids::model::kid->new({id_kid=>1});
printf q[%d %s %s %d\n],
$oKid->id_kid,
$oKid->name,
$oKid->dob,
$oKid->id_family;
Updating stuff
To modify an existing entity we load it with its primary key, add the
value to change then ask it to update:
my $oKid = Grandkids::model::kid->new({id_kid=>1});
$oKid->name('Billy');
$oKid->update or die;
Deleting stuff
Deleting entries from the database is easy. Read with an entity's
primary key, then call delete:
my $oKid = Grandkids::model::kid->new({id_kid=>1});
$oKid->delete or die;
Secondary Keys
ClearPress::model supports the use of a unique secondary key (not compound). If, say, a user has a unique username, the primary id would be id_user or similar, and the secondary key would be username. ClearPress::model::init will check for a non-numeric scalar value in the constructor, and attempt to load on that when the primary key is not present. Similarly it will also attempt to load against a named field.
package app::model::user;
use strict;
use warnings;
__PACKAGE__->mk_accessors(fields());
sub fields {
return qw(id_user username);
}
sub secondary_key {
return q[username];
}
1;
INSERT INTO user VALUES(1,'bob123');
use app::model::user;
my $u = app::model::user->new(1);
my $u = app::model::user->new('bob123');
my $u = app::model::user->new({ id_user => 1 });
my $u = app::model::user->new({ username => 'bob123' });