The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

BEGIN {
$DBICx::MapMaker::VERSION = '0.03';
}
# ABSTRACT: automatically create a DBIx::Class mapping table
use Moose;
our $VERSION;
our $AUTHORITY = 'CPAN:JROCKWAY';
# avoid clogging up our methods
my $other = sub { return 'right' if shift eq 'left'; return 'left' };
for my $direction (qw/left right/){
my $other = $other->($direction);
my $oname = "${other}_name";
has "${direction}_class" => (
is => 'ro',
isa => 'Str',
required => 1,
);
has "${direction}_name" => (
is => 'ro',
isa => 'Str',
required => 1,
);
has "${direction}_to_map_relation" => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my $self = shift;
return $self->$oname . '_map';
}
);
has "${other}s_from_${direction}" => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my $self = shift;
return $self->$oname . 's';
},
);
# TODO support extra columns
# XXX: hack
has "suppress_${direction}_m2m" => (
is => 'ro',
isa => 'Bool',
default => sub { undef },
);
}
has tablename => (
is => 'ro',
isa => 'Str',
lazy => 1,
default => sub {
my $self = shift;
my ($l,$r) = ($self->left_name, $self->right_name);
return "map_${l}_${r}";
},
);
# load up the classes
sub BUILD {
my $self = shift;
for my $class (map { $self->$_ } qw/left_class right_class/){
Class::MOP::load_class($class);
}
}
sub setup_table {
my ($self, $class) = @_;
$class->load_components(qw/Core/);
$class->table($self->tablename);
my ($left_class, $right_class) = ($self->left_class, $self->right_class);
my ($left_name, $right_name) = ($self->left_name, $self->right_name);
my $l_info = $left_class->column_info($left_class->primary_columns);
my $r_info = $right_class->column_info($right_class->primary_columns);
# NOTE:
# we never want auto-incrementing
# in a mapping table, so explicitly disable it
$class->add_columns(
$left_name => { %$l_info, is_auto_increment => 0, is_nullable => 0, },
$right_name => { %$r_info, is_auto_increment => 0, is_nullable => 0, },
);
$class->set_primary_key($left_name, $right_name);
# us -> them
$class->belongs_to( $left_name => $left_class );
$class->belongs_to( $right_name => $right_class );
# them -> us
my $lmap = $self->left_to_map_relation;
my $rmap = $self->right_to_map_relation;
$left_class->has_many( $lmap => $class, $left_name );
$right_class->has_many( $rmap => $class, $right_name );
# many2many
my $rights_from_left = $self->rights_from_left;
my $lefts_from_right = $self->lefts_from_right;
$left_class->many_to_many( $rights_from_left => $lmap => $right_name )
unless $self->suppress_left_m2m;
$right_class->many_to_many( $lefts_from_right => $rmap => $left_name )
unless $self->suppress_right_m2m;
}
1;
=pod
=head1 NAME
DBICx::MapMaker - automatically create a DBIx::Class mapping table
=head1 VERSION
version 0.03
=head1 SYNOPSIS
A common SQL pattern is the "many to many" relationship; a row in the
"left table" may point to many rows in the "right table", and a row in
the "right table" may point to many rows in the "left table". This
module automatically creates a L<DBIx::Class|DBIx::Class> result
source for that table, and sets up the six necessary relationships.
Here's how to use it. Imagine you have some tables called
C<MySchema::A> and C<MySchema::B>, each with a primary key, that you'd
like to join. To create the mapping table, you'll write a module like
this:
package MySchema::MapAB;
use DBICx::MapMaker;
use base 'DBIx::Class';
my $map = DBICx::MapMaker->new(
left_class => 'MySchema::A',
right_class => 'MySchema::B',
left_name => 'a',
right_name => 'b',
);
$map->setup_table(__PACKAGE__);
Then, you can:
my $a = $schema->resultset('A')->find(1);
$a->b_map; # the mapping table
$a->bs; # a list of bs that this a has
my $b = $schema->resultset('B')->find(42);
$b->a_map; # the mapping table
$b->as; # a list of as that this b has
=head1 METHODS
=head2 new
Create a C<MapMaker>. See L</ATTRIBUTES> below for a description of
the attributes you can pass to the constructor.
=head2 setup_table($class)
Makes C<$class> into the mapping table. C<$class> should be a
subclass of C<DBIx::Class>.
=head1 ATTRIBUTES
Here are the attributes that you can pass to the constructor:
=head2 left_class right_class
The class name of the left/right table (the tables that have a m2m
relationship between them).
Required.
=head2 left_name right_name
The column name for the left/right table's primary key in the map
table.
Required.
=head2 left_to_map_relation right_to_map_relation
The name of the relationship from the left/right table to the map
table.
Optional. Defaults to the name of the opposite table's name with
C<_map> appended. (If C<right_name> is C<foo>, then
C<left_to_map_relation> will be C<foo_map>.)
=head2 rights_from_left lefts_from_right
The name of the m2m relationship. C<rights_from_left> is the method
you'll call on a C<left> row to get the corresponding C<right>s.
(C<lefts_from_right> is the opposite.)
Optional. Defaults to the name of the row returned with "s" appended.
If C<left_name> is "foo", then C<lefts_from_right> will be "foos" by
default.
=head2 tablename
The name of the created mapping table.
Optional. Defaults to "map_C<left_name>_C<right_name>". (With C<foo>
and C<bar>, C<map_foo_bar>.)
=head1 AUTHORS
Jonathan Rockway C<< <jrockway@cpan.org> >>
Stevan Little C<< <stevan.little@iinteractive.com> >>
Adam Herzog C<< <adam@adamherzog.com> >>
=head1 COPYRIGHT AND LICENSE
Copyright 2008 Infinity Interactive, Inc.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 AUTHOR
Jonathan Rockway <jrockway@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011 by Jonathan Rockway.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
__END__