more informative error in related_source when relationship doesn't exist
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSource.pm
CommitLineData
9c992ba1 1package DBIx::Class::ResultSource;
2
3use strict;
4use warnings;
5
6use DBIx::Class::ResultSet;
7
8use Carp qw/croak/;
9
10use base qw/DBIx::Class/;
11__PACKAGE__->load_components(qw/AccessorGroup/);
12
13__PACKAGE__->mk_group_accessors('simple' =>
87f0da6a 14 qw/_ordered_columns _columns _primaries _unique_constraints name resultset_class result_class schema from _relationships/);
9c992ba1 15
16=head1 NAME
17
18DBIx::Class::ResultSource - Result source object
19
20=head1 SYNOPSIS
21
22=head1 DESCRIPTION
23
24A ResultSource is a component of a schema from which results can be directly
25retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
26
27=head1 METHODS
28
29=cut
30
31sub new {
32 my ($class, $attrs) = @_;
33 $class = ref $class if ref $class;
34 my $new = bless({ %{$attrs || {}} }, $class);
35 $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
571dced3 36 $new->{_ordered_columns} ||= [];
9c992ba1 37 $new->{_columns} ||= {};
8452e496 38 $new->{_relationships} ||= {};
9c992ba1 39 $new->{name} ||= "!!NAME NOT SET!!";
40 return $new;
41}
42
43sub add_columns {
44 my ($self, @cols) = @_;
571dced3 45 $self->_ordered_columns( \@cols )
46 if !$self->_ordered_columns;
20518cb4 47 my @added;
48 my $columns = $self->_columns;
9c992ba1 49 while (my $col = shift @cols) {
53509665 50
30126ac7 51 my $column_info = ref $cols[0] ? shift(@cols) : {};
53509665 52 # If next entry is { ... } use that for the column info, if not
53 # use an empty hashref
54
20518cb4 55 push(@added, $col) unless exists $columns->{$col};
56
57 $columns->{$col} = $column_info;
9c992ba1 58 }
20518cb4 59 push @{ $self->_ordered_columns }, @added;
30126ac7 60 return $self;
9c992ba1 61}
62
63*add_column = \&add_columns;
64
65=head2 add_columns
66
67 $table->add_columns(qw/col1 col2 col3/);
68
69 $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
70
71Adds columns to the result source. If supplied key => hashref pairs uses
72the hashref as the column_info for that column.
73
74=head2 add_column
75
76 $table->add_column('col' => \%info?);
77
78Convenience alias to add_columns
79
80=cut
81
82sub resultset {
83 my $self = shift;
84 return $self->resultset_class->new($self);
85}
86
3842b955 87=head2 has_column
88
9c992ba1 89 if ($obj->has_column($col)) { ... }
90
91Returns 1 if the source has a column of this name, 0 otherwise.
92
93=cut
94
95sub has_column {
96 my ($self, $column) = @_;
97 return exists $self->_columns->{$column};
98}
99
100=head2 column_info
101
102 my $info = $obj->column_info($col);
103
104Returns the column metadata hashref for a column.
105
106=cut
107
108sub column_info {
109 my ($self, $column) = @_;
110 croak "No such column $column" unless exists $self->_columns->{$column};
a953d8d9 111 if ( (! $self->_columns->{$column}->{data_type})
112 && $self->schema && $self->storage() ){
113 my $info;
114############ eval for the case of storage without table
115 eval{
116 $info = $self->storage->columns_info_for ( $self->from() );
117 };
118 if ( ! $@ ){
119 for my $col ( keys %{$self->_columns} ){
120 for my $i ( keys %{$info->{$col}} ){
121 $self->_columns()->{$col}->{$i} = $info->{$col}->{$i};
122 }
123 }
124 }
125 }
9c992ba1 126 return $self->_columns->{$column};
127}
128
129=head2 columns
130
20518cb4 131 my @column_names = $obj->columns;
132
133Returns all column names in the order they were declared to add_columns
87f0da6a 134
135=cut
9c992ba1 136
137sub columns {
138 croak "columns() is a read-only accessor, did you mean add_columns()?" if (@_ > 1);
571dced3 139 return @{shift->{_ordered_columns}||[]};
140}
141
87f0da6a 142=head2 set_primary_key(@cols)
143
9c992ba1 144Defines one or more columns as primary key for this source. Should be
145called after C<add_columns>.
87f0da6a 146
147Additionally, defines a unique constraint named C<primary>.
148
149=cut
9c992ba1 150
151sub set_primary_key {
152 my ($self, @cols) = @_;
153 # check if primary key columns are valid columns
154 for (@cols) {
155 $self->throw("No such column $_ on table ".$self->name)
156 unless $self->has_column($_);
157 }
158 $self->_primaries(\@cols);
87f0da6a 159
160 $self->add_unique_constraint(primary => \@cols);
9c992ba1 161}
162
87f0da6a 163=head2 primary_columns
164
9c992ba1 165Read-only accessor which returns the list of primary keys.
30126ac7 166
87f0da6a 167=cut
9c992ba1 168
169sub primary_columns {
170 return @{shift->_primaries||[]};
171}
172
87f0da6a 173=head2 add_unique_constraint
174
175Declare a unique constraint on this source. Call once for each unique
176constraint.
177
178 # For e.g. UNIQUE (column1, column2)
179 __PACKAGE__->add_unique_constraint(constraint_name => [ qw/column1 column2/ ]);
180
181=cut
182
183sub add_unique_constraint {
184 my ($self, $name, $cols) = @_;
185
186 for (@$cols) {
187 $self->throw("No such column $_ on table ".$self->name)
188 unless $self->has_column($_);
189 }
190
191 my %unique_constraints = $self->unique_constraints;
192 $unique_constraints{$name} = $cols;
193 $self->_unique_constraints(\%unique_constraints);
194}
195
196=head2 unique_constraints
197
198Read-only accessor which returns the list of unique constraints on this source.
199
200=cut
201
202sub unique_constraints {
203 return %{shift->_unique_constraints||{}};
204}
205
9c992ba1 206=head2 from
207
208Returns an expression of the source to be supplied to storage to specify
209retrieval from this source; in the case of a database the required FROM clause
210contents.
211
212=cut
213
214=head2 storage
215
216Returns the storage handle for the current schema
217
218=cut
219
220sub storage { shift->schema->storage; }
221
8452e496 222=head2 add_relationship
223
224 $source->add_relationship('relname', 'related_source', $cond, $attrs);
225
226The relation name can be arbitrary, but must be unique for each relationship
227attached to this result source. 'related_source' should be the name with
228which the related result source was registered with the current schema
229(for simple schemas this is usally either Some::Namespace::Foo or just Foo)
230
231The condition needs to be an SQL::Abstract-style representation of the join
232between the tables. For example, if you're creating a rel from Foo to Bar,
233
234 { 'foreign.foo_id' => 'self.id' }
235
236will result in the JOIN clause
237
238 foo me JOIN bar bar ON bar.foo_id = me.id
239
240You can specify as many foreign => self mappings as necessary.
241
242Valid attributes are as follows:
243
244=over 4
245
246=item join_type
247
248Explicitly specifies the type of join to use in the relationship. Any SQL
249join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in the SQL
250command immediately before C<JOIN>.
251
252=item proxy
253
254An arrayref containing a list of accessors in the foreign class to proxy in
255the main class. If, for example, you do the following:
256
257 __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => qw[/ margle /] });
258
259Then, assuming Bar has an accessor named margle, you can do:
260
261 my $obj = Foo->find(1);
262 $obj->margle(10); # set margle; Bar object is created if it doesn't exist
263
264=item accessor
265
266Specifies the type of accessor that should be created for the relationship.
267Valid values are C<single> (for when there is only a single related object),
268C<multi> (when there can be many), and C<filter> (for when there is a single
269related object, but you also want the relationship accessor to double as
270a column accessor). For C<multi> accessors, an add_to_* method is also
271created, which calls C<create_related> for the relationship.
272
273=back
274
275=cut
276
277sub add_relationship {
278 my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
aa562407 279 croak "Can't create relationship without join condition" unless $cond;
8452e496 280 $attrs ||= {};
87772e46 281
8452e496 282 my %rels = %{ $self->_relationships };
283 $rels{$rel} = { class => $f_source_name,
87772e46 284 source => $f_source_name,
8452e496 285 cond => $cond,
286 attrs => $attrs };
287 $self->_relationships(\%rels);
288
30126ac7 289 return $self;
87772e46 290
953a18ef 291 # XXX disabled. doesn't work properly currently. skip in tests.
292
8452e496 293 my $f_source = $self->schema->source($f_source_name);
294 unless ($f_source) {
295 eval "require $f_source_name;";
296 if ($@) {
297 die $@ unless $@ =~ /Can't locate/;
298 }
299 $f_source = $f_source_name->result_source;
87772e46 300 #my $s_class = ref($self->schema);
301 #$f_source_name =~ m/^${s_class}::(.*)$/;
302 #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
303 #$f_source = $self->schema->source($f_source_name);
8452e496 304 }
305 return unless $f_source; # Can't test rel without f_source
306
307 eval { $self->resolve_join($rel, 'me') };
308
309 if ($@) { # If the resolve failed, back out and re-throw the error
310 delete $rels{$rel}; #
311 $self->_relationships(\%rels);
aa562407 312 croak "Error creating relationship $rel: $@";
8452e496 313 }
314 1;
315}
316
317=head2 relationships()
318
319Returns all valid relationship names for this source
320
321=cut
322
323sub relationships {
324 return keys %{shift->_relationships};
325}
326
327=head2 relationship_info($relname)
328
329Returns the relationship information for the specified relationship name
330
331=cut
332
333sub relationship_info {
334 my ($self, $rel) = @_;
335 return $self->_relationships->{$rel};
336}
337
953a18ef 338=head2 has_relationship($rel)
339
340Returns 1 if the source has a relationship of this name, 0 otherwise.
341
342=cut
343
344sub has_relationship {
345 my ($self, $rel) = @_;
346 return exists $self->_relationships->{$rel};
347}
348
8452e496 349=head2 resolve_join($relation)
350
351Returns the join structure required for the related result source
352
353=cut
354
355sub resolve_join {
87772e46 356 my ($self, $join, $alias) = @_;
357 if (ref $join eq 'ARRAY') {
358 return map { $self->resolve_join($_, $alias) } @$join;
359 } elsif (ref $join eq 'HASH') {
360 return map { $self->resolve_join($_, $alias),
361 $self->related_source($_)->resolve_join($join->{$_}, $_) }
362 keys %$join;
363 } elsif (ref $join) {
aa562407 364 croak ("No idea how to resolve join reftype ".ref $join);
87772e46 365 } else {
3842b955 366 my $rel_info = $self->relationship_info($join);
aa562407 367 croak("No such relationship ${join}") unless $rel_info;
3842b955 368 my $type = $rel_info->{attrs}{join_type} || '';
953a18ef 369 return [ { $join => $self->related_source($join)->from,
370 -join_type => $type },
3842b955 371 $self->resolve_condition($rel_info->{cond}, $join, $alias) ];
953a18ef 372 }
373}
374
3842b955 375=head2 resolve_condition($cond, $rel, $alias|$object)
953a18ef 376
3842b955 377Resolves the passed condition to a concrete query fragment. If given an alias,
953a18ef 378returns a join condition; if given an object, inverts that object to produce
379a related conditional from that object.
380
381=cut
382
383sub resolve_condition {
3842b955 384 my ($self, $cond, $rel, $for) = @_;
953a18ef 385 #warn %$cond;
386 if (ref $cond eq 'HASH') {
387 my %ret;
388 while (my ($k, $v) = each %{$cond}) {
389 # XXX should probably check these are valid columns
aa562407 390 $k =~ s/^foreign\.// || croak "Invalid rel cond key ${k}";
391 $v =~ s/^self\.// || croak "Invalid rel cond val ${v}";
953a18ef 392 if (ref $for) { # Object
3842b955 393 #warn "$self $k $for $v";
394 $ret{$k} = $for->get_column($v);
395 #warn %ret;
953a18ef 396 } else {
3842b955 397 $ret{"${rel}.${k}"} = "${for}.${v}";
953a18ef 398 }
953a18ef 399 }
400 return \%ret;
5efe4c79 401 } elsif (ref $cond eq 'ARRAY') {
402 return [ map { $self->resolve_condition($_, $rel, $for) } @$cond ];
953a18ef 403 } else {
404 die("Can't handle this yet :(");
87772e46 405 }
406}
407
953a18ef 408
87772e46 409=head2 related_source($relname)
410
411Returns the result source for the given relationship
412
413=cut
414
415sub related_source {
416 my ($self, $rel) = @_;
aea52c85 417 if( !$self->has_relationship( $rel ) ) {
418 croak "No such relationship '$rel'";
419 }
87772e46 420 return $self->schema->source($self->relationship_info($rel)->{source});
8452e496 421}
422
9c992ba1 4231;
424
425=head1 AUTHORS
426
427Matt S. Trout <mst@shadowcatsystems.co.uk>
428
429=head1 LICENSE
430
431You may distribute this code under the same terms as Perl itself.
432
433=cut
434