DBIx and Perl Inheritance

I am currently trying to implement the following script with DBIx:

The product table contains “common products” and “products with a package” (package products are collections of common 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, the product is a generic product or the product package is saved in the type column.

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

  • Product ( type doesn't 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 doing

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

All common products are displayed. Although the resulting objects have an instance of BundleProduct , the generated SQL contains the WHERE clause of the GeneralProduct class ( type = 'general'). Even worse: if I try to get Product (the base class of BundleProduct and GeneralProduct ), the condition type = 'general' will also be applied! It seems that the definition inside GeneralProduct overwrites all other definitions.

What is wrong with my design?

+7
source share
3 answers

Using resultset_attributes not recommended. You must implement the result set class for Product with the bundle_products and general_products methods:

 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 for specific products as follows:

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

See the documentation for the resultset_attributes documentation .

Also see DBIx :: Class :: DynamicSubclass . It adds some useful features to the results of a subclass.

+4
source

It can always be defaulted for the general, because the data does not swell to the Enum object?

Surprised that it does not give a clear error, but perhaps adding the following (in the Product package) will fix your problem:

 __PACKAGE__->load_components(qw/InflateColumn::Object::Enum/); 

Additionally, also try adding is_enum => 1 to the type column definition:

 type => { data_type => "enum", is_enum => 1, default_value => "general", is_nullable => 0, extra => { list => ["general", "bundle"], }, }, 

NB. This should ensure the use of the inflation object ( Object::Enum ), while without it I believe that it is trying to use enum native for the RDBMS used (if it is present).

Fingers crossed it. If you do not try to remove default_value to see how this affects things.

0
source

It may be a bit of OT, but I always seem to encounter some insidious problem of implementing an application when working with enumeration data types in my schemas.

Therefore, I no longer use them. I use foreign key relationships and separate tables or a combo table containing all the codes:

 <id, code_type, code_name> < 1, 'product_type', 'bundle'> < 2, 'product_type', 'general'> 

and then I join product_type from product.product_type_id = code_table.id

This method has greatly simplified the implementation of the application much easier due to some additional database management at the beginning of the project.

0
source

All Articles