File Coverage

blib/lib/Rose/DB/Object/Metadata/Relationship/ManyToMany.pm
Criterion Covered Total %
statement 115 149 77.1
branch 44 92 47.8
condition 12 43 27.9
subroutine 18 21 85.7
pod 4 8 50.0
total 193 313 61.6


line stmt bran cond sub pod time code
1             package Rose::DB::Object::Metadata::Relationship::ManyToMany;
2              
3 2     2   15 use strict;
  2         4  
  2         65  
4              
5 2     2   11 use Carp();
  2         11  
  2         57  
6 2     2   12 use Scalar::Util qw(weaken);
  2         4  
  2         135  
7              
8 2     2   13 use Rose::DB::Object::Metadata::Relationship;
  2         4  
  2         119  
9             our @ISA = qw(Rose::DB::Object::Metadata::Relationship);
10              
11 2     2   14 use Rose::DB::Object::Exception;
  2         4  
  2         63  
12 2     2   11 use Rose::Object::MakeMethods::Generic;
  2         4  
  2         18  
13 2     2   59 use Rose::DB::Object::MakeMethods::Generic;
  2         3  
  2         16  
14              
15 2     2   63 use Rose::DB::Object::Constants qw(PRIVATE_PREFIX);
  2         4  
  2         294  
16              
17             our $VERSION = '0.784';
18              
19             our $Debug = 0;
20              
21             __PACKAGE__->default_auto_method_types(qw(find get_set_on_save add_on_save));
22              
23             __PACKAGE__->add_common_method_maker_argument_names
24             (
25             qw(share_db map_class map_from map_to manager_class manager_method
26             manager_count_method manager_iterator_method manager_find_method
27             manager_args query_args map_record_method)
28             );
29              
30             use Rose::Object::MakeMethods::Generic
31             (
32 2         13 boolean =>
33             [
34             'share_db' => { default => 1 },
35             ],
36 2     2   15 );
  2         5  
37              
38             Rose::Object::MakeMethods::Generic->make_methods
39             (
40             { preserve_existing => 1 },
41             scalar =>
42             [
43             __PACKAGE__->common_method_maker_argument_names,
44             ],
45              
46             # These are set by the method maker when make_methods() is called
47              
48             scalar =>
49             [
50             'foreign_class', # class to be fetched
51             ],
52              
53             hash =>
54             [
55             # Map from map-table columns to self-table columns
56             'column_map',
57             ]
58             );
59              
60             __PACKAGE__->method_maker_info
61             (
62             count =>
63             {
64             class => 'Rose::DB::Object::MakeMethods::Generic',
65             type => 'objects_by_map',
66             interface => 'count',
67             },
68              
69             find =>
70             {
71             class => 'Rose::DB::Object::MakeMethods::Generic',
72             type => 'objects_by_map',
73             interface => 'find',
74             },
75              
76             iterator =>
77             {
78             class => 'Rose::DB::Object::MakeMethods::Generic',
79             type => 'objects_by_map',
80             interface => 'iterator',
81             },
82              
83             get_set =>
84             {
85             class => 'Rose::DB::Object::MakeMethods::Generic',
86             type => 'objects_by_map',
87             interface => 'get_set',
88             },
89              
90             get_set_now =>
91             {
92             class => 'Rose::DB::Object::MakeMethods::Generic',
93             type => 'objects_by_map',
94             interface => 'get_set_now',
95             },
96              
97             get_set_on_save =>
98             {
99             class => 'Rose::DB::Object::MakeMethods::Generic',
100             type => 'objects_by_map',
101             interface => 'get_set_on_save',
102             },
103              
104             add_now =>
105             {
106             class => 'Rose::DB::Object::MakeMethods::Generic',
107             type => 'objects_by_map',
108             interface => 'add_now',
109             },
110              
111             add_on_save =>
112             {
113             class => 'Rose::DB::Object::MakeMethods::Generic',
114             type => 'objects_by_map',
115             interface => 'add_on_save',
116             },
117             );
118              
119 49     49 1 382 sub type { 'many to many' }
120              
121 0     0 1 0 sub is_singular { 0 }
122              
123 2     2   703 use constant MAP_RECORD_ATTR => PRIVATE_PREFIX . '_map_record';
  2         4  
  2         153  
