Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Re: A Growing Dislike for SQL-OOP Mappers

by siracusa (Friar)
on Aug 22, 2005 at 17:27 UTC ( [id://485734]=note: print w/replies, xml ) Need Help??


in reply to A Growing Dislike for SQL-OOP Mappers

Maybe you're just using the wrong RDBMS-OO mappers? ;)
These SQL->OOP mappers need to make their create/new subs private (_new, _create) in some way shape or form; yet still be subclassable. Breaking new() for the consumer is a sin in my book since it's the most common creation idiom out there. Using a mapper should NOT decrease my options in terms of what public subs I want in my API.

I agree about keeping new() as simple as possible. It's a lot easier to decrease API granularity in your subclass than it is to increase it. Here's an example using a different RDBMS-OO mapper that has a finer API granularity than Class::DBI when it comes to object construction.

Set up abstracted data source:

package MyApp::DB; use Rose::DB; our @ISA = qw(Rose::DB); MyApp::DB->register_db( domain => 'default', type => 'default', driver => 'mysql', database => 'mydb', host => 'localhost', username => 'someuser', password => 'mysecret'); 1;

Set up base class for all DB-backed objects:

package MyApp::DB::Object; use Rose::DB::Object; our @ISA = qw(Rose::DB::Object); sub init_db { MyApp::DB->new } 1;

Finally, the example gallery object class with the customized constructor:

package MyApp::DB::Gallery; use MyApp::DB::Object; our @ISA = qw(MyApp::DB::Object); __PACKAGE__->meta->table('galleries'); __PACKAGE__->meta->columns(...); sub new { my($class, %args) = @_; my $dir = $args{'directory'}; if (-e $dir) { die "directory '$dir' exists"; } mkdir $dir or die "mkdir($dir) - $!"; # The following is roughly equivalent to find_or_create() my $self = $class->SUPER::new(%args); $self->load(speculative => 1); # Find... if($self->not_found) { $self->save; # ...or create } return $self; } 1;

A little different than what you're used to, perhaps, but there it is.

Now, how does the consumer of your module add legs to a table? That's right, the CDBI specific method ->add_to_tablelegs({}). That's silly. What if I change the DB layer? You're stuck with a cruddy API, even if you map add_to_tablelegs to SomeOtherDBILayer::add_child_object(). MyApp::SomeTable should just have an add() method to abstract that out. How many people do that that use CDBI? Who knows. The same goes for find_or_create, search_like, and all the others. They're convenience methods for the module writer, but should NEVER be exposed to the module consumer.

I'm not a fan of "add related object" APIs that are auto-created by default. Having method generators available for those abilities is fine, but it's rare that an RDBMS-OO mapper has enough information to correctly guess when and how to do such things--let alone what to name the methods. Your example illustrates this.

What I would probably do is write the method myself, since it's so simple:

package MyApp::Table; ... sub add_leg { my($self, $leg) = @_; $leg->table_id($self->id); # tie leg to this table $leg->save; push(@{$self->{'legs'}}, $leg); }

...or maybe it's not so simple, depending on how you want add_leg() to act, which kind of further proves the point. The existence of a relationship between two tables does not dictate (or even suggest, really) anything about how to "correctly" add rows to a related table.

Oh sure, you can know which columns point to which other columns and other technical details, but semantically, when it comes to adding rows to related tables, a lot is unknown. Is there a limit on the number of related rows that should exist? Should related rows be saved immediately or only saved when the "master" row is saved? Is it even permissible to add rows to a particular related table? There are too many questions for a "safe" add-related-rows method to be implicitly created by default, IMO.

Anyway, no matter what you do, if you want to support multiple RDBMS-OO mappers on the back-end, you're going to have to make some sort of "adapter" layer to map from your API to the DB layer's API.

That's a lot of work, and it seems to me that the cost/benefit ratio is hard to justify. I'd say just pick an RDBMS-OO mapper you like (or even none at all, and use straight DBI) and then stick with it. If you really need to tear it out later and replace it, then you can create the required adapter layer in order to maintain your API. But if you never have to do that, then you just saved yourself a lot of work.

Replies are listed 'Best First'.
Re^2: A Growing Dislike for SQL-OOP Mappers
by jk2addict (Chaplain) on Aug 22, 2005 at 17:38 UTC
    That's a lot of work, and it seems to me that the cost/benefit ratio is hard to justify. I'd say just pick an RDBMS-OO mapper you like (or even none at all, and use straight DBI) and then stick with it. If you really need to tear it out later and replace it, then you can create the required adapter layer in order to maintain your API. But if you never have to do that, then you just saved yourself a lot of work.

    Quite a bit of work. But that's a matter of perspective depending on the situation. If I'm stuck with say for example Class::DBI, I still have to create a layer to seperate it's is-a junk from my top-level module. If I'm already doing the work to do that, the rest is easy.

    As you point out, Rose::DB::Object is a little more polite about things. In the big scheme of things, I'd never need multiple SQL->OOP mappers. My frustration with isa-a and Class::DBI/DBIx::Class has turned into hacking/learning session on using multiple layers or abstracting out the is-a problems. Add 3 parts of Because I Can to that recipe. :-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://485734]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2024-04-19 06:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found