[Dbix-class] coercing Result objects

Dave Howorth dhoworth at mrc-lmb.cam.ac.uk
Tue Nov 13 14:55:09 GMT 2012


I have an odd setup of Result classes, because I live with an odd
database. There are a bunch of tables that are all very similar but all
a bit different. So I have a bunch of Result classes that directly
reflect each individual table with all its quirks. And I also have an
overarching virtual Result class that makes a view by taking a UNION of
all the tables and extracting the common fields. It all works.

Sometimes, I retrieve a record from one of the tables and need to
convert it to the general form, or I retrieve a record via the
overarching class and need to get the specific form. So the Result
classes have methods that perform this transformation. At the moment,
they do it by doing ->find($id) on a resultset of the appropriate class.

But this isn't very nice because it hits the database again to retrieve
the same data just to wrap it in a different object. And that can be a
performance problem, quite apart from being ugly.

So is there any way to tell DBIC to rewrap the data in different object
clothes without hitting the database each time?

I have one table that looks something like:

package MySchema::Result::Foo;

__PACKAGE__->table("foo");
__PACKAGE__->add_columns(
  "cf_id",
  { data_type => "mediumint", is_nullable => 0 },
  "cf_name",
  { data_type => "text", is_nullable => 1 },
  "cf_comment",
  { data_type => "text", is_nullable => 1 },
  "cf_attribute",
  {
    data_type => "enum",
    default_value => "foo",
    extra => { list => ["foo", "bar"] },
    is_nullable => 0,
  },
);

cf_id, cf_name and cf_comment are the same as in all the other tables,
but cf_attribute is unique to this table.

And I have an overarching virtual table that looks like:

package MySchema::Result::Node;

__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
__PACKAGE__->table('nodes');
__PACKAGE__->result_source_instance->is_virtual(1);

__PACKAGE__->result_source_instance->view_definition(
   "SELECT
        cf_id      AS id,
        cf_name    AS name,
        cf_comment AS _comment
        FROM foo
    UNION
    SELECT
       ... etc for other tables ..."
);

__PACKAGE__->add_columns(
    id => {
        data_type   => 'mediumint',
        is_nullable => 0,
    },
    name => {
        data_type   => 'text',
        is_nullable => 1,
    },
    _comment => {
        data_type   => 'text',
        is_nullable => 1,
    },
);


So given an object $foo that is a MySchema::Result::Foo, can I repackage
it as a MySchema::Result::Node without going back to the database? And
without hand-coding it all? I think something like this ought to work,
but I'd rather not have to reinvent it if it already exists:

package MySchema::Result::Foo;

sub node {
  my $foo     = shift;
  my $schema  = $foo->result_source->schema;
  my $node_rs = $schema->resultset('Node');
  my $node    = $node_rs->new_result({
                      id       => $foo->cf_id,
                      name     => $foo->cf_name,
                      _comment => $foo->cf_comment,
                });
  return $node;
}

or maybe

sub node {
  my $foo     = shift;
  my $node    = MySchema::Result::Node->new({
                      id       => $foo->cf_id,
                      name     => $foo->cf_name,
                      _comment => $foo->cf_comment,
                      result_source => $foo->result_source,
                });
  return $node;
}



More information about the DBIx-Class mailing list