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 ();
9 our $VERSION = '0.03999_01';
13 DBIx::Class::Schema::Loader::RelBuilder - Builds relationships for DBIx::Class::Schema::Loader
17 See L<DBIx::Class::Schema::Loader>
21 This class builds relationships for L<DBIx::Class::Schema::Loader>. This
22 is module is not (yet) for external use.
28 Arguments: schema_class (scalar), fk_info (hashref), inflect_plural, inflect_singular
30 C<$schema_class> should be a schema class name, where the source
31 classes have already been set up and registered. Column info, primary
32 key, and unique constraints will be drawn from this schema for all
33 of the existing source monikers.
35 The fk_info hashref's contents should take the form:
40 local_columns => [ 'col2', 'col3' ],
41 remote_columns => [ 'col5', 'col7' ],
42 remote_moniker => 'AnotherTableMoniker',
46 AnotherTableMoniker => [
52 Options inflect_plural and inflect_singular are optional, and are better documented
53 in L<DBIx::Class::Schema::Loader::Base>.
57 This method will return the generated relationships as a hashref per table moniker,
58 containing an arrayref of code strings which can be "eval"-ed in the context of
59 the source class, like:
62 'Some::Source::Class' => [
63 "belongs_to( col1 => 'AnotherTableMoniker' )",
64 "has_many( anothers => 'AnotherTableMoniker', 'col15' )",
66 'Another::Source::Class' => [
72 You might want to use this in building an on-disk source class file, by
73 adding each string to the appropriate source class file,
74 prefixed by C<__PACKAGE__-E<gt>>.
79 my ( $class, $schema, $fk_info, $inflect_pl, $inflect_singular ) = @_;
84 inflect_plural => $inflect_pl,
85 inflect_singular => $inflect_singular,
88 bless $self => $class;
94 # pluralize a relationship name
96 my ($self, $relname) = @_;
98 if( ref $self->{inflect_plural} eq 'HASH' ) {
99 return $self->{inflect_plural}->{$relname}
100 if exists $self->{inflect_plural}->{$relname};
102 elsif( ref $self->{inflect_plural} eq 'CODE' ) {
103 my $inflected = $self->{inflect_plural}->($relname);
104 return $inflected if $inflected;
107 return $self->{legacy_default_inflections}
108 ? Lingua::EN::Inflect::PL($relname)
109 : Lingua::EN::Inflect::Number::to_PL($relname);
112 # Singularize a relationship name
113 sub _inflect_singular {
114 my ($self, $relname) = @_;
116 if( ref $self->{inflect_singular} eq 'HASH' ) {
117 return $self->{inflect_singular}->{$relname}
118 if exists $self->{inflect_singular}->{$relname};
120 elsif( ref $self->{inflect_singular} eq 'CODE' ) {
121 my $inflected = $self->{inflect_singular}->($relname);
122 return $inflected if $inflected;
125 return $self->{legacy_default_inflections}
127 : Lingua::EN::Inflect::Number::to_S($relname);
135 foreach my $local_moniker (keys %{$self->{fk_info}}) {
136 my $local_table = $self->{schema}->source($local_moniker)->from;
137 my $local_class = $self->{schema}->class($local_moniker);
138 my $rels = $self->{fk_info}->{$local_moniker};
141 foreach my $rel (@$rels) {
142 next if !$rel->{remote_source};
143 $counters{$rel->{remote_source}}++;
146 foreach my $rel (@$rels) {
147 next if !$rel->{remote_source};
148 my $local_cols = $rel->{local_columns};
149 my $remote_cols = $rel->{remote_columns};
150 my $remote_moniker = $rel->{remote_source};
151 my $remote_obj = $self->{schema}->source($remote_moniker);
152 my $remote_class = $self->{schema}->class($remote_moniker);
153 my $remote_table = $remote_obj->from;
154 $remote_cols ||= [ $remote_obj->primary_columns ];
156 if($#$local_cols != $#$remote_cols) {
157 croak "Column count mismatch: $local_moniker (@$local_cols) "
158 . "$remote_moniker (@$remote_cols)";
162 foreach my $i (0 .. $#$local_cols) {
163 $cond{$remote_cols->[$i]} = $local_cols->[$i];
166 # If more than one rel between this pair of tables, use the
167 # local col name(s) as the relname in the foreign source, instead
168 # of the local table name.
170 if($counters{$remote_moniker} > 1) {
171 $local_relname = $self->_inflect_plural(
172 lc($local_table) . q{_} . join(q{_}, @$local_cols)
175 $local_relname = $self->_inflect_plural(lc $local_table);
178 # for single-column case, set the relname to the column name,
179 # to make filter accessors work
181 if(scalar keys %cond == 1) {
182 my ($col) = keys %cond;
183 $remote_relname = $self->_inflect_singular($cond{$col});
186 $remote_relname = $self->_inflect_singular(lc $remote_table);
189 my %rev_cond = reverse %cond;
191 for (keys %rev_cond) {
192 $rev_cond{"foreign.$_"} = "self.".$rev_cond{$_};
193 delete $rev_cond{$_};
196 push(@{$all_code->{$local_class}},
197 { method => 'belongs_to',
198 args => [ $remote_relname,
205 push(@{$all_code->{$remote_class}},
206 { method => 'has_many',
207 args => [ $local_relname,