Class::DBI is more a mapper from database to object set than the other way around. It's not an intrinsic limitation - it can work either way - but its assumptions and documentation don't fit nearly as well if your starting point is a class hierarchy and you want to express it in database form.
however, there are several ways that it could handle your example. You could do almost exactly what you describe, though i don't think it the best approach. something like this should work:
Package My::Hosts;
use base qw(My::Class::DBI::Subclass);
__PACKAGE__->table('hosts');
__PACKAGE__->columns(Primary => qw(ID));
__PACKAGE__->columns(Essential => qw(name IPAddress location OS));
Package My::MailServers;
use base qw(My::Hosts);
__PACKAGE__->columns(mailserver => qw(MTA version));
Package My::WebServers;
use base qw(My::Hosts);
__PACKAGE__->columns(webserver => qw(httpd version CGI));
which would allow you to use the same table for each class, and webservers would inherit host methods as i think you want. As a child of Schwern, its encapsulation is very sound.
however, that isn't the best way to use the module, nor - imho - a very good way to use the database. I'd probably use three tables and define many to one relationships linking webservers to hosts. You may after all have more than one webserver to describe for each host:
Package My::Hosts;
use base qw(My::Class::DBI::Subclass);
__PACKAGE__->table('hosts');
__PACKAGE__->columns(Primary => qw(ID));
__PACKAGE__->columns(Essential => qw(name IPAddress location OS));
__PACKAGE__->has_many('mailservers', 'My::MailServers', 'host');
__PACKAGE__->has_many('webservers', 'My::WebServers', 'host');
# has_many also defines a column in the foreign class:
# in this case webserver->host
# which returns the relevant Hosts object when called.
Package My::MailServers;
use base qw(My::Class::DBI::Subclass);
__PACKAGE__->table('mailservers');
__PACKAGE__->columns(Primary => qw(ID));
__PACKAGE__->columns(Essential => qw(MTA version));
Package My::WebServers;
use base qw(My::Class::DBI::Subclass);
__PACKAGE__->table('webservers');
__PACKAGE__->columns(Primary => qw(ID));
__PACKAGE__->columns(Essential => qw(httpd version CGI mod_perl port h
+ttps));
which is what Class::DBI does best. It will leave you with possibilities like this:
my $webserver = My::WebServers->retrieve($id);
my $version = $webserver->version;
my $ip = $webserver->host->IPAddress;
my @mailservers_on_this_host = $webserver->host->mailservers;
my @hosts_with_webservers = map { $_->host } My::WebServer->retrieve_a
+ll;
my @same thing = grep { $_->webservers } My::Hosts->retrieve_all;
# ...
If defining relationships is going to be a pain on the input side - it sometimes is, if you're pulling data in from flat files, for example - then I think the official approach would be something like:
Package My::Hosts;
use base qw(My::Class::DBI::Subclass);
__PACKAGE__->table('hosts');
__PACKAGE__->columns(Primary => qw(ID));
__PACKAGE__->columns(Essential => qw(name IPAddress location OS));
__PACKAGE__->columns(mailserver => qw(MTA version));
__PACKAGE__->columns(webserver => qw(httpd version CGI));
__PACKAGE__->make_filter(mailservers => 'not MTA is NULL');
__PACKAGE__->make_filter(webservers => 'not httpd is NULL');
Which gives you a single table and reasonably efficient access to the various subsets of your data. I haven't really used it this way, though, and I'm not sure about using 'is NULL' in the filters - think it might be database dependent - but it does serve to demonstrate that there are many ways to do what you require.
The other thing I'd suggest is that you run a factory class in front of the set of data-mapping classes. it's not essential if you're working in a controlled setting, but it greatly adds to the flexibility of the app and makes it easier to grow, and becomes almost essential when you start to use templating systems for output. I've put together a Class::DBI::Factory and associated handler and pager modules that might make their way onto CPAN in a few days if they meet with the approval of the poop-group, but it has all gone a bit over the top in its efforts to be all things to all people, and something much simpler would suffice for the sort of example you've described.
|