124 2     2   13 use constant MAP_RECORD_METHOD => 'map_record';
  2         4  
  2         213  
125              
126             MAKE_MAP_RECORD_METHOD:
127             {
128             my $counter = 1;
129              
130             sub make_map_record_method
131             {
132 1     1 0 5 my($map_to_class, $map_record_method, $map_class) = @_;
133              
134 1         6 my $key = MAP_RECORD_ATTR . '_' . $counter++;
135              
136 2     2   14 no strict 'refs';
  2         4  
  2         3383  
137 1         14 *{"${map_to_class}::$map_record_method"} = sub
138             {
139 0     0   0 my($self) = shift;
140              
141 0 0       0 if(@_)
142             {
143 0         0 my $arg = shift;
144 0         0 my $weaken = shift;
145              
146 0 0 0     0 if(ref $arg eq 'HASH')
    0          
147             {
148 0         0 return $self->{$key} = $map_class->new(%$arg);
149             }
150             elsif(!ref $arg || !UNIVERSAL::isa($arg, $map_class))
151             {
152 0         0 Carp::croak "Illegal map record argument: $arg";
153             }
154              
155 0 0       0 return $weaken ? (weaken($self->{$key} = $arg)) : ($self->{$key} = $arg);
156             }
157              
158 0         0 return $self->{$key}; # ||= $map_class->new;
159 1         15 };
160              
161 1         14 $map_to_class->meta->map_record_method_key($map_record_method => $key);
162              
163 1         3 return $key;
164             }
165             }
166              
167             sub manager_args
168             {
169 71     71 1 270 my($self) = shift;
170              
171 71 100       228 return $self->{'manager_args'} unless(@_);
172              
173 9         24 my $args = $self->{'manager_args'} = shift;
174              
175 9 50       25 if(my $method = $args->{'with_map_records'})
176             {
177 9 50       36 $method = MAP_RECORD_METHOD unless($method =~ /^[A-Za-z_]\w*$/);
178              
179 9         27 $self->map_record_method($method);
180             }
181              
182 9         21 return $args;
183             }
184              
185             sub build_method_name_for_type
186             {
187 42     42 1 87 my($self, $type) = @_;
188              
189 42 100 33     262 if($type eq 'get_set' || $type eq 'get_set_now' || $type eq 'get_set_on_save')
    100 66        
    50 66        
    0          
    0          
190             {
191 14         56 return $self->name;
192             }
193             elsif($type eq 'add_now' || $type eq 'add_on_save')
194             {
195 14         78 return 'add_' . $self->name;
196             }
197             elsif($type eq 'find')
198             {
199 14         80 return 'find_' . $self->name;
200             }
201             elsif($type eq 'iterator')
202             {
203 0         0 return $self->name . '_iterator';
204             }
205             elsif($type eq 'count')
206             {
207 0         0 return $self->name . '_count';
208             }
209              
210 0         0 return undef;
211             }
212              
213             sub sanity_check
214             {
215 2     2 0 5 my($self) = shift;
216              
217 2 50       8 defined $self->map_class or
218             Carp::croak $self->type, " relationship '", $self->name,
219             "' is missing a map_class";
220              
221 2         5 return 1;
222             }
223             my $i;
224             my %C;
225              
226             sub is_ready_to_make_methods
227             {
228 21     21 0 48 my($self) = shift;
229              
230 21         36 my $error;
231              
232             TRY:
233             {
234 21         34 local $@;
  21         38  
235              
236             # This code is (ug) duplicated from the method-maker itself, and
237             # slightly modified to run here. If the method-maker can't get all
238             # the info it needs, then we're not yet ready to make these methods.
239             eval
240 21         42 {
241             # Workaround for http://rt.perl.org/rt3/Ticket/Display.html?id=60890
242 21         82 local $SIG{'__DIE__'};
243              
244 21 50       88 my $map_class = $self->map_class or die "Missing map class";
245              
246 21 50       77 unless(UNIVERSAL::isa($map_class, 'Rose::DB::Object'))
247             {
248 0         0 die Rose::DB::Object::Exception::ClassNotReady->new(
249             "Map class $map_class not yet ready");
250             }
251              
252 21 50       66 my $map_meta = $map_class->meta or die
253             Rose::DB::Object::Exception::ClassNotReady->new(
254             "Missing meta object for $map_class");
255              
256 21         56 my $map_from = $self->map_from;
257 21         55 my $map_to = $self->map_to;
258 21         37 my $relationship = $self;
259              
260 21         59 my $target_class = $self->parent->class;
261 21 50       59 my $meta = $target_class->meta or die
262             Rose::DB::Object::Exception::ClassNotReady->new(
263             "Missing meta object for $target_class");
264              
265 21         96 my($map_to_class, $map_to_meta, $map_to_method);
266              
267             # "map" is the map table, "self" is the $target_class, and "remote"
268             # is the foreign object class
269 21         0 my(%map_column_to_self_method,
270             %map_column_to_self_column,
271             %map_method_to_remote_method);
272              
273             # Also grab the foreign object class that the mapper points to,
274             # the relationship name that points back to us, and the class
275             # name of the objects we really want to fetch.
276 21         0 my($require_objects, $local_rel, $foreign_class, %seen_fk);
277              
278 21         77 foreach my $item ($map_meta->foreign_keys, $map_meta->relationships)
279             {
280             # Track which foreign keys we've seen
281 56 100       395 if($item->isa('Rose::DB::Object::Metadata::ForeignKey'))
    50          
282             {
283 28         75 $seen_fk{$item->id}++;
284             }
285             elsif($item->isa('Rose::DB::Object::Metadata::Relationship'))
286             {
287             # Skip a relationship if we've already seen the equivalent foreign key
288 28 50       89 next if($seen_fk{$item->id});
289             }
290              
291 28 100 66     351 if($item->can('class') && $item->class eq $target_class)
292             {
293             # Skip if there was an explicit local relationship name and
294             # this is not that name.
295 14 50 33     65 unless($map_from && $item->name ne $map_from)
296             {
297 14 50       36 if(%map_column_to_self_method)
298             {
299 0         0 die Rose::DB::Object::Exception::ClassNotReady->new(
300             "Map class $map_class has more than one foreign key " .
301             "and/or 'many to one' relationship that points to the " .
302             "class $target_class. Please specify one by name " .
303             "with a 'local' parameter in the 'map' hash");
304             }
305              
306 14         51 $map_from = $local_rel = $item->name;
307              
308 14 50       69 my $map_columns =
309             $item->can('column_map') ? $item->column_map : $item->key_columns;
310              
311             # "local" and "foreign" here are relative to the *mapper* class
312 14         118 while(my($local_column, $foreign_column) = each(%$map_columns))
313             {
314 14 50       44 my $foreign_method = $meta->column_accessor_method_name($foreign_column)
315             or die Rose::DB::Object::Exception::ClassNotReady->new(
316             "Missing accessor method for column '$foreign_column'" .
317             " in class " . $meta->class);
318 14         48 $map_column_to_self_method{$local_column} = $foreign_method;
319 14         55 $map_column_to_self_column{$local_column} = $foreign_column;
320             }
321              
322 14         50 next;
323             }
324             }
325              
326 14 50 33     59 if($item->isa('Rose::DB::Object::Metadata::ForeignKey') ||
327             $item->type eq 'many to one')
328             {
329             # Skip if there was an explicit foreign relationship name and
330             # this is not that name.
331 14 50 33     45 next if($map_to && $item->name ne $map_to);
332              
333 14         44 $map_to = $item->name;
334              
335 14 50       40 if($require_objects)
336             {
337 0         0 die Rose::DB::Object::Exception::ClassNotReady->new(
338             "Map class $map_class has more than one foreign key " .
339             "and/or 'many to one' relationship that points to a " .
340             "class other than $target_class. Please specify one " .
341             "by name with a 'foreign' parameter in the 'map' hash");
342             }
343              
344 14         48 $map_to_class = $item->class;
345              
346 14 50       58 unless(UNIVERSAL::isa($map_to_class, 'Rose::DB::Object'))
347             {
348 0         0 die Rose::DB::Object::Exception::ClassNotReady->new(
349             "Map-to-class $map_to_class not yet ready");
350             }
351              
352 14 50       42 $map_to_meta = $map_to_class->meta or die
353             Rose::DB::Object::Exception::ClassNotReady->new(
354             "Missing meta object for $map_to_class");
355              
356 14 50       81 my $map_columns =
357             $item->can('column_map') ? $item->column_map : $item->key_columns;
358              
359             # "local" and "foreign" here are relative to the *mapper* class
360 14         105 while(my($local_column, $foreign_column) = each(%$map_columns))
361             {
362 14 50       47 my $local_method = $map_meta->column_accessor_method_name($local_column)
363             or die Rose::DB::Object::Exception::ClassNotReady->new(
364             "Missing accessor method for column '$local_column'" .
365             " in class " . $map_meta->class);
366              
367 14 50       37 my $foreign_method = $map_to_meta->column_accessor_method_name($foreign_column)
368             or die Rose::DB::Object::Exception::ClassNotReady->new(
369             "Missing accessor method for column '$foreign_column'" .
370             " in class " . $map_to_meta->class);
371              
372             # local foreign
373             # Map:color_id => Color:id
374 14         56 $map_method_to_remote_method{$local_method} = $foreign_method;
375             }
376              
377 14         67 $require_objects = [ $item->name ];
378 14         36 $foreign_class = $item->class;
379              
380 14   50     44 $map_to_method = $item->method_name('get_set') ||
381             $item->method_name('get_set_now') ||
382             $item->method_name('get_set_on_save') ||
383             die Rose::DB::Object::Exception::ClassNotReady->new(
384             "No 'get_*' method found for " . $item->name);
385             }
386             }
387              
388 21 100       185 unless(%map_column_to_self_method)
389             {
390 7         45 die Rose::DB::Object::Exception::ClassNotReady->new(
391             "Could not find a foreign key or 'many to one' relationship " .
392             "in $map_class that points to $target_class");
393             }
394              
395 14 50       36 unless(%map_column_to_self_column)
396             {
397 0   0     0 die Rose::DB::Object::Exception::ClassNotReady->new(
398             "Could not find a foreign key or 'many to one' relationship " .
399             "in $map_class that points to " . ($map_to_class || $map_to));
400             }
401              
402 14 50       35 unless($require_objects)
403             {
404             # Make a second attempt to find a suitable foreign relationship in the
405             # map class, this time looking for links back to $target_class so long as
406             # it's a different relationship than the one used in the local link.
407 0         0 foreach my $item ($map_meta->foreign_keys, $map_meta->relationships)
408             {
409             # Skip a relationship if we've already seen the equivalent foreign key
410 0 0       0 if($item->isa('Rose::DB::Object::Metadata::Relationship'))
411             {
412 0 0       0 next if($seen_fk{$item->id});
413             }
414              
415 0 0 0     0 if(($item->isa('Rose::DB::Object::Metadata::ForeignKey') ||
      0        
      0        
416             $item->type eq 'many to one') &&
417             $item->class eq $target_class && $item->name ne $local_rel)
418             {
419 0 0       0 if($require_objects)
420             {
421 0         0 die Rose::DB::Object::Exception::ClassNotReady->new(
422             "Map class $map_class has more than two foreign keys " .
423             "and/or 'many to one' relationships that points to a " .
424             "$target_class. Please specify which ones to use " .
425             "by including 'local' and 'foreign' parameters in the " .
426             "'map' hash");
427             }
428              
429 0         0 $require_objects = [ $item->name ];
430 0         0 $foreign_class = $item->class;
431 0   0     0 $map_to_method = $item->method_name('get_set') ||
432             $item->method_name('get_set_now') ||
433             $item->method_name('get_set_on_save') ||
434             die Rose::DB::Object::Exception::ClassNotReady->new(
435             "No 'get_*' method found for " . $item->name);
436             }
437             }
438             }
439              
440 14 50       74 unless($require_objects)
441             {
442 0         0 die Rose::DB::Object::Exception::ClassNotReady->new(
443             "Could not find a foreign key or 'many to one' relationship " .
444             "in $map_class that points to a class other than $target_class");
445             }
446              
447 14 50       89 unless($foreign_class)
448             {
449 0         0 die Rose::DB::Object::Exception::ClassNotReady->new("Missing foreign class");
450             }
451             };
452              
453 21         146 $error = $@;
454             }
455              
456 21 100       115 if($error)
457             {
458 7 50 33     31 if($Debug || $Rose::DB::Object::Metadata::Debug)
459             {
460 0         0 my $err = $error;
461 0         0 $err =~ s/ at .*//;
462 0         0 warn $self->parent->class, ': many-to-many relationship ', $self->name, " NOT READY - $err";
463             }
464              
465 7 50       27 die $error unless(UNIVERSAL::isa($error, 'Rose::DB::Object::Exception::ClassNotReady'));
466             }
467              
468 21 100       75 return $error ? 0 : 1;
469             }
470              
471             sub perl_relationship_definition_attributes
472             {
473 0     0 0   grep { $_ !~ /^(?:column_map|foreign_class)$/ }
  0            
474             shift->SUPER::perl_relationship_definition_attributes(@_);
475             }
476              
477             1;
478              
479             __END__
480              
481             =head1 NAME
482              
483             Rose::DB::Object::Metadata::Relationship::ManyToMany - Many to many table relationship metadata object.
484              
485             =head1 SYNOPSIS
486              
487             use Rose::DB::Object::Metadata::Relationship::ManyToMany;
488              
489             $rel = Rose::DB::Object::Metadata::Relationship::ManyToMany->new(...);
490             $rel->make_methods(...);
491             ...
492              
493             =head1 DESCRIPTION
494              
495             Objects of this class store and manipulate metadata for relationships in which rows from one table are connected to rows in another table through an intermediate table that maps between them.
496              
497             This class inherits from L<Rose::DB::Object::Metadata::Relationship>. Inherited methods that are not overridden will not be documented a second time here. See the L<Rose::DB::Object::Metadata::Relationship> documentation for more information.
498              
499             =head1 EXAMPLE
500              
501             Consider the following tables.
502              
503             CREATE TABLE widgets
504             (
505             id SERIAL PRIMARY KEY,
506             name VARCHAR(255)
507             );
508              
509             CREATE TABLE colors
510             (
511             id SERIAL PRIMARY KEY,
512             name VARCHAR(255)
513             );
514              
515             CREATE TABLE widget_color_map
516             (
517             id SERIAL PRIMARY KEY,
518             widget_id INT NOT NULL REFERENCES widgets (id),
519             color_id INT NOT NULL REFERENCES colors (id),
520             UNIQUE(widget_id, color_id)
521             );
522              
523             Given these tables, each widget can have zero or more colors, and each color can be applied to zero or more widgets. This is the type of "many to many" relationship that this class is designed to handle.
524              
525             In order to do so, each of the three of the tables that participate in the relationship must be fronted by its own L<Rose::DB::Object>-derived class. Let's call those classes C<Widget>, C<Color>, and C<WidgetColorMap>.
526              
527             The class that maps between the other two classes is called the "L<map class|/map_class>." In this example, it's C<WidgetColorMap>. The map class B<must> have a foreign key and/or "many to one" relationship pointing to each of the two classes that it maps between.
528              
529             When it comes to actually creating the three classes that participate in a "many to many" relationship, there's a bit of a "chicken and egg" problem. All these classes need to know about each other more or less "simultaneously," but they must be defined in a serial fashion, and may be loaded in any order by the user.
530              
531             In order to account for this, method creation may be deferred for any foreign key or relationship that does not yet have all the information it requires to do its job. This should be transparent to the developer.
532              
533             Here's a complete example using the C<Widget>, C<Color>, and C<WidgetColorMap> classes. First, the C<Widget> class which has a "many to many" relationship through which it can retrieve its colors.
534              
535             package Widget;
536              
537             use base 'Rose::DB::Object';
538              
539             __PACKAGE__->meta->setup
540             (
541             table => 'widgets',
542              
543             columns =>
544             [
545             id => { type => 'int', primary_key => 1 },
546             name => { type => 'varchar', length => 255 },
547             ],
548              
549             relationships =>
550             [
551             # Define "many to many" relationship to get colors
552             colors =>
553             {
554             type => 'many to many',
555             map_class => 'WidgetColorMap',
556              
557             # These are only necessary if the relationship is ambiguous
558             #map_from => 'widget',
559             #map_to => 'color',
560             },
561             ],
562             );
563              
564             1;
565              
566             Next, the C<Color> class which has a "many to many" relationship through which it can retrieve all the widgets that have this color.
567              
568             package Color;
569              
570             use base 'Rose::DB::Object';
571              
572             __PACKAGE__->meta->setup
573             (
574             table => 'colors',
575              
576             columns =>
577             [
578             id => { type => 'int', primary_key => 1 },
579             name => { type => 'varchar', length => 255 },
580             ],
581              
582             relationships =>
583             [
584             # Define "many to many" relationship to get widgets
585             widgets =>
586             {
587             type => 'many to many',
588             map_class => 'WidgetColorMap',
589              
590             # These are only necessary if the relationship is ambiguous
591             #map_from => 'color',
592             #map_to => 'widget',
593             },
594             ],
595             );
596              
597             1;
598              
599             Finally, the C<WidgetColorMap> class must have a foreign key or "many to one" relationship for each of the two classes that it maps between (C<Widget> and C<Color>).
600              
601             package WidgetColorMap;
602              
603             use base 'Rose::DB::Object';
604              
605             __PACKAGE__->meta->setup
606             (
607             table => 'widget_color_map',
608              
609             columns =>
610             [
611             id => { type => 'int', primary_key => 1 },
612             widget_id => { type => 'int' },
613             color_id => { type => 'int' },
614             ],
615              
616             foreign_keys =>
617             [
618             # Define foreign keys that point to each of the two classes
619             # that this class maps between.
620             color =>
621             {
622             class => 'Color',
623             key_columns => { color_id => 'id' },
624             },
625              
626             widget =>
627             {
628             class => 'Widget',
629             key_columns => { widget_id => 'id' },
630             },
631             ],
632             );
633              
634             1;
635              
636             Here's an initial set of data and some examples of the above classes in action. First, the data:
637              
638             INSERT INTO widgets (id, name) VALUES (1, 'Sprocket');
639             INSERT INTO widgets (id, name) VALUES (2, 'Flange');
640              
641             INSERT INTO colors (id, name) VALUES (1, 'Red');
642             INSERT INTO colors (id, name) VALUES (2, 'Green');
643             INSERT INTO colors (id, name) VALUES (3, 'Blue');
644              
645             INSERT INTO widget_color_map (widget_id, color_id) VALUES (1, 1);
646             INSERT INTO widget_color_map (widget_id, color_id) VALUES (1, 2);
647             INSERT INTO widget_color_map (widget_id, color_id) VALUES (2, 3);
648              
649             Now the code:
650              
651             use Widget;
652             use Color;
653              
654             $widget = Widget->new(id => 1);
655             $widget->load;
656              
657             @colors = map { $_->name } $widget->colors; # ('Red', 'Green')
658              
659             $color = Color->new(id => 1);
660             $color->load;
661              
662             @widgets = map { $_->name } $color->widgets; # ('Sprocket')
663              
664             =head1 METHOD MAP
665              
666             =over 4
667              
668             =item C<count>
669              
670             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'count'> ...
671              
672             =item C<find>
673              
674             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'find'> ...
675              
676             =item C<iterator>
677              
678             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'iterator'> ...
679              
680             =item C<get_set>
681              
682             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>,
683             C<interface =E<gt> 'get_set'> ...
684              
685             =item C<get_set_now>
686              
687             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'get_set_now'> ...
688              
689             =item C<get_set_on_save>
690              
691             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'get_set_on_save'> ...
692              
693             =item C<add_now>
694              
695             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'add_now'> ...
696              
697             =item C<add_on_save>
698              
699             L<Rose::DB::Object::MakeMethods::Generic>, L<objects_by_map|Rose::DB::Object::MakeMethods::Generic/objects_by_map>, C<interface =E<gt> 'add_on_save'> ...
700              
701             =back
702              
703             See the L<Rose::DB::Object::Metadata::Relationship|Rose::DB::Object::Metadata::Relationship/"MAKING METHODS"> documentation for an explanation of this method map.
704              
705             =head1 CLASS METHODS
706              
707             =over 4
708              
709             =item B<default_auto_method_types [TYPES]>
710              
711             Get or set the default list of L<auto_method_types|Rose::DB::Object::Metadata::Relationship/auto_method_types>. TYPES should be a list of relationship method types. Returns the list of default relationship method types (in list context) or a reference to an array of the default relationship method types (in scalar context). The default list contains "get_set_on_save" and "add_on_save".
712              
713             =back
714              
715             =head1 OBJECT METHODS
716              
717             =over 4
718              
719             =item B<build_method_name_for_type TYPE>
720              
721             Return a method name for the relationship method type TYPE.
722              
723             For the method types "get_set", "get_set_now", and "get_set_on_save", the relationship's L<name|Rose::DB::Object::Metadata::Relationship/name> is returned.
724              
725             For the method types "add_now" and "add_on_save", the relationship's L<name|Rose::DB::Object::Metadata::Relationship/name> prefixed with "add_" is returned.
726              
727             For the method type "find", the relationship's L<name|Rose::DB::Object::Metadata::Relationship/name> prefixed with "find_" is returned.
728              
729             For the method type "count", the relationship's L<name|Rose::DB::Object::Metadata::Relationship/name> suffixed with "_count" is returned.
730              
731             For the method type "iterator", the relationship's L<name|Rose::DB::Object::Metadata::Relationship/name> suffixed with "_iterator" is returned.
732              
733             Otherwise, undef is returned.
734              
735             =item B<is_singular>
736              
737             Returns false.
738              
739             =item B<manager_class [CLASS]>
740              
741             Get or set the name of the L<Rose::DB::Object::Manager>-derived class that the L<map_class|/map_class> will use to fetch records. The L<make_methods|Rose::DB::Object::Metadata::Relationship/make_methods> method will use L<Rose::DB::Object::Manager> if this value is left undefined.
742              
743             =item B<manager_method [METHOD]>
744              
745             Get or set the name of the L<manager_class|/manager_class> class method to call when fetching records. The L<make_methods|Rose::DB::Object::Metadata::Relationship/make_methods> method will use L<get_objects|Rose::DB::Object::Manager/get_objects> if this value is left undefined.
746              
747             =item B<manager_count_method [METHOD]>
748              
749             Get or set the name of the L<manager_class|/manager_class> class method to call when counting objects. The L<make_methods|Rose::DB::Object::Metadata::Relationship/make_methods> method will use L<get_objects_count|Rose::DB::Object::Manager/get_objects_count> if this value is left undefined.
750              
751             =item B<manager_iterator_method [METHOD]>
752              
753             Get or set the name of the L<manager_class|/manager_class> class method to call when creating an iterator. The L<make_methods|Rose::DB::Object::Metadata::Relationship/make_methods> method will use L<get_objects_iterator|Rose::DB::Object::Manager/get_objects_iterator> if this value is left undefined.
754              
755             =item B<manager_args [HASHREF]>
756              
757             Get or set a reference to a hash of name/value arguments to pass to the L<manager_method|/manager_method> when fetching objects. For example, this can be used to enforce a particular sort order for objects fetched via this relationship. Modifying the L<example|/EXAMPLE> above:
758              
759             Widget->meta->add_relationship
760             (
761             colors =>
762             {
763             type => 'many to many',
764             map_class => 'WidgetColorMap',
765             manager_args => { sort_by => Color->meta->table . '.name' },
766             },
767             );
768              
769             This would ensure that a C<Widget>'s C<colors()> are listed in alphabetical order. Note that the "name" column is prefixed by the name of the table fronted by the C<Color> class. This is important because several tables may have a column named "name." If this relationship is used to form a JOIN in a query along with one of those tables, then the "name" column will be ambiguous. Adding a table name prefix disambiguates the column name.
770              
771             Also note that the table name is not hard-coded. Instead, it is fetched from the L<Rose::DB::Object>-derived class that fronts the table. This is more verbose, but is a much better choice than including the literal table name when it comes to long-term maintenance of the code.
772              
773             See the documentation for L<Rose::DB::Object::Manager>'s L<get_objects|Rose::DB::Object::Manager/get_objects> method for a full list of valid arguments for use with the C<manager_args> parameter, but remember that you can define your own custom L<manager_class> and thus can also define what kinds of arguments C<manager_args> will accept.
774              
775             B<Note:> when the name of a relationship that has C<manager_args> is used in a L<Rose::DB::Object::Manager> L<with_objects|Rose::DB::Object::Manager/with_objects> or L<require_objects|Rose::DB::Object::Manager/require_objects> parameter value, I<only> the L<sort_by|Rose::DB::Object::Manager/sort_by> argument will be copied from C<manager_args> and incorporated into the query.
776              
777             =item B<map_class [CLASS]>
778              
779             Get or set the name of the L<Rose::DB::Object>-derived class that fronts the table that maps between the other two tables. This class must have a foreign key and/or "many to one" relationship for each of the two tables that it maps between.
780              
781             In the L<example|EXAMPLE> above, the map class is C<WidgetColorMap>.
782              
783             =item B<map_from [NAME]>
784              
785             Get or set the name of the "many to one" relationship or foreign key in L<map_class|/map_class> that points to the object of the current class. Setting this value is only necessary if the L<map class|/map_class> has more than one foreign key or "many to one" relationship that points to one of the classes that it maps between.
786              
787             In the L<example|EXAMPLE> above, the value of L<map_from|/map_from> would be "widget" when defining the "many to many" relationship in the C<Widget> class, or "color" when defining the "many to many" relationship in the C<Color> class. Neither of these settings is necessary in the example because the C<WidgetColorMap> class has one foreign key that points to each class, so there is no ambiguity.
788              
789             =item B<map_to [NAME]>
790              
791             Get or set the name of the "many to one" relationship or foreign key in L<map_class|/map_class> that points to the "foreign" object to be fetched. Setting this value is only necessary if the L<map class|/map_class> has more than one foreign key or "many to one" relationship that points to one of the classes that it maps between.
792              
793             In the L<example|EXAMPLE> above, the value of L<map_from> would be "color" when defining the "many to many" relationship in the C<Widget> class, or "widget" when defining the "many to many" relationship in the C<Color> class. Neither of these settings is necessary in the example because the C<WidgetColorMap> class has one foreign key that points to each class, so there is no ambiguity.
794              
795             =item B<query_args [ARRAYREF]>
796              
797             Get or set a reference to an array of query arguments to add to the L<query|Rose::DB::Object::Manager/query> passed to the L<manager_method|/manager_method> when fetching objects.
798              
799             This can be used to limit the objects fetched via this relationship. For example, modifying the L<example|/EXAMPLE> above:
800              
801             Widget->meta->add_relationship
802             (
803             colors =>
804             {
805             type => 'many to many',
806             map_class => 'WidgetColorMap',
807             query_args => [ name => { like => '%e%' } ],
808             },
809             );
810              
811             See the documentation for L<Rose::DB::Object::Manager>'s L<get_objects|Rose::DB::Object::Manager/get_objects> method for a full list of valid C<query> arguments.
812              
813             =item B<share_db [BOOL]>
814              
815             Get or set a boolean flag that indicates whether or not all of the classes involved in fetching objects via this relationship (including the objects themselves) will share the same L<Rose::DB>-derived L<db|Rose::DB::Object/db> object. Defaults to true.
816              
817             =item B<type>
818              
819             Returns "many to many".
820              
821             =back
822              
823             =head1 AUTHOR
824              
825             John C. Siracusa (siracusa@gmail.com)
826              
827             =head1 LICENSE
828              
829             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is
830             free software; you can redistribute it and/or modify it under the same terms
831             as Perl itself.