File Coverage

blib/lib/Class/ReluctantORM/Relationship.pm
Criterion Covered Total %
statement 21 147 14.2
branch 1 46 2.1
condition 0 8 0.0
subroutine 7 42 16.6
pod 22 25 88.0
total 51 268 19.0


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::Relationship;
2              
3             =head1 NAME
4              
5             Class::ReluctantORM::Relationship - Represent links between classes
6              
7             =head1 SYNOPSIS
8              
9             # Add relationships to a Class::ReluctantORM Class
10             Pirate->has_one(...); # See Class::ReluctantORM::Relationship::HasOne
11             Pirate->has_many(...); # See Class::ReluctantORM::Relationship::HasMany
12             Pirate->has_many_many(...); # See Class::ReluctantORM::Relationship::HasManyMany
13             Pirate->has_lazy(...); # See Class::ReluctantORM::Relationship::HasLazy
14              
15             # Get relationships from a defined Class::ReluctantORM Class
16             $rel = Pirate->relationships('method_name');
17             $rels_by_name_href = Pirate->relationships();
18             @all_rels = Pirate->relationships();
19              
20             # Get information from a relationship
21             $str = $rel->type(); # 'has_one'
22             $str = $rel->linked_class(); # 'Ship'
23             $str = $rel->linking_class(); # 'Pirate'
24             $str = $rel->method_name(); # 'ship'
25             $str = $rel->name(); # 'ship' (alias for method_name)
26             $int = $rel->lower_multiplicity() # 0 for optionals, 1 for required
27             $int = $rel->upper_multiplicity() # 1 for one/lazy, undef for many/many-many
28              
29             # If Ship->has_many(Pirate), you'll get the opposite relation here
30             # There's no requirement that relationships be invertable, so this is often undef
31             $invrel = $rel->inverse_relationship();
32              
33             @fields = $rel->local_key_fields(); # fields in Pirate that link to Ship
34             @fields = $rel->remote_key_fields(); # array of fields in Ship that link to Pirate
35             $int = $rel->join_depth(); # 0, 1, or 2
36              
37             # SQL Support
38             $tbl = $rel->local_sql_table();
39             $tbl = $rel->remote_sql_table();
40             $tbl = $rel->join_sql_table();
41             @cols = $rel->local_key_sql_columns();
42             @cols = $rel->remote_key_sql_columns();
43             @cols = $rel->join_local_key_sql_columns();
44             @cols = $rel->join_remote_key_sql_columns();
45             @cols = $rel->additional_output_sql_columns();
46              
47             =head1 DESCRIPTION
48              
49             Represents a relationship between two Class::ReluctantORM classes.
50              
51             TB Classes have instances of Relationships as class data. An instance of a
52             Relationship does not contain data pertaining to a particular TB object;
53             for that, see Collection.
54              
55             =head1 INITIALIZATION
56              
57             =cut
58              
59              
60 1     1   6 use strict;
  1         2  
  1         36  
61 1     1   5 use warnings;
  1         3  
  1         28  
62              
63 1     1   5 use Data::Dumper;
  1         1  
  1         65  
64 1     1   6 use Class::ReluctantORM::Utilities qw(conditional_load_subdir install_method install_method_generator);
  1         2  
  1         62  
65 1     1   7 use base 'Class::Accessor';
  1         2  
  1         99  
66 1     1   6 use Class::ReluctantORM::SQL::Aliases;
  1         2  
  1         223  
