| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Class::DBI::Relationship::IsA; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Class::DBI::Relationship::IsA - A Class::DBI module for 'Is A' relationships |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Class::DBI::Relationship::IsA Provides an Is A relationship between Class::DBI classes/tables. |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
By using this module you can emulate some features of inheritance both within your database and classes through the Class::DBI API. |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
NOTE: This module is still experimental, several very nasty bugs have been found (and fixed) others may still be lurking - see CAVEATS AND BUGS below. |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Warning Will Robinson! |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
In your database (assuming mysql): |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
create table person ( |
|
22
|
|
|
|
|
|
|
personid int primary key auto_increment, |
|
23
|
|
|
|
|
|
|
firstname varchar(32), |
|
24
|
|
|
|
|
|
|
initials varchar(16), |
|
25
|
|
|
|
|
|
|
surname varchar(64), |
|
26
|
|
|
|
|
|
|
date_of_birth datetime |
|
27
|
|
|
|
|
|
|
); |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
create table artist ( |
|
30
|
|
|
|
|
|
|
artistid int primary key auto_increment, |
|
31
|
|
|
|
|
|
|
alias varchar(128), |
|
32
|
|
|
|
|
|
|
person int |
|
33
|
|
|
|
|
|
|
); |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
In your classes: |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
package Music::DBI; |
|
39
|
|
|
|
|
|
|
use base 'Class::DBI'; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Music::DBI->connection('dbi:mysql:dbname', 'username', 'password'); |
|
42
|
|
|
|
|
|
|
__PACKAGE__->add_relationship_type(is_a => 'Class::DBI::Relationship::IsA'); |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Superclass: |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
package Music::Person; |
|
47
|
|
|
|
|
|
|
use base 'Music::DBI'; |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
Music::Person->table('person'); |
|
50
|
|
|
|
|
|
|
Music::Person->columns(All => qw/personid firstname initials surname date_of_birth/); |
|
51
|
|
|
|
|
|
|
Music::Person->columns(Primary => qw/personid/); # Good practice, less likely to break IsA |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Child class: |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
package Music::Artist; |
|
56
|
|
|
|
|
|
|
use base 'Music::DBI'; |
|
57
|
|
|
|
|
|
|
use Music::Person; # required for access to Music::Person methods |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Music::Artist->table('artist'); |
|
60
|
|
|
|
|
|
|
Music::Artist->columns(All => qw/artistid alias/); |
|
61
|
|
|
|
|
|
|
Music::Person->columns(Primary => qw/personid/); # Good practice, less likely to break IsA |
|
62
|
|
|
|
|
|
|
Music::Artist->has_many(cds => 'Music::CD'); |
|
63
|
|
|
|
|
|
|
Music::Artist->is_a(person => 'Person'); # Music::Artist inherits accessors from Music::Person |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
... elsewhere ... |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
use Music::Artist; |
|
68
|
|
|
|
|
|
|
my $artist = Music::Artist->create( {firstname=>'Sarah', surname=>'Geller', alias=>'Buffy'}); |
|
69
|
|
|
|
|
|
|
$artist->initials('M'); |
|
70
|
|
|
|
|
|
|
$artist->update(); |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
|
73
|
|
|
|
|
|
|
|
|
74
|
1
|
|
|
1
|
|
942
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
53
|
|
|
75
|
|
|
|
|
|
|
our $VERSION = '0.05'; |
|
76
|
|
|
|
|
|
|
|
|
77
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
36
|
|
|
78
|
1
|
|
|
1
|
|
16
|
use base qw( Class::DBI::Relationship ); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1146
|
|
|
79
|
1
|
|
|
1
|
|
7092
|
use Class::DBI::AbstractSearch; |
|
|
1
|
|
|
|
|
27322
|
|
|
|
1
|
|
|
|
|
77
|
|
|
80
|
|
|
|
|
|
|
|
|
81
|
1
|
|
|
1
|
|
1275
|
use Data::Dumper; |
|
|
1
|
|
|
|
|
7132
|
|
|
|
1
|
|
|
|
|
830
|
|
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub remap_arguments { |
|
84
|
0
|
|
|
0
|
1
|
|
my $proto = shift; |
|
85
|
0
|
|
|
|
|
|
my $class = shift; |
|
86
|
0
|
0
|
|
|
|
|
$class->_invalid_object_method('is_a()') if ref $class; |
|
87
|
0
|
0
|
|
|
|
|
my $column = $class->find_column(shift) |
|
88
|
|
|
|
|
|
|
or return $class->_croak("is_a needs a valid column"); |
|
89
|
0
|
0
|
|
|
|
|
my $f_class = shift |
|
90
|
|
|
|
|
|
|
or $class->_croak("$class $column needs an associated class"); |
|
91
|
0
|
|
|
|
|
|
my %meths = @_; |
|
92
|
0
|
|
|
|
|
|
my @f_cols; |
|
93
|
0
|
|
|
|
|
|
foreach my $f_col ($f_class->all_columns) { |
|
94
|
0
|
0
|
|
|
|
|
push @f_cols, $f_col |
|
95
|
|
|
|
|
|
|
unless $f_col eq $f_class->primary_column; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
0
|
|
|
|
|
|
$class->__grouper->add_group(TEMP => map { $_->name } @f_cols); |
|
|
0
|
|
|
|
|
|
|
|
98
|
0
|
|
|
|
|
|
$class->__grouper->add_group(__INHERITED => map { $_->name } @f_cols); |
|
|
0
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
|
$class->mk_classdata('__isa_rels'); |
|
100
|
0
|
|
|
|
|
|
$class->__isa_rels({ }); |
|
101
|
0
|
|
|
|
|
|
return ($class, $column, $f_class, \%meths); |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub triggers { |
|
105
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
106
|
0
|
|
|
|
|
|
$self->class->_require_class($self->foreign_class); |
|
107
|
0
|
|
|
|
|
|
my $column = $self->accessor; |
|
108
|
|
|
|
|
|
|
return ( |
|
109
|
|
|
|
|
|
|
select => $self->_inflator, |
|
110
|
|
|
|
|
|
|
before_create => $self->_creator, |
|
111
|
|
|
|
|
|
|
before_update => sub { |
|
112
|
0
|
0
|
|
0
|
|
|
if (my $f_obj = $_[0]->$column()) { $f_obj->update } |
|
|
0
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
}, |
|
114
|
|
|
|
|
|
|
|
|
115
|
0
|
|
|
|
|
|
); |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub methods { |
|
119
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
120
|
0
|
|
|
|
|
|
$self->class->_require_class($self->foreign_class); |
|
121
|
|
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
my $foreign_class = $self->foreign_class; |
|
123
|
0
|
|
|
|
|
|
my $class = $self->class; |
|
124
|
0
|
|
|
|
|
|
warn "foreign class : $foreign_class\n"; |
|
125
|
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
|
warn "getting relationships..\n"; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
|
my $parent_relation_fields = $self->_inject_inherited_relationships(class=>$class, foreign=>$foreign_class); |
|
130
|
|
|
|
|
|
|
|
|
131
|
0
|
|
|
|
|
|
my $forbidden_fields = "(id|${class}_?u?id"; |
|
132
|
0
|
0
|
|
|
|
|
$forbidden_fields .= ($foreign_class->columns('Primary')) ? '|' . $foreign_class->columns('Primary') .')' : ')' ; |
|
133
|
0
|
|
|
|
|
|
warn "forbidden_fields : $forbidden_fields\n"; |
|
134
|
|
|
|
|
|
|
|
|
135
|
0
|
|
|
|
|
|
my %methods; |
|
136
|
0
|
|
|
|
|
|
my $acc_name = $self->accessor->name; |
|
137
|
0
|
|
|
|
|
|
foreach my $f_col ($self->foreign_class->all_columns) { |
|
138
|
0
|
|
|
|
|
|
warn "f_col : $f_col, acc_name : $acc_name\n"; |
|
139
|
0
|
0
|
0
|
|
|
|
next if ($f_col eq $acc_name or $f_col =~ /$forbidden_fields/i or $parent_relation_fields->{$f_col}); |
|
|
|
|
0
|
|
|
|
|
|
140
|
0
|
0
|
|
|
|
|
if ($class->can('pure_accessor_name')) { |
|
141
|
|
|
|
|
|
|
# provide seperate read/write accessor, read only accessor and write only mutator |
|
142
|
0
|
|
|
|
|
|
$methods{ucfirst($class->pure_accessor_name($f_col))} |
|
143
|
|
|
|
|
|
|
= $methods{$class->pure_accessor_name($f_col)} = $self->_get_methods($acc_name, $f_col,'ro'); |
|
144
|
0
|
|
|
|
|
|
$methods{ucfirst($class->mutator_name($f_col))} |
|
145
|
|
|
|
|
|
|
= $methods{$class->mutator_name($f_col)} = $self->_get_methods($acc_name, $f_col,'wo'); |
|
146
|
0
|
|
|
|
|
|
$methods{ucfirst($class->accessor_name($f_col))} |
|
147
|
|
|
|
|
|
|
= $methods{$class->accessor_name($f_col)} = $self->_get_methods($acc_name, $f_col,'rw'); |
|
148
|
|
|
|
|
|
|
} else { |
|
149
|
0
|
0
|
|
|
|
|
if ( $class->mutator_name($f_col) eq $class->accessor_name($f_col) ) { |
|
150
|
|
|
|
|
|
|
# provide read/write accessor |
|
151
|
0
|
|
|
|
|
|
$methods{ucfirst($class->accessor_name($f_col))} |
|
152
|
|
|
|
|
|
|
= $methods{$class->accessor_name($f_col)} = $self->_get_methods($acc_name, $f_col,'rw'); |
|
153
|
|
|
|
|
|
|
} else { |
|
154
|
|
|
|
|
|
|
# provide seperate read only accessor and write only mutator |
|
155
|
0
|
|
|
|
|
|
$methods{ucfirst($class->accessor_name($f_col))} |
|
156
|
|
|
|
|
|
|
= $methods{$class->accessor_name($f_col)} = $self->_get_methods($acc_name, $f_col,'ro'); |
|
157
|
0
|
|
|
|
|
|
$methods{ucfirst($class->mutator_name($f_col))} |
|
158
|
|
|
|
|
|
|
= $methods{$class->mutator_name($f_col)} = $self->_get_methods($acc_name, $f_col,'wo'); |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
0
|
0
|
|
|
|
|
$methods{search_where} = $self->search_where if $self->class->can('search_where'); |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
return( |
|
166
|
0
|
|
|
|
|
|
%methods, |
|
167
|
|
|
|
|
|
|
search => $self->search, |
|
168
|
|
|
|
|
|
|
search_like => $self->search_like, |
|
169
|
|
|
|
|
|
|
all_columns => $self->all_columns, |
|
170
|
|
|
|
|
|
|
); |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub search { |
|
174
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
175
|
0
|
|
|
|
|
|
my $SUPER = $self->foreign_class; |
|
176
|
0
|
|
|
|
|
|
my $col = $self->accessor; |
|
177
|
|
|
|
|
|
|
{ |
|
178
|
1
|
|
|
1
|
|
10
|
no strict "refs"; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
228
|
|
|
|
0
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
*{$self->class."::orig_search"} = \&{"Class::DBI::search"}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
return sub { |
|
182
|
0
|
|
|
0
|
|
|
my ($self, %args) = (@_); |
|
183
|
0
|
|
|
|
|
|
my (%child, %parent); |
|
184
|
0
|
|
|
|
|
|
foreach my $key (keys %args) { |
|
185
|
0
|
0
|
|
|
|
|
$child{$key} = $args{$key} if $self->has_real_column($key); |
|
186
|
0
|
0
|
|
|
|
|
$parent{$key} = $args{$key} if $SUPER->has_real_column($key); |
|
187
|
|
|
|
|
|
|
} |
|
188
|
0
|
0
|
|
|
|
|
if(%parent) { |
|
189
|
0
|
|
|
|
|
|
return map { $self->orig_search($col => $_->id, %child) |
|
|
0
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
} $SUPER->search(%parent); |
|
191
|
|
|
|
|
|
|
} else { |
|
192
|
0
|
|
|
|
|
|
return $self->orig_search(%child); |
|
193
|
|
|
|
|
|
|
} |
|
194
|
0
|
|
|
|
|
|
}; |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub search_like { |
|
198
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
199
|
0
|
|
|
|
|
|
my $SUPER = $self->foreign_class; |
|
200
|
0
|
|
|
|
|
|
my $col = $self->accessor; |
|
201
|
|
|
|
|
|
|
{ |
|
202
|
1
|
|
|
1
|
|
4
|
no strict "refs"; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
225
|
|
|
|
0
|
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
|
*{$self->class."::orig_search_like"} = \&{"Class::DBI::search_like"}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
return sub { |
|
206
|
0
|
|
|
0
|
|
|
my ($self, %args) = (@_); |
|
207
|
0
|
|
|
|
|
|
my (%child, %parent); |
|
208
|
0
|
|
|
|
|
|
foreach my $key (keys %args) { |
|
209
|
0
|
0
|
|
|
|
|
$child{$key} = $args{$key} if $self->has_real_column($key); |
|
210
|
0
|
0
|
|
|
|
|
$parent{$key} = $args{$key} if $SUPER->has_real_column($key); |
|
211
|
|
|
|
|
|
|
} |
|
212
|
0
|
0
|
|
|
|
|
if(%parent) { |
|
213
|
0
|
|
|
|
|
|
return map { $self->orig_search_like($col => $_->id, %child) |
|
|
0
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
} $SUPER->search_like(%parent); |
|
215
|
|
|
|
|
|
|
} else { |
|
216
|
0
|
|
|
|
|
|
return $self->orig_search_like(%child); |
|
217
|
|
|
|
|
|
|
} |
|
218
|
0
|
|
|
|
|
|
}; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub search_where { |
|
222
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
223
|
0
|
|
|
|
|
|
my $SUPER = $self->foreign_class; |
|
224
|
0
|
|
|
|
|
|
my $col = $self->accessor; |
|
225
|
|
|
|
|
|
|
{ |
|
226
|
1
|
|
|
1
|
|
5
|
no strict "refs"; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
407
|
|
|
|
0
|
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
*{$self->class."::orig_search_where"} = \&{"Class::DBI::AbstractSearch::search_where"}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
return sub { |
|
231
|
0
|
|
|
0
|
|
|
my ($self, %args) = (@_); |
|
232
|
0
|
|
|
|
|
|
my (%child, %parent); |
|
233
|
0
|
|
|
|
|
|
foreach my $key (keys %args) { |
|
234
|
0
|
0
|
|
|
|
|
$child{$key} = $args{$key} if $self->has_real_column($key); |
|
235
|
0
|
0
|
|
|
|
|
$parent{$key} = $args{$key} if $SUPER->has_real_column($key); |
|
236
|
|
|
|
|
|
|
} |
|
237
|
0
|
0
|
|
|
|
|
if(%parent) { |
|
238
|
0
|
|
|
|
|
|
return map { $self->orig_search_where($col->name => $_->id, %child) |
|
|
0
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
} $SUPER->search_where(%parent); |
|
240
|
|
|
|
|
|
|
} else { |
|
241
|
0
|
|
|
|
|
|
return $self->orig_search_where(%child); |
|
242
|
|
|
|
|
|
|
} |
|
243
|
0
|
|
|
|
|
|
}; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub all_columns { |
|
247
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
248
|
0
|
|
|
|
|
|
my $SUPER = $self->foreign_class; |
|
249
|
0
|
|
|
|
|
|
my $col = $self->accessor; |
|
250
|
|
|
|
|
|
|
{ |
|
251
|
1
|
|
|
1
|
|
7
|
no strict "refs"; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
466
|
|
|
|
0
|
|
|
|
|
|
|
|
252
|
0
|
|
|
|
|
|
*{$self->class."::orig_all_columns"} = \&{"Class::DBI::all_columns"}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
return sub { |
|
255
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
256
|
0
|
|
|
|
|
|
return ($self->orig_all_columns, $self->columns('TEMP')); |
|
257
|
0
|
|
|
|
|
|
}; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
################################################################################ |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub _inject_inherited_relationships { |
|
264
|
0
|
|
|
0
|
|
|
my ($self,%params) = @_; |
|
265
|
0
|
|
|
|
|
|
my $class = $params{class}; |
|
266
|
0
|
|
|
|
|
|
my $foreign_class = $params{foreign}; |
|
267
|
0
|
|
|
|
|
|
my $fields = {}; |
|
268
|
|
|
|
|
|
|
|
|
269
|
0
|
|
|
|
|
|
my %current_relationships = (); |
|
270
|
|
|
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
|
if ($class->can('meta_info')) { |
|
272
|
0
|
|
|
|
|
|
warn "class has meta_info "; |
|
273
|
|
|
|
|
|
|
# warn Dumper($class->meta_info); |
|
274
|
0
|
|
|
|
|
|
my $meta_info = $class->meta_info; |
|
275
|
0
|
|
|
|
|
|
foreach my $relation_type ( keys %$meta_info ) { |
|
276
|
0
|
0
|
|
|
|
|
next if ($relation_type eq 'is_a'); |
|
277
|
0
|
|
|
|
|
|
foreach my $relname (keys %{$meta_info->{$relation_type}}) { |
|
|
0
|
|
|
|
|
|
|
|
278
|
0
|
|
|
|
|
|
$current_relationships{$relname} = 1; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
|
|
283
|
0
|
0
|
|
|
|
|
if ($foreign_class->can('meta_info')) { |
|
284
|
0
|
|
|
|
|
|
warn "foreign class has meta_info "; |
|
285
|
|
|
|
|
|
|
# warn Dumper($class->meta_info); |
|
286
|
0
|
|
|
|
|
|
my $meta_info = $foreign_class->meta_info; |
|
287
|
0
|
|
|
|
|
|
foreach my $relation_type ( keys %$meta_info ) { |
|
288
|
0
|
0
|
|
|
|
|
next if ($relation_type eq 'is_a'); |
|
289
|
0
|
|
|
|
|
|
foreach my $relname (keys %{$meta_info->{$relation_type}}) { |
|
|
0
|
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
warn "adding new relationship : $relname \n"; |
|
291
|
0
|
|
|
|
|
|
$fields->{$relname} = 1; |
|
292
|
0
|
|
|
|
|
|
$self->_inject_inherited_method($class, $relname); |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
} |
|
296
|
0
|
|
|
|
|
|
return $fields; |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub _inject_inherited_method { |
|
300
|
0
|
|
|
0
|
|
|
my ($self,$class,$accessor_name) = @_; |
|
301
|
0
|
|
|
|
|
|
my $parent_accessor = $self->accessor; |
|
302
|
|
|
|
|
|
|
my $method = sub { |
|
303
|
0
|
|
|
0
|
|
|
warn "injected method $accessor_name , calling $accessor_name on parent via $parent_accessor \n"; |
|
304
|
0
|
|
|
|
|
|
warn "..called with args ", join(', ',@_), "\n"; |
|
305
|
0
|
|
|
|
|
|
my ($self, @args) = @_; |
|
306
|
0
|
|
|
|
|
|
$self->$parent_accessor->$accessor_name(@args); |
|
307
|
0
|
|
|
|
|
|
}; |
|
308
|
|
|
|
|
|
|
{ |
|
309
|
1
|
|
|
1
|
|
5
|
no strict "refs"; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
909
|
|
|
|
0
|
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
|
*{"${class}::${accessor_name}"} = $method; |
|
|
0
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub _creator { |
|
315
|
0
|
|
|
0
|
|
|
my $proto = shift; |
|
316
|
0
|
|
|
|
|
|
my $col = $proto->accessor; |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
return sub { |
|
319
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
320
|
0
|
|
|
|
|
|
my $meta = $self->meta_info(is_a => $col); |
|
321
|
0
|
|
|
|
|
|
my $f_class = $meta->foreign_class; |
|
322
|
|
|
|
|
|
|
|
|
323
|
0
|
|
|
|
|
|
my $hash = { }; |
|
324
|
|
|
|
|
|
|
|
|
325
|
0
|
|
|
|
|
|
foreach ($self->__grouper->group_cols('TEMP')) { |
|
326
|
0
|
0
|
|
|
|
|
next unless defined($self->_attrs($_)); |
|
327
|
0
|
|
|
|
|
|
$hash->{$_} = $self->_attrs($_); |
|
328
|
|
|
|
|
|
|
} |
|
329
|
0
|
|
|
|
|
|
my $f_pk = $f_class->primary_column; |
|
330
|
0
|
0
|
|
|
|
|
if ($self->_attrs($f_pk)) { |
|
331
|
0
|
|
|
|
|
|
$hash->{$f_pk} = $self->_attrs($f_pk); |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
my $f_obj = $f_class->create($hash); |
|
335
|
0
|
|
|
|
|
|
$proto->_import_column_values($self, $f_class, $f_obj); |
|
336
|
|
|
|
|
|
|
|
|
337
|
0
|
|
|
|
|
|
return $self->_attribute_store($col => $f_obj->id); |
|
338
|
0
|
|
|
|
|
|
}; |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub _inflator { |
|
342
|
0
|
|
|
0
|
|
|
my $proto = shift; |
|
343
|
0
|
|
|
|
|
|
my $col = $proto->accessor; |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
return sub { |
|
346
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
347
|
0
|
|
|
|
|
|
my $value = $self->$col; |
|
348
|
0
|
|
|
|
|
|
my $meta = $self->meta_info(is_a => $col); |
|
349
|
0
|
|
|
|
|
|
my $f_class = $meta->foreign_class; |
|
350
|
|
|
|
|
|
|
|
|
351
|
0
|
0
|
0
|
|
|
|
return if ref($value) and $value->isa($f_class); |
|
352
|
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
$value = $f_class->_simple_bless($value); |
|
354
|
0
|
|
|
|
|
|
$proto->_import_column_values($self, $f_class, $value); |
|
355
|
|
|
|
|
|
|
|
|
356
|
0
|
|
|
|
|
|
return $self->_attribute_store($col => $value); |
|
357
|
0
|
|
|
|
|
|
}; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub _import_column_values { |
|
361
|
0
|
|
|
0
|
|
|
my ($self, $class, $f_class, $f_obj) = (@_); |
|
362
|
0
|
|
|
|
|
|
foreach ($f_class->all_columns) { |
|
363
|
0
|
0
|
|
|
|
|
$class->_attribute_store($_, $f_obj->$_) |
|
364
|
|
|
|
|
|
|
unless $_->name eq $class->primary_column->name; |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
sub _set_up_class_data { |
|
369
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
370
|
0
|
|
|
|
|
|
$self->class->_extend_class_data(__isa_rels => $self->accessor => |
|
371
|
0
|
|
|
|
|
|
[ $self->foreign_class, %{ $self->args } ]); |
|
372
|
0
|
|
|
|
|
|
$self->SUPER::_set_up_class_data; |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub _get_methods { |
|
377
|
0
|
|
|
0
|
|
|
my ($self, $acc_name, $f_col, $mode) = @_; |
|
378
|
0
|
|
|
|
|
|
warn "_get_methods $acc_name, $f_col, $mode \n"; |
|
379
|
0
|
|
|
|
|
|
warn join(', ',caller()); |
|
380
|
0
|
|
|
|
|
|
my $method; |
|
381
|
|
|
|
|
|
|
MODE: { |
|
382
|
0
|
0
|
|
|
|
|
if ($mode eq 'rw') { |
|
|
0
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
$method = sub { |
|
384
|
0
|
|
|
0
|
|
|
warn "artificial method $acc_name/$f_col called with args ", join(', ',@_), "\n"; |
|
385
|
0
|
|
|
|
|
|
my ($self, @args) = @_; |
|
386
|
0
|
0
|
|
|
|
|
if(@args) { |
|
387
|
0
|
|
|
|
|
|
$self->$acc_name->$f_col(@args); |
|
388
|
0
|
|
|
|
|
|
return; |
|
389
|
|
|
|
|
|
|
} else { |
|
390
|
0
|
|
|
|
|
|
return $self->$acc_name->$f_col; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
0
|
|
|
|
|
|
}; |
|
393
|
0
|
|
|
|
|
|
last MODE; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
0
|
0
|
|
|
|
|
if ($mode eq 'ro') { |
|
396
|
|
|
|
|
|
|
$method = sub { |
|
397
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
398
|
0
|
|
|
|
|
|
return $self->$acc_name->$f_col; |
|
399
|
0
|
|
|
|
|
|
}; |
|
400
|
0
|
|
|
|
|
|
last MODE; |
|
401
|
|
|
|
|
|
|
} |
|
402
|
0
|
0
|
|
|
|
|
if ($mode eq 'wo') { |
|
403
|
|
|
|
|
|
|
$method = sub { |
|
404
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
405
|
0
|
|
|
|
|
|
$self->$acc_name->$f_col(@_); |
|
406
|
0
|
|
|
|
|
|
return; |
|
407
|
0
|
|
|
|
|
|
}; |
|
408
|
0
|
|
|
|
|
|
last MODE; |
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
else { |
|
412
|
0
|
|
|
|
|
|
die "can't get method for mode :$mode\n"; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
} # end of MODE |
|
415
|
0
|
|
|
|
|
|
return $method; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
################################################################################ |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 BUGS AND CAVEATS |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
* Multiple inheritance is not supported, this is unlikely to change for the forseable future |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
* is_a must be called after all other cdbi relationship methods otherwise inherited methods and |
|
425
|
|
|
|
|
|
|
accessors may be over-ridden or clash unexpectedly |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
* non Class::DBI attributes and methods are not inherited via this module |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
* The update method is called on the inherited object when the inhertiting object has update called |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
* Always specify the primary key using columns(Primary => qw/../) if you don't bad things could happen, think of the movies 'Tremors', 'Poltergeist' and 'Evil Dead' all rolled into one but without any heros. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
* Very Bad Things can and may occur when using this module even if you use good practice and are cautious -- this includes but is not limited to infinite loops, memory leaks and data corruption. |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=head1 DEPENDANCIES |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
L |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
L |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
L |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
L |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=head1 AUTHOR |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
Richard Hundt, Erichard@webtk.org.ukE |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head1 MAINTAINER |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
Aaron Trevena Eaaron.trevena@droogs.orgE |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
Licensed for use, modification and distribution under the Artistic |
|
458
|
|
|
|
|
|
|
and GNU GPL licenses. |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
Copyright (C) 2004 by Richard Hundt and Aaron Trevena |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
|
463
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.8.1 or, |
|
464
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=cut |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
################################################################################ |
|
470
|
|
|
|
|
|
|
################################################################################ |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
1; |
|
473
|
|
|
|
|
|
|
|