Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DBIx and inheritance in Perl

I'm currently trying to implement the following scenario with DBIx:

The table products contains "general products" and "bundle products" (bundle products are collections of general products):

package Product;
use base 'DBIx::Class::Core';
__PACKAGE__->table("products");
__PACKAGE__->add_columns(
  "productId",
  { data_type => "varchar", is_nullable => 0, size => 10},
  "name",
  { data_type => "varchar", is_nullable => 1, size => 150},
  "type",
  {
     data_type => "enum",
     default_value => "general",
     extra => {
       list => ["general", "bundle"],
     },
     is_nullable => 0,
  });

As you can see, wether the product is a general product or a bundle product is saved in the column type.

Now I would like to encapsulate this information in the class identity: I would like to have following classes:

  • Product (type does not matter)
  • BundleProduct (type = 'bundle')
  • GeneralProduct (type = 'general')

I wrote:

package BundleProduct;
use base 'Product';

__PACKAGE__->resultset_attributes({ where => { 'type' => 'bundle' } });
1;

and

package GeneralProduct;
use base 'Product';

__PACKAGE__->resultset_attributes({ where => { 'type' => 'general' } });
1;

But when executing

my @allProducts = $schema->resultset('BundleProduct')->all;

all general products are fetched. Although the resulting objects are of instance BundleProduct, the generated SQL contains the WHERE-condition of the class GeneralProduct (type = 'general'). Even worse: If I try to fetch a Product (base class of BundleProduct and GeneralProduct) the condition type = 'general' is applied, too! It seems that the definition within GeneralProduct overwrites all other definitions.

What is wrong with my design?

like image 669
Victor-Philipp Negoescu Avatar asked Oct 11 '12 16:10

Victor-Philipp Negoescu


1 Answers

The usage of resultset_attributes is not recommended. You should implement a result set class for Product with methods bundle_products and general_products:

package My::Schema::ResultSet::Product;
use base 'DBIx::Class::ResultSet';

sub bundle_products  { shift->search({ type => 'bundle' }); }
sub general_products { shift->search({ type => 'general' }); }

Then you can search specific products like this:

$schema->resultset('Product')->bundle_products->all;
$schema->resultset('Product')->general_products->all;

See the documentation of resultset_attributes.

Also have a look at DBIx::Class::DynamicSubclass. It adds some useful features when subclassing results.

like image 164
nwellnhof Avatar answered Oct 20 '22 05:10

nwellnhof