| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # the contents of this file are Copyright (c) 2009 Daniel Norman | 
| 2 |  |  |  |  |  |  | # This program is free software; you can redistribute it and/or | 
| 3 |  |  |  |  |  |  | # modify it under the terms of the GNU General Public License as | 
| 4 |  |  |  |  |  |  | # published by the Free Software Foundation. | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | package DBR::Config::Relation; | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 18 |  |  | 18 |  | 124 | use strict; | 
|  | 18 |  |  |  |  | 41 |  | 
|  | 18 |  |  |  |  | 794 |  | 
| 9 | 18 |  |  | 18 |  | 109 | use base 'DBR::Common'; | 
|  | 18 |  |  |  |  | 44 |  | 
|  | 18 |  |  |  |  | 2056 |  | 
| 10 | 18 |  |  | 18 |  | 155 | use DBR::Config::Table; | 
|  | 18 |  |  |  |  | 40 |  | 
|  | 18 |  |  |  |  | 386 |  | 
| 11 | 18 |  |  | 18 |  | 100 | use DBR::Config::Field; | 
|  | 18 |  |  |  |  | 37 |  | 
|  | 18 |  |  |  |  | 609 |  | 
| 12 | 18 |  |  | 18 |  | 96 | use Carp; | 
|  | 18 |  |  |  |  | 48 |  | 
|  | 18 |  |  |  |  | 1663 |  | 
| 13 | 18 |  |  | 18 |  | 112 | use Clone 'clone'; | 
|  | 18 |  |  |  |  | 47 |  | 
|  | 18 |  |  |  |  | 38748 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | my %TYPES = ( | 
| 16 |  |  |  |  |  |  | 1 => { name => 'parentof', mode => '1toM', opposite => 2 }, #reciprocal | 
| 17 |  |  |  |  |  |  | 2 => { name => 'childof',  mode => 'Mto1', opposite => 1 }, | 
| 18 |  |  |  |  |  |  | 3 => { name => 'assoc',    mode => 'MtoM' }, | 
| 19 |  |  |  |  |  |  | 4 => { name => 'other',    mode => 'MtoM' }, | 
| 20 |  |  |  |  |  |  | ); | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | map { $TYPES{$_}{type_id} = $_ } keys %TYPES; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub list_types{ | 
| 25 | 18 |  |  | 18 | 0 | 159 | return clone( [ sort {$a->{type_id} <=> $b->{type_id} } values %TYPES ] ); | 
|  | 84 |  |  |  |  | 1393 |  | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | my %RELATIONS_BY_ID; | 
| 30 |  |  |  |  |  |  | sub load{ | 
| 31 | 34 |  |  | 34 | 0 | 100 | my( $package ) = shift; | 
| 32 | 34 |  |  |  |  | 161 | my %params = @_; | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 34 |  |  |  |  | 221 | my $self = { session => $params{session} }; | 
| 35 | 34 |  |  |  |  | 220 | bless( $self, $package ); # Dummy object | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 34 |  | 50 |  |  | 159 | my $instance = $params{instance} || return $self->_error('instance is required'); | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 34 |  | 50 |  |  | 152 | my $table_ids = $params{table_id} || return $self->_error('table_id is required'); | 
| 40 | 34 | 50 |  |  |  | 211 | $table_ids = [$table_ids] unless ref($table_ids) eq 'ARRAY'; | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 34 | 50 |  |  |  | 171 | return 1 unless @$table_ids; | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 34 |  | 50 |  |  | 1961 | my $dbrh = $instance->connect || return $self->_error("Failed to connect to ${\$instance->name}"); | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 34 | 50 |  |  |  | 474 | return $self->_error('Failed to select from dbr_relationships') unless | 
| 47 |  |  |  |  |  |  | my $relations = $dbrh->select( | 
| 48 |  |  |  |  |  |  | -table => 'dbr_relationships', | 
| 49 |  |  |  |  |  |  | -fields => 'relationship_id from_name from_table_id from_field_id to_name to_table_id to_field_id type', | 
| 50 |  |  |  |  |  |  | -where  => { from_table_id => ['d in',@$table_ids] }, | 
| 51 |  |  |  |  |  |  | ); | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 34 |  |  |  |  | 183 | my @rel_ids; | 
| 54 | 34 |  |  |  |  | 125 | foreach my $relation (@$relations){ | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 24 | 50 |  |  |  | 193 | my $table1 = DBR::Config::Table->_register_relation( | 
| 57 |  |  |  |  |  |  | table_id    => $relation->{to_table_id}, | 
| 58 |  |  |  |  |  |  | name        => $relation->{from_name}, #yes, this is kinda confusing | 
| 59 |  |  |  |  |  |  | relation_id => $relation->{relationship_id}, | 
| 60 |  |  |  |  |  |  | ) or return $self->_error('failed to register to relationship'); | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 24 | 50 |  |  |  | 142 | my $table2 = DBR::Config::Table->_register_relation( | 
| 63 |  |  |  |  |  |  | table_id    => $relation->{from_table_id}, | 
| 64 |  |  |  |  |  |  | name        => $relation->{to_name}, #yes, this is kinda confusing | 
| 65 |  |  |  |  |  |  | relation_id => $relation->{relationship_id}, | 
| 66 |  |  |  |  |  |  | ) or return $self->_error('failed to register from relationship'); | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 24 |  |  |  |  | 103 | $relation->{same_schema} = ( $table1->{schema_id} == $table2->{schema_id} ); | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 24 |  |  |  |  | 106 | $RELATIONS_BY_ID{ $relation->{relationship_id} } = $relation; | 
| 72 | 24 |  |  |  |  | 140 | push @rel_ids, $relation->{relationship_id}; | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 34 |  |  |  |  | 214 | return 1; | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | sub new { | 
| 81 | 44 |  |  | 44 | 0 | 185 | my $package = shift; | 
| 82 | 44 |  |  |  |  | 207 | my %params = @_; | 
| 83 | 44 |  |  |  |  | 268 | my $self = { | 
| 84 |  |  |  |  |  |  | session      => $params{session}, | 
| 85 |  |  |  |  |  |  | relation_id => $params{relation_id}, | 
| 86 |  |  |  |  |  |  | table_id    => $params{table_id}, | 
| 87 |  |  |  |  |  |  | }; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 44 |  |  |  |  | 156 | bless( $self, $package ); | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 44 | 50 |  |  |  | 210 | return $self->_error('relation_id is required') unless $self->{relation_id}; | 
| 92 | 44 | 50 |  |  |  | 148 | return $self->_error('table_id is required')    unless $self->{table_id}; | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 44 | 50 |  |  |  | 266 | my $ref = $RELATIONS_BY_ID{ $self->{relation_id} } or return $self->_error('invalid relation_id'); | 
| 96 | 44 | 50 |  |  |  | 194 | return $self->_error("Invalid type_id $ref->{type}") unless $TYPES{ $ref->{type} }; | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 44 | 100 |  |  |  | 232 | if($ref->{from_table_id} == $self->{table_id}){ | 
|  |  | 50 |  |  |  |  |  | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 25 |  |  |  |  | 85 | $self->{forward} = 'from'; | 
| 101 | 25 |  |  |  |  | 77 | $self->{reverse} = 'to'; | 
| 102 | 25 |  |  |  |  | 85 | $self->{type_id} = $ref->{type}; | 
| 103 |  |  |  |  |  |  | }elsif($ref->{to_table_id} == $self->{table_id}){ | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 19 |  |  |  |  | 86 | $self->{forward} = 'to'; | 
| 106 | 19 |  |  |  |  | 55 | $self->{reverse} = 'from'; | 
| 107 | 19 |  | 33 |  |  | 105 | $self->{type_id} = $TYPES{ $ref->{type} }->{opposite} || $ref->{type}; | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | }else{ | 
| 110 | 0 |  |  |  |  | 0 | return $self->_error("table_id $self->{table_id} is invalid for this relationship"); | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 44 |  |  |  |  | 241 | return( $self ); | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 | 0 |  |  | 0 | 0 | 0 | sub relation_id { $_[0]->{relation_id} } | 
| 117 | 35 |  |  | 35 | 0 | 229 | sub name     { $RELATIONS_BY_ID{  $_[0]->{relation_id} }->{ $_[0]->{reverse}  . '_name' }    } # Name is always the opposite of everything else | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | sub field_id { | 
| 120 | 35 |  |  | 35 | 0 | 62 | my $self = shift; | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 35 |  |  |  |  | 301 | return $RELATIONS_BY_ID{  $self->{relation_id} }->{ $self->{forward}  . '_field_id' }; | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | sub field { | 
| 126 | 9 |  |  | 9 | 0 | 18 | my $self = shift; | 
| 127 | 9 |  |  |  |  | 36 | my $field_id = $RELATIONS_BY_ID{  $self->{relation_id} }->{ $self->{forward}  . '_field_id' }; | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 9 | 50 |  |  |  | 48 | my $field = DBR::Config::Field->new( | 
| 130 |  |  |  |  |  |  | session  => $self->{session}, | 
| 131 |  |  |  |  |  |  | field_id => $field_id, | 
| 132 |  |  |  |  |  |  | ) or return $self->_error('failed to create field object'); | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 9 |  |  |  |  | 39 | return $field; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | sub mapfield { | 
| 138 | 21 |  |  | 21 | 0 | 57 | my $self = shift; | 
| 139 | 21 |  |  |  |  | 283 | my $mapfield_id = $RELATIONS_BY_ID{  $self->{relation_id} }->{ $self->{reverse}  . '_field_id' }; | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 21 | 50 |  |  |  | 135 | my $field = DBR::Config::Field->new( | 
| 142 |  |  |  |  |  |  | session  => $self->{session}, | 
| 143 |  |  |  |  |  |  | field_id => $mapfield_id, | 
| 144 |  |  |  |  |  |  | ) or return $self->_error('failed to create field object'); | 
| 145 |  |  |  |  |  |  |  | 
| 146 | 21 |  |  |  |  | 95 | return $field; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | sub table { | 
| 150 | 12 |  |  | 12 | 0 | 35 | my $self = shift; | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 12 |  |  |  |  | 144 | return DBR::Config::Table->new( | 
| 153 |  |  |  |  |  |  | session   => $self->{session}, | 
| 154 |  |  |  |  |  |  | table_id => $RELATIONS_BY_ID{  $self->{relation_id} }->{$self->{forward} . '_table_id'} | 
| 155 |  |  |  |  |  |  | ); | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | sub maptable { | 
| 159 | 21 |  |  | 21 | 0 | 49 | my $self = shift; | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 21 |  |  |  |  | 176 | return DBR::Config::Table->new( | 
| 162 |  |  |  |  |  |  | session   => $self->{session}, | 
| 163 |  |  |  |  |  |  | table_id => $RELATIONS_BY_ID{  $self->{relation_id} }->{$self->{reverse} . '_table_id'} | 
| 164 |  |  |  |  |  |  | ); | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | sub is_to_one{ | 
| 168 | 30 |  |  | 30 | 0 | 137 | my $mode = $TYPES{ $_[0]->{type_id} }->{mode}; | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 30 | 100 |  |  |  | 365 | return 1 if $mode eq 'Mto1'; | 
| 171 | 4 | 50 |  |  |  | 14 | return 1 if $mode eq '1to1'; | 
| 172 |  |  |  |  |  |  |  | 
| 173 | 4 |  |  |  |  | 12 | return 0; | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 28 |  |  | 28 | 0 | 219 | sub is_same_schema{ $RELATIONS_BY_ID{  shift->{relation_id} }->{same_schema} } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub index{ | 
| 180 | 66 |  |  | 66 | 0 | 117 | my $self = shift; | 
| 181 | 66 |  |  |  |  | 108 | my $set = shift; | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 66 | 100 |  |  |  | 186 | if(defined($set)){ | 
| 184 | 8 | 50 |  |  |  | 36 | croak "Cannot set the index on a relation object twice" if defined($self->{index}); # I want this to fail obnoxiously | 
| 185 | 8 |  |  |  |  | 25 | $self->{index} = $set; | 
| 186 | 8 |  |  |  |  | 28 | return 1; | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 58 |  |  |  |  | 195 | return $self->{index}; | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | 1; |