67              
68             our $DEBUG = 0;
69              
70             our %PENDING_CODE_FOR_CLASSES;
71             our $OCTR_IS_LOADING;
72             our @REL_CLASSES;
73              
74              
75              
76             BEGIN {
77 1 50   1   5 unless ($OCTR_IS_LOADING) {
78 1         2 $OCTR_IS_LOADING = 1;
79 1         6 @REL_CLASSES = conditional_load_subdir(__PACKAGE__);
80             }
81             }
82              
83             foreach my $class (@REL_CLASSES) {
84             if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . "- calling _initialize on $class\n"; }
85             $class->_initialize();
86             }
87              
88             =head2 $rel_class->_initialize();
89              
90             The relationship class should do any one-time setup, like registering
91             methods with Class::ReluctantORM. Note that this is per-relationship-class
92             initialization, not per relationship initialization.
93              
94             The default implementation does nothing.
95              
96             =cut
97              
98 0     0     sub _initialize { }
99              
100              
101             # This called from new to initialize relations. The parent will not have PKs at this time.
102 0     0     sub _handle_implicit_new { Class::ReluctantORM::Exception::Call::PureVirtual->croak('_handle_implicit_new'); }
103              
104             # This called from create to initialize relations, after the new() and insert() call. The parent will have PKs at this time.
105 0     0     sub _handle_implicit_create { Class::ReluctantORM::Exception::Call::PureVirtual->croak('_handle_implicit_create'); }
106              
107             # This called from insert() when a primary key changes on the linking object
108 0     0     sub _notify_key_change_on_linking_object { Class::ReluctantORM::Exception::Call::PureVirtual->croak('_notify_key_change_on_linking_object'); }
109              
110             =head1 DELAYED LOADING FACILITY
111              
112             Because Class::ReluctantORM classes are naturally interdependent, it's unlikely that a
113             relationship will always be able to complete its setup, because the remote end
114             may not be loaded yet. The Relationship base class provides a facility for
115             the delayed execution of setup code.
116              
117             =cut
118              
119             =head2 Class::ReluctantORM::Relationship->notify_class_available($tb_class);
120              
121             Notifies the delayed-loading subsystem that a Class::ReluctantORM class has become available.
122             At this point, any relationships that were waiting on this class will finish their setup.
123              
124             You should not override this method.
125              
126             =cut
127              
128             sub notify_class_available {
129 0     0 1   my $class = shift;
130 0           my $tb_class = shift;
131              
132 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - got notification that $tb_class is available\n"; }
  0            
133              
134             # If there is anything waiting on this class, execute it.
135 0 0         foreach my $code (@{$PENDING_CODE_FOR_CLASSES{$tb_class} || []}) {
  0            
136 0           $code->();
137             }
138             }
139              
140             =head2 $rel_class->delay_until_class_is_available($tb_class, $coderef);
141              
142             Registers a coderef to be executed later when the given Class::ReluctantORM
143             class is loaded.
144              
145             If the requested class has already been loaded, the code is executed immediately.
146              
147             =cut
148              
149             sub delay_until_class_is_available {
150 0     0 1   my $class = shift;
151 0           my $tb_class = shift;
152 0           my $coderef = shift;
153 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - delay_until_class_available considering code at $coderef\n"; }
  0            
154 0 0         if (Class::ReluctantORM->is_class_available($tb_class)) {
155 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - delay_until_class_available doing immediate execution for class $tb_class\n"; }
  0            
156 0           $coderef->();
157             } else {
158 0 0         if ($DEBUG > 1) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - delay_until_class_available doing delayed execution for class $tb_class\n"; }
  0            
159 0   0       $PENDING_CODE_FOR_CLASSES{$tb_class} ||= [];
160 0           push @{$PENDING_CODE_FOR_CLASSES{$tb_class}}, $coderef;
  0            
161             }
162             }
163              
164              
165             sub new {
166 0     0 1   my $relclass = shift;
167 0           return bless {}, $relclass;
168             }
169              
170             =head1 ATTRIBUTES OF RELATIONSHIPS
171              
172             =cut
173              
174             =head2 $str = $rel->type();
175              
176             Returns the type of the relationship - 'has_one', 'has_many', etc.
177              
178             =cut
179              
180 0     0 1   sub type { Class::ReluctantORM::Exception::Call::PureVirtual->croak('type'); }
181              
182             =for devdocs
183              
184             =head2 $method_name = RelationshipClass->_setup_method_name();
185              
186             Returns the name of a method you can call to set up a relationship. Default implementation is to just return the string returned by type().
187              
188             =cut
189              
190 0     0     sub _setup_method_name { return $_[0]->type(); }
191              
192             =for devdocs
193              
194             =head2 $hashref = $rel->_original_args_hashref;
195              
196             Returns a hashref of (possibly scrubbed) arguments passed to the setup method to initiate the relationship. You should set this value whenever a new relationship is created. This is used by CRO->clone_relationship().
197              
198             =cut
199              
200             __PACKAGE__->mk_accessors(qw(_original_args_arrayref));
201              
202             =head2 $str = $rel->method_name();
203              
204             =head2 $str = $rel->name();
205              
206             The method that this relationship will add to the linking class (eg, $pirate->ship()). As this is unique on the class, this is
207             also used as the name of the relationship.
208              
209             =cut
210              
211             __PACKAGE__->mk_accessors(qw(method_name));
212 0     0 1   sub name { return shift->method_name(); } # Alias
213              
214             =head2 $str = $rel->linking_class();
215              
216             The class that initiated the relationship. This is the "parent" class.
217              
218             =cut
219              
220              
221             =head2 $str = $rel->linked_class();
222              
223             The string name of the class on the far end of the connection. The "child" class. For HasLazy,
224             this may not be a Class::ReluctantORM subclass; it may even just be SCALAR.
225              
226             =cut
227              
228             __PACKAGE__->mk_accessors(qw(linked_class));
229              
230             =head2 $str = $rel->linking_class();
231              
232             The class that initiated the relationship. This is the "parent" class.
233              
234             =cut
235              
236             __PACKAGE__->mk_accessors(qw(linking_class));
237              
238             =head2 $int = $rel->join_depth();
239              
240             Count of how many joins are required by this relationship in a SQL query. May range from 0 (lazy) to 2 (has_many_many).
241              
242             =cut
243              
244             __PACKAGE__->mk_accessors(qw(join_depth));
245              
246             =head2 $int = $rel->lower_multiplicity()
247              
248             Returns the lower bound on the multiplicty of the remote end relationship (ie, the "0" in "one to 0 or 1", or the "1" in "one to 1 or more").
249              
250             =cut
251              
252 0     0 1   sub lower_multiplicity { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
253              
254             =head2 $int = $rel->upper_multiplicity()
255              
256             Returns the upper bound on the multiplicty of the remote end of the relationship (ie, the "1" in "one to 0 or 1", or the "more" in "one to 1 or more").
257              
258             undef is used to represent "no limit".
259              
260             =cut
261              
262 0     0 1   sub upper_multiplicity { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
263              
264             =head2 $invrel = $rel->inverse_relationship();
265              
266             Returns the inverse Relationship, if any. If you have defined Pirate->has_one(Ship) and Ship->has_many(Pirate),
267             then you can use this method to obtain the inverse relationship. Note that you must manually make relationships bidirectional.
268              
269             Inverse relationships are available by default whenever you have defined exactly one set of bidirectional relations between two classes.
270             If you have multiple relations between classes, you can use the 'inverse' option to the relationship setup method to specifiy one.
271              
272             See Class::ReluctantORM::Manual::Relationships for more details.
273              
274             =cut
275              
276             __PACKAGE__->mk_accessors(qw(inverse_relationship));
277              
278             =head2 @fields = $rel->local_key_fields();
279              
280             Returns an array of the field names used on the linking (local) end of the relationship.
281              
282             =cut
283              
284             sub local_key_fields {
285 0     0 1   my $rel = shift;
286 0 0         if (@_) {
287 0           $rel->set('local_key_fields', [@_]);
288             }
289 0           my $fields = $rel->get('local_key_fields');
290 0 0         return @{$fields || []};
  0            
291             }
292              
293             =head2 @column_names = $rel->local_key_columns();
294              
295             Returns an array of the column names used on the linking (local) end of the relationship.
296              
297             =cut
298              
299              
300             sub local_key_columns {
301 0     0 1   my $rel = shift;
302 0           my $class = $rel->linking_class();
303 0           return map { $class->column_name($_) } $rel->local_key_fields();
  0            
304             }
305              
306             =head2 @column_names = $rel->rmeote_key_columns();
307              
308             Returns an array of the column names used on the remote (linked) end of the relationship.
309              
310             =cut
311              
312             sub remote_key_columns {
313 0     0 0   my $rel = shift;
314 0           my $class = $rel->linked_class();
315 0           return map { $class->column_name($_) } $rel->remote_key_fields();
  0            
316             }
317              
318             =head2 @fields = $rel->remote_key_fields();
319              
320             Returns an array of the field names used on the remote (linked) end of the relationship.
321              
322             =cut
323              
324             sub remote_key_fields {
325 0     0 1   my $rel = shift;
326 0 0         if (@_) {
327 0           $rel->set('remote_key_fields', [@_]);
328             }
329 0           my $fields = $rel->get('remote_key_fields');
330 0 0         return @{$fields || []};
  0            
331             }
332              
333             =head1 SQL SUPPORT
334              
335             These functions provide support for the abstract SQL query representation system.
336              
337             =cut
338              
339             =head2 $int = $rel->join_depth();
340              
341             Returns the number of join steps needed to perform a fetch deep inbolving this
342             relationship. Will return 0 (for Lazy relationships), 1 (for has_one and has_many)
343             or 2 (for has_many_many).
344              
345             =cut
346              
347             # sub defined by mk_accessor
348              
349             =head2 $tbl = $rel->local_sql_table();
350              
351             Returns a Class::ReluctantORM::SQL::Table object representing the local table.
352              
353             This is always available.
354              
355             =cut
356              
357             sub local_sql_table {
358 0     0 1   my $rel = shift;
359 0           return Table->new($rel->linking_class);
360             }
361              
362             =head2 $tbl = $rel->remote_sql_table();
363              
364             Returns a Class::ReluctantORM::SQL::Table object representing the table of the linked class.
365              
366             This is only available if join_depth is 1 or greater.
367              
368             =cut
369              
370             sub remote_sql_table {
371 0     0 1   my $rel = shift;
372 0           my $class = $rel->linked_class();
373 0 0         unless ($class) { return undef; }
  0            
374 0           return Table->new($class);
375             }
376              
377             =head2 $tbl = $rel->join_sql_table();
378              
379             Returns a Class::ReluctantORM::SQL::Table object representing the join table.
380              
381             This is only available if join_depth is 2.
382              
383             The default implementation always returns undef.
384              
385             =cut
386              
387 0     0 1   sub join_sql_table { return undef; }
388              
389             =head2 $type = $rel->join_type();
390              
391             Returns the join type for the (first) join required by this relationship, if any.
392              
393             Default returns 'NONE'.
394              
395             =cut
396              
397 0     0 1   sub join_type { return 'NONE'; }
398              
399             =head2 @cols = $rel->local_key_sql_columns();
400              
401             Returns a list of Class::ReluctantORM::SQL::Columns involved in the relationship on the local table.
402              
403             Always available.
404              
405             =cut
406              
407             sub local_key_sql_columns {
408 0     0 1   my $rel = shift;
409 0           my $table = $rel->local_sql_table();
410 0           my @cols = map {
411 0           Column->new(
412             column => $_,
413             table => $table,
414             );
415             } $rel->linking_class->column_name($rel->local_key_fields());
416 0           return @cols;
417             }
418              
419             =head2 @cols = $rel->remote_key_sql_columns();
420              
421             Returns a list of Class::ReluctantORM::SQL::Columns involved in the relationship on the remote table.
422              
423             Available if join_depth is greater than 1. If join depth is 2, refers to the farthest columns.
424              
425             If not available, returns an empty list.
426              
427             =cut
428              
429             sub remote_key_sql_columns {
430 0     0 1   my $rel = shift;
431 0           my $table = $rel->remote_sql_table();
432 0 0         unless ($table) { return (); }
  0            
433 0           my @cols = map {
434 0           Column->new(
435             column => $_,
436             table => $table,
437             );
438             } $rel->linked_class->column_name($rel->remote_key_fields());
439 0           return @cols;
440             }
441              
442             =head2 @cols = $rel->join_local_key_sql_columns();
443              
444             Returns a list of Class::ReluctantORM::SQL::Columns involved in the relationship
445             on the join table for the linking class.
446              
447             Available if join depth is 2.
448              
449             If not available, returns an empty list.
450              
451             The default implementation returns an empty list.
452              
453             =cut
454 0     0 0   sub join_local_key_columns { return (); }
455 0     0 1   sub join_local_key_sql_columns { return (); }
456              
457             =head2 @cols = $rel->join_remote_key_sql_columns();
458              
459             Returns a list of Class::ReluctantORM::SQL::Columns involved in the relationship
460             on the join table for the linked class.
461              
462             Available if join depth is 2.
463              
464             If not available, returns an empty list.
465              
466             The default implementation returns an empty list.
467              
468             =cut
469              
470 0     0 0   sub join_remote_key_columns { return (); }
471 0     0 1   sub join_remote_key_sql_columns { return (); }
472              
473             =head2 @cols = $rel->additional_output_sql_columns();
474              
475             Returns a list of columns that should also be selected when fetching items that make up this relationship.
476              
477             Default implementation is to return nothing.
478              
479             =cut
480              
481 0     0 1   sub additional_output_sql_columns { return (); }
482              
483             =head2 $int = $rel->matches_join_criterion($crit);
484              
485             Given a SQL Criterion, returns an integer indicating which, if any, of the join levels the criterion could be used to represent. This is used to support SQL annotation.
486              
487             The return value will be between 0 and $rel->join_depth(), inclusive. If there is no match, the return value will be 0.
488              
489             =cut
490              
491             sub matches_join_criterion {
492 0     0 1   my $rel = shift;
493 0           my $crit = shift;
494              
495 0           for my $level (1..($rel->join_depth())) {
496 0           my $rel_crit = $rel->default_sql_join_criteria($level);
497 0 0         if ($rel_crit->is_equivalent($crit)) {
498 0           return $level;
499             }
500             }
501              
502 0           return 0;
503              
504             }
505              
506             =begin devdocs
507              
508             This isn't publicly documented yet, because it might be a bad idea.
509              
510             =head2 $crit = $rel->default_sql_join_criteria($level);
511              
512             Returns a Criterion that could be used to represent the Join criteria for the relationship. $level must be an integer less than or equal to $rel->join_depth().
513              
514             We can't use this for fetch_deep processing, because it doesn't take into account extra join options/criteria.
515              
516             =cut
517              
518             sub default_sql_join_criteria {
519 0     0 1   my $rel = shift;
520 0           my $level = shift;
521 0 0         if ($level > $rel->join_depth()) {
522 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'level', value => $level, error => "Max value is " . $rel->join_depth());
523             }
524              
525 0           my (@left_cols, @right_cols);
526              
527 0 0 0       if (0) { # for formatting
    0 0        
    0          
528 0 0         } elsif ($rel->join_depth == 1 && $level == 1) {
529             # Local directly to remote
530 0           @left_cols = $rel->local_key_sql_columns();
531 0           @right_cols = $rel->remote_key_sql_columns();
532              
533             } elsif ($rel->join_depth == 2 && $level == 1) {
534             # Local to join
535 0           @left_cols = $rel->local_key_sql_columns();
536 0           @right_cols = $rel->join_local_key_sql_columns();
537              
538             } elsif ($rel->join_depth == 2 && $level == 2) {
539             # Join to remote
540 0           @left_cols = $rel->join_remote_key_sql_columns();
541 0           @right_cols = $rel->remote_key_sql_columns();
542              
543             } else {
544 0           Class::ReluctantORM::Exception::NotImplemented->croak("Don't know how to handle relationships with more than 2 join levels");
545             }
546              
547             # Build criteria pair-wise
548 0           my $crit;
549 0           for my $i (0..$#left_cols) {
550 0           my $new_crit = Criterion->new('=', $left_cols[$i], $right_cols[$i]);
551              
552 0 0         if ($crit) {
553 0           $crit = Criterion->new('AND', $crit, $new_crit);
554             } else {
555 0           $crit = $new_crit;
556             }
557             }
558 0           return $crit;
559             }
560              
561              
562              
563             =head1 OTHER RELATIONSHIP METHODS
564              
565             =cut
566              
567             =head2 $bool = $rel->is_populated_in_object($cro_obj);
568              
569             Returns true if the relationship is "populated" (fetched) in the given ReluctantORM object.
570              
571             =cut
572              
573 0     0 1   sub is_populated_in_object { Class::ReluctantORM::Exception::Call::PureVirtual->croak('is_populated_in_object'); }
574              
575             =begin devdocs
576              
577             =head2 $bool = $rel->_mark_unpopulated_in_object($cro_obj);
578              
579             Should be called when the Class::ReluctantORM object needs to mark the relationship unfetched (for example, a local key has changed).
580              
581             =cut
582              
583 0     0     sub _mark_unpopulated_in_object { Class::ReluctantORM::Exception::Call::PureVirtual->croak('_mark_unpopulated_in_object'); }
584              
585             =begin devdocs
586              
587             =head2 $rel->_merge_children($cro_obj, $children_ref);
588              
589             Called when the children specified in $children_ref (an array ref) should be merged into the existing collection for this relationship in $cro_obj. This can happen in Class::ReluctantORM::new when an object has already been fetched with this relationship and has been found in the Registry, but the call to new specifies child objects as well.
590              
591             =cut
592              
593 0     0     sub _merge_children { Class::ReluctantORM::Exception::Call::PureVirtual->croak('_merge_children'); }
594              
595             =begin devdocs
596              
597             =head2 $rawval = $rel->_raw_mutator($cro_obj);
598              
599             =head2 $newval = $rel->_raw_mutator($cro_obj, $new_value);
600              
601             Performs a "raw" (non-filtered) access or write to the underlying collection of the CRO object.
602              
603             =cut
604              
605 0     0     sub _raw_mutator { Class::ReluctantORM::Exception::Call::PureVirtual->croak('raw_mutator'); }
606              
607              
608              
609             sub _install_search_by_with_methods {
610 0     0     my $rel = shift;
611 0           my $class = $rel->linking_class();
612 0           my $rel_name = $rel->name();
613              
614             install_method_generator
615             (
616             $class,
617             sub {
618 0     0     my ($class, $proposed_method_name) = @_;
619              
620             # Look for search_with_pirates pattern
621 0           my ($fetch_mode, $found_rel_name) = $proposed_method_name =~ /^(search|fetch)_with_($rel_name)$/;
622 0 0         if ($fetch_mode) {
623 0           my $make_fatal = $fetch_mode eq 'fetch';
624 0 0         my $base_key_spec
625             = ($class->primary_key_column_count == 1) ?
626             ($class->primary_key_columns())[0] :
627             [ $class->primary_key_columns() ];
628 0           return $class->_make_fetcher($base_key_spec, $make_fatal, $rel_name)
629             }
630              
631             # Look for search_by_name_with_pirates pattern
632 0           my $regex = '^(search|fetch)_by_(' . join('|', $class->fields) . ')_with_(' . $rel_name . ')$';
633 0           my ($fetch_mode2, $field_name, $found_rel_name2) = $proposed_method_name =~ $regex;
634 0 0         if ($fetch_mode2) {
635 0           my $make_fatal = $fetch_mode2 eq 'fetch';
636 0           return $class->_make_fetcher($field_name, $make_fatal, $rel_name)
637             }
638              
639             # No patterns left - decline
640 0           return undef;
641             }
642 0           );
643             }
644              
645              
646             =head1 AUTHOR
647              
648             Clinton Wolfe
649              
650             =cut
651              
652             1;