more assorted kleenoops
[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/;
9c992ba1 8
6da5894c 9use Storable;
1225fc4d 10use Scalar::Util qw/weaken/;
6da5894c 11
9c992ba1 12use base qw/DBIx::Class/;
13__PACKAGE__->load_components(qw/AccessorGroup/);
14
15__PACKAGE__->mk_group_accessors('simple' =>
fc969005 16 qw/_ordered_columns _columns _primaries _unique_constraints name resultset_attributes schema from _relationships/);
17__PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class result_class/);
9c992ba1 18
19=head1 NAME
20
21DBIx::Class::ResultSource - Result source object
22
23=head1 SYNOPSIS
24
25=head1 DESCRIPTION
26
27A ResultSource is a component of a schema from which results can be directly
28retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
29
30=head1 METHODS
31
32=cut
33
34sub new {
35 my ($class, $attrs) = @_;
36 $class = ref $class if ref $class;
1225fc4d 37 my $new = bless({ %{$attrs || {}}, _resultset => undef }, $class);
9c992ba1 38 $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
5ac6a044 39 $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} };
6da5894c 40 $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}];
41 $new->{_columns} = { %{$new->{_columns}||{}} };
42 $new->{_relationships} = { %{$new->{_relationships}||{}} };
9c992ba1 43 $new->{name} ||= "!!NAME NOT SET!!";
5afa2a15 44 $new->{_columns_info_loaded} ||= 0;
9c992ba1 45 return $new;
46}
47
988bf309 48=pod
49
5ac6a044 50=head2 add_columns
51
52 $table->add_columns(qw/col1 col2 col3/);
53
54 $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
55
56Adds columns to the result source. If supplied key => hashref pairs uses
57the hashref as the column_info for that column.
58
988bf309 59Repeated calls of this method will add more columns, not replace them.
60
61The contents of the column_info are not set in stone, the following
62keys are currently recognised/used by DBIx::Class.
63
64=over 4
65
66=item accessor
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
73This contains the column type, it is automatically filled by the
74L<SQL::Translator::Producer::DBIx::Class::File> producer, and the
75L<DBIx::Class::Schema::Loader> module. If you do not enter the
76data_type, DBIx::Class will attempt to retrieve it from the
77database for you, using L<DBI>s column_info method. The values of this
78key are typically upper-cased.
79
80Currently there is no standard set of values for the data_type, use
81whatever your database(s) support.
82
83=item size
84
85The length of your column, if it is a column type that can have a size
86restriction. This is currently not used by DBIx::Class.
87
88=item is_nullable
89
90If the column is allowed to contain NULL values, set a true value
91(typically 1), here. This is currently not used by DBIx::Class.
92
93=item is_auto_increment
94
95Set this to a true value if this is a column that is somehow
96automatically filled. This is currently not used by DBIx::Class.
97
98=item is_foreign_key
99
100Set this to a true value if this column represents a key from a
101foreign table. This is currently not used by DBIx::Class.
102
103=item default_value
104
105Set this to the default value which will be inserted into this column
106by the database. Can contain either values or functions. This is
107currently not used by DBIx::Class.
108
109=item sequence
110
111If your column is using a sequence to create it's values, set the name
112of the sequence here, to allow the values to be retrieved
113automatically by the L<DBIx::Class::PK::Auto> module. PK::Auto will
114attempt to retrieve the sequence name from the database, if this value
115is left unset.
116
117=back
118
5ac6a044 119=head2 add_column
120
121 $table->add_column('col' => \%info?);
122
123Convenience alias to add_columns
124
125=cut
126
9c992ba1 127sub add_columns {
128 my ($self, @cols) = @_;
8e04bf91 129 $self->_ordered_columns(\@cols) unless $self->_ordered_columns;
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
9c992ba1 150Returns 1 if the source has a column of this name, 0 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) = @_;
701da8c4 170 $self->throw_exception("No such column $column")
171 unless exists $self->_columns->{$column};
5afa2a15 172 #warn $self->{_columns_info_loaded}, "\n";
8e04bf91 173 if ( ! $self->_columns->{$column}{data_type}
174 and ! $self->{_columns_info_loaded}
175 and $self->schema and $self->storage )
176 {
177 $self->{_columns_info_loaded}++;
178 my $info;
179 # eval for the case of storage without table
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
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;
701da8c4 202 $self->throw_exception("columns() is a read-only accessor, did you mean add_columns()?") if (@_ > 1);
203 return @{$self->{_ordered_columns}||[]};
571dced3 204}
205
87c4e602 206=head2 set_primary_key
207
208=head3 Arguments: (@cols)
87f0da6a 209
9c992ba1 210Defines one or more columns as primary key for this source. Should be
211called after C<add_columns>.
87f0da6a 212
213Additionally, defines a unique constraint named C<primary>.
214
988bf309 215The primary key columns are used by L<DBIx::Class::PK::Auto> to
216retrieve automatically created values from the database.
217
87f0da6a 218=cut
9c992ba1 219
220sub set_primary_key {
221 my ($self, @cols) = @_;
222 # check if primary key columns are valid columns
8e04bf91 223 foreach my $col (@cols) {
224 $self->throw_exception("No such column $col on table " . $self->name)
225 unless $self->has_column($col);
9c992ba1 226 }
227 $self->_primaries(\@cols);
87f0da6a 228
229 $self->add_unique_constraint(primary => \@cols);
9c992ba1 230}
231
87f0da6a 232=head2 primary_columns
233
9c992ba1 234Read-only accessor which returns the list of primary keys.
30126ac7 235
87f0da6a 236=cut
9c992ba1 237
238sub primary_columns {
239 return @{shift->_primaries||[]};
240}
241
87f0da6a 242=head2 add_unique_constraint
243
244Declare a unique constraint on this source. Call once for each unique
988bf309 245constraint. Unique constraints are used when you call C<find> on a
958916aa 246L<DBIx::Class::ResultSet>, only columns in the constraint are searched,
87f0da6a 247
248 # For e.g. UNIQUE (column1, column2)
249 __PACKAGE__->add_unique_constraint(constraint_name => [ qw/column1 column2/ ]);
250
251=cut
252
253sub add_unique_constraint {
254 my ($self, $name, $cols) = @_;
255
8e04bf91 256 foreach my $col (@$cols) {
257 $self->throw_exception("No such column $col on table " . $self->name)
258 unless $self->has_column($col);
87f0da6a 259 }
260
261 my %unique_constraints = $self->unique_constraints;
262 $unique_constraints{$name} = $cols;
263 $self->_unique_constraints(\%unique_constraints);
264}
265
266=head2 unique_constraints
267
268Read-only accessor which returns the list of unique constraints on this source.
269
270=cut
271
272sub unique_constraints {
273 return %{shift->_unique_constraints||{}};
274}
275
9c992ba1 276=head2 from
277
278Returns an expression of the source to be supplied to storage to specify
279retrieval from this source; in the case of a database the required FROM clause
280contents.
281
282=cut
283
284=head2 storage
285
988bf309 286Returns the storage handle for the current schema.
287
288See also: L<DBIx::Class::Storage>
9c992ba1 289
290=cut
291
292sub storage { shift->schema->storage; }
293
8452e496 294=head2 add_relationship
295
296 $source->add_relationship('relname', 'related_source', $cond, $attrs);
297
298The relation name can be arbitrary, but must be unique for each relationship
299attached to this result source. 'related_source' should be the name with
300which the related result source was registered with the current schema
301(for simple schemas this is usally either Some::Namespace::Foo or just Foo)
302
303The condition needs to be an SQL::Abstract-style representation of the join
988bf309 304between the tables. For example, if you're creating a rel from Author to Book,
305
306 { 'foreign.author_id' => 'self.id' }
307
308will result in the JOIN clause
309
310 author me JOIN book foreign ON foreign.author_id = me.id
311
8452e496 312You can specify as many foreign => self mappings as necessary.
313
988bf309 314Valid attributes are as follows:
315
316=over 4
317
318=item join_type
319
320Explicitly specifies the type of join to use in the relationship. Any
321SQL join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in
322the SQL command immediately before C<JOIN>.
323
324=item proxy
325
326An arrayref containing a list of accessors in the foreign class to
327proxy in the main class. If, for example, you do the following:
328
329 __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => [ qw/margle/] });
330
331Then, assuming Bar has an accessor named margle, you can do:
332
333 my $obj = Foo->find(1);
334 $obj->margle(10); # set margle; Bar object is created if it doesn't exist
335
336=item accessor
337
338Specifies the type of accessor that should be created for the
339relationship. Valid values are C<single> (for when there is only a single
340related object), C<multi> (when there can be many), and C<filter> (for
341when there is a single related object, but you also want the relationship
342accessor to double as a column accessor). For C<multi> accessors, an
343add_to_* method is also created, which calls C<create_related> for the
344relationship.
345
8452e496 346=back
347
348=cut
349
350sub add_relationship {
351 my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
701da8c4 352 $self->throw_exception("Can't create relationship without join condition") unless $cond;
8452e496 353 $attrs ||= {};
87772e46 354
8452e496 355 my %rels = %{ $self->_relationships };
356 $rels{$rel} = { class => $f_source_name,
87772e46 357 source => $f_source_name,
8452e496 358 cond => $cond,
359 attrs => $attrs };
360 $self->_relationships(\%rels);
361
30126ac7 362 return $self;
87772e46 363
953a18ef 364 # XXX disabled. doesn't work properly currently. skip in tests.
365
8452e496 366 my $f_source = $self->schema->source($f_source_name);
367 unless ($f_source) {
368 eval "require $f_source_name;";
369 if ($@) {
370 die $@ unless $@ =~ /Can't locate/;
371 }
372 $f_source = $f_source_name->result_source;
87772e46 373 #my $s_class = ref($self->schema);
374 #$f_source_name =~ m/^${s_class}::(.*)$/;
375 #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
376 #$f_source = $self->schema->source($f_source_name);
8452e496 377 }
378 return unless $f_source; # Can't test rel without f_source
379
380 eval { $self->resolve_join($rel, 'me') };
381
382 if ($@) { # If the resolve failed, back out and re-throw the error
383 delete $rels{$rel}; #
384 $self->_relationships(\%rels);
701da8c4 385 $self->throw_exception("Error creating relationship $rel: $@");
8452e496 386 }
387 1;
388}
389
87c4e602 390=head2 relationships
8452e496 391
392Returns all valid relationship names for this source
393
394=cut
395
396sub relationships {
397 return keys %{shift->_relationships};
398}
399
87c4e602 400=head2 relationship_info
401
402=head3 Arguments: ($relname)
8452e496 403
404Returns the relationship information for the specified relationship name
405
406=cut
407
408sub relationship_info {
409 my ($self, $rel) = @_;
410 return $self->_relationships->{$rel};
411}
412
87c4e602 413=head2 has_relationship
414
415=head3 Arguments: ($rel)
953a18ef 416
417Returns 1 if the source has a relationship of this name, 0 otherwise.
988bf309 418
419=cut
953a18ef 420
421sub has_relationship {
422 my ($self, $rel) = @_;
423 return exists $self->_relationships->{$rel};
424}
425
87c4e602 426=head2 resolve_join
427
428=head3 Arguments: ($relation)
8452e496 429
430Returns the join structure required for the related result source
431
432=cut
433
434sub resolve_join {
489709af 435 my ($self, $join, $alias, $seen) = @_;
436 $seen ||= {};
87772e46 437 if (ref $join eq 'ARRAY') {
489709af 438 return map { $self->resolve_join($_, $alias, $seen) } @$join;
87772e46 439 } elsif (ref $join eq 'HASH') {
489709af 440 return
887ce227 441 map {
442 my $as = ($seen->{$_} ? $_.'_'.($seen->{$_}+1) : $_);
443 ($self->resolve_join($_, $alias, $seen),
444 $self->related_source($_)->resolve_join($join->{$_}, $as, $seen));
445 } keys %$join;
87772e46 446 } elsif (ref $join) {
701da8c4 447 $self->throw_exception("No idea how to resolve join reftype ".ref $join);
87772e46 448 } else {
489709af 449 my $count = ++$seen->{$join};
450 #use Data::Dumper; warn Dumper($seen);
451 my $as = ($count > 1 ? "${join}_${count}" : $join);
3842b955 452 my $rel_info = $self->relationship_info($join);
701da8c4 453 $self->throw_exception("No such relationship ${join}") unless $rel_info;
3842b955 454 my $type = $rel_info->{attrs}{join_type} || '';
489709af 455 return [ { $as => $self->related_source($join)->from,
953a18ef 456 -join_type => $type },
489709af 457 $self->resolve_condition($rel_info->{cond}, $as, $alias) ];
953a18ef 458 }
459}
460
87c4e602 461=head2 resolve_condition
462
463=head3 Arguments: ($cond, $as, $alias|$object)
953a18ef 464
3842b955 465Resolves the passed condition to a concrete query fragment. If given an alias,
953a18ef 466returns a join condition; if given an object, inverts that object to produce
467a related conditional from that object.
468
469=cut
470
471sub resolve_condition {
489709af 472 my ($self, $cond, $as, $for) = @_;
953a18ef 473 #warn %$cond;
474 if (ref $cond eq 'HASH') {
475 my %ret;
476 while (my ($k, $v) = each %{$cond}) {
477 # XXX should probably check these are valid columns
701da8c4 478 $k =~ s/^foreign\.// || $self->throw_exception("Invalid rel cond key ${k}");
479 $v =~ s/^self\.// || $self->throw_exception("Invalid rel cond val ${v}");
953a18ef 480 if (ref $for) { # Object
3842b955 481 #warn "$self $k $for $v";
482 $ret{$k} = $for->get_column($v);
483 #warn %ret;
fde6e28e 484 } elsif (ref $as) { # reverse object
485 $ret{$v} = $as->get_column($k);
953a18ef 486 } else {
489709af 487 $ret{"${as}.${k}"} = "${for}.${v}";
953a18ef 488 }
953a18ef 489 }
490 return \%ret;
5efe4c79 491 } elsif (ref $cond eq 'ARRAY') {
489709af 492 return [ map { $self->resolve_condition($_, $as, $for) } @$cond ];
953a18ef 493 } else {
494 die("Can't handle this yet :(");
87772e46 495 }
496}
497
87c4e602 498=head2 resolve_prefetch
499
500=head3 Arguments: (hashref/arrayref/scalar)
988bf309 501
b3e8ac9b 502Accepts one or more relationships for the current source and returns an
503array of column names for each of those relationships. Column names are
504prefixed relative to the current source, in accordance with where they appear
505in the supplied relationships. Examples:
506
5ac6a044 507 my $source = $schema->resultset('Tag')->source;
b3e8ac9b 508 @columns = $source->resolve_prefetch( { cd => 'artist' } );
509
510 # @columns =
511 #(
512 # 'cd.cdid',
513 # 'cd.artist',
514 # 'cd.title',
515 # 'cd.year',
516 # 'cd.artist.artistid',
517 # 'cd.artist.name'
518 #)
519
520 @columns = $source->resolve_prefetch( qw[/ cd /] );
521
522 # @columns =
523 #(
524 # 'cd.cdid',
525 # 'cd.artist',
526 # 'cd.title',
527 # 'cd.year'
528 #)
529
530 $source = $schema->resultset('CD')->source;
531 @columns = $source->resolve_prefetch( qw[/ artist producer /] );
532
533 # @columns =
534 #(
535 # 'artist.artistid',
536 # 'artist.name',
537 # 'producer.producerid',
538 # 'producer.name'
539 #)
988bf309 540
b3e8ac9b 541=cut
542
543sub resolve_prefetch {
0f66a01b 544 my ($self, $pre, $alias, $seen, $order, $collapse) = @_;
489709af 545 $seen ||= {};
b3e8ac9b 546 #$alias ||= $self->name;
547 #warn $alias, Dumper $pre;
548 if( ref $pre eq 'ARRAY' ) {
0f66a01b 549 return
550 map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) }
551 @$pre;
b3e8ac9b 552 }
553 elsif( ref $pre eq 'HASH' ) {
554 my @ret =
555 map {
0f66a01b 556 $self->resolve_prefetch($_, $alias, $seen, $order, $collapse),
489709af 557 $self->related_source($_)->resolve_prefetch(
0f66a01b 558 $pre->{$_}, "${alias}.$_", $seen, $order, $collapse)
559 } keys %$pre;
b3e8ac9b 560 #die Dumper \@ret;
561 return @ret;
562 }
563 elsif( ref $pre ) {
a86b1efe 564 $self->throw_exception(
565 "don't know how to resolve prefetch reftype ".ref($pre));
b3e8ac9b 566 }
567 else {
489709af 568 my $count = ++$seen->{$pre};
569 my $as = ($count > 1 ? "${pre}_${count}" : $pre);
b3e8ac9b 570 my $rel_info = $self->relationship_info( $pre );
a86b1efe 571 $self->throw_exception( $self->name . " has no such relationship '$pre'" )
572 unless $rel_info;
489709af 573 my $as_prefix = ($alias =~ /^.*?\.(.*)$/ ? $1.'.' : '');
a86b1efe 574 my $rel_source = $self->related_source($pre);
0f66a01b 575
576 if (exists $rel_info->{attrs}{accessor}
577 && $rel_info->{attrs}{accessor} eq 'multi') {
578 $self->throw_exception(
579 "Can't prefetch has_many ${pre} (join cond too complex)")
580 unless ref($rel_info->{cond}) eq 'HASH';
581 my @key = map { (/^foreign\.(.*)$/ ? ($1) : ()); }
582 keys %{$rel_info->{cond}};
583 $collapse->{"${as_prefix}${pre}"} = \@key;
5a5bec6c 584 my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY'
585 ? @{$rel_info->{attrs}{order_by}}
586 : (defined $rel_info->{attrs}{order_by}
587 ? ($rel_info->{attrs}{order_by})
588 : ()));
589 push(@$order, map { "${as}.$_" } (@key, @ord));
0f66a01b 590 }
591
489709af 592 return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] }
a86b1efe 593 $rel_source->columns;
b3e8ac9b 594 #warn $alias, Dumper (\@ret);
489709af 595 #return @ret;
b3e8ac9b 596 }
597}
953a18ef 598
87c4e602 599=head2 related_source
600
601=head3 Arguments: ($relname)
87772e46 602
988bf309 603Returns the result source object for the given relationship
87772e46 604
605=cut
606
607sub related_source {
608 my ($self, $rel) = @_;
aea52c85 609 if( !$self->has_relationship( $rel ) ) {
701da8c4 610 $self->throw_exception("No such relationship '$rel'");
aea52c85 611 }
87772e46 612 return $self->schema->source($self->relationship_info($rel)->{source});
8452e496 613}
614
5ac6a044 615=head2 resultset
616
988bf309 617Returns a resultset for the given source, by calling:
5ac6a044 618
988bf309 619 $self->resultset_class->new($self, $self->resultset_attributes)
5ac6a044 620
621=head2 resultset_class
622
988bf309 623Set the class of the resultset, this is useful if you want to create your
624own resultset methods. Create your own class derived from
625L<DBIx::Class::ResultSet>, and set it here.
5ac6a044 626
627=head2 resultset_attributes
628
988bf309 629Specify here any attributes you wish to pass to your specialised resultset.
5ac6a044 630
631=cut
632
633sub resultset {
634 my $self = shift;
e0f56292 635 $self->throw_exception('resultset does not take any arguments. If you want another resultset, call it on the schema instead.') if scalar @_;
1225fc4d 636 return $self->{_resultset} if ref $self->{_resultset} eq $self->resultset_class;
637 return $self->{_resultset} = do {
638 my $rs = $self->resultset_class->new($self, $self->{resultset_attributes});
639 weaken $rs->result_source;
640 $rs;
641 };
5ac6a044 642}
643
701da8c4 644=head2 throw_exception
645
988bf309 646See throw_exception in L<DBIx::Class::Schema>.
701da8c4 647
648=cut
649
650sub throw_exception {
651 my $self = shift;
652 if (defined $self->schema) {
653 $self->schema->throw_exception(@_);
654 } else {
655 croak(@_);
656 }
657}
658
659
9c992ba1 660=head1 AUTHORS
661
662Matt S. Trout <mst@shadowcatsystems.co.uk>
663
664=head1 LICENSE
665
666You may distribute this code under the same terms as Perl itself.
667
668=cut
669