Moved PK::Auto into core
[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;
701da8c4 7use Carp::Clan qw/^DBIx::Class/;
6da5894c 8use Storable;
9
9c992ba1 10use base qw/DBIx::Class/;
11__PACKAGE__->load_components(qw/AccessorGroup/);
12
aa1088bf 13__PACKAGE__->mk_group_accessors('simple' => qw/_ordered_columns
14 _columns _primaries _unique_constraints name resultset_attributes
15 schema from _relationships/);
16
17__PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class
bab77431 18 result_class source_name/);
9c992ba1 19
75d07914 20=head1 NAME
9c992ba1 21
22DBIx::Class::ResultSource - Result source object
23
24=head1 SYNOPSIS
25
26=head1 DESCRIPTION
27
28A ResultSource is a component of a schema from which results can be directly
29retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
30
31=head1 METHODS
32
33=cut
34
35sub new {
36 my ($class, $attrs) = @_;
37 $class = ref $class if ref $class;
1225fc4d 38 my $new = bless({ %{$attrs || {}}, _resultset => undef }, $class);
9c992ba1 39 $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
5ac6a044 40 $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} };
6da5894c 41 $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}];
42 $new->{_columns} = { %{$new->{_columns}||{}} };
43 $new->{_relationships} = { %{$new->{_relationships}||{}} };
9c992ba1 44 $new->{name} ||= "!!NAME NOT SET!!";
5afa2a15 45 $new->{_columns_info_loaded} ||= 0;
9c992ba1 46 return $new;
47}
48
988bf309 49=pod
50
5ac6a044 51=head2 add_columns
52
53 $table->add_columns(qw/col1 col2 col3/);
54
55 $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
56
2053ab2a 57Adds columns to the result source. If supplied key => hashref pairs, uses
58the hashref as the column_info for that column. Repeated calls of this
59method will add more columns, not replace them.
5ac6a044 60
2053ab2a 61The contents of the column_info are not set in stone. The following
62keys are currently recognised/used by DBIx::Class:
988bf309 63
64=over 4
65
75d07914 66=item accessor
988bf309 67
68Use this to set the name of the accessor for this column. If unset,
69the name of the column will be used.
70
71=item data_type
72
2053ab2a 73This contains the column type. It is automatically filled by the
988bf309 74L<SQL::Translator::Producer::DBIx::Class::File> producer, and the
2053ab2a 75L<DBIx::Class::Schema::Loader> module. If you do not enter a
988bf309 76data_type, DBIx::Class will attempt to retrieve it from the
2053ab2a 77database for you, using L<DBI>'s column_info method. The values of this
988bf309 78key are typically upper-cased.
79
2053ab2a 80Currently there is no standard set of values for the data_type. Use
81whatever your database supports.
988bf309 82
83=item size
84
85The length of your column, if it is a column type that can have a size
75d07914 86restriction. This is currently not used by DBIx::Class.
988bf309 87
88=item is_nullable
89
2053ab2a 90Set this to a true value for a columns that is allowed to contain
91NULL values. This is currently not used by DBIx::Class.
988bf309 92
93=item is_auto_increment
94
2053ab2a 95Set this to a true value for a column whose value is somehow
96automatically set. This is used to determine which columns to empty
e666492c 97when cloning objects using C<copy>.
988bf309 98
99=item is_foreign_key
100
2053ab2a 101Set this to a true value for a column that contains a key from a
988bf309 102foreign table. This is currently not used by DBIx::Class.
103
104=item default_value
105
2053ab2a 106Set this to the default value which will be inserted into a column
107by the database. Can contain either a value or a function. This is
75d07914 108currently not used by DBIx::Class.
988bf309 109
110=item sequence
111
2053ab2a 112Set this on a primary key column to the name of the sequence used to
113generate a new key value. If not specified, L<DBIx::Class::PK::Auto>
114will attempt to retrieve the name of the sequence from the database
115automatically.
988bf309 116
117=back
118
5ac6a044 119=head2 add_column
120
121 $table->add_column('col' => \%info?);
122
2053ab2a 123Convenience alias to add_columns.
5ac6a044 124
125=cut
126
9c992ba1 127sub add_columns {
128 my ($self, @cols) = @_;
8e04bf91 129 $self->_ordered_columns(\@cols) unless $self->_ordered_columns;
002a359a 130
20518cb4 131 my @added;
132 my $columns = $self->_columns;
9c992ba1 133 while (my $col = shift @cols) {
8e04bf91 134 # If next entry is { ... } use that for the column info, if not
135 # use an empty hashref
30126ac7 136 my $column_info = ref $cols[0] ? shift(@cols) : {};
20518cb4 137 push(@added, $col) unless exists $columns->{$col};
20518cb4 138 $columns->{$col} = $column_info;
9c992ba1 139 }
20518cb4 140 push @{ $self->_ordered_columns }, @added;
30126ac7 141 return $self;
9c992ba1 142}
143
144*add_column = \&add_columns;
145
3842b955 146=head2 has_column
147
988bf309 148 if ($obj->has_column($col)) { ... }
149
2053ab2a 150Returns true if the source has a column of this name, false otherwise.
988bf309 151
152=cut
9c992ba1 153
154sub has_column {
155 my ($self, $column) = @_;
156 return exists $self->_columns->{$column};
157}
158
87c4e602 159=head2 column_info
9c992ba1 160
988bf309 161 my $info = $obj->column_info($col);
9c992ba1 162
988bf309 163Returns the column metadata hashref for a column. See the description
164of add_column for information on the contents of the hashref.
9c992ba1 165
988bf309 166=cut
9c992ba1 167
168sub column_info {
169 my ($self, $column) = @_;
75d07914 170 $self->throw_exception("No such column $column")
701da8c4 171 unless exists $self->_columns->{$column};
5afa2a15 172 #warn $self->{_columns_info_loaded}, "\n";
75d07914 173 if ( ! $self->_columns->{$column}{data_type}
174 and ! $self->{_columns_info_loaded}
8e04bf91 175 and $self->schema and $self->storage )
176 {
177 $self->{_columns_info_loaded}++;
178 my $info;
75d07914 179 # eval for the case of storage without table
8e04bf91 180 eval { $info = $self->storage->columns_info_for($self->from) };
181 unless ($@) {
182 foreach my $col ( keys %{$self->_columns} ) {
183 foreach my $i ( keys %{$info->{$col}} ) {
184 $self->_columns->{$col}{$i} = $info->{$col}{$i};
185 }
a953d8d9 186 }
8e04bf91 187 }
a953d8d9 188 }
9c992ba1 189 return $self->_columns->{$column};
190}
191
192=head2 columns
193
20518cb4 194 my @column_names = $obj->columns;
195
2053ab2a 196Returns all column names in the order they were declared to add_columns.
87f0da6a 197
198=cut
9c992ba1 199
200sub columns {
8e04bf91 201 my $self = shift;
aa1088bf 202 $self->throw_exception(
203 "columns() is a read-only accessor, did you mean add_columns()?"
204 ) if (@_ > 1);
701da8c4 205 return @{$self->{_ordered_columns}||[]};
571dced3 206}
207
002a359a 208=head2 remove_columns
209
210 $table->remove_columns(qw/col1 col2 col3/);
211
212Removes columns from the result source.
213
214=head2 remove_column
215
216 $table->remove_column('col');
217
218Convenience alias to remove_columns.
219
220=cut
221
222sub remove_columns {
223 my ($self, @cols) = @_;
224
225 return unless $self->_ordered_columns;
226
227 my $columns = $self->_columns;
228 my @remaining;
229
230 foreach my $col (@{$self->_ordered_columns}) {
231 push @remaining, $col unless grep(/$col/, @cols);
232 }
233
234 foreach (@cols) {
235 undef $columns->{$_};
236 };
237
238 $self->_ordered_columns(\@remaining);
239}
240
241*remove_column = \&remove_columns;
242
87c4e602 243=head2 set_primary_key
244
27f01d1f 245=over 4
246
ebc77b53 247=item Arguments: @cols
27f01d1f 248
249=back
87f0da6a 250
9c992ba1 251Defines one or more columns as primary key for this source. Should be
252called after C<add_columns>.
87f0da6a 253
254Additionally, defines a unique constraint named C<primary>.
255
988bf309 256The primary key columns are used by L<DBIx::Class::PK::Auto> to
75d07914 257retrieve automatically created values from the database.
988bf309 258
87f0da6a 259=cut
9c992ba1 260
261sub set_primary_key {
262 my ($self, @cols) = @_;
263 # check if primary key columns are valid columns
8e04bf91 264 foreach my $col (@cols) {
265 $self->throw_exception("No such column $col on table " . $self->name)
266 unless $self->has_column($col);
9c992ba1 267 }
268 $self->_primaries(\@cols);
87f0da6a 269
270 $self->add_unique_constraint(primary => \@cols);
9c992ba1 271}
272
87f0da6a 273=head2 primary_columns
274
9c992ba1 275Read-only accessor which returns the list of primary keys.
30126ac7 276
87f0da6a 277=cut
9c992ba1 278
279sub primary_columns {
280 return @{shift->_primaries||[]};
281}
282
87f0da6a 283=head2 add_unique_constraint
284
285Declare a unique constraint on this source. Call once for each unique
58b5bb8c 286constraint.
27f01d1f 287
288 # For UNIQUE (column1, column2)
289 __PACKAGE__->add_unique_constraint(
290 constraint_name => [ qw/column1 column2/ ],
291 );
87f0da6a 292
58b5bb8c 293Unique constraints are used, for example, when you call
294L<DBIx::Class::ResultSet/find>. Only columns in the constraint are searched.
295
87f0da6a 296=cut
297
298sub add_unique_constraint {
299 my ($self, $name, $cols) = @_;
300
8e04bf91 301 foreach my $col (@$cols) {
302 $self->throw_exception("No such column $col on table " . $self->name)
303 unless $self->has_column($col);
87f0da6a 304 }
305
306 my %unique_constraints = $self->unique_constraints;
307 $unique_constraints{$name} = $cols;
308 $self->_unique_constraints(\%unique_constraints);
309}
310
311=head2 unique_constraints
312
313Read-only accessor which returns the list of unique constraints on this source.
314
315=cut
316
317sub unique_constraints {
318 return %{shift->_unique_constraints||{}};
319}
320
e6a0e17c 321=head2 unique_constraint_names
322
323Returns the list of unique constraint names defined on this source.
324
325=cut
326
327sub unique_constraint_names {
328 my ($self) = @_;
329
330 my %unique_constraints = $self->unique_constraints;
331
332 return keys %unique_constraints;
333}
334
335=head2 unique_constraint_columns
336
337Returns the list of columns that make up the specified unique constraint.
338
339=cut
340
341sub unique_constraint_columns {
342 my ($self, $constraint_name) = @_;
343
344 my %unique_constraints = $self->unique_constraints;
345
346 $self->throw_exception(
347 "Unknown unique constraint $constraint_name on '" . $self->name . "'"
348 ) unless exists $unique_constraints{$constraint_name};
349
350 return @{ $unique_constraints{$constraint_name} };
351}
352
9c992ba1 353=head2 from
354
355Returns an expression of the source to be supplied to storage to specify
2053ab2a 356retrieval from this source. In the case of a database, the required FROM
357clause contents.
9c992ba1 358
f9b7bd6e 359=head2 schema
360
361Returns the L<DBIx::Class::Schema> object that this result source
362belongs too.
9c992ba1 363
364=head2 storage
365
75d07914 366Returns the storage handle for the current schema.
988bf309 367
368See also: L<DBIx::Class::Storage>
9c992ba1 369
370=cut
371
372sub storage { shift->schema->storage; }
373
8452e496 374=head2 add_relationship
375
376 $source->add_relationship('relname', 'related_source', $cond, $attrs);
377
24d67825 378The relationship name can be arbitrary, but must be unique for each
379relationship attached to this result source. 'related_source' should
380be the name with which the related result source was registered with
381the current schema. For example:
8452e496 382
24d67825 383 $schema->source('Book')->add_relationship('reviews', 'Review', {
384 'foreign.book_id' => 'self.id',
385 });
386
2053ab2a 387The condition C<$cond> needs to be an L<SQL::Abstract>-style
24d67825 388representation of the join between the tables. For example, if you're
389creating a rel from Author to Book,
988bf309 390
391 { 'foreign.author_id' => 'self.id' }
392
393will result in the JOIN clause
394
395 author me JOIN book foreign ON foreign.author_id = me.id
396
8452e496 397You can specify as many foreign => self mappings as necessary.
398
988bf309 399Valid attributes are as follows:
400
401=over 4
402
403=item join_type
404
405Explicitly specifies the type of join to use in the relationship. Any
406SQL join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in
407the SQL command immediately before C<JOIN>.
408
409=item proxy
410
24d67825 411An arrayref containing a list of accessors in the foreign class to proxy in
412the main class. If, for example, you do the following:
002a359a 413
24d67825 414 CD->might_have(liner_notes => 'LinerNotes', undef, {
415 proxy => [ qw/notes/ ],
416 });
002a359a 417
24d67825 418Then, assuming LinerNotes has an accessor named notes, you can do:
988bf309 419
24d67825 420 my $cd = CD->find(1);
2053ab2a 421 # set notes -- LinerNotes object is created if it doesn't exist
422 $cd->notes('Notes go here');
988bf309 423
424=item accessor
425
426Specifies the type of accessor that should be created for the
75d07914 427relationship. Valid values are C<single> (for when there is only a single
428related object), C<multi> (when there can be many), and C<filter> (for
429when there is a single related object, but you also want the relationship
430accessor to double as a column accessor). For C<multi> accessors, an
431add_to_* method is also created, which calls C<create_related> for the
988bf309 432relationship.
433
8452e496 434=back
435
436=cut
437
438sub add_relationship {
439 my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
27f01d1f 440 $self->throw_exception("Can't create relationship without join condition")
441 unless $cond;
8452e496 442 $attrs ||= {};
87772e46 443
8452e496 444 my %rels = %{ $self->_relationships };
445 $rels{$rel} = { class => $f_source_name,
87772e46 446 source => $f_source_name,
8452e496 447 cond => $cond,
448 attrs => $attrs };
449 $self->_relationships(\%rels);
450
30126ac7 451 return $self;
87772e46 452
953a18ef 453 # XXX disabled. doesn't work properly currently. skip in tests.
454
8452e496 455 my $f_source = $self->schema->source($f_source_name);
456 unless ($f_source) {
457 eval "require $f_source_name;";
458 if ($@) {
459 die $@ unless $@ =~ /Can't locate/;
460 }
461 $f_source = $f_source_name->result_source;
87772e46 462 #my $s_class = ref($self->schema);
463 #$f_source_name =~ m/^${s_class}::(.*)$/;
464 #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
465 #$f_source = $self->schema->source($f_source_name);
8452e496 466 }
467 return unless $f_source; # Can't test rel without f_source
468
469 eval { $self->resolve_join($rel, 'me') };
470
471 if ($@) { # If the resolve failed, back out and re-throw the error
75d07914 472 delete $rels{$rel}; #
8452e496 473 $self->_relationships(\%rels);
701da8c4 474 $self->throw_exception("Error creating relationship $rel: $@");
8452e496 475 }
476 1;
477}
478
87c4e602 479=head2 relationships
8452e496 480
2053ab2a 481Returns all relationship names for this source.
8452e496 482
483=cut
484
485sub relationships {
486 return keys %{shift->_relationships};
487}
488
87c4e602 489=head2 relationship_info
490
27f01d1f 491=over 4
492
ebc77b53 493=item Arguments: $relname
27f01d1f 494
495=back
8452e496 496
2053ab2a 497Returns a hash of relationship information for the specified relationship
498name.
8452e496 499
500=cut
501
502sub relationship_info {
503 my ($self, $rel) = @_;
504 return $self->_relationships->{$rel};
75d07914 505}
8452e496 506
87c4e602 507=head2 has_relationship
508
27f01d1f 509=over 4
510
ebc77b53 511=item Arguments: $rel
27f01d1f 512
513=back
953a18ef 514
2053ab2a 515Returns true if the source has a relationship of this name, false otherwise.
988bf309 516
517=cut
953a18ef 518
519sub has_relationship {
520 my ($self, $rel) = @_;
521 return exists $self->_relationships->{$rel};
522}
523
de60a93d 524=head2 reverse_relationship_info
525
526=over 4
527
528=item Arguments: $relname
529
530=back
531
bab77431 532Returns an array of hash references of relationship information for
de60a93d 533the other side of the specified relationship name.
534
535=cut
536
537sub reverse_relationship_info {
538 my ($self, $rel) = @_;
539 my $rel_info = $self->relationship_info($rel);
540 my $ret = {};
541
542 return $ret unless ((ref $rel_info->{cond}) eq 'HASH');
543
544 my @cond = keys(%{$rel_info->{cond}});
545 my @refkeys = map {/^\w+\.(\w+)$/} @cond;
546 my @keys = map {$rel_info->{cond}->{$_} =~ /^\w+\.(\w+)$/} @cond;
bab77431 547
de60a93d 548 # Get the related result source for this relationship
549 my $othertable = $self->related_source($rel);
550
551 # Get all the relationships for that source that related to this source
552 # whose foreign column set are our self columns on $rel and whose self
bab77431 553 # columns are our foreign columns on $rel.
de60a93d 554 my @otherrels = $othertable->relationships();
555 my $otherrelationship;
556 foreach my $otherrel (@otherrels) {
557 my $otherrel_info = $othertable->relationship_info($otherrel);
558
559 my $back = $othertable->related_source($otherrel);
560 next unless $back->name eq $self->name;
561
562 my @othertestconds;
563
564 if (ref $otherrel_info->{cond} eq 'HASH') {
565 @othertestconds = ($otherrel_info->{cond});
566 }
567 elsif (ref $otherrel_info->{cond} eq 'ARRAY') {
568 @othertestconds = @{$otherrel_info->{cond}};
569 }
570 else {
571 next;
572 }
573
574 foreach my $othercond (@othertestconds) {
575 my @other_cond = keys(%$othercond);
576 my @other_refkeys = map {/^\w+\.(\w+)$/} @other_cond;
577 my @other_keys = map {$othercond->{$_} =~ /^\w+\.(\w+)$/} @other_cond;
bab77431 578 next if (!$self->compare_relationship_keys(\@refkeys, \@other_keys) ||
de60a93d 579 !$self->compare_relationship_keys(\@other_refkeys, \@keys));
580 $ret->{$otherrel} = $otherrel_info;
581 }
582 }
583 return $ret;
584}
585
586=head2 compare_relationship_keys
587
588=over 4
589
590=item Arguments: $keys1, $keys2
591
592=back
593
594Returns true if both sets of keynames are the same, false otherwise.
595
596=cut
597
598sub compare_relationship_keys {
599 my ($self, $keys1, $keys2) = @_;
600
601 # Make sure every keys1 is in keys2
602 my $found;
603 foreach my $key (@$keys1) {
604 $found = 0;
605 foreach my $prim (@$keys2) {
606 if ($prim eq $key) {
607 $found = 1;
608 last;
609 }
610 }
611 last unless $found;
612 }
613
614 # Make sure every key2 is in key1
615 if ($found) {
616 foreach my $prim (@$keys2) {
617 $found = 0;
618 foreach my $key (@$keys1) {
619 if ($prim eq $key) {
620 $found = 1;
621 last;
622 }
623 }
624 last unless $found;
625 }
626 }
627
628 return $found;
629}
630
87c4e602 631=head2 resolve_join
632
27f01d1f 633=over 4
634
ebc77b53 635=item Arguments: $relation
27f01d1f 636
637=back
8452e496 638
2053ab2a 639Returns the join structure required for the related result source.
8452e496 640
641=cut
642
643sub resolve_join {
489709af 644 my ($self, $join, $alias, $seen) = @_;
645 $seen ||= {};
87772e46 646 if (ref $join eq 'ARRAY') {
489709af 647 return map { $self->resolve_join($_, $alias, $seen) } @$join;
87772e46 648 } elsif (ref $join eq 'HASH') {
489709af 649 return
887ce227 650 map {
651 my $as = ($seen->{$_} ? $_.'_'.($seen->{$_}+1) : $_);
652 ($self->resolve_join($_, $alias, $seen),
653 $self->related_source($_)->resolve_join($join->{$_}, $as, $seen));
654 } keys %$join;
87772e46 655 } elsif (ref $join) {
701da8c4 656 $self->throw_exception("No idea how to resolve join reftype ".ref $join);
87772e46 657 } else {
489709af 658 my $count = ++$seen->{$join};
659 #use Data::Dumper; warn Dumper($seen);
660 my $as = ($count > 1 ? "${join}_${count}" : $join);
3842b955 661 my $rel_info = $self->relationship_info($join);
701da8c4 662 $self->throw_exception("No such relationship ${join}") unless $rel_info;
3842b955 663 my $type = $rel_info->{attrs}{join_type} || '';
489709af 664 return [ { $as => $self->related_source($join)->from,
953a18ef 665 -join_type => $type },
489709af 666 $self->resolve_condition($rel_info->{cond}, $as, $alias) ];
953a18ef 667 }
668}
669
87c4e602 670=head2 resolve_condition
671
27f01d1f 672=over 4
673
ebc77b53 674=item Arguments: $cond, $as, $alias|$object
27f01d1f 675
676=back
953a18ef 677
3842b955 678Resolves the passed condition to a concrete query fragment. If given an alias,
953a18ef 679returns a join condition; if given an object, inverts that object to produce
680a related conditional from that object.
681
682=cut
683
684sub resolve_condition {
489709af 685 my ($self, $cond, $as, $for) = @_;
953a18ef 686 #warn %$cond;
687 if (ref $cond eq 'HASH') {
688 my %ret;
bd054cb4 689 foreach my $k (keys %{$cond}) {
690 my $v = $cond->{$k};
953a18ef 691 # XXX should probably check these are valid columns
27f01d1f 692 $k =~ s/^foreign\.// ||
75d07914 693 $self->throw_exception("Invalid rel cond key ${k}");
27f01d1f 694 $v =~ s/^self\.// ||
75d07914 695 $self->throw_exception("Invalid rel cond val ${v}");
953a18ef 696 if (ref $for) { # Object
3842b955 697 #warn "$self $k $for $v";
698 $ret{$k} = $for->get_column($v);
699 #warn %ret;
2c037e6b 700 } elsif (!defined $for) { # undef, i.e. "no object"
701 $ret{$k} = undef;
fde6e28e 702 } elsif (ref $as) { # reverse object
703 $ret{$v} = $as->get_column($k);
2c037e6b 704 } elsif (!defined $as) { # undef, i.e. "no reverse object"
705 $ret{$v} = undef;
953a18ef 706 } else {
489709af 707 $ret{"${as}.${k}"} = "${for}.${v}";
953a18ef 708 }
953a18ef 709 }
710 return \%ret;
5efe4c79 711 } elsif (ref $cond eq 'ARRAY') {
489709af 712 return [ map { $self->resolve_condition($_, $as, $for) } @$cond ];
953a18ef 713 } else {
714 die("Can't handle this yet :(");
87772e46 715 }
716}
717
87c4e602 718=head2 resolve_prefetch
719
27f01d1f 720=over 4
721
ebc77b53 722=item Arguments: hashref/arrayref/scalar
27f01d1f 723
724=back
988bf309 725
b3e8ac9b 726Accepts one or more relationships for the current source and returns an
727array of column names for each of those relationships. Column names are
728prefixed relative to the current source, in accordance with where they appear
729in the supplied relationships. Examples:
730
5ac6a044 731 my $source = $schema->resultset('Tag')->source;
b3e8ac9b 732 @columns = $source->resolve_prefetch( { cd => 'artist' } );
733
734 # @columns =
735 #(
736 # 'cd.cdid',
737 # 'cd.artist',
738 # 'cd.title',
739 # 'cd.year',
740 # 'cd.artist.artistid',
741 # 'cd.artist.name'
742 #)
743
744 @columns = $source->resolve_prefetch( qw[/ cd /] );
745
746 # @columns =
747 #(
748 # 'cd.cdid',
749 # 'cd.artist',
750 # 'cd.title',
751 # 'cd.year'
752 #)
753
754 $source = $schema->resultset('CD')->source;
755 @columns = $source->resolve_prefetch( qw[/ artist producer /] );
756
757 # @columns =
758 #(
759 # 'artist.artistid',
760 # 'artist.name',
761 # 'producer.producerid',
762 # 'producer.name'
75d07914 763 #)
988bf309 764
b3e8ac9b 765=cut
766
767sub resolve_prefetch {
0f66a01b 768 my ($self, $pre, $alias, $seen, $order, $collapse) = @_;
489709af 769 $seen ||= {};
b3e8ac9b 770 #$alias ||= $self->name;
771 #warn $alias, Dumper $pre;
772 if( ref $pre eq 'ARRAY' ) {
0f66a01b 773 return
774 map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) }
775 @$pre;
b3e8ac9b 776 }
777 elsif( ref $pre eq 'HASH' ) {
778 my @ret =
779 map {
0f66a01b 780 $self->resolve_prefetch($_, $alias, $seen, $order, $collapse),
489709af 781 $self->related_source($_)->resolve_prefetch(
0f66a01b 782 $pre->{$_}, "${alias}.$_", $seen, $order, $collapse)
783 } keys %$pre;
b3e8ac9b 784 #die Dumper \@ret;
785 return @ret;
786 }
787 elsif( ref $pre ) {
a86b1efe 788 $self->throw_exception(
789 "don't know how to resolve prefetch reftype ".ref($pre));
b3e8ac9b 790 }
791 else {
489709af 792 my $count = ++$seen->{$pre};
793 my $as = ($count > 1 ? "${pre}_${count}" : $pre);
b3e8ac9b 794 my $rel_info = $self->relationship_info( $pre );
a86b1efe 795 $self->throw_exception( $self->name . " has no such relationship '$pre'" )
796 unless $rel_info;
37f23589 797 my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : '');
a86b1efe 798 my $rel_source = $self->related_source($pre);
0f66a01b 799
800 if (exists $rel_info->{attrs}{accessor}
801 && $rel_info->{attrs}{accessor} eq 'multi') {
802 $self->throw_exception(
803 "Can't prefetch has_many ${pre} (join cond too complex)")
804 unless ref($rel_info->{cond}) eq 'HASH';
37f23589 805 my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); }
0f66a01b 806 keys %{$rel_info->{cond}};
807 $collapse->{"${as_prefix}${pre}"} = \@key;
5a5bec6c 808 my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY'
809 ? @{$rel_info->{attrs}{order_by}}
810 : (defined $rel_info->{attrs}{order_by}
811 ? ($rel_info->{attrs}{order_by})
812 : ()));
813 push(@$order, map { "${as}.$_" } (@key, @ord));
0f66a01b 814 }
815
489709af 816 return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
a86b1efe 817 $rel_source->columns;
b3e8ac9b 818 #warn $alias, Dumper (\@ret);
489709af 819 #return @ret;
b3e8ac9b 820 }
821}
953a18ef 822
87c4e602 823=head2 related_source
824
27f01d1f 825=over 4
826
ebc77b53 827=item Arguments: $relname
27f01d1f 828
829=back
87772e46 830
2053ab2a 831Returns the result source object for the given relationship.
87772e46 832
833=cut
834
835sub related_source {
836 my ($self, $rel) = @_;
aea52c85 837 if( !$self->has_relationship( $rel ) ) {
701da8c4 838 $self->throw_exception("No such relationship '$rel'");
aea52c85 839 }
87772e46 840 return $self->schema->source($self->relationship_info($rel)->{source});
8452e496 841}
842
77254782 843=head2 related_class
844
27f01d1f 845=over 4
846
ebc77b53 847=item Arguments: $relname
27f01d1f 848
849=back
77254782 850
2053ab2a 851Returns the class name for objects in the given relationship.
77254782 852
853=cut
854
855sub related_class {
856 my ($self, $rel) = @_;
857 if( !$self->has_relationship( $rel ) ) {
858 $self->throw_exception("No such relationship '$rel'");
859 }
860 return $self->schema->class($self->relationship_info($rel)->{source});
861}
862
5ac6a044 863=head2 resultset
864
bcc5a210 865Returns a resultset for the given source. This will initially be created
866on demand by calling
5ac6a044 867
988bf309 868 $self->resultset_class->new($self, $self->resultset_attributes)
5ac6a044 869
bcc5a210 870but is cached from then on unless resultset_class changes.
871
5ac6a044 872=head2 resultset_class
873
988bf309 874Set the class of the resultset, this is useful if you want to create your
875own resultset methods. Create your own class derived from
876L<DBIx::Class::ResultSet>, and set it here.
5ac6a044 877
878=head2 resultset_attributes
879
988bf309 880Specify here any attributes you wish to pass to your specialised resultset.
5ac6a044 881
882=cut
883
884sub resultset {
885 my $self = shift;
27f01d1f 886 $self->throw_exception(
887 'resultset does not take any arguments. If you want another resultset, '.
888 'call it on the schema instead.'
889 ) if scalar @_;
428c2b82 890
891 # disabled until we can figure out a way to do it without consistency issues
892 #
893 #return $self->{_resultset}
894 # if ref $self->{_resultset} eq $self->resultset_class;
895 #return $self->{_resultset} =
896
897 return $self->resultset_class->new(
27f01d1f 898 $self, $self->{resultset_attributes}
899 );
5ac6a044 900}
901
bab77431 902=head2 source_name
903
904=over 4
905
906=item Arguments: $source_name
907
908=back
909
910Set the name of the result source when it is loaded into a schema.
911This is usefull if you want to refer to a result source by a name other than
912its class name.
913
914 package ArchivedBooks;
915 use base qw/DBIx::Class/;
916 __PACKAGE__->table('books_archive');
917 __PACKAGE__->source_name('Books');
918
919 # from your schema...
920 $schema->resultset('Books')->find(1);
921
701da8c4 922=head2 throw_exception
923
2053ab2a 924See L<DBIx::Class::Schema/"throw_exception">.
701da8c4 925
926=cut
927
928sub throw_exception {
929 my $self = shift;
75d07914 930 if (defined $self->schema) {
701da8c4 931 $self->schema->throw_exception(@_);
932 } else {
933 croak(@_);
934 }
935}
936
9c992ba1 937=head1 AUTHORS
938
939Matt S. Trout <mst@shadowcatsystems.co.uk>
940
941=head1 LICENSE
942
943You may distribute this code under the same terms as Perl itself.
944
945=cut
946