X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSource.pm;h=1be530e774a989e881004b67094077f67ab554c0;hb=096f421241;hp=954e8daf13bc5011f50f753799c679ef79650baf;hpb=988bf309a079c45bf5323d806453a9b430138a96;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 954e8da..1be530e 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -5,17 +5,19 @@ use warnings; use DBIx::Class::ResultSet; use Carp::Clan qw/^DBIx::Class/; - use Storable; use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/AccessorGroup/); -__PACKAGE__->mk_group_accessors('simple' => - qw/_ordered_columns _columns _primaries _unique_constraints name resultset_attributes schema from _relationships/); -__PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class result_class/); +__PACKAGE__->mk_group_accessors('simple' => qw/_ordered_columns + _columns _primaries _unique_constraints name resultset_attributes + schema from _relationships column_info_from_storage source_name/); + +__PACKAGE__->mk_group_accessors('component_class' => qw/resultset_class + result_class/); -=head1 NAME +=head1 NAME DBIx::Class::ResultSource - Result source object @@ -28,12 +30,25 @@ retrieved, most usually a table (see L) =head1 METHODS +=pod + +=head2 new + + $class->new(); + + $class->new({attribute_name => value}); + +Creates a new ResultSource object. Not normally called directly by end users. + =cut sub new { my ($class, $attrs) = @_; $class = ref $class if ref $class; - my $new = bless({ %{$attrs || {}} }, $class); + + my $new = { %{$attrs || {}}, _resultset => undef }; + bless $new, $class; + $new->{resultset_class} ||= 'DBIx::Class::ResultSet'; $new->{resultset_attributes} = { %{$new->{resultset_attributes} || {}} }; $new->{_ordered_columns} = [ @{$new->{_ordered_columns}||[]}]; @@ -41,6 +56,9 @@ sub new { $new->{_relationships} = { %{$new->{_relationships}||{}} }; $new->{name} ||= "!!NAME NOT SET!!"; $new->{_columns_info_loaded} ||= 0; + if(!defined $new->column_info_from_storage) { + $new->{column_info_from_storage} = 1 + } return $new; } @@ -52,66 +70,65 @@ sub new { $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...); -Adds columns to the result source. If supplied key => hashref pairs uses -the hashref as the column_info for that column. - -Repeated calls of this method will add more columns, not replace them. +Adds columns to the result source. If supplied key => hashref pairs, uses +the hashref as the column_info for that column. Repeated calls of this +method will add more columns, not replace them. -The contents of the column_info are not set in stone, the following -keys are currently recognised/used by DBIx::Class. +The contents of the column_info are not set in stone. The following +keys are currently recognised/used by DBIx::Class: =over 4 -=item accessor +=item accessor Use this to set the name of the accessor for this column. If unset, the name of the column will be used. =item data_type -This contains the column type, it is automatically filled by the +This contains the column type. It is automatically filled by the L producer, and the -L module. If you do not enter the +L module. If you do not enter a data_type, DBIx::Class will attempt to retrieve it from the -database for you, using Ls column_info method. The values of this +database for you, using L's column_info method. The values of this key are typically upper-cased. -Currently there is no standard set of values for the data_type, use -whatever your database(s) support. +Currently there is no standard set of values for the data_type. Use +whatever your database supports. =item size The length of your column, if it is a column type that can have a size -restriction. This is currently not used by DBIx::Class. +restriction. This is currently not used by DBIx::Class. =item is_nullable -If the column is allowed to contain NULL values, set a true value -(typically 1), here. This is currently not used by DBIx::Class. +Set this to a true value for a columns that is allowed to contain +NULL values. This is currently not used by DBIx::Class. =item is_auto_increment -Set this to a true value if this is a column that is somehow -automatically filled. This is currently not used by DBIx::Class. +Set this to a true value for a column whose value is somehow +automatically set. This is used to determine which columns to empty +when cloning objects using C. =item is_foreign_key -Set this to a true value if this column represents a key from a +Set this to a true value for a column that contains a key from a foreign table. This is currently not used by DBIx::Class. =item default_value -Set this to the default value which will be inserted into this column -by the database. Can contain either values or functions. This is -currently not used by DBIx::Class. +Set this to the default value which will be inserted into a column +by the database. Can contain either a value or a function. This is +currently not used by DBIx::Class. =item sequence -If your column is using a sequence to create it's values, set the name -of the sequence here, to allow the values to be retrieved -automatically by the L module. PK::Auto will -attempt to retrieve the sequence name from the database, if this value -is left unset. +Set this on a primary key column to the name of the sequence used to +generate a new key value. If not specified, L +will attempt to retrieve the name of the sequence from the database +automatically. =back @@ -119,24 +136,21 @@ is left unset. $table->add_column('col' => \%info?); -Convenience alias to add_columns +Convenience alias to add_columns. =cut sub add_columns { my ($self, @cols) = @_; - $self->_ordered_columns( \@cols ) - if !$self->_ordered_columns; + $self->_ordered_columns(\@cols) unless $self->_ordered_columns; + my @added; my $columns = $self->_columns; while (my $col = shift @cols) { - + # If next entry is { ... } use that for the column info, if not + # use an empty hashref my $column_info = ref $cols[0] ? shift(@cols) : {}; - # If next entry is { ... } use that for the column info, if not - # use an empty hashref - push(@added, $col) unless exists $columns->{$col}; - $columns->{$col} = $column_info; } push @{ $self->_ordered_columns }, @added; @@ -149,7 +163,7 @@ sub add_columns { if ($obj->has_column($col)) { ... } -Returns 1 if the source has a column of this name, 0 otherwise. +Returns true if the source has a column of this name, false otherwise. =cut @@ -158,7 +172,7 @@ sub has_column { return exists $self->_columns->{$column}; } -=head2 column_info +=head2 column_info my $info = $obj->column_info($col); @@ -169,44 +183,100 @@ of add_column for information on the contents of the hashref. sub column_info { my ($self, $column) = @_; - $self->throw_exception("No such column $column") + $self->throw_exception("No such column $column") unless exists $self->_columns->{$column}; #warn $self->{_columns_info_loaded}, "\n"; - if ( ! $self->_columns->{$column}->{data_type} - && ! $self->{_columns_info_loaded} - && $self->schema && $self->storage() ){ - $self->{_columns_info_loaded}++; - my $info; -############ eval for the case of storage without table - eval{ - $info = $self->storage->columns_info_for ( $self->from() ); - }; - if ( ! $@ ){ - for my $col ( keys %{$self->_columns} ){ - for my $i ( keys %{$info->{$col}} ){ - $self->_columns()->{$col}->{$i} = $info->{$col}->{$i}; - } - } + if ( ! $self->_columns->{$column}{data_type} + and $self->column_info_from_storage + and ! $self->{_columns_info_loaded} + and $self->schema and $self->storage ) + { + $self->{_columns_info_loaded}++; + my $info; + my $lc_info; + # eval for the case of storage without table + eval { $info = $self->storage->columns_info_for( $self->from ) }; + unless ($@) { + for my $realcol ( keys %{$info} ) { + $lc_info->{lc $realcol} = $info->{$realcol}; + } + foreach my $col ( keys %{$self->_columns} ) { + $self->_columns->{$col} = { %{ $self->_columns->{$col}}, %{$info->{$col} || $lc_info->{lc $col}} }; } + } } return $self->_columns->{$column}; } +=head2 column_info_from_storage + +Enables or disables the on-demand automatic loading of the above +column metadata from storage as neccesary. Defaults to true in the +current release, but will default to false in future releases starting +with 0.08000. This is *deprecated*, and should not be used. It will +be removed before 1.0. + + __PACKAGE__->column_info_from_storage(0); + __PACKAGE__->column_info_from_storage(1); + =head2 columns my @column_names = $obj->columns; -Returns all column names in the order they were declared to add_columns +Returns all column names in the order they were declared to add_columns. =cut sub columns { - my $self=shift; - $self->throw_exception("columns() is a read-only accessor, did you mean add_columns()?") if (@_ > 1); + my $self = shift; + $self->throw_exception( + "columns() is a read-only accessor, did you mean add_columns()?" + ) if (@_ > 1); return @{$self->{_ordered_columns}||[]}; } -=head2 set_primary_key(@cols) +=head2 remove_columns + + $table->remove_columns(qw/col1 col2 col3/); + +Removes columns from the result source. + +=head2 remove_column + + $table->remove_column('col'); + +Convenience alias to remove_columns. + +=cut + +sub remove_columns { + my ($self, @cols) = @_; + + return unless $self->_ordered_columns; + + my $columns = $self->_columns; + my @remaining; + + foreach my $col (@{$self->_ordered_columns}) { + push @remaining, $col unless grep(/$col/, @cols); + } + + foreach (@cols) { + delete $columns->{$_}; + }; + + $self->_ordered_columns(\@remaining); +} + +*remove_column = \&remove_columns; + +=head2 set_primary_key + +=over 4 + +=item Arguments: @cols + +=back Defines one or more columns as primary key for this source. Should be called after C. @@ -214,16 +284,16 @@ called after C. Additionally, defines a unique constraint named C. The primary key columns are used by L to -retrieve automatically created values from the database. +retrieve automatically created values from the database. =cut sub set_primary_key { my ($self, @cols) = @_; # check if primary key columns are valid columns - for (@cols) { - $self->throw_exception("No such column $_ on table ".$self->name) - unless $self->has_column($_); + foreach my $col (@cols) { + $self->throw_exception("No such column $col on table " . $self->name) + unless $self->has_column($col); } $self->_primaries(\@cols); @@ -243,20 +313,35 @@ sub primary_columns { =head2 add_unique_constraint Declare a unique constraint on this source. Call once for each unique -constraint. Unique constraints are used when you call C on a -Ladd_unique_constraint( + constraint_name => [ qw/column1 column2/ ], + ); - # For e.g. UNIQUE (column1, column2) - __PACKAGE__->add_unique_constraint(constraint_name => [ qw/column1 column2/ ]); +Alternatively, you can specify only the columns: + + __PACKAGE__->add_unique_constraint([ qw/column1 column2/ ]); + +This will result in a unique constraint named C, where +C is replaced with the table name. + +Unique constraints are used, for example, when you call +L. Only columns in the constraint are searched. =cut sub add_unique_constraint { - my ($self, $name, $cols) = @_; + my $self = shift; + my $cols = pop @_; + my $name = shift; + + $name ||= $self->name_unique_constraint($cols); - for (@$cols) { - $self->throw_exception("No such column $_ on table ".$self->name) - unless $self->has_column($_); + foreach my $col (@$cols) { + $self->throw_exception("No such column $col on table " . $self->name) + unless $self->has_column($col); } my %unique_constraints = $self->unique_constraints; @@ -264,6 +349,22 @@ sub add_unique_constraint { $self->_unique_constraints(\%unique_constraints); } +=head2 name_unique_constraint + +Return a name for a unique constraint containing the specified columns. These +names consist of the table name and each column name, separated by underscores. + +For example, a constraint on a table named C containing the columns +C and C would result in a constraint name of C<cd_artist_title>. + +=cut + +sub name_unique_constraint { + my ($self, $cols) = @_; + + return join '_', $self->name, @$cols; +} + =head2 unique_constraints Read-only accessor which returns the list of unique constraints on this source. @@ -274,17 +375,52 @@ sub unique_constraints { return %{shift->_unique_constraints||{}}; } +=head2 unique_constraint_names + +Returns the list of unique constraint names defined on this source. + +=cut + +sub unique_constraint_names { + my ($self) = @_; + + my %unique_constraints = $self->unique_constraints; + + return keys %unique_constraints; +} + +=head2 unique_constraint_columns + +Returns the list of columns that make up the specified unique constraint. + +=cut + +sub unique_constraint_columns { + my ($self, $constraint_name) = @_; + + my %unique_constraints = $self->unique_constraints; + + $self->throw_exception( + "Unknown unique constraint $constraint_name on '" . $self->name . "'" + ) unless exists $unique_constraints{$constraint_name}; + + return @{ $unique_constraints{$constraint_name} }; +} + =head2 from Returns an expression of the source to be supplied to storage to specify -retrieval from this source; in the case of a database the required FROM clause -contents. +retrieval from this source. In the case of a database, the required FROM +clause contents. -=cut +=head2 schema + +Returns the L<DBIx::Class::Schema> object that this result source +belongs too. =head2 storage -Returns the storage handle for the current schema. +Returns the storage handle for the current schema. See also: L<DBIx::Class::Storage> @@ -296,13 +432,18 @@ sub storage { shift->schema->storage; } $source->add_relationship('relname', 'related_source', $cond, $attrs); -The relation name can be arbitrary, but must be unique for each relationship -attached to this result source. 'related_source' should be the name with -which the related result source was registered with the current schema -(for simple schemas this is usally either Some::Namespace::Foo or just Foo) +The relationship name can be arbitrary, but must be unique for each +relationship attached to this result source. 'related_source' should +be the name with which the related result source was registered with +the current schema. For example: + + $schema->source('Book')->add_relationship('reviews', 'Review', { + 'foreign.book_id' => 'self.id', + }); -The condition needs to be an SQL::Abstract-style representation of the join -between the tables. For example, if you're creating a rel from Author to Book, +The condition C<$cond> needs to be an L<SQL::Abstract>-style +representation of the join between the tables. For example, if you're +creating a rel from Author to Book, { 'foreign.author_id' => 'self.id' } @@ -324,24 +465,27 @@ the SQL command immediately before C<JOIN>. =item proxy -An arrayref containing a list of accessors in the foreign class to -proxy in the main class. If, for example, you do the following: +An arrayref containing a list of accessors in the foreign class to proxy in +the main class. If, for example, you do the following: - __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => [ qw/margle/] }); + CD->might_have(liner_notes => 'LinerNotes', undef, { + proxy => [ qw/notes/ ], + }); -Then, assuming Bar has an accessor named margle, you can do: +Then, assuming LinerNotes has an accessor named notes, you can do: - my $obj = Foo->find(1); - $obj->margle(10); # set margle; Bar object is created if it doesn't exist + my $cd = CD->find(1); + # set notes -- LinerNotes object is created if it doesn't exist + $cd->notes('Notes go here'); =item accessor Specifies the type of accessor that should be created for the -relationship. Valid values are C<single> (for when there is only a single -related object), C<multi> (when there can be many), and C<filter> (for -when there is a single related object, but you also want the relationship -accessor to double as a column accessor). For C<multi> accessors, an -add_to_* method is also created, which calls C<create_related> for the +relationship. Valid values are C<single> (for when there is only a single +related object), C<multi> (when there can be many), and C<filter> (for +when there is a single related object, but you also want the relationship +accessor to double as a column accessor). For C<multi> accessors, an +add_to_* method is also created, which calls C<create_related> for the relationship. =back @@ -350,7 +494,8 @@ relationship. sub add_relationship { my ($self, $rel, $f_source_name, $cond, $attrs) = @_; - $self->throw_exception("Can't create relationship without join condition") unless $cond; + $self->throw_exception("Can't create relationship without join condition") + unless $cond; $attrs ||= {}; my %rels = %{ $self->_relationships }; @@ -366,10 +511,7 @@ sub add_relationship { my $f_source = $self->schema->source($f_source_name); unless ($f_source) { - eval "require $f_source_name;"; - if ($@) { - die $@ unless $@ =~ /Can't locate/; - } + $self->ensure_class_loaded($f_source_name); $f_source = $f_source_name->result_source; #my $s_class = ref($self->schema); #$f_source_name =~ m/^${s_class}::(.*)$/; @@ -381,16 +523,16 @@ sub add_relationship { eval { $self->resolve_join($rel, 'me') }; if ($@) { # If the resolve failed, back out and re-throw the error - delete $rels{$rel}; # + delete $rels{$rel}; # $self->_relationships(\%rels); $self->throw_exception("Error creating relationship $rel: $@"); } 1; } -=head2 relationships() +=head2 relationships -Returns all valid relationship names for this source +Returns all relationship names for this source. =cut @@ -398,20 +540,33 @@ sub relationships { return keys %{shift->_relationships}; } -=head2 relationship_info($relname) +=head2 relationship_info -Returns the relationship information for the specified relationship name +=over 4 + +=item Arguments: $relname + +=back + +Returns a hash of relationship information for the specified relationship +name. =cut sub relationship_info { my ($self, $rel) = @_; return $self->_relationships->{$rel}; -} +} -=head2 has_relationship($rel) +=head2 has_relationship + +=over 4 + +=item Arguments: $rel + +=back -Returns 1 if the source has a relationship of this name, 0 otherwise. +Returns true if the source has a relationship of this name, false otherwise. =cut @@ -420,9 +575,122 @@ sub has_relationship { return exists $self->_relationships->{$rel}; } -=head2 resolve_join($relation) +=head2 reverse_relationship_info -Returns the join structure required for the related result source +=over 4 + +=item Arguments: $relname + +=back + +Returns an array of hash references of relationship information for +the other side of the specified relationship name. + +=cut + +sub reverse_relationship_info { + my ($self, $rel) = @_; + my $rel_info = $self->relationship_info($rel); + my $ret = {}; + + return $ret unless ((ref $rel_info->{cond}) eq 'HASH'); + + my @cond = keys(%{$rel_info->{cond}}); + my @refkeys = map {/^\w+\.(\w+)$/} @cond; + my @keys = map {$rel_info->{cond}->{$_} =~ /^\w+\.(\w+)$/} @cond; + + # Get the related result source for this relationship + my $othertable = $self->related_source($rel); + + # Get all the relationships for that source that related to this source + # whose foreign column set are our self columns on $rel and whose self + # columns are our foreign columns on $rel. + my @otherrels = $othertable->relationships(); + my $otherrelationship; + foreach my $otherrel (@otherrels) { + my $otherrel_info = $othertable->relationship_info($otherrel); + + my $back = $othertable->related_source($otherrel); + next unless $back->name eq $self->name; + + my @othertestconds; + + if (ref $otherrel_info->{cond} eq 'HASH') { + @othertestconds = ($otherrel_info->{cond}); + } + elsif (ref $otherrel_info->{cond} eq 'ARRAY') { + @othertestconds = @{$otherrel_info->{cond}}; + } + else { + next; + } + + foreach my $othercond (@othertestconds) { + my @other_cond = keys(%$othercond); + my @other_refkeys = map {/^\w+\.(\w+)$/} @other_cond; + my @other_keys = map {$othercond->{$_} =~ /^\w+\.(\w+)$/} @other_cond; + next if (!$self->compare_relationship_keys(\@refkeys, \@other_keys) || + !$self->compare_relationship_keys(\@other_refkeys, \@keys)); + $ret->{$otherrel} = $otherrel_info; + } + } + return $ret; +} + +=head2 compare_relationship_keys + +=over 4 + +=item Arguments: $keys1, $keys2 + +=back + +Returns true if both sets of keynames are the same, false otherwise. + +=cut + +sub compare_relationship_keys { + my ($self, $keys1, $keys2) = @_; + + # Make sure every keys1 is in keys2 + my $found; + foreach my $key (@$keys1) { + $found = 0; + foreach my $prim (@$keys2) { + if ($prim eq $key) { + $found = 1; + last; + } + } + last unless $found; + } + + # Make sure every key2 is in key1 + if ($found) { + foreach my $prim (@$keys2) { + $found = 0; + foreach my $key (@$keys1) { + if ($prim eq $key) { + $found = 1; + last; + } + } + last unless $found; + } + } + + return $found; +} + +=head2 resolve_join + +=over 4 + +=item Arguments: $relation + +=back + +Returns the join structure required for the related result source. =cut @@ -453,7 +721,13 @@ sub resolve_join { } } -=head2 resolve_condition($cond, $as, $alias|$object) +=head2 resolve_condition + +=over 4 + +=item Arguments: $cond, $as, $alias|$object + +=back Resolves the passed condition to a concrete query fragment. If given an alias, returns a join condition; if given an object, inverts that object to produce @@ -466,14 +740,23 @@ sub resolve_condition { #warn %$cond; if (ref $cond eq 'HASH') { my %ret; - while (my ($k, $v) = each %{$cond}) { + foreach my $k (keys %{$cond}) { + my $v = $cond->{$k}; # XXX should probably check these are valid columns - $k =~ s/^foreign\.// || $self->throw_exception("Invalid rel cond key ${k}"); - $v =~ s/^self\.// || $self->throw_exception("Invalid rel cond val ${v}"); + $k =~ s/^foreign\.// || + $self->throw_exception("Invalid rel cond key ${k}"); + $v =~ s/^self\.// || + $self->throw_exception("Invalid rel cond val ${v}"); if (ref $for) { # Object #warn "$self $k $for $v"; $ret{$k} = $for->get_column($v); #warn %ret; + } elsif (!defined $for) { # undef, i.e. "no object" + $ret{$k} = undef; + } elsif (ref $as) { # reverse object + $ret{$v} = $as->get_column($k); + } elsif (!defined $as) { # undef, i.e. "no reverse object" + $ret{$v} = undef; } else { $ret{"${as}.${k}"} = "${for}.${v}"; } @@ -486,7 +769,13 @@ sub resolve_condition { } } -=head2 resolve_prefetch (hashref/arrayref/scalar) +=head2 resolve_prefetch + +=over 4 + +=item Arguments: hashref/arrayref/scalar + +=back Accepts one or more relationships for the current source and returns an array of column names for each of those relationships. Column names are @@ -525,48 +814,75 @@ in the supplied relationships. Examples: # 'artist.name', # 'producer.producerid', # 'producer.name' - #) + #) =cut sub resolve_prefetch { - my ($self, $pre, $alias, $seen) = @_; + my ($self, $pre, $alias, $seen, $order, $collapse) = @_; $seen ||= {}; - use Data::Dumper; #$alias ||= $self->name; #warn $alias, Dumper $pre; if( ref $pre eq 'ARRAY' ) { - return map { $self->resolve_prefetch( $_, $alias, $seen ) } @$pre; + return + map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) } + @$pre; } elsif( ref $pre eq 'HASH' ) { my @ret = map { - $self->resolve_prefetch($_, $alias, $seen), + $self->resolve_prefetch($_, $alias, $seen, $order, $collapse), $self->related_source($_)->resolve_prefetch( - $pre->{$_}, "${alias}.$_", $seen) - } keys %$pre; + $pre->{$_}, "${alias}.$_", $seen, $order, $collapse) + } keys %$pre; #die Dumper \@ret; return @ret; } elsif( ref $pre ) { - $self->throw_exception( "don't know how to resolve prefetch reftype " . ref $pre); + $self->throw_exception( + "don't know how to resolve prefetch reftype ".ref($pre)); } else { my $count = ++$seen->{$pre}; my $as = ($count > 1 ? "${pre}_${count}" : $pre); my $rel_info = $self->relationship_info( $pre ); - $self->throw_exception( $self->name . " has no such relationship '$pre'" ) unless $rel_info; - my $as_prefix = ($alias =~ /^.*?\.(.*)$/ ? $1.'.' : ''); + $self->throw_exception( $self->name . " has no such relationship '$pre'" ) + unless $rel_info; + my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); + my $rel_source = $self->related_source($pre); + + if (exists $rel_info->{attrs}{accessor} + && $rel_info->{attrs}{accessor} eq 'multi') { + $self->throw_exception( + "Can't prefetch has_many ${pre} (join cond too complex)") + unless ref($rel_info->{cond}) eq 'HASH'; + my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); } + keys %{$rel_info->{cond}}; + $collapse->{"${as_prefix}${pre}"} = \@key; + my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' + ? @{$rel_info->{attrs}{order_by}} + : (defined $rel_info->{attrs}{order_by} + ? ($rel_info->{attrs}{order_by}) + : ())); + push(@$order, map { "${as}.$_" } (@key, @ord)); + } + return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } - $self->related_source($pre)->columns; + $rel_source->columns; #warn $alias, Dumper (\@ret); #return @ret; } } -=head2 related_source($relname) +=head2 related_source + +=over 4 + +=item Arguments: $relname + +=back -Returns the result source object for the given relationship +Returns the result source object for the given relationship. =cut @@ -578,12 +894,35 @@ sub related_source { return $self->schema->source($self->relationship_info($rel)->{source}); } +=head2 related_class + +=over 4 + +=item Arguments: $relname + +=back + +Returns the class name for objects in the given relationship. + +=cut + +sub related_class { + my ($self, $rel) = @_; + if( !$self->has_relationship( $rel ) ) { + $self->throw_exception("No such relationship '$rel'"); + } + return $self->schema->class($self->relationship_info($rel)->{source}); +} + =head2 resultset -Returns a resultset for the given source, by calling: +Returns a resultset for the given source. This will initially be created +on demand by calling $self->resultset_class->new($self, $self->resultset_attributes) +but is cached from then on unless resultset_class changes. + =head2 resultset_class Set the class of the resultset, this is useful if you want to create your @@ -598,25 +937,57 @@ Specify here any attributes you wish to pass to your specialised resultset. sub resultset { my $self = shift; - return $self->resultset_class->new($self, $self->{resultset_attributes}); + $self->throw_exception( + 'resultset does not take any arguments. If you want another resultset, '. + 'call it on the schema instead.' + ) if scalar @_; + + # disabled until we can figure out a way to do it without consistency issues + # + #return $self->{_resultset} + # if ref $self->{_resultset} eq $self->resultset_class; + #return $self->{_resultset} = + + return $self->resultset_class->new( + $self, $self->{resultset_attributes} + ); } +=head2 source_name + +=over 4 + +=item Arguments: $source_name + +=back + +Set the name of the result source when it is loaded into a schema. +This is usefull if you want to refer to a result source by a name other than +its class name. + + package ArchivedBooks; + use base qw/DBIx::Class/; + __PACKAGE__->table('books_archive'); + __PACKAGE__->source_name('Books'); + + # from your schema... + $schema->resultset('Books')->find(1); + =head2 throw_exception -See throw_exception in L<DBIx::Class::Schema>. +See L<DBIx::Class::Schema/"throw_exception">. =cut sub throw_exception { my $self = shift; - if (defined $self->schema) { + if (defined $self->schema) { $self->schema->throw_exception(@_); } else { croak(@_); } } - =head1 AUTHORS Matt S. Trout <mst@shadowcatsystems.co.uk>