[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