From: Jess Robinson Date: Sat, 4 Sep 2010 17:40:18 +0000 (+0100) Subject: Relationship documentation on extended (custom) relationship conditions X-Git-Tag: v0.08190~1^2~17 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=13523f299f81871f7c33fb2e662fc2b5b009f9d2 Relationship documentation on extended (custom) relationship conditions --- diff --git a/lib/DBIx/Class/Relationship.pm b/lib/DBIx/Class/Relationship.pm index d4926d1..ff26676 100644 --- a/lib/DBIx/Class/Relationship.pm +++ b/lib/DBIx/Class/Relationship.pm @@ -105,20 +105,23 @@ L. All helper methods are called similar to the following template: - __PACKAGE__->$method_name('relname', 'Foreign::Class', \%cond | \@cond, \%attrs); + __PACKAGE__->$method_name('relname', 'Foreign::Class', + \%cond | \@cond | \&conf, \%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>. +Both C and C are optional. Pass C for C if +you want to use the default value for it, but still want to set C. -See L for documentation on the -attributes that are allowed in the C<\%attrs> argument. +See L for full documentation on definition of the C argument. + +See L for documentation on the +attributes that are allowed in the C argument. =head2 belongs_to =over 4 -=item Arguments: $accessor_name, $related_class, $our_fk_column|\%cond|\@cond?, \%attrs? +=item Arguments: $accessor_name, $related_class, $our_fk_column|\%cond|\@cond|\$cond?, \%attrs? =back @@ -127,7 +130,7 @@ class's primary key in one (or more) of the calling class columns. This relationship defaults to using C<$accessor_name> as the column name in this class to resolve the join against the primary key from C<$related_class>, unless C<$our_fk_column> specifies the foreign key column -in this class or C specifies a reference to a join condition hash. +in this class or C specifies a reference to a join condition. =over @@ -155,13 +158,11 @@ OR =item cond -A hashref where the keys are C and -the values are C. This is useful for -relations that are across multiple columns. +A hashref, arrayref or coderef specifying a custom join expression. For +documentation see L. =back - # in a Book class (where Author has many Books) My::DBIC::Schema::Book->belongs_to( author => @@ -191,12 +192,14 @@ relations that are across multiple columns. $book->get_column('author_id'); -If the relationship is optional -- i.e. the column containing the foreign key -can be NULL -- then the belongs_to relationship does the right thing. Thus, in -the example above C<$obj-Eauthor> would return C. However in this -case you would probably want to set the C attribute so that a C is done, which makes complex resultsets involving C or C -operations work correctly. The modified declaration is shown below: +If the relationship is optional -- i.e. the column containing the +foreign key can be NULL -- then the belongs_to relationship does the +right thing. Thus, in the example above C<< $obj->author >> would +return C. However in this case you would probably want to set +the L attribute so that +a C is done, which makes complex resultsets involving +C or C operations work correctly. The modified +declaration is shown below: # in a Book class (where Author has_many Books) __PACKAGE__->belongs_to( @@ -213,13 +216,13 @@ in the $attr hashref. By default, DBIC will return undef and avoid querying the database if a C accessor is called when any part of the foreign key IS NULL. To -disable this behavior, pass C<< undef_on_null_fk => 0 >> in the C<$attr> +disable this behavior, pass C<< undef_on_null_fk => 0 >> in the C<\%attrs> hashref. NOTE: If you are used to L relationships, this is the equivalent of C. -See L for documentation on relationship +See L for documentation on relationship methods and valid relationship attributes. Also see L for a L which can be assigned to relationships as well. @@ -228,7 +231,7 @@ which can be assigned to relationships as well. =over 4 -=item Arguments: $accessor_name, $related_class, $their_fk_column|\%cond|\@cond?, \%attrs? +=item Arguments: $accessor_name, $related_class, $their_fk_column|\%cond|\@cond\&cond?, \%attrs? =back @@ -238,7 +241,7 @@ records in the foreign table (e.g. a C). This relationship defaults to using the end of this classes namespace as the foreign key in C<$related_class> to resolve the join, unless C<$their_fk_column> specifies the foreign key column in C<$related_class> or C -specifies a reference to a join condition hash. +specifies a reference to a join condition. =over @@ -267,19 +270,8 @@ OR =item cond -A hashref where the keys are C and -the values are C. This is useful for -relations that are across multiple columns. - -OR - -An arrayref containing an SQL::Abstract-like condition. For example a -link table where two columns link back to the same table. This is an -OR condition. - - My::Schema::Item->has_many('rels', 'My::Schema::Relationships', - [ { 'foreign.LItemID' => 'self.ID' }, - { 'foreign.RItemID' => 'self.ID'} ]); +A hashref, arrayref or coderef specifying a custom join expression. For +documentation see L. =back @@ -329,13 +321,14 @@ OR condition. $author->add_to_books(\%col_data); -Three methods are created when you create a has_many relationship. The first -method is the expected accessor method, C<$accessor_name()>. The second is -almost exactly the same as the accessor method but "_rs" is added to the end of -the method name. This method works just like the normal accessor, except that -it always returns a resultset, even in list context. The third method, -named C<< add_to_$relname >>, will also be added to your Row items; this -allows you to insert new related items, using the same mechanism as in +Three methods are created when you create a has_many relationship. +The first method is the expected accessor method, C<$accessor_name()>. +The second is almost exactly the same as the accessor method but "_rs" +is added to the end of the method name, eg C<$accessor_name_rs()>. +This method works just like the normal accessor, except that it always +returns a resultset, even in list context. The third method, named C<< +add_to_$relname >>, will also be added to your Row items; this allows +you to insert new related items, using the same mechanism as in L. If you delete an object in a class with a C relationship, all @@ -352,16 +345,17 @@ the related objects will be copied as well. To turn this behaviour off, pass C<< cascade_copy => 0 >> in the C<$attr> hashref. The behaviour defaults to C<< cascade_copy => 1 >>. -See L for documentation on relationship -methods and valid relationship attributes. Also see L -for a L -which can be assigned to relationships as well. +See L for documentation on +relationship methods and valid relationship attributes. Also see +L for a L which can be assigned to +relationships as well. =head2 might_have =over 4 -=item Arguments: $accessor_name, $related_class, $their_fk_column|\%cond|\@cond?, \%attrs? +=item Arguments: $accessor_name, $related_class, $their_fk_column|\%cond|\@cond|\&cond?, \%attrs? =back @@ -369,7 +363,7 @@ Creates an optional one-to-one relationship with a class. This relationship defaults to using C<$accessor_name> as the foreign key in C<$related_class> to resolve the join, unless C<$their_fk_column> specifies the foreign key column in C<$related_class> or C specifies a reference to a join -condition hash. +condition. =over @@ -397,9 +391,8 @@ OR =item cond -A hashref where the keys are C and -the values are C. This is useful for -relations that are across multiple columns. +A hashref, arrayref or coderef specifying a custom join expression. For +documentation see L. =back @@ -436,27 +429,28 @@ update, so if your database has a constraint on the relationship, it will have deleted/updated the related records or raised an exception before DBIx::Class gets to perform the cascaded operation. -See L for documentation on relationship -methods and valid relationship attributes. Also see L -for a L -which can be assigned to relationships as well. +See L for documentation on +relationship methods and valid relationship attributes. Also see +L for a L which can be assigned to +relationships as well. -Note that if you supply a condition on which to join, if the column in the +Note that if you supply a condition on which to join, and the column in the current table allows nulls (i.e., has the C attribute set to a true value), than C will warn about this because it's naughty and -you shouldn't do that. +you shouldn't do that. The warning will look something like: - "might_have/has_one" must not be on columns with is_nullable set to true (MySchema::SomeClass/key) + "might_have/has_one" must not be on columns with is_nullable set to true (MySchema::SomeClass/key) If you must be naughty, you can suppress the warning by setting C environment variable to a true value. Otherwise, -you probably just want to use C. +you probably just meant to use C. =head2 has_one =over 4 -=item Arguments: $accessor_name, $related_class, $their_fk_column|\%cond|\@cond?, \%attrs? +=item Arguments: $accessor_name, $related_class, $their_fk_column|\%cond|\@cond|\&cond?, \%attrs? =back @@ -464,7 +458,7 @@ Creates a one-to-one relationship with a class. This relationship defaults to using C<$accessor_name> as the foreign key in C<$related_class> to resolve the join, unless C<$their_fk_column> specifies the foreign key column in C<$related_class> or C specifies a reference to a join -condition hash. +condition. =over @@ -492,9 +486,8 @@ OR =item cond -A hashref where the keys are C and -the values are C. This is useful for -relations that are across multiple columns. +A hashref, arrayref or coderef specifying a custom join expression. For +documentation see L. =back @@ -527,17 +520,19 @@ always present. The only difference between C and C is that C uses an (ordinary) inner join, whereas C defaults to a left join. -The has_one relationship should be used when a row in the table has exactly one -related row in another table. If the related row might not exist in the foreign -table, use the L relationship. +The has_one relationship should be used when a row in the table must +have exactly one related row in another table. If the related row +might not exist in the foreign table, use the +L relationship. In the above example, each Book in the database is associated with exactly one ISBN object. -See L for documentation on relationship -methods and valid relationship attributes. Also see L -for a L -which can be assigned to relationships as well. +See L for documentation on +relationship methods and valid relationship attributes. Also see +L for a L which can be assigned to +relationships as well. Note that if you supply a condition on which to join, if the column in the current table allows nulls (i.e., has the C attribute set to a @@ -628,10 +623,11 @@ set: C, C, C, and similarly named accessors will be created for the Role class for the C many_to_many relationship. -See L for documentation on relationship -methods and valid relationship attributes. Also see L -for a L -which can be assigned to relationships as well. +See L for documentation on +relationship methods and valid relationship attributes. Also see +L for a L which can be assigned to +relationships as well. =cut diff --git a/lib/DBIx/Class/Relationship/Base.pm b/lib/DBIx/Class/Relationship/Base.pm index 71554c7..44496c4 100644 --- a/lib/DBIx/Class/Relationship/Base.pm +++ b/lib/DBIx/Class/Relationship/Base.pm @@ -15,6 +15,17 @@ DBIx::Class::Relationship::Base - Inter-table relationships =head1 SYNOPSIS + __PACKAGE__->add_relationship('spiders', + 'My::DB::Result::Creatures', + sub { + my ( $me_alias, $rel_alias) = @_; + return + { "${rel_alias}.id" => { '=' => \"${me_alias}.id"}, + "${rel_alias}.type" => { '=', "arachnid" }, + }; + + }); + =head1 DESCRIPTION This class provides methods to describe the relationships between the @@ -27,50 +38,146 @@ methods, for predefined ones, look in L. =over 4 -=item Arguments: 'relname', 'Foreign::Class', $cond, $attrs +=item Arguments: 'relname', 'Foreign::Class', $condition, $attrs =back - __PACKAGE__->add_relationship('relname', 'Foreign::Class', $cond, $attrs); + __PACKAGE__->add_relationship('relname', + 'Foreign::Class', + $condition, $attrs); + +Create a custom relationship between one result source and another +source, indicated by its class name. =head3 condition -The condition needs to be an L-style representation of the -join between the tables. When resolving the condition for use in a C, -keys using the pseudo-table C are resolved to mean "the Table on the -other side of the relationship", and values using the pseudo-table C -are resolved to mean "the Table this class is representing". Other -restrictions, such as by value, sub-select and other tables, may also be -used. Please check your database for C parameter support. +The condition argument describes the JOIN expression used to connect +the two sources when creating SQL queries. -For example, if you're creating a relationship from C to C, where -the C table has a column C containing the ID of the C -row: +To create simple equality joins, supply a hashref containing the +remote table column name as the key(s), and the local table column +name as the value(s), for example: { 'foreign.author_id' => 'self.id' } -will result in the C clause +will result in the C clause: author me JOIN book book ON book.author_id = me.id -For multi-column foreign keys, you will need to specify a C-to-C -mapping for each column in the key. For example, if you're creating a -relationship from C to C, where the C table refers to a -publisher and a type (e.g. "paperback"): +This describes a relationship between the C table and the +C table where the C table has a column C +containing the ID value of the C. + +C and C are psuedo aliases and must be entered +literally. They will be replaced with the actual correct table alias +when the SQL is produced. + +Similarly: { 'foreign.publisher_id' => 'self.publisher_id', 'foreign.type_id' => 'self.type_id', } -This will result in the C clause: +will result in the C clause: book me JOIN edition edition ON edition.publisher_id = me.publisher_id AND edition.type_id = me.type_id -Each key-value pair provided in a hashref will be used as Ced conditions. -To add an Ced condition, use an arrayref of hashrefs. See the -L documentation for more details. +This describes the relationship from C to C, where the +C table refers to a publisher and a type (e.g. "paperback"): + +As is the default in L, the key-value pairs will be +Ced in the result. C can be achieved with an arrayref, for +example: + + [ + { 'foreign.left_itemid' => 'self.id' }, + { 'foreign.right_itemid' => 'self.id' }, + ] + +which results in the C clause: + + items me JOIN related_items rel_link ON rel_link.left_itemid = me.id + OR rel_link.right_itemid = me.id + +This describes the relationship from C to C, +where C is a many-to-many linking table, linking Items +back to themselves. + +To create joins which describe more than a simple equality of column +values, the custom join condition coderef syntax can be used: + + sub { + my ( $me_alias, $rel_alias ) = @_; + return + ({ "${rel_alias}.artist" => { '=' => \"${me_alias}.artistid"}, + "${rel_alias}.year" => { '>', "1979", + '<', "1990" } + }); + } + +this will result in the C clause: + + artist me LEFT JOIN cd cds_80s_noopt ON + ( cds_80s_noopt.artist = me.artistid + AND ( cds_80s_noopt.year < ? AND cds_80s_noopt.year > ? ) + ) + +with the bind values: + + '1990', '1979' + +C<$rel_alias> is the equivalent to C in the simple syntax, +and will be replaced by the actual remote table alias in the produced +SQL. Similarly, C<$me_alias> is the equivalent to C and will be +replaced with the local table alias in the SQL. + +The actual syntax returned by the coderef should be valid +L syntax, similar to normal +L conditions. + +To help optimise the SQL produced, a second optional hashref can be +returned to be used when the relationship accessor is called directly +on a Row object: + + sub { + my ( $me_alias, $rel_alias, $me_result_source, + $rel_name, $optional_me_object ) = @_; + return + ({ "${rel_alias}.artist" => { '=' => \"${me_alias}.artistid"}, + "${rel_alias}.year" => { '>', "1979", + '<', "1990" } + }, + $optional_me_object && + { "${rel_alias}.artist" => $optional_me_object->artistid, + "${rel_alias}.year" => { '>', "1979", + '<', "1990" } + }); + } + +Now this code: + + my $artist = $schema->resultset("Artist")->find({ id => 4 }); + $artist->cds_80s->all; + +Produces: + + SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track + FROM cd me + WHERE ( ( me.artist = ? AND ( me.year < ? AND me.year > ? ) ) ) + +With the bind values: + + '4', '1990', '1979' + +The C<$optional_me_object> used to create the second hashref contains +a row object, the object that the relation accessor was called on. + +C<$me_result_source> the L of the table +being searched on, and C<$rel_name>, the name of the relation +containing this condition, are also provided as arguments. These may +be useful to more complicated condition calculation. =head3 attributes