File Coverage

blib/lib/Fey/Meta/Role/Relationship/ViaFK.pm
Criterion Covered Total %
statement 39 39 100.0
branch 14 16 87.5
condition n/a
subroutine 10 10 100.0
pod 0 1 0.0
total 63 66 95.4


line stmt bran cond sub pod time code
1             package Fey::Meta::Role::Relationship::ViaFK;
2              
3 10     10   7569 use strict;
  10         18  
  10         368  
4 10     10   55 use warnings;
  10         19  
  10         357  
5 10     10   54 use namespace::autoclean;
  10         19  
  10         107  
6              
7             our $VERSION = '0.47';
8              
9 10     10   1029 use Fey::Exceptions qw( param_error );
  10         19  
  10         710  
10 10     10   57 use Fey::ORM::Types qw( Bool );
  10         28  
  10         107  
11              
12 10     10   51626 use Moose::Role;
  10         22  
  10         104  
13              
14             has fk => (
15             is => 'ro',
16             isa => 'Fey::FK',
17             lazy => 1,
18             builder => '_build_fk',
19             predicate => '_has_fk',
20             writer => '_set_fk',
21             );
22              
23             has _is_has_many => (
24             is => 'ro',
25             isa => Bool,
26             lazy => 1,
27             default => sub { ( ref $_[0] ) =~ /HasMany/ ? 1 : 0 },
28             );
29              
30       16 0   sub BUILD { }
31              
32             after BUILD => sub {
33             my $self = shift;
34              
35             return unless $self->_has_fk();
36              
37             $self->_set_fk( $self->_invert_fk_if_necessary( $self->fk() ) );
38             };
39              
40             sub _build_fk {
41 14     14   30 my $self = shift;
42              
43 14         497 $self->_find_one_fk_between_tables(
44             $self->table(),
45             $self->foreign_table(),
46             );
47             }
48              
49             sub _find_one_fk_between_tables {
50 14     14   34 my $self = shift;
51 14         25 my $source_table = shift;
52 14         21 my $target_table = shift;
53              
54 14         406 my @fk = $source_table->schema()
55             ->foreign_keys_between_tables( $source_table, $target_table );
56              
57 14 100       70348 my $desc = $self->_is_has_many() ? 'has_many' : 'has_one';
58              
59 14 100       72 if ( @fk == 0 ) {
    100          
60 2         60 param_error
61             'There are no foreign keys between the table for this class, '
62             . $source_table->name()
63             . " and the table you passed to $desc(), "
64             . $target_table->name() . '.';
65             }
66             elsif ( @fk > 1 ) {
67 2         73 param_error
68             'There is more than one foreign key between the table for this class, '
69             . $source_table->name()
70             . " and the table you passed to $desc(), "
71             . $target_table->name()
72             . '. You must specify one explicitly.';
73             }
74              
75 10         49 return $self->_invert_fk_if_necessary( $fk[0] );
76             }
77              
78             # We may need to invert the meaning of source & target since source &
79             # target for an FK object are sort of arbitrary. The source should be
80             # "our" table, and the target the foreign table.
81             sub _invert_fk_if_necessary {
82 12     12   26 my $self = shift;
83 12         21 my $fk = shift;
84              
85             # Self-referential keys are a special case, and that case differs
86             # for has_one vs has_many.
87 12 100       339 if ( $fk->is_self_referential() ) {
88 2 100       464 if ( $self->_is_has_many() ) {
89             return $fk
90             unless $fk->target_table()
91 1 50       42 ->has_candidate_key( @{ $fk->target_columns() } );
  1         50  
92             }
93             else {
94              
95             # A self-referential key is a special case. If the target
96             # columns are _not_ a key, then we need to invert source &
97             # target so we do our select by a key. This doesn't
98             # address a pathological case where neither source nor
99             # target column sets make up a key. That shouldn't happen,
100             # though ;)
101             return $fk
102             if $fk->target_table()
103 1 50       19 ->has_candidate_key( @{ $fk->target_columns() } );
  1         25  
104             }
105             }
106             else {
107 10 100       821 return $fk
108             if $fk->target_table()->name() eq $self->foreign_table->name();
109             }
110              
111 5         3234 return Fey::FK->new(
112             source_columns => $fk->target_columns(),
113             target_columns => $fk->source_columns(),
114             );
115             }
116              
117             1;