[Dbix-class] Should all result classes set C3 MRO?
Andrew Gregory
andrew.gregory.8 at gmail.com
Tue Jul 24 16:05:09 GMT 2018
Following the example in the cookbook[1], I moved components into
a base result class, only to discover that cascaded deletion no longer
worked. Apparently, load_components sets the MRO to C3, which
DBIx::Class relies on heavily. Moving those calls out of the
individual result classes caused them to revert to perl's default DFS
MRO. Since InflateColumn is a subclass of DBIx::Class::Row, Row's
delete method appears before CascadeActions wrapper under DFS. An
example script is below.
Is there any way to set the MRO for all loaded classes so that a base
class, or even DBIx::Class itself, could handle this transparently, or
do all classes need to individually ensure that they use C3?
[1]: https://metacpan.org/pod/distribution/DBIx-Class/lib/DBIx/Class/Manual/Cookbook.pod#Move-Common-Startup-into-a-Base-Class
## broken cascade example
package Test::Schema::Result;
use base 'DBIx::Class::Core';
## loads DBIx::Class::Row, putting it above
## DBIx::Class::Relationship::CascadeActions for default DFS MRO
__PACKAGE__->load_components('InflateColumn::DateTime');
package Test::Schema::Result::Bar;
use base 'Test::Schema::Result';
## uncommenting either of these set C3 MRO, putting CascadeActions before Row
#use mro 'c3';
#__PACKAGE__->load_components('InflateColumn::DateTime');
__PACKAGE__->table('Bar');
__PACKAGE__->add_columns( id => { data_type => 'integer', is_numeric => 1 } );
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many( foo => 'Test::Schema::Result::Foo' => 'bar_id' );
package Test::Schema::Result::Foo;
use base 'Test::Schema::Result';
__PACKAGE__->table('foo');
__PACKAGE__->add_columns(
id => { data_type => 'integer', is_numeric => 1 },
bar_id => { data_type => 'integer', is_numeric => 1 },
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->belongs_to( bar => 'Test::Schema::Result::Bar' => 'id' );
package Test::Schema;
use base 'DBIx::Class::Schema';
__PACKAGE__->register_class( Bar => 'Test::Schema::Result::Bar' );
__PACKAGE__->register_class( Foo => 'Test::Schema::Result::Foo' );
package main;
use Test::More;
my $schema = Test::Schema->connect('dbi:SQLite:dbname=:memory:');
$schema->deploy;
my ( $bar_rs, $foo_rs )
= ( $schema->resultset('Bar'), $schema->resultset('Foo') );
$bar_rs->create( { id => 1 } );
$foo_rs->create( { id => 1, bar_id => 1 } );
is( $bar_rs->count, 1 );
is( $foo_rs->count, 1 );
$bar_rs->find(1)->delete; # should cascade to delete foo as well
is( $bar_rs->count, 0 );
is( $foo_rs->count, 0 );
More information about the DBIx-Class
mailing list