/usr/local/CPAN/DBR/DBR/Config/Relation.pm
# the contents of this file are Copyright (c) 2009 Daniel Norman
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
package DBR::Config::Relation;
use strict;
use base 'DBR::Common';
use DBR::Config::Table;
use DBR::Config::Field;
use Carp;
use Clone 'clone';
my %TYPES = (
1 => { name => 'parentof', mode => '1toM', opposite => 2 }, #reciprocal
2 => { name => 'childof', mode => 'Mto1', opposite => 1 },
3 => { name => 'assoc', mode => 'MtoM' },
4 => { name => 'other', mode => 'MtoM' },
);
map { $TYPES{$_}{type_id} = $_ } keys %TYPES;
sub list_types{
return clone( [ sort {$a->{type_id} <=> $b->{type_id} } values %TYPES ] );
}
my %RELATIONS_BY_ID;
sub load{
my( $package ) = shift;
my %params = @_;
my $self = { session => $params{session} };
bless( $self, $package ); # Dummy object
my $instance = $params{instance} || return $self->_error('instance is required');
my $table_ids = $params{table_id} || return $self->_error('table_id is required');
$table_ids = [$table_ids] unless ref($table_ids) eq 'ARRAY';
return 1 unless @$table_ids;
my $dbrh = $instance->connect || return $self->_error("Failed to connect to ${\$instance->name}");
return $self->_error('Failed to select from dbr_relationships') unless
my $relations = $dbrh->select(
-table => 'dbr_relationships',
-fields => 'relationship_id from_name from_table_id from_field_id to_name to_table_id to_field_id type',
-where => { from_table_id => ['d in',@$table_ids] },
);
my @rel_ids;
foreach my $relation (@$relations){
my $table1 = DBR::Config::Table->_register_relation(
table_id => $relation->{to_table_id},
name => $relation->{from_name}, #yes, this is kinda confusing
relation_id => $relation->{relationship_id},
) or return $self->_error('failed to register to relationship');
my $table2 = DBR::Config::Table->_register_relation(
table_id => $relation->{from_table_id},
name => $relation->{to_name}, #yes, this is kinda confusing
relation_id => $relation->{relationship_id},
) or return $self->_error('failed to register from relationship');
$relation->{same_schema} = ( $table1->{schema_id} == $table2->{schema_id} );
$RELATIONS_BY_ID{ $relation->{relationship_id} } = $relation;
push @rel_ids, $relation->{relationship_id};
}
return 1;
}
sub new {
my $package = shift;
my %params = @_;
my $self = {
session => $params{session},
relation_id => $params{relation_id},
table_id => $params{table_id},
};
bless( $self, $package );
return $self->_error('relation_id is required') unless $self->{relation_id};
return $self->_error('table_id is required') unless $self->{table_id};
my $ref = $RELATIONS_BY_ID{ $self->{relation_id} } or return $self->_error('invalid relation_id');
return $self->_error("Invalid type_id $ref->{type}") unless $TYPES{ $ref->{type} };
if($ref->{from_table_id} == $self->{table_id}){
$self->{forward} = 'from';
$self->{reverse} = 'to';
$self->{type_id} = $ref->{type};
}elsif($ref->{to_table_id} == $self->{table_id}){
$self->{forward} = 'to';
$self->{reverse} = 'from';
$self->{type_id} = $TYPES{ $ref->{type} }->{opposite} || $ref->{type};
}else{
return $self->_error("table_id $self->{table_id} is invalid for this relationship");
}
return( $self );
}
sub relation_id { $_[0]->{relation_id} }
sub name { $RELATIONS_BY_ID{ $_[0]->{relation_id} }->{ $_[0]->{reverse} . '_name' } } # Name is always the opposite of everything else
sub field_id {
my $self = shift;
return $RELATIONS_BY_ID{ $self->{relation_id} }->{ $self->{forward} . '_field_id' };
}
sub field {
my $self = shift;
my $field_id = $RELATIONS_BY_ID{ $self->{relation_id} }->{ $self->{forward} . '_field_id' };
my $field = DBR::Config::Field->new(
session => $self->{session},
field_id => $field_id,
) or return $self->_error('failed to create field object');
return $field;
}
sub mapfield {
my $self = shift;
my $mapfield_id = $RELATIONS_BY_ID{ $self->{relation_id} }->{ $self->{reverse} . '_field_id' };
my $field = DBR::Config::Field->new(
session => $self->{session},
field_id => $mapfield_id,
) or return $self->_error('failed to create field object');
return $field;
}
sub table {
my $self = shift;
return DBR::Config::Table->new(
session => $self->{session},
table_id => $RELATIONS_BY_ID{ $self->{relation_id} }->{$self->{forward} . '_table_id'}
);
}
sub maptable {
my $self = shift;
return DBR::Config::Table->new(
session => $self->{session},
table_id => $RELATIONS_BY_ID{ $self->{relation_id} }->{$self->{reverse} . '_table_id'}
);
}
sub is_to_one{
my $mode = $TYPES{ $_[0]->{type_id} }->{mode};
return 1 if $mode eq 'Mto1';
return 1 if $mode eq '1to1';
return 0;
}
sub is_same_schema{ $RELATIONS_BY_ID{ shift->{relation_id} }->{same_schema} }
sub index{
my $self = shift;
my $set = shift;
if(defined($set)){
croak "Cannot set the index on a relation object twice" if defined($self->{index}); # I want this to fail obnoxiously
$self->{index} = $set;
return 1;
}
return $self->{index};
}
1;