1 package DBIx::Class::Schema::Loader::RelBuilder;
5 use Carp::Clan qw/^DBIx::Class/;
6 use Lingua::EN::Inflect ();
7 use Lingua::EN::Inflect::Number ();
11 DBIx::Class::Schema::Loader::RelBuilder - Builds relationships for DBIx::Class::Schema::Loader
15 See L<DBIx::Class::Schema::Loader>
19 This class builds relationships for L<DBIx::Class::Schema::Loader>. This
20 is module is not (yet) for external use.
26 Arguments: schema_class (scalar), fk_info (hashref), inflect_plural, inflect_singular
28 C<$schema_class> should be a schema class name, where the source
29 classes have already been set up and registered. Column info, primary
30 key, and unique constraints will be drawn from this schema for all
31 of the existing source monikers.
33 The fk_info hashref's contents should take the form:
38 local_columns => [ 'col2', 'col3' ],
39 remote_columns => [ 'col5', 'col7' ],
40 remote_moniker => 'AnotherTableMoniker',
44 AnotherTableMoniker => [
50 Options inflect_plural and inflect_singular are optional, and are better documented
51 in L<DBIx::Class::Schema::Loader::Base>.
55 This method will return the generated relationships as a hashref per table moniker,
56 containing an arrayref of code strings which can be "eval"-ed in the context of
57 the source class, like:
60 'Some::Source::Class' => [
61 "belongs_to( col1 => 'AnotherTableMoniker' )",
62 "has_many( anothers => 'AnotherTableMoniker', 'col15' )",
64 'Another::Source::Class' => [
70 You might want to use this in building an on-disk source class file, by
71 adding each string to the appropriate source class file,
72 prefixed by C<__PACKAGE__-E<gt>>.
77 my ( $class, $schema, $fk_info, $inflect_pl, $inflect_singular ) = @_;
82 inflect_plural => $inflect_pl,
83 inflect_singular => $inflect_singular,
86 bless $self => $class;
92 # pluralize a relationship name
94 my ($self, $relname) = @_;
96 if( ref $self->{inflect_plural} eq 'HASH' ) {
97 return $self->{inflect_plural}->{$relname}
98 if exists $self->{inflect_plural}->{$relname};
100 elsif( ref $self->{inflect_plural} eq 'CODE' ) {
101 my $inflected = $self->{inflect_plural}->($relname);
102 return $inflected if $inflected;
105 return $self->{legacy_default_inflections}
106 ? Lingua::EN::Inflect::PL($relname)
107 : Lingua::EN::Inflect::Number::to_PL($relname);
110 # Singularize a relationship name
111 sub _inflect_singular {
112 my ($self, $relname) = @_;
114 if( ref $self->{inflect_singular} eq 'HASH' ) {
115 return $self->{inflect_singular}->{$relname}
116 if exists $self->{inflect_singular}->{$relname};
118 elsif( ref $self->{inflect_singular} eq 'CODE' ) {
119 my $inflected = $self->{inflect_singular}->($relname);
120 return $inflected if $inflected;
123 return $self->{legacy_default_inflections}
125 : Lingua::EN::Inflect::Number::to_S($relname);
133 foreach my $local_moniker (keys %{$self->{fk_info}}) {
134 my $local_table = $self->{schema}->source($local_moniker)->from;
135 my $local_class = $self->{schema}->class($local_moniker);
136 my $rels = $self->{fk_info}->{$local_moniker};
139 foreach my $rel (@$rels) {
140 next if !$rel->{remote_source};
141 $counters{$rel->{remote_source}}++;
144 foreach my $rel (@$rels) {
145 next if !$rel->{remote_source};
146 my $local_cols = $rel->{local_columns};
147 my $remote_cols = $rel->{remote_columns};
148 my $remote_moniker = $rel->{remote_source};
149 my $remote_obj = $self->{schema}->source($remote_moniker);
150 my $remote_class = $self->{schema}->class($remote_moniker);
151 my $remote_table = $remote_obj->from;
152 $remote_cols ||= [ $remote_obj->primary_columns ];
154 if($#$local_cols != $#$remote_cols) {
155 croak "Column count mismatch: $local_moniker (@$local_cols) "
156 . "$remote_moniker (@$remote_cols)";
160 foreach my $i (0 .. $#$local_cols) {
161 $cond{$remote_cols->[$i]} = $local_cols->[$i];
164 # If more than one rel between this pair of tables, use the
165 # local col name(s) as the relname in the foreign source, instead
166 # of the local table name.
168 if($counters{$remote_moniker} > 1) {
169 $local_relname = $self->_inflect_plural(
170 lc($local_table) . q{_} . join(q{_}, @$local_cols)
173 $local_relname = $self->_inflect_plural(lc $local_table);
176 # for single-column case, set the relname to the column name,
177 # to make filter accessors work
179 if(scalar keys %cond == 1) {
180 my ($col) = keys %cond;
181 $remote_relname = $self->_inflect_singular($cond{$col});
184 $remote_relname = $self->_inflect_singular(lc $remote_table);
187 my %rev_cond = reverse %cond;
189 for (keys %rev_cond) {
190 $rev_cond{"foreign.$_"} = "self.".$rev_cond{$_};
191 delete $rev_cond{$_};
194 push(@{$all_code->{$local_class}},
195 { method => 'belongs_to',
196 args => [ $remote_relname,
203 push(@{$all_code->{$remote_class}},
204 { method => 'has_many',
205 args => [ $local_relname,