Possible imrovement to Class::DBI::retrieve

[prev] [thread] [next] [Date index for 2005/06/10]

From: leif.eriksen
Subject: Possible imrovement to Class::DBI::retrieve
Date: 02:10 on 10 Jun 2005
I'm fairly new to using C::DBI (though that hasnt stopped me from being a C::DBI hoplite for the Kwalitee effort), so I may be using it incorrectly.

I need to spend a little time explaining what I am doing first.

I am modelling two tables, one is Item, the other is Box. A Box contains Items. I do this with a FK constraint on Item, stating it references Box(Id). Items and Boxes also have an 'Area' property, indicating they are designated to be used in Victoria or NSW (states in Australia). Items for a particular Area can only go in a Box with the same area. So the Item and Box packages kinda look like

package Item;
...
use base qw(Project::DBI);

__PACKAGE__->table('Item');
__PACKAGE__->columns(All => qw(Id Area Box...));
__PACKAGE__->has_a(Box => 'Project::Box');
__PACKAGE__->add_constraint('CheckArea', Area => \&check_area);
...
sub check_area {
  # check that the Item Area == Box Area
  my ($area, $self, $column, $others) = @_;
  ...
  my $box = $self->_retrieve_box($others);
  return $area == $box->Area() ? 1 : 0;
}

sub _retrieve_box {
  my ($self, $others) = @_;

  my $box;

  if (ref($self)) { # changing an existing Item
    if (defined $self->Box()) {
      $box = Box->retrieve($self->Box());
    }
  } else { # creating a new tag
    if (exists $others->{box}) { # a Box was specified in the create
      $box = Box->retrieve($others->{box});
    }
  }

  return $box;
}

package Box;
...
use base qw(Project::DBI);

__PACKAGE__->table('Box');
__PACKAGE__->columns(All => qw(Id Area ...));
__PACKAGE__->has_many(Items => 'Project::Item');
...
1;

Now because I'm a good little vegemite, I have written a test harness to make sure everyting operates as I expect, and here's where my problem begins.

In Item.t, I create a Box first, then I try to create an Item to go in that Box.

So that test code kinda looks like this

#!/usr/bin/perl -w
use strict;
use Test::More qw(no_plan);
BEGIN {
 use_ok('Item');
}
use Box;
my $box = Box->create({Id=>1, Area=>'Vic'});
$box->update()->dbi_commit();

my $item = Item->create({Id=>1, Area=>'Vic', Box=>$box}); #$box->isa('Box'), not a simple scalar
...

This code fails in Class::DBI::retrieve (v0.96) 

sub retrieve {
        my $class           = shift;
        my @primary_columns = $class->primary_columns
                or return $class->_croak(
                "Can't retrieve unless primary columns are defined");
        my %key_value;
        if (@_ == 1 && @primary_columns == 1) {
                my $id = shift;
                return unless defined $id;
                ********FAILS HERE***********
                return $class->_croak("Can't retrieve a reference") if ref($id);
                ********FAILS HERE***********
                $key_value{ $primary_columns[0] } = $id;
 
Now because I call retrieve with $box, and ref($box) is 'Box', I get the 'Cant't retrieve a reference' message.

My workaround is to do this in _retrieve_box

$box += 0;

which 'scalarises' $box, so it is no longer a reference. I guess I could also stringify it too, but in my case the Id is an integer in the DB.

My thoughts are that C::DBI::retrieve shouldnt fail if $id is a reference, only if it isnt a Class::DBI reference. So I'd change the code from 

... if ref($id)
to

if (ref($id)) {
  return $class->_croak("Can't retrieve a reference") if not $id->isa('Class::DBI');
  $key_value{ $primary_columns[0] } = "$id"; # call overloaded '""' operator
}
...

That way we can retrieve() based on Class::DBI objects and scalars - I guess that boils down to retrieving ourselves...
my $box = Box->create({Id=>1, Area=>'Vic'});
...
my $same_box = Box->retrieve($box);

Does what I propose sound reasonable ?

Leif

Possible imrovement to Class::DBI::retrieve
leif.eriksen 02:10 on 10 Jun 2005

Re: Possible imrovement to Class::DBI::retrieve
William Ross 09:26 on 10 Jun 2005

Generated at 16:36 on 28 Jul 2005 by mariachi v0.52