Add unique constraint declaration and new ResultSet method, update_or_create
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSource.pm
1 package DBIx::Class::ResultSource;
2
3 use strict;
4 use warnings;
5
6 use DBIx::Class::ResultSet;
7
8 use Carp qw/croak/;
9
10 use base qw/DBIx::Class/;
11 __PACKAGE__->load_components(qw/AccessorGroup/);
12
13 __PACKAGE__->mk_group_accessors('simple' =>
14   qw/_ordered_columns _columns _primaries _unique_constraints name resultset_class result_class schema from _relationships/);
15
16 =head1 NAME 
17
18 DBIx::Class::ResultSource - Result source object
19
20 =head1 SYNOPSIS
21
22 =head1 DESCRIPTION
23
24 A ResultSource is a component of a schema from which results can be directly
25 retrieved, most usually a table (see L<DBIx::Class::ResultSource::Table>)
26
27 =head1 METHODS
28
29 =cut
30
31 sub new {
32   my ($class, $attrs) = @_;
33   $class = ref $class if ref $class;
34   my $new = bless({ %{$attrs || {}} }, $class);
35   $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
36   $new->{_ordered_columns} ||= [];
37   $new->{_columns} ||= {};
38   $new->{_relationships} ||= {};
39   $new->{name} ||= "!!NAME NOT SET!!";
40   return $new;
41 }
42
43 sub add_columns {
44   my ($self, @cols) = @_;
45   $self->_ordered_columns( \@cols )
46     if !$self->_ordered_columns;
47   my @added;
48   my $columns = $self->_columns;
49   while (my $col = shift @cols) {
50
51     my $column_info = ref $cols[0] ? shift(@cols) : {};
52       # If next entry is { ... } use that for the column info, if not
53       # use an empty hashref
54
55     push(@added, $col) unless exists $columns->{$col};
56
57     $columns->{$col} = $column_info;
58   }
59   push @{ $self->_ordered_columns }, @added;
60   return $self;
61 }
62
63 *add_column = \&add_columns;
64
65 =head2 add_columns
66
67   $table->add_columns(qw/col1 col2 col3/);
68
69   $table->add_columns('col1' => \%col1_info, 'col2' => \%col2_info, ...);
70
71 Adds columns to the result source. If supplied key => hashref pairs uses
72 the hashref as the column_info for that column.
73
74 =head2 add_column
75
76   $table->add_column('col' => \%info?);
77
78 Convenience alias to add_columns
79
80 =cut
81
82 sub resultset {
83   my $self = shift;
84   return $self->resultset_class->new($self);
85 }
86
87 =head2 has_column
88
89   if ($obj->has_column($col)) { ... }                                           
90                                                                                 
91 Returns 1 if the source has a column of this name, 0 otherwise.
92                                                                                 
93 =cut                                                                            
94
95 sub has_column {
96   my ($self, $column) = @_;
97   return exists $self->_columns->{$column};
98 }
99
100 =head2 column_info 
101
102   my $info = $obj->column_info($col);                                           
103
104 Returns the column metadata hashref for a column.
105                                                                                 
106 =cut                                                                            
107
108 sub column_info {
109   my ($self, $column) = @_;
110   croak "No such column $column" unless exists $self->_columns->{$column};
111   if ( (! $self->_columns->{$column}->{data_type})
112        && $self->schema && $self->storage() ){
113       my $info;
114 ############ eval for the case of storage without table 
115       eval{
116           $info = $self->storage->columns_info_for ( $self->from() );
117       };
118       if ( ! $@ ){
119           for my $col ( keys %{$self->_columns} ){
120               for my $i ( keys %{$info->{$col}} ){
121                   $self->_columns()->{$col}->{$i} = $info->{$col}->{$i};
122               }
123           }
124       }
125   }
126   return $self->_columns->{$column};
127 }
128
129 =head2 columns
130
131   my @column_names = $obj->columns;
132
133 Returns all column names in the order they were declared to add_columns
134
135 =cut
136
137 sub columns {
138   croak "columns() is a read-only accessor, did you mean add_columns()?" if (@_ > 1);
139   return @{shift->{_ordered_columns}||[]};
140 }
141
142 =head2 set_primary_key(@cols)
143
144 Defines one or more columns as primary key for this source. Should be
145 called after C<add_columns>.
146
147 Additionally, defines a unique constraint named C<primary>.
148
149 =cut
150
151 sub set_primary_key {
152   my ($self, @cols) = @_;
153   # check if primary key columns are valid columns
154   for (@cols) {
155     $self->throw("No such column $_ on table ".$self->name)
156       unless $self->has_column($_);
157   }
158   $self->_primaries(\@cols);
159
160   $self->add_unique_constraint(primary => \@cols);
161 }
162
163 =head2 primary_columns
164
165 Read-only accessor which returns the list of primary keys.
166
167 =cut
168
169 sub primary_columns {
170   return @{shift->_primaries||[]};
171 }
172
173 =head2 add_unique_constraint
174
175 Declare a unique constraint on this source. Call once for each unique
176 constraint.
177
178   # For e.g. UNIQUE (column1, column2)
179   __PACKAGE__->add_unique_constraint(constraint_name => [ qw/column1 column2/ ]);
180
181 =cut
182
183 sub add_unique_constraint {
184   my ($self, $name, $cols) = @_;
185
186   for (@$cols) {
187     $self->throw("No such column $_ on table ".$self->name)
188       unless $self->has_column($_);
189   }
190
191   my %unique_constraints = $self->unique_constraints;
192   $unique_constraints{$name} = $cols;
193   $self->_unique_constraints(\%unique_constraints);
194 }
195
196 =head2 unique_constraints
197
198 Read-only accessor which returns the list of unique constraints on this source.
199
200 =cut
201
202 sub unique_constraints {
203   return %{shift->_unique_constraints||{}};
204 }
205
206 =head2 from
207
208 Returns an expression of the source to be supplied to storage to specify
209 retrieval from this source; in the case of a database the required FROM clause
210 contents.
211
212 =cut
213
214 =head2 storage
215
216 Returns the storage handle for the current schema
217
218 =cut
219
220 sub storage { shift->schema->storage; }
221
222 =head2 add_relationship
223
224   $source->add_relationship('relname', 'related_source', $cond, $attrs);
225
226 The relation name can be arbitrary, but must be unique for each relationship
227 attached to this result source. 'related_source' should be the name with
228 which the related result source was registered with the current schema
229 (for simple schemas this is usally either Some::Namespace::Foo or just Foo)
230
231 The condition needs to be an SQL::Abstract-style representation of the join
232 between the tables. For example, if you're creating a rel from Foo to Bar,
233
234   { 'foreign.foo_id' => 'self.id' }                                             
235                                                                                 
236 will result in the JOIN clause                                                  
237                                                                                 
238   foo me JOIN bar bar ON bar.foo_id = me.id                                     
239                                                                                 
240 You can specify as many foreign => self mappings as necessary.
241
242 Valid attributes are as follows:                                                
243                                                                                 
244 =over 4                                                                         
245                                                                                 
246 =item join_type                                                                 
247                                                                                 
248 Explicitly specifies the type of join to use in the relationship. Any SQL       
249 join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in the SQL      
250 command immediately before C<JOIN>.                                             
251                                                                                 
252 =item proxy                                                                     
253                                                                                 
254 An arrayref containing a list of accessors in the foreign class to proxy in     
255 the main class. If, for example, you do the following:                          
256                                                                                 
257   __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => qw[/ margle /] });    
258                                                                                 
259 Then, assuming Bar has an accessor named margle, you can do:                    
260                                                                                 
261   my $obj = Foo->find(1);                                                       
262   $obj->margle(10); # set margle; Bar object is created if it doesn't exist     
263                                                                                 
264 =item accessor                                                                  
265                                                                                 
266 Specifies the type of accessor that should be created for the relationship.     
267 Valid values are C<single> (for when there is only a single related object),    
268 C<multi> (when there can be many), and C<filter> (for when there is a single    
269 related object, but you also want the relationship accessor to double as        
270 a column accessor). For C<multi> accessors, an add_to_* method is also          
271 created, which calls C<create_related> for the relationship.                    
272                                                                                 
273 =back
274
275 =cut
276
277 sub add_relationship {
278   my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
279   croak "Can't create relationship without join condition" unless $cond;
280   $attrs ||= {};
281
282   my %rels = %{ $self->_relationships };
283   $rels{$rel} = { class => $f_source_name,
284                   source => $f_source_name,
285                   cond  => $cond,
286                   attrs => $attrs };
287   $self->_relationships(\%rels);
288
289   return $self;
290
291   # XXX disabled. doesn't work properly currently. skip in tests.
292
293   my $f_source = $self->schema->source($f_source_name);
294   unless ($f_source) {
295     eval "require $f_source_name;";
296     if ($@) {
297       die $@ unless $@ =~ /Can't locate/;
298     }
299     $f_source = $f_source_name->result_source;
300     #my $s_class = ref($self->schema);
301     #$f_source_name =~ m/^${s_class}::(.*)$/;
302     #$self->schema->register_class(($1 || $f_source_name), $f_source_name);
303     #$f_source = $self->schema->source($f_source_name);
304   }
305   return unless $f_source; # Can't test rel without f_source
306
307   eval { $self->resolve_join($rel, 'me') };
308
309   if ($@) { # If the resolve failed, back out and re-throw the error
310     delete $rels{$rel}; # 
311     $self->_relationships(\%rels);
312     croak "Error creating relationship $rel: $@";
313   }
314   1;
315 }
316
317 =head2 relationships()
318
319 Returns all valid relationship names for this source
320
321 =cut
322
323 sub relationships {
324   return keys %{shift->_relationships};
325 }
326
327 =head2 relationship_info($relname)
328
329 Returns the relationship information for the specified relationship name
330
331 =cut
332
333 sub relationship_info {
334   my ($self, $rel) = @_;
335   return $self->_relationships->{$rel};
336
337
338 =head2 has_relationship($rel)
339
340 Returns 1 if the source has a relationship of this name, 0 otherwise.
341                                                                                 
342 =cut                                                                            
343
344 sub has_relationship {
345   my ($self, $rel) = @_;
346   return exists $self->_relationships->{$rel};
347 }
348
349 =head2 resolve_join($relation)
350
351 Returns the join structure required for the related result source
352
353 =cut
354
355 sub resolve_join {
356   my ($self, $join, $alias) = @_;
357   if (ref $join eq 'ARRAY') {
358     return map { $self->resolve_join($_, $alias) } @$join;
359   } elsif (ref $join eq 'HASH') {
360     return map { $self->resolve_join($_, $alias),
361                  $self->related_source($_)->resolve_join($join->{$_}, $_) }
362            keys %$join;
363   } elsif (ref $join) {
364     croak ("No idea how to resolve join reftype ".ref $join);
365   } else {
366     my $rel_info = $self->relationship_info($join);
367     croak("No such relationship ${join}") unless $rel_info;
368     my $type = $rel_info->{attrs}{join_type} || '';
369     return [ { $join => $self->related_source($join)->from,
370                -join_type => $type },
371              $self->resolve_condition($rel_info->{cond}, $join, $alias) ];
372   }
373 }
374
375 =head2 resolve_condition($cond, $rel, $alias|$object)
376
377 Resolves the passed condition to a concrete query fragment. If given an alias,
378 returns a join condition; if given an object, inverts that object to produce
379 a related conditional from that object.
380
381 =cut
382
383 sub resolve_condition {
384   my ($self, $cond, $rel, $for) = @_;
385   #warn %$cond;
386   if (ref $cond eq 'HASH') {
387     my %ret;
388     while (my ($k, $v) = each %{$cond}) {
389       # XXX should probably check these are valid columns
390       $k =~ s/^foreign\.// || croak "Invalid rel cond key ${k}";
391       $v =~ s/^self\.// || croak "Invalid rel cond val ${v}";
392       if (ref $for) { # Object
393         #warn "$self $k $for $v";
394         $ret{$k} = $for->get_column($v);
395         #warn %ret;
396       } else {
397         $ret{"${rel}.${k}"} = "${for}.${v}";
398       }
399     }
400     return \%ret;
401   } elsif (ref $cond eq 'ARRAY') {
402     return [ map { $self->resolve_condition($_, $rel, $for) } @$cond ];
403   } else {
404    die("Can't handle this yet :(");
405   }
406 }
407
408
409 =head2 related_source($relname)
410
411 Returns the result source for the given relationship
412
413 =cut
414
415 sub related_source {
416   my ($self, $rel) = @_;
417   return $self->schema->source($self->relationship_info($rel)->{source});
418 }
419
420 1;
421
422 =head1 AUTHORS
423
424 Matt S. Trout <mst@shadowcatsystems.co.uk>
425
426 =head1 LICENSE
427
428 You may distribute this code under the same terms as Perl itself.
429
430 =cut
431