From: David Kamholz Date: Sun, 11 Dec 2005 00:03:22 +0000 (+0000) Subject: merge resultset branch through revision 371 X-Git-Tag: v0.05005~119^2~27 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=bfab575afa37545ee175b824cea554c9c37ab6f5;p=dbsrgits%2FDBIx-Class.git merge resultset branch through revision 371 --- diff --git a/Changes b/Changes index 3590e63..98709c1 100644 --- a/Changes +++ b/Changes @@ -4,6 +4,7 @@ Revision history for DBIx::Class 0.04001 - Fix so set_inflated_column calls set_column + - Allow syntax errors in relationship classes to be reported. 0.04 2005-11-26 - Moved get_simple and set_simple into AccessorGroup diff --git a/MANIFEST b/MANIFEST index 2ff9869..655bea9 100644 --- a/MANIFEST +++ b/MANIFEST @@ -67,7 +67,9 @@ META.yml README t/02pod.t t/03podcoverage.t +t/04dont_break_c3.t t/19quotes.t +t/20setuperrors.t t/basicrels/01core.t t/basicrels/04db.t t/basicrels/05multipk.t diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 1977a1e..261402e 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -24,6 +24,9 @@ \#$ \b\.# +# avoid OS X finder files +\.DS_Store$ + # Don't ship the test db ^t/var diff --git a/lib/DBIx/Class/Manual.pod b/lib/DBIx/Class/Manual.pod index c7ba1fb..f95b8aa 100644 --- a/lib/DBIx/Class/Manual.pod +++ b/lib/DBIx/Class/Manual.pod @@ -1,6 +1,6 @@ =head1 NAME -DBIx::Class::Manual- Index of the Manual +DBIx::Class::Manual - Index of the Manual =head1 DESCRIPTION diff --git a/lib/DBIx/Class/Relationship.pm b/lib/DBIx/Class/Relationship.pm index fd9152f..65d646e 100644 --- a/lib/DBIx/Class/Relationship.pm +++ b/lib/DBIx/Class/Relationship.pm @@ -26,37 +26,56 @@ DBIx::Class::Relationship - Inter-table relationships =head1 DESCRIPTION This class handles relationships between the tables in your database -model. It allows your to set up relationships, and to perform joins -on searches. +model. It allows you to set up relationships and perform joins on them. -This POD details only the convenience methods for setting up standard -relationship types. For more information see ::Relationship::Base +Only the helper methods for setting up standard relationship types +are documented here. For the basic, lower-level methods, see +L. =head1 METHODS -All convenience methods take a signature of the following format - +All helper methods take the following arguments: - __PACKAGE__>method_name('relname', 'Foreign::Class', $join?, $attrs?); + __PACKAGE__>method_name('relname', 'Foreign::Class', $cond, $attrs); + +Both C<$cond> and C<$attrs> are optional. Pass C for C<$cond> if +you want to use the default value for it, but still want to set C<$attrs>. +The following attributes are recognize: +=over 4 +=item join_type -=over 4 +Explicitly specifies the type of join to use in the relationship. Any SQL +join type is valid, e.g. C or C. It will be placed in the SQL +command immediately before C. -=item has_one +=item proxy - my $f_obj = $obj->relname; +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 /] }); + +Then, assuming Bar has an accessor named margle, you can do: -Creates a one-one relationship with another class; defaults to PK-PK for -the join condition unless a condition is specified. + my $obj = Foo->find(1); + $obj->margle(10); # set margle; Bar object is created if it doesn't exist + +=back -=item might_have +=head2 belongs_to my $f_obj = $obj->relname; -Creates an optional one-one relationship with another class; defaults to PK-PK -for the join condition unless a condition is specified. + $obj->relname($new_f_obj); + +Creates a relationship where we store the foreign class' PK; if $join is a +column name instead of a condition that is assumed to be the FK, if not +has_many assumes the FK is the relname is that is a column on the current +class. -=item has_many +=head2 has_many my @f_obj = $obj->relname($cond?, $attrs?); my $f_result_set = $obj->relname($cond?, $attrs?); @@ -65,23 +84,24 @@ for the join condition unless a condition is specified. Creates a one-many relationship with another class; -=item belongs_to +=head2 might_have my $f_obj = $obj->relname; - $obj->relname($new_f_obj); +Creates an optional one-one relationship with another class; defaults to PK-PK +for the join condition unless a condition is specified. -Creates a relationship where we store the foreign class' PK; if $join is a -column name instead of a condition that is assumed to be the FK, if not -has_many assumes the FK is the relname is that is a column on the current -class. +=head2 has_one + + my $f_obj = $obj->relname; + +Creates a one-one relationship with another class; defaults to PK-PK for +the join condition unless a condition is specified. =cut 1; -=back - =head1 AUTHORS Matt S. Trout diff --git a/lib/DBIx/Class/Relationship/Base.pm b/lib/DBIx/Class/Relationship/Base.pm index b1ca7fb..4590985 100644 --- a/lib/DBIx/Class/Relationship/Base.pm +++ b/lib/DBIx/Class/Relationship/Base.pm @@ -43,6 +43,9 @@ sub add_relationship { die "Can't create relationship without join condition" unless $cond; $attrs ||= {}; eval "require $f_class;"; + if ($@) { + $class->throw($@) unless $@ =~ /Can't locate/; + } my %rels = %{ $class->_relationships }; $rels{$rel} = { class => $f_class, cond => $cond, diff --git a/lib/DBIx/Class/Relationship/BelongsTo.pm b/lib/DBIx/Class/Relationship/BelongsTo.pm index aca69aa..ef6f904 100644 --- a/lib/DBIx/Class/Relationship/BelongsTo.pm +++ b/lib/DBIx/Class/Relationship/BelongsTo.pm @@ -6,14 +6,26 @@ use warnings; sub belongs_to { my ($class, $rel, $f_class, $cond, $attrs) = @_; eval "require $f_class"; + if ($@) { + $class->throw($@) unless $@ =~ /Can't locate/; + } + my %f_primaries; $f_primaries{$_} = 1 for eval { $f_class->primary_columns }; my $f_loaded = !$@; + # single key relationship - if (not defined $cond) { - $class->throw("Can't infer join condition for ${rel} on ${class}; unable to load ${f_class}") unless $f_loaded; - my ($pri, $too_many) = keys %f_primaries; - $class->throw("Can't infer join condition for ${rel} on ${class}; ${f_class} has multiple primary key") if $too_many; + if (!ref $cond) { + my ($pri,$too_many); + if (!defined $cond) { + $class->throw("Can't infer join condition for ${rel} on ${class}; unable to load ${f_class}") unless $f_loaded; + ($pri, $too_many) = keys %f_primaries; + $class->throw("Can't infer join condition for ${rel} on ${class}; ${f_class} has no primary keys") unless defined $pri; + $class->throw("Can't infer join condition for ${rel} on ${class}; ${f_class} has multiple primary key") if $too_many; + } + else { + $pri = $cond; + } my $acc_type = ($class->has_column($rel)) ? 'filter' : 'single'; $class->add_relationship($rel, $f_class, { "foreign.${pri}" => "self.${rel}" }, @@ -21,7 +33,7 @@ sub belongs_to { ); } # multiple key relationship - else { + elsif (ref $cond eq 'HASH') { my $cond_rel; for (keys %$cond) { if (m/\./) { # Explicit join condition @@ -35,6 +47,9 @@ sub belongs_to { { accessor => 'single', %{$attrs || {}} } ); } + else { + $class->throw('third argument for belongs_to must be undef, a column name, or a join condition'); + } return 1; } diff --git a/lib/DBIx/Class/Relationship/HasMany.pm b/lib/DBIx/Class/Relationship/HasMany.pm index f30ff50..fdf5dd6 100644 --- a/lib/DBIx/Class/Relationship/HasMany.pm +++ b/lib/DBIx/Class/Relationship/HasMany.pm @@ -7,6 +7,9 @@ sub has_many { my ($class, $rel, $f_class, $cond, $attrs) = @_; eval "require $f_class"; + if ($@) { + $class->throw($@) unless $@ =~ /Can't locate/; + } unless (ref $cond) { my ($pri, $too_many) = $class->primary_columns; diff --git a/lib/DBIx/Class/Relationship/HasOne.pm b/lib/DBIx/Class/Relationship/HasOne.pm index d114a39..432c10e 100644 --- a/lib/DBIx/Class/Relationship/HasOne.pm +++ b/lib/DBIx/Class/Relationship/HasOne.pm @@ -14,6 +14,10 @@ sub has_one { sub _has_one { my ($class, $join_type, $rel, $f_class, $cond, $attrs) = @_; eval "require $f_class"; + if ($@) { + $class->throw($@) unless $@ =~ /Can't locate/; + } + unless (ref $cond) { my ($pri, $too_many) = $class->primary_columns; $class->throw( "might_have/has_one can only infer join for a single primary key; ${class} has more" ) diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index f5574ed..8b8da5f 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -9,24 +9,26 @@ use Data::Page; =head1 NAME -DBIX::Class::ResultSet - Responsible for fetching and creating resultset. +DBIx::Class::ResultSet - Responsible for fetching and creating resultset. -=head1 SYNOPSIS; +=head1 SYNOPSIS -$rs=MyApp::DB::Class->search(registered=>1); +my $rs = MyApp::DB::Class->search(registered => 1); +my @rows = MyApp::DB::Class->search(foo => 'bar'); =head1 DESCRIPTION -The resultset is also known as an iterator. +The resultset is also known as an iterator. It is responsible for handling +queries that may return an arbitrary number of rows, e.g. via C +or a C relationship. =head1 METHODS -=over 4 +=head2 new($db_class, \%$attrs) -=item new - -The resultset constructor. Takes a db class and an -attribute hash (see below for more info on attributes) +The resultset constructor. Takes a table class and an attribute hash +(see below for more information on attributes). Does not perform +any queries -- these are executed as needed by the other methods. =cut @@ -69,7 +71,7 @@ sub new { return $new; } -=item search +=head2 search my @obj = $rs->search({ foo => 3 }); # "... WHERE foo = 3" my $new_rs = $rs->search({ foo => 3 }); @@ -104,14 +106,15 @@ sub search { return (wantarray ? $rs->all : $rs); } -=item search_literal +=head2 search_literal my @obj = $rs->search_literal($literal_where_cond, @bind); my $new_rs = $rs->search_literal($literal_where_cond, @bind); Pass a literal chunk of SQL to be added to the conditional part of the resultset -=cut +=cut + sub search_literal { my ($self, $cond, @vals) = @_; my $attrs = (ref $vals[$#vals] eq 'HASH' ? { %{ pop(@vals) } } : {}); @@ -119,9 +122,9 @@ sub search_literal { return $self->search(\$cond, $attrs); } -=item cursor +=head2 cursor -Return a storage driven cursor to the given resultset. +Returns a storage-driven cursor to the given resultset. =cut @@ -137,7 +140,7 @@ sub cursor { $attrs->{where},$attrs); } -=item search_like +=head2 search_like Identical to search except defaults to 'LIKE' instead of '=' in condition @@ -154,9 +157,9 @@ sub search_like { return $class->search($query, { %$attrs }); } -=item slice +=head2 slice($first, $last) -return a number of elements from the given resultset. +Returns a subset of elements from the resultset. =cut @@ -170,9 +173,9 @@ sub slice { return (wantarray ? $slice->all : $slice); } -=item next +=head2 next -Returns the next element in this resultset. +Returns the next element in the resultset (undef is there is none). =cut @@ -222,9 +225,9 @@ sub _construct_object { return $new; } -=item count +=head2 count -Performs an SQL count with the same query as the resultset was built +Performs an SQL C with the same query as the resultset was built with to find the number of elements. If passed arguments, does a search on the resultset and counts the results of that. @@ -249,18 +252,18 @@ sub count { : $self->{count}; } -=item count_literal +=head2 count_literal -Calls search_literal with the passed arguments, then count +Calls search_literal with the passed arguments, then count. =cut sub count_literal { shift->search_literal(@_)->count; } -=item all +=head2 all -Returns all elements in the resultset. Is called implictly if the search -method is used in list context. +Returns all elements in the resultset. Called implictly if the resultset +is returned in list context. =cut @@ -270,9 +273,9 @@ sub all { $self->cursor->all; } -=item reset +=head2 reset -Reset this resultset's cursor, so you can iterate through the elements again. +Resets the resultset's cursor, so you can iterate through the elements again. =cut @@ -282,9 +285,9 @@ sub reset { return $self; } -=item first +=head2 first -resets the resultset and returns the first element. +Resets the resultset and returns the first element. =cut @@ -292,7 +295,7 @@ sub first { return $_[0]->reset->next; } -=item delete +=head2 delete Deletes all elements in the resultset. @@ -306,7 +309,7 @@ sub delete { *delete_all = \&delete; # Yeah, yeah, yeah ... -=item pager +=head2 pager Returns a L object for the current resultset. Only makes sense for queries with page turned on. @@ -324,9 +327,9 @@ sub pager { return $self->{pager}; } -=item page +=head2 page($page_num) -Returns a new resultset representing a given page. +Returns a new resultset for the specified page. =cut @@ -337,49 +340,56 @@ sub page { return $self->new($self->{source}, $attrs); } -=back - =head1 Attributes -The resultset is responsible for handling the various attributes that -can be passed in with the search functions. Here's an overview of them: +The resultset takes various attributes that modify its behavior. +Here's an overview of them: + +=head2 order_by -=over 4 +Which column(s) to order the results by. This is currently passed +through directly to SQL, so you can give e.g. C for a +descending order. -=item order_by +=head2 cols -Which column to order the results by. +Which columns should be retrieved. -=item cols +=head2 join -Which cols should be retrieved on the first search. +Contains a list of relationships that should be joined for this query. Can also +contain a hash reference to refer to that relation's relations. So, if one column +in your class C foo and another C bar, you can do +C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C). +If a foo contains many margles and you want to join those too, you can do +C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the +related table as well, see C below. -=item join +=head2 prefetch -Contains a list of relations that should be joined for this query. Can also -contain a hash referece to refer to that relation's relations. +Contains a list of relationships that should be fetched along with the main +query (when they are accessed afterwards they will have already been +"prefetched"). This is useful for when you know you will need the related +object(s), because it saves a query. Currently limited to prefetching +one relationship deep, so unlike C, prefetch must be an arrayref. -=item from +=head2 from -This attribute can contain a arrayref of elements. each element can be another +This attribute can contain a arrayref of elements. Each element can be another arrayref, to nest joins, or it can be a hash which represents the two sides of the join. -*NOTE* Use this on your own risk. This allows you to shoot your foot off! +NOTE: Use this on your own risk. This allows you to shoot your foot off! -=item page +=head2 page -Should the resultset be paged? This can also be enabled by using the -'page' option. +For a paged resultset, specifies which page to retrieve. Leave unset +for an unpaged resultset. -=item rows +=head2 rows -For paged resultsset, how many rows per page +For a paged resultset, how many rows per page -=item offset - -For paged resultsset, which page to start on. - -=back +=cut 1; diff --git a/t/lib/DBICTest/Schema/HelperRels.pm b/t/lib/DBICTest/Schema/HelperRels.pm index bd74259..2f99d7b 100644 --- a/t/lib/DBICTest/Schema/HelperRels.pm +++ b/t/lib/DBICTest/Schema/HelperRels.pm @@ -26,7 +26,7 @@ DBICTest::Schema::SelfRef->has_many( DBICTest::Schema::Tag->belongs_to('cd', 'DBICTest::Schema::CD'); -DBICTest::Schema::Track->belongs_to('cd', 'DBICTest::Schema::CD'); +DBICTest::Schema::Track->belongs_to('cd', 'DBICTest::Schema::CD', 'cdid'); DBICTest::Schema::TwoKeys->belongs_to('artist', 'DBICTest::Schema::Artist');