1 package DBIx::Class::Schema::Loader::RelBuilder;
5 use Carp::Clan qw/^DBIx::Class/;
6 use Lingua::EN::Inflect::Number ();
8 our $VERSION = '0.03999_01';
12 DBIx::Class::Schema::Loader::RelBuilder - Builds relationships for DBIx::Class::Schema::Loader
16 See L<DBIx::Class::Schema::Loader>
20 This class builds relationships for L<DBIx::Class::Schema::Loader>. This
21 is module is not (yet) for external use.
27 Arguments: schema_class (scalar), fk_info (hashref), inflect_plural, inflect_singular
29 C<$schema_class> should be a schema class name, where the source
30 classes have already been set up and registered. Column info, primary
31 key, and unique constraints will be drawn from this schema for all
32 of the existing source monikers.
34 The fk_info hashref's contents should take the form:
39 local_columns => [ 'col2', 'col3' ],
40 remote_columns => [ 'col5', 'col7' ],
41 remote_moniker => 'AnotherTableMoniker',
45 AnotherTableMoniker => [
51 Options inflect_plural and inflect_singular are optional, and are better documented
52 in L<DBIx::Class::Schema::Loader::Base>.
56 This method will return the generated relationships as a hashref per table moniker,
57 containing an arrayref of code strings which can be "eval"-ed in the context of
58 the source class, like:
61 'Some::Source::Class' => [
62 "belongs_to( col1 => 'AnotherTableMoniker' )",
63 "has_many( anothers => 'AnotherTableMoniker', 'col15' )",
65 'Another::Source::Class' => [
71 You might want to use this in building an on-disk source class file, by
72 adding each string to the appropriate source class file,
73 prefixed by C<__PACKAGE__-E<gt>>.
78 my ( $class, $schema, $fk_info, $inflect_pl, $inflect_singular ) = @_;
83 inflect_plural => $inflect_pl,
84 inflect_singular => $inflect_singular,
87 bless $self => $class;
93 # pluralize a relationship name
95 my ($self, $relname) = @_;
97 if( ref $self->{inflect_plural} eq 'HASH' ) {
98 return $self->{inflect_plural}->{$relname}
99 if exists $self->{inflect_plural}->{$relname};
101 elsif( ref $self->{inflect_plural} eq 'CODE' ) {
102 my $inflected = $self->{inflect_plural}->($relname);
103 return $inflected if $inflected;
106 return Lingua::EN::Inflect::Number::to_PL($relname);
109 # Singularize a relationship name
110 sub _inflect_singular {
111 my ($self, $relname) = @_;
113 if( ref $self->{inflect_singular} eq 'HASH' ) {
114 return $self->{inflect_singular}->{$relname}
115 if exists $self->{inflect_singular}->{$relname};
117 elsif( ref $self->{inflect_singular} eq 'CODE' ) {
118 my $inflected = $self->{inflect_singular}->($relname);
119 return $inflected if $inflected;
122 return Lingua::EN::Inflect::Number::to_S($relname);
130 foreach my $local_moniker (keys %{$self->{fk_info}}) {
131 my $local_table = $self->{schema}->source($local_moniker)->from;
132 my $local_class = $self->{schema}->class($local_moniker);
133 my $rels = $self->{fk_info}->{$local_moniker};
136 foreach my $rel (@$rels) {
137 next if !$rel->{remote_source};
138 $counters{$rel->{remote_source}}++;
141 foreach my $rel (@$rels) {
142 next if !$rel->{remote_source};
143 my $local_cols = $rel->{local_columns};
144 my $remote_cols = $rel->{remote_columns};
145 my $remote_moniker = $rel->{remote_source};
146 my $remote_obj = $self->{schema}->source($remote_moniker);
147 my $remote_class = $self->{schema}->class($remote_moniker);
148 my $remote_table = $remote_obj->from;
149 $remote_cols ||= [ $remote_obj->primary_columns ];
151 if($#$local_cols != $#$remote_cols) {
152 croak "Column count mismatch: $local_moniker (@$local_cols) "
153 . "$remote_moniker (@$remote_cols)";
157 foreach my $i (0 .. $#$local_cols) {
158 $cond{$remote_cols->[$i]} = $local_cols->[$i];
161 # If more than one rel between this pair of tables, use the
162 # local col name(s) as the relname in the foreign source, instead
163 # of the local table name.
165 if($counters{$remote_moniker} > 1) {
166 $local_relname = $self->_inflect_plural(
167 lc($local_table) . q{_} . join(q{_}, @$local_cols)
170 $local_relname = $self->_inflect_plural(lc $local_table);
173 # for single-column case, set the relname to the column name,
174 # to make filter accessors work
176 if(scalar keys %cond == 1) {
177 my ($col) = keys %cond;
178 $remote_relname = $self->_inflect_singular($cond{$col});
181 $remote_relname = $self->_inflect_singular(lc $remote_table);
184 my %rev_cond = reverse %cond;
186 for (keys %rev_cond) {
187 $rev_cond{"foreign.$_"} = "self.".$rev_cond{$_};
188 delete $rev_cond{$_};
191 push(@{$all_code->{$local_class}},
192 { method => 'belongs_to',
193 args => [ $remote_relname,
200 push(@{$all_code->{$remote_class}},
201 { method => 'has_many',
202 args => [ $local_relname,