1 package DBIx::Class::ResultSource;
6 use DBIx::Class::ResultSet;
7 use Carp::Clan qw/^DBIx::Class/;
11 use base qw/DBIx::Class/;
12 __PACKAGE__->load_components(qw/AccessorGroup/);
14 __PACKAGE__->mk_group_accessors('simple' =>
15 qw/_ordered_columns _columns _primaries _unique_constraints name resultset_class result_class schema from _relationships/);
19 DBIx::Class::ResultSource - Result source object
25 A ResultSource is a component of a schema from which results can be directly
26 retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
33 my ($class, $attrs) = @_;
34 $class = ref $class if ref $class;
35 my $new = bless({ %{$attrs || {}} }, $class);
36 $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
37 $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}];
38 $new->{_columns} = { %{$new->{_columns}||{}} };
39 $new->{_relationships} = { %{$new->{_relationships}||{}} };
40 $new->{name} ||= "!!NAME NOT SET!!";
45 my ($self, @cols) = @_;
46 $self->_ordered_columns( \@cols )
47 if !$self->_ordered_columns;
49 my $columns = $self->_columns;
50 while (my $col = shift @cols) {
52 my $column_info = ref $cols[0] ? shift(@cols) : {};
53 # If next entry is { ... } use that for the column info, if not
54 # use an empty hashref
56 push(@added, $col) unless exists $columns->{$col};
58 $columns->{$col} = $column_info;
60 push @{ $self->_ordered_columns }, @added;
64 *add_column = \&add_columns;
68 $table->add_columns(qw/col1 col2 col3/);
70 $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
72 Adds columns to the result source. If supplied key => hashref pairs uses
73 the hashref as the column_info for that column.
77 $table->add_column('col' => \%info?);
79 Convenience alias to add_columns
85 return $self->resultset_class->new($self);
90 if ($obj->has_column($col)) { ... }
92 Returns 1 if the source has a column of this name, 0 otherwise.
97 my ($self, $column) = @_;
98 return exists $self->_columns->{$column};
103 my $info = $obj->column_info($col);
105 Returns the column metadata hashref for a column.
110 my ($self, $column) = @_;
111 $self->throw_exception("No such column $column")
112 unless exists $self->_columns->{$column};
113 if ( (! $self->_columns->{$column}->{data_type})
114 && $self->schema && $self->storage() ){
116 ############ eval for the case of storage without table
118 $info = $self->storage->columns_info_for ( $self->from() );
121 for my $col ( keys %{$self->_columns} ){
122 for my $i ( keys %{$info->{$col}} ){
123 $self->_columns()->{$col}->{$i} = $info->{$col}->{$i};
128 return $self->_columns->{$column};
133 my @column_names = $obj->columns;
135 Returns all column names in the order they were declared to add_columns
141 $self->throw_exception("columns() is a read-only accessor, did you mean add_columns()?") if (@_ > 1);
142 return @{$self->{_ordered_columns}||[]};
145 =head2 set_primary_key(@cols)
147 Defines one or more columns as primary key for this source. Should be
148 called after C<add_columns>.
150 Additionally, defines a unique constraint named C<primary>.
154 sub set_primary_key {
155 my ($self, @cols) = @_;
156 # check if primary key columns are valid columns
158 $self->throw_exception("No such column $_ on table ".$self->name)
159 unless $self->has_column($_);
161 $self->_primaries(\@cols);
163 $self->add_unique_constraint(primary => \@cols);
166 =head2 primary_columns
168 Read-only accessor which returns the list of primary keys.
172 sub primary_columns {
173 return @{shift->_primaries||[]};
176 =head2 add_unique_constraint
178 Declare a unique constraint on this source. Call once for each unique
181 # For e.g. UNIQUE (column1, column2)
182 __PACKAGE__->add_unique_constraint(constraint_name => [ qw/column1 column2/ ]);
186 sub add_unique_constraint {
187 my ($self, $name, $cols) = @_;
190 $self->throw_exception("No such column $_ on table ".$self->name)
191 unless $self->has_column($_);
194 my %unique_constraints = $self->unique_constraints;
195 $unique_constraints{$name} = $cols;
196 $self->_unique_constraints(\%unique_constraints);
199 =head2 unique_constraints
201 Read-only accessor which returns the list of unique constraints on this source.
205 sub unique_constraints {
206 return %{shift->_unique_constraints||{}};
211 Returns an expression of the source to be supplied to storage to specify
212 retrieval from this source; in the case of a database the required FROM clause
219 Returns the storage handle for the current schema
223 sub storage { shift->schema->storage; }
225 =head2 add_relationship
227 $source->add_relationship('relname', 'related_source', $cond, $attrs);
229 The relation name can be arbitrary, but must be unique for each relationship
230 attached to this result source. 'related_source' should be the name with
231 which the related result source was registered with the current schema
232 (for simple schemas this is usally either Some::Namespace::Foo or just Foo)
234 The condition needs to be an SQL::Abstract-style representation of the join
235 between the tables. For example, if you're creating a rel from Foo to Bar,
237 { 'foreign.foo_id' => 'self.id' }
239 will result in the JOIN clause
241 foo me JOIN bar bar ON bar.foo_id = me.id
243 You can specify as many foreign => self mappings as necessary.
245 Valid attributes are as follows:
251 Explicitly specifies the type of join to use in the relationship. Any SQL
252 join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in the SQL
253 command immediately before C<JOIN>.
257 An arrayref containing a list of accessors in the foreign class to proxy in
258 the main class. If, for example, you do the following:
260 __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => qw[/ margle /] });
262 Then, assuming Bar has an accessor named margle, you can do:
264 my $obj = Foo->find(1);
265 $obj->margle(10); # set margle; Bar object is created if it doesn't exist
269 Specifies the type of accessor that should be created for the relationship.
270 Valid values are C<single> (for when there is only a single related object),
271 C<multi> (when there can be many), and C<filter> (for when there is a single
272 related object, but you also want the relationship accessor to double as
273 a column accessor). For C<multi> accessors, an add_to_* method is also
274 created, which calls C<create_related> for the relationship.
280 sub add_relationship {
281 my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
282 $self->throw_exception("Can't create relationship without join condition") unless $cond;
285 my %rels = %{ $self->_relationships };
286 $rels{$rel} = { class => $f_source_name,
287 source => $f_source_name,
290 $self->_relationships(\%rels);
294 # XXX disabled. doesn't work properly currently. skip in tests.
296 my $f_source = $self->schema->source($f_source_name);
298 eval "require $f_source_name;";
300 die $@ unless $@ =~ /Can't locate/;
302 $f_source = $f_source_name->result_source;
303 #my $s_class = ref($self->schema);
304 #$f_source_name =~ m/^${s_class}::(.*)$/;
305 #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
306 #$f_source = $self->schema->source($f_source_name);
308 return unless $f_source; # Can't test rel without f_source
310 eval { $self->resolve_join($rel, 'me') };
312 if ($@) { # If the resolve failed, back out and re-throw the error
313 delete $rels{$rel}; #
314 $self->_relationships(\%rels);
315 $self->throw_exception("Error creating relationship $rel: $@");
320 =head2 relationships()
322 Returns all valid relationship names for this source
327 return keys %{shift->_relationships};
330 =head2 relationship_info($relname)
332 Returns the relationship information for the specified relationship name
336 sub relationship_info {
337 my ($self, $rel) = @_;
338 return $self->_relationships->{$rel};
341 =head2 has_relationship($rel)
343 Returns 1 if the source has a relationship of this name, 0 otherwise.
347 sub has_relationship {
348 my ($self, $rel) = @_;
349 return exists $self->_relationships->{$rel};
352 =head2 resolve_join($relation)
354 Returns the join structure required for the related result source
359 my ($self, $join, $alias) = @_;
360 if (ref $join eq 'ARRAY') {
361 return map { $self->resolve_join($_, $alias) } @$join;
362 } elsif (ref $join eq 'HASH') {
363 return map { $self->resolve_join($_, $alias),
364 $self->related_source($_)->resolve_join($join->{$_}, $_) }
366 } elsif (ref $join) {
367 $self->throw_exception("No idea how to resolve join reftype ".ref $join);
369 my $rel_info = $self->relationship_info($join);
370 $self->throw_exception("No such relationship ${join}") unless $rel_info;
371 my $type = $rel_info->{attrs}{join_type} || '';
372 return [ { $join => $self->related_source($join)->from,
373 -join_type => $type },
374 $self->resolve_condition($rel_info->{cond}, $join, $alias) ];
378 =head2 resolve_condition($cond, $rel, $alias|$object)
380 Resolves the passed condition to a concrete query fragment. If given an alias,
381 returns a join condition; if given an object, inverts that object to produce
382 a related conditional from that object.
386 sub resolve_condition {
387 my ($self, $cond, $rel, $for) = @_;
389 if (ref $cond eq 'HASH') {
391 while (my ($k, $v) = each %{$cond}) {
392 # XXX should probably check these are valid columns
393 $k =~ s/^foreign\.// || $self->throw_exception("Invalid rel cond key ${k}");
394 $v =~ s/^self\.// || $self->throw_exception("Invalid rel cond val ${v}");
395 if (ref $for) { # Object
396 #warn "$self $k $for $v";
397 $ret{$k} = $for->get_column($v);
400 $ret{"${rel}.${k}"} = "${for}.${v}";
404 } elsif (ref $cond eq 'ARRAY') {
405 return [ map { $self->resolve_condition($_, $rel, $for) } @$cond ];
407 die("Can't handle this yet :(");
411 =head2 resolve_prefetch (hashref/arrayref/scalar)
413 Accepts one or more relationships for the current source and returns an
414 array of column names for each of those relationships. Column names are
415 prefixed relative to the current source, in accordance with where they appear
416 in the supplied relationships. Examples:
418 my $source = $schema->$resultset('Tag')->source;
419 @columns = $source->resolve_prefetch( { cd => 'artist' } );
427 # 'cd.artist.artistid',
431 @columns = $source->resolve_prefetch( qw[/ cd /] );
441 $source = $schema->resultset('CD')->source;
442 @columns = $source->resolve_prefetch( qw[/ artist producer /] );
448 # 'producer.producerid',
454 sub resolve_prefetch {
455 my( $self, $pre, $alias ) = @_;
457 #$alias ||= $self->name;
458 #warn $alias, Dumper $pre;
459 if( ref $pre eq 'ARRAY' ) {
460 return map { $self->resolve_prefetch( $_, $alias ) } @$pre;
462 elsif( ref $pre eq 'HASH' ) {
465 $self->resolve_prefetch($_, $alias),
466 $self->related_source($_)->resolve_prefetch( $pre->{$_}, $_ )
473 $self->throw_exception( "don't know how to resolve prefetch reftype " . ref $pre);
476 my $rel_info = $self->relationship_info( $pre );
477 $self->throw_exception( $self->name . " has no such relationship '$pre'" ) unless $rel_info;
478 my $prefix = $alias && $alias ne 'me' ? "$alias.$pre" : $pre;
479 my @ret = map { "$prefix.$_" } $self->related_source($pre)->columns;
480 #warn $alias, Dumper (\@ret);
485 =head2 related_source($relname)
487 Returns the result source for the given relationship
492 my ($self, $rel) = @_;
493 if( !$self->has_relationship( $rel ) ) {
494 $self->throw_exception("No such relationship '$rel'");
496 return $self->schema->source($self->relationship_info($rel)->{source});
501 =head2 throw_exception
503 See schema's throw_exception
507 sub throw_exception {
509 if (defined $self->schema) {
510 $self->schema->throw_exception(@_);
519 Matt S. Trout <mst@shadowcatsystems.co.uk>
523 You may distribute this code under the same terms as Perl itself.