343c0d02d8388f4c2c12ac621a3d070f93081513
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Relationship / Base.pm
1 package DBIx::Class::Relationship::Base;
2
3 use strict;
4 use warnings;
5
6 use base qw/DBIx::Class/;
7
8 =head1 NAME 
9
10 DBIx::Class::Relationship::Base - Inter-table relationships
11
12 =head1 SYNOPSIS
13
14 =head1 DESCRIPTION
15
16 This class provides methods to describe the relationships between the
17 tables in your database model. These are the "bare bones" relationships
18 methods, for predefined ones, look in L<DBIx::Class::Relationship>. 
19
20 =head1 METHODS
21
22 =head2 add_relationship
23
24 =head3 Arguments: ('relname', 'Foreign::Class', $cond, $attrs)
25
26   __PACKAGE__->add_relationship('relname', 'Foreign::Class', $cond, $attrs);
27
28 The condition needs to be an SQL::Abstract-style representation of the
29 join between the tables. When resolving the condition for use in a JOIN,
30 keys using the psuedo-table I<foreign> are resolved to mean "the Table on the
31 other side of the relationship", and values using the psuedo-table I<self>
32 are resolved to mean "the Table this class is representing". Other
33 restrictions, such as by value, sub-select and other tables, may also be
34 used. Please check your database for JOIN parameter support.
35
36 For example, if you're creating a rel from Author to Book, where the Book
37 table has a column author_id containing the ID of the Author row:
38
39   { 'foreign.author_id' => 'self.id' }
40
41 will result in the JOIN clause
42
43   author me JOIN book book ON bar.author_id = me.id
44
45 You can specify as many foreign => self mappings as necessary. Each key/value
46 pair provided in a hashref will be used as ANDed conditions, to add an ORed
47 condition, use an arrayref of hashrefs. See the L<SQL::Abstract> documentation
48 for more details.
49
50 Valid attributes are as follows:
51
52 =over 4
53
54 =item join_type
55
56 Explicitly specifies the type of join to use in the relationship. Any SQL
57 join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in the SQL
58 command immediately before C<JOIN>.
59
60 =item proxy
61
62 An arrayref containing a list of accessors in the foreign class to create in
63 the main class. If, for example, you do the following:
64   
65   MyDB::Schema::CD->might_have(liner_notes => 'MyDB::Schema::LinerNotes', undef, {
66     proxy => [ qw/notes/ ],
67   });
68   
69 Then, assuming MyDB::Schema::LinerNotes has an accessor named notes, you can do:
70
71   my $cd = MyDB::Schema::CD->find(1);
72   $cd->notes('Notes go here'); # set notes -- LinerNotes object is
73                                # created if it doesn't exist
74   
75 =item accessor
76
77 Specifies the type of accessor that should be created for the relationship.
78 Valid values are C<single> (for when there is only a single related object),
79 C<multi> (when there can be many), and C<filter> (for when there is a single
80 related object, but you also want the relationship accessor to double as
81 a column accessor). For C<multi> accessors, an add_to_* method is also
82 created, which calls C<create_related> for the relationship.
83
84 =back
85
86 =head2 register_relationship
87
88 =head3 Arguments: ($relname, $rel_info)
89
90 Registers a relationship on the class. This is called internally by
91 L<DBIx::Class::ResultSourceProxy> to set up Accessors and Proxies.
92
93 =cut
94
95 sub register_relationship { }
96
97 =head2 related_resultset($name)
98
99   $rs = $obj->related_resultset('related_table');
100
101 Returns a L<DBIx::Class::ResultSet> for the relationship named $name.
102
103 =cut
104
105 sub related_resultset {
106   my $self = shift;
107   $self->throw_exception("Can't call *_related as class methods")
108     unless ref $self;
109   my $rel = shift;
110   my $rel_obj = $self->relationship_info($rel);
111   $self->throw_exception( "No such relationship ${rel}" )
112     unless $rel_obj;
113   
114   return $self->{related_resultsets}{$rel} ||= do {
115     my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {});
116     $attrs = { %{$rel_obj->{attrs} || {}}, %$attrs };
117
118     $self->throw_exception( "Invalid query: @_" )
119       if (@_ > 1 && (@_ % 2 == 1));
120     my $query = ((@_ > 1) ? {@_} : shift);
121
122     my $cond = $self->result_source->resolve_condition(
123       $rel_obj->{cond}, $rel, $self
124     );
125     if (ref $cond eq 'ARRAY') {
126       $cond = [ map { my $hash;
127         foreach my $key (keys %$_) {
128           my $newkey = $key =~ /\./ ? "me.$key" : $key;
129           $hash->{$newkey} = $_->{$key};
130         }; $hash } @$cond ];
131     } else {
132       foreach my $key (grep { ! /\./ } keys %$cond) {
133         $cond->{"me.$key"} = delete $cond->{$key};
134       }
135     }
136     $query = ($query ? { '-and' => [ $cond, $query ] } : $cond);
137     $self->result_source->related_source($rel)->resultset->search(
138       $query, $attrs
139     );
140   };
141 }
142
143 =head2 search_related
144
145   $rs->search_related('relname', $cond, $attrs);
146
147 Run a search on a related resultset. The search will be restricted to the
148 item or items represented by the L<DBIx::Class::ResultSet> it was called
149 upon. This method can be called on a ResultSet, a Row or a ResultSource class.
150
151 =cut
152
153 sub search_related {
154   return shift->related_resultset(shift)->search(@_);
155 }
156
157 =head2 count_related
158
159   $obj->count_related('relname', $cond, $attrs);
160
161 Returns the count of all the items in the related resultset, restricted by the
162 current item or where conditions. Can be called on a
163 L<DBIx::Classl::Manual::Glossary/"ResultSet"> or a
164 L<DBIx::Class::Manual::Glossary/"Row"> object.
165
166 =cut
167
168 sub count_related {
169   my $self = shift;
170   return $self->search_related(@_)->count;
171 }
172
173 =head2 new_related
174
175   my $new_obj = $obj->new_related('relname', \%col_data);
176
177 Create a new item of the related foreign class. If called on a
178 L<DBIx::Class::Manual::Glossary/"Row"> object, it will magically
179 set any primary key values into foreign key columns for you. The newly
180 created item will not be saved into your storage until you call C<insert>
181 on it.
182
183 =cut
184
185 sub new_related {
186   my ($self, $rel, $values, $attrs) = @_;
187   return $self->search_related($rel)->new($values, $attrs);
188 }
189
190 =head2 create_related
191
192   my $new_obj = $obj->create_related('relname', \%col_data);
193
194 Creates a new item, similarly to new_related, and also inserts the item's data
195 into your storage medium. See the distinction between C<create> and C<new>
196 in L<DBIx::Class::ResultSet> for details.
197
198 =cut
199
200 sub create_related {
201   my $self = shift;
202   my $rel = shift;
203   my $obj = $self->search_related($rel)->create(@_);
204   delete $self->{related_resultsets}->{$rel};
205   return $obj;
206 }
207
208 =head2 find_related
209
210   my $found_item = $obj->find_related('relname', @pri_vals | \%pri_vals);
211
212 Attempt to find a related object using its primary key or unique constraints.
213 See C<find> in L<DBIx::Class::ResultSet> for details.
214
215 =cut
216
217 sub find_related {
218   my $self = shift;
219   my $rel = shift;
220   return $self->search_related($rel)->find(@_);
221 }
222
223 =head2 find_or_create_related
224
225   my $new_obj = $obj->find_or_create_related('relname', \%col_data);
226
227 Find or create an item of a related class. See C<find_or_create> in
228 L<DBIx::Class::ResultSet> for details.
229
230 =cut
231
232 sub find_or_create_related {
233   my $self = shift;
234   return $self->find_related(@_) || $self->create_related(@_);
235 }
236
237 =head2 set_from_related
238
239   $book->set_from_related('author', $author_obj);
240
241 Set column values on the current object, using related values from the given
242 related object. This is used to associate previously separate objects, for
243 example, to set the correct author for a book, find the Author object, then
244 call set_from_related on the book.
245
246 The columns are only set in the local copy of the object, call C<update> to set
247 them in the storage.
248
249 =cut
250
251 sub set_from_related {
252   my ($self, $rel, $f_obj) = @_;
253   my $rel_obj = $self->relationship_info($rel);
254   $self->throw_exception( "No such relationship ${rel}" ) unless $rel_obj;
255   my $cond = $rel_obj->{cond};
256   $self->throw_exception(
257     "set_from_related can only handle a hash condition; the ".
258     "condition for $rel is of type ".
259     (ref $cond ? ref $cond : 'plain scalar')
260   ) unless ref $cond eq 'HASH';
261   my $f_class = $self->result_source->schema->class($rel_obj->{class});
262   $self->throw_exception( "Object $f_obj isn't a ".$f_class )
263     unless $f_obj->isa($f_class);
264   $self->set_columns(
265     $self->result_source->resolve_condition(
266        $rel_obj->{cond}, $f_obj, $rel));
267   return 1;
268 }
269
270 =head2 update_from_related
271
272   $book->update_from_related('author', $author_obj);
273
274 As C<set_from_related>, but the changes are immediately updated onto your
275 storage.
276
277 =cut
278
279 sub update_from_related {
280   my $self = shift;
281   $self->set_from_related(@_);
282   $self->update;
283 }
284
285 =head2 delete_related
286
287   $obj->delete_related('relname', $cond, $attrs);
288
289 Delete any related item subject to the given conditions.
290
291 =cut
292
293 sub delete_related {
294   my $self = shift;
295   my $obj = $self->search_related(@_)->delete;
296   delete $self->{related_resultsets}->{$_[0]};
297   return $obj;
298 }
299
300 1;
301
302 =head1 AUTHORS
303
304 Matt S. Trout <mst@shadowcatsystems.co.uk>
305
306 =head1 LICENSE
307
308 You may distribute this code under the same terms as Perl itself.
309
310 =cut
311