]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm
adding "merge" and "remote_update" methods. these are propogated to all writable...
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / CDBI.pm
1 package OpenILS::Application::Storage::CDBI;
2 use base qw/Class::DBI/;
3 use Class::DBI;
4 use Class::DBI::AbstractSearch;
5
6 use OpenILS::Application::Storage::CDBI::actor;
7 use OpenILS::Application::Storage::CDBI::action;
8 use OpenILS::Application::Storage::CDBI::asset;
9 use OpenILS::Application::Storage::CDBI::biblio;
10 use OpenILS::Application::Storage::CDBI::config;
11 use OpenILS::Application::Storage::CDBI::metabib;
12 use OpenILS::Application::Storage::CDBI::money;
13 use OpenILS::Application::Storage::CDBI::permission;
14
15 use OpenSRF::Utils::Logger;
16 use OpenSRF::EX qw/:try/;
17
18 our $VERSION = undef;
19 my $log = 'OpenSRF::Utils::Logger';
20
21 sub child_init {
22         my $self = shift;
23
24         $log->debug("Creating ImaDBI Querys", DEBUG);
25         __PACKAGE__->set_sql( 'OILSFastSearch', <<"     SQL", 'Main');
26                 SELECT  %s
27                   FROM  %s
28                   WHERE %s = ?
29         SQL
30
31         __PACKAGE__->set_sql( 'OILSFastOrderedSearchLike', <<"  SQL", 'Main');
32                 SELECT  %s
33                   FROM  %s
34                   WHERE %s LIKE ?
35                   ORDER BY %s
36         SQL
37
38         __PACKAGE__->set_sql( 'OILSFastOrderedSearch', <<"      SQL", 'Main');
39                 SELECT  %s
40                   FROM  %s
41                   WHERE %s = ?
42                   ORDER BY %s
43         SQL
44
45         $log->debug("Calling Driver child_init", DEBUG);
46         $self->SUPER::child_init(@_);
47
48 }
49
50 sub fast_flesh_sth {
51         my $class = shift;
52         $class = ref($class) || $class;
53
54         my $field = shift;
55         my $value = shift;
56         my $order = shift;
57         my $like = shift;
58
59
60         if (!(defined($order) and ref($order) and ref($order) eq 'HASH')) {
61                 if (defined($value) and ref($value) and ref($value) eq 'HASH') {
62                         $order = $value;
63                         $value = undef;
64                 } else {
65                         $order = { order_by => $class->columns('Primary') }
66                 }
67         }
68
69         unless (defined $value) {
70                 $value = $field;
71                 ($field) = $class->columns('Primary');
72         }
73
74         unless (defined $field) {
75                 ($field) = $class->columns('Primary');
76         }
77
78         unless ($order->{order_by}) {
79                 $order = { order_by => $class->columns('Primary') }
80         }
81
82         my $fm_class = 'Fieldmapper::'.$class;
83         my $field_list = join ',', $class->columns('All');
84         
85         my $sth;
86         if (!$like) {
87                 $sth = $class->sql_OILSFastOrderedSearch( $field_list, $class->table, $field, $order->{order_by});
88         } else {
89                 $sth = $class->sql_OILSFastOrderedSearchLike( $field_list, $class->table, $field, $order->{order_by});
90         }
91         $sth->execute($value);
92         return $sth;
93 }
94
95 sub fast_flesh {
96         my $self = shift;
97         return map $class->construct($_), $self->fast_flesh_sth(@_)->fetchall_hash;
98 }
99
100 sub fast_fieldmapper {
101         my $self = shift;
102         my $id = shift;
103         my $col = shift;
104         my $like = shift;
105         my $options = shift;
106         my $class = ref($self) || $self;
107         my $fm_class = 'Fieldmapper::'.$class;
108         my @fms;
109         $log->debug("fast_fieldmapper() ==> Retrieving $fm_class", INTERNAL);
110         if ($like < 2) {
111                 for my $hash ($self->fast_flesh_sth( $col, "$id", { order_by => $col }, $like )->fetchall_hash) {
112                         my $fm = $fm_class->new;
113                         for my $field ( $fm_class->real_fields ) {
114                                 $fm->$field( $$hash{$field} );
115                         }
116                         push @fms, $fm;
117                 }
118         } else {
119                 my $search_type = 'search';
120                 if ($like == 2) {
121                         $search_type = 'search_fts'
122                 } elsif ($like == 3) {
123                         $search_type = 'search_regex'
124                 }
125
126                 for my $obj ($class->$search_type({ $col => $id}, $options)) {
127                         push @fms, $obj->to_fieldmapper;
128                 }
129         }
130         return @fms;
131 }
132
133 sub retrieve {
134         my $self = shift;
135         my $arg = shift;
136         if (ref($arg) and UNIVERSAL::isa($arg => 'Fieldmapper')) {
137                 my ($col) = $self->primary_column;
138                 $log->debug("Using field $col as the primary key", INTERNAL);
139                 $arg = $arg->$col;
140         }
141         $log->debug("Retrieving $self with $arg", INTERNAL);
142         my $rec;
143         try {
144                 $rec = $self->SUPER::retrieve("$arg");
145         } catch Error with {
146                 $log->debug("Could not retrieve $self with $arg! -- ".shift(), DEBUG);
147                 return undef;
148         };
149         return $rec;
150 }
151
152 sub to_fieldmapper {
153         my $obj = shift;
154         my $class = ref($obj) || $obj;
155
156         my $fm_class = 'Fieldmapper::'.$class;
157         my $fm = $fm_class->new;
158
159         if (ref($obj)) {
160                 for my $field ( $fm->real_fields ) {
161                         $fm->$field( $obj->$field );
162                 }
163         }
164
165         return $fm;
166 }
167
168 sub merge {
169         my $self = shift;
170         my $search = shift;
171         my $arg = shift;
172
173         delete $$arg{$_} for (keys %$search);
174
175         my @objs = $self->search_where($search);
176         if (@objs == 1) {
177                 return $objs[0]->update($arg)->id;
178         } elsif (@objs == 0) {
179                 return $self->create($arg)->id;
180         } else {
181                 throw OpenSRF::EX::WARN ("Non-unique search key for merge.  Perhaps you meant to use remote_update?");
182         }
183 }
184
185 sub remote_update {
186         my $self = shift;
187         my $search = shift;
188         my $arg = shift;
189
190         delete $$arg{$_} for (keys %$search);
191
192         my @objs = $self->search_where($search);
193         if (@objs == 0) {
194                 throw OpenSRF::EX::WARN ("No objects found for remote_update.  Perhaps you meant to use merge?");
195         } else {
196                 $_->update($arg) for (@objs);
197                 return scalar(@objs);
198         }
199 }
200
201 sub create {
202         my $self = shift;
203         my $arg = shift;
204
205         $log->debug("\$arg is $arg (".ref($arg).")",DEBUG);
206
207         if (ref($arg)) {
208                 return $self->create_from_fieldmapper($arg,@_);
209         }
210
211         return $self->SUPER::create($arg,@_);
212 }
213
214 sub create_from_fieldmapper {
215         my $obj = shift;
216         my $fm = shift;
217         my @params = @_;
218
219         $log->debug("Creating node of type ".ref($fm), DEBUG);
220
221         my $class = ref($obj) || $obj;
222         my ($primary) = $class->columns('Primary');
223
224         if (ref($fm)) {
225                 my %hash;
226                 if (UNIVERSAL::isa($fm => 'Fieldmapper')) {
227                         %hash = map { defined $fm->$_ ?
228                                                 ($_ => $fm->$_) :
229                                                 ()
230                                         } grep { $_ ne $primary } $class->columns('All');
231
232                         if ($class->find_column( 'last_xact_id' )) {
233                                 my $xact_id = $class->current_xact_id;
234                                 throw Error unless ($xact_id);
235                                 $hash{last_xact_id} = $xact_id;
236                         }
237                 } else {
238                         %hash = map { exists $fm->{$_} ?
239                                                 ($_ => $fm->{$_}) :
240                                                 ()
241                                         } grep { $_ ne $primary } $class->columns('All');
242                 }
243
244                 return $class->create( \%hash, @params );
245         } else {
246                 return undef;
247         }
248 }
249
250 sub delete {
251         my $self = shift;
252         my $arg = shift;
253
254         my $class = ref($self) || $self;
255
256         if (ref($arg) and UNIVERSAL::isa($arg => 'Fieldmapper')) {
257                 $self = $self->retrieve($arg);
258                 unless (defined $self) {
259                         $log->debug("ARG! Couldn't retrieve record ".$arg->id, DEBUG);
260                         throw OpenSRF::EX::WARN ("ARG! Couldn't retrieve record ");
261                 }
262         }
263
264         if ($class->find_column( 'last_xact_id' )) {
265                 my $xact_id = $self->current_xact_id;
266                 throw Error unless ($xact_id);
267                 $self->last_xact_id( $class->current_xact_id );
268                 $self->SUPER::update;
269         }
270
271         $self->SUPER::delete;
272         return 1;
273 }
274
275 sub update {
276         my $self = shift;
277         my $arg = shift;
278
279         $log->debug("Attempting to update using $arg", DEBUG) if ($arg);
280
281         if (ref($arg)) {
282                 $self = $self->modify_from_fieldmapper($arg);
283                 $log->debug("Modification of $self seems to have failed....", DEBUG);
284                 return undef unless (defined $self);
285         }
286
287         $log->debug("Calling Class::DBI->update on modified object $self", DEBUG);
288         return $self->SUPER::update if ($self->is_changed);
289         return 0;
290 }
291
292 sub modify_from_fieldmapper {
293         my $obj = shift;
294         my $fm = shift;
295
296         $log->debug("Modifying object using fieldmapper", DEBUG);
297
298         my $class = ref($obj) || $obj;
299
300         if (!ref($obj)) {
301                 $obj = $class->retrieve($fm);
302                 unless ($obj) {
303                         $log->debug("Retrieve of $class using $fm (".$fm->id.") failed! -- ".shift(), ERROR);
304                         throw OpenSRF::EX::WARN ("No $class with id of ".$fm->id."!!");
305                 }
306         }
307
308         my %hash;
309         
310         if (ref($fm) and UNIVERSAL::isa($fm => 'Fieldmapper')) {
311                 %hash = map { defined $fm->$_ ?
312                                 ($_ => ''.$fm->$_) :
313                                 ()
314                         } $fm->real_fields;
315         } else {
316                 %hash = %{$fm};
317         }
318
319         my $au = $obj->autoupdate;
320         $obj->autoupdate(0);
321         
322         for my $field ( keys %hash ) {
323                 $obj->$field( $hash{$field} ) if ($obj->$field ne $hash{$field});
324                 $log->debug("Setting field $field on $obj to $hash{$field}",INTERNAL);
325         }
326
327         if ($class->find_column( 'last_xact_id' ) and $obj->is_changed) {
328                 my $xact_id = $obj->current_xact_id;
329                 throw Error unless ($xact_id);
330                 $obj->last_xact_id( $xact_id );
331         } else {
332                 $obj->autoupdate($au)
333         }
334
335         return $obj;
336 }
337
338
339
340         #-------------------------------------------------------------------------------
341         actor::user->has_a( home_ou => 'actor::org_unit' );
342         actor::user->has_a( card => 'actor::card' );
343         actor::user->has_a( standing => 'config::standing' );
344         actor::user->has_a( profile => 'actor::profile' );
345         actor::user->has_a( mailing_address => 'actor::user_address' );
346         actor::user->has_a( billing_address => 'actor::user_address' );
347         actor::user->has_a( ident_type => 'config::identification_type' );
348         actor::user->has_a( ident_type2 => 'config::identification_type' );
349         actor::user->has_a( net_access_level => 'config::net_access_level' );
350
351         actor::user_address->has_a( usr => 'actor::user' );
352         
353         actor::card->has_a( usr => 'actor::user' );
354         
355         actor::org_unit->has_a( parent_ou => 'actor::org_unit' );
356         actor::org_unit->has_a( ou_type => 'actor::org_unit_type' );
357         #actor::org_unit->has_a( address => 'actor::org_address' );
358
359         actor::stat_cat_entry->has_a( stat_cat => 'actor::stat_cat' );
360         actor::stat_cat->has_many( entries => 'actor::stat_cat_entry' );
361         actor::stat_cat_entry_user_map->has_a( stat_cat => 'actor::stat_cat' );
362         actor::stat_cat_entry_user_map->has_a( stat_cat_entry => 'actor::stat_cat_entry' );
363         actor::stat_cat_entry_user_map->has_a( target_usr => 'actor::user' );
364
365         asset::stat_cat_entry->has_a( stat_cat => 'asset::stat_cat' );
366         asset::stat_cat->has_many( entries => 'asset::stat_cat_entry' );
367         asset::stat_cat_entry_copy_map->has_a( stat_cat => 'asset::stat_cat' );
368         asset::stat_cat_entry_copy_map->has_a( stat_cat_entry => 'asset::stat_cat_entry' );
369         asset::stat_cat_entry_copy_map->has_a( owning_copy => 'asset::copy' );
370
371         action::survey_response->has_a( usr => 'actor::user' );
372         action::survey_response->has_a( survey => 'action::survey' );
373         action::survey_response->has_a( question => 'action::survey_question' );
374         action::survey_response->has_a( answer => 'action::survey_answer' );
375
376         action::survey_question->has_a( survey => 'action::survey' );
377
378         action::survey_answer->has_a( question => 'action::survey' );
379
380         asset::copy_note->has_a( owning_copy => 'asset::copy' );
381
382         actor::user->has_many( stat_cat_entries => [ 'actor::stat_cat_entry_user_map' => 'stat_cat_entry' ] );
383         actor::user->has_many( stat_cat_entry_user_maps => 'actor::stat_cat_entry_user_map' );
384
385         asset::copy->has_many( stat_cat_entries => [ 'asset::stat_cat_entry_copy_map' => 'stat_cat_entry' ] );
386         asset::copy->has_many( stat_cat_entry_copy_maps => 'asset::stat_cat_entry_copy_map' );
387
388         asset::copy->has_a( call_number => 'asset::call_number' );
389         asset::copy->has_a( creator => 'actor::user' );
390         asset::copy->has_a( editor => 'actor::user' );
391         asset::copy->has_a( status => 'config::copy_status' );
392         asset::copy->has_a( location => 'asset::copy_location' );
393         asset::copy->has_a( circ_lib => 'actor::org_unit' );
394
395         asset::call_number_note->has_a( call_number => 'asset::call_number' );
396
397         asset::call_number->has_a( record => 'biblio::record_entry' );
398         asset::call_number->has_a( creator => 'actor::user' );
399         asset::call_number->has_a( editor => 'actor::user' );
400
401         biblio::record_note->has_a( record => 'biblio::record_entry' );
402         
403         biblio::record_entry->has_a( creator => 'actor::user' );
404         biblio::record_entry->has_a( editor => 'actor::user' );
405         
406         metabib::metarecord->has_a( master_record => 'biblio::record_entry' );
407         
408         metabib::record_descriptor->has_a( record => 'biblio::record_entry' );
409         
410         metabib::full_rec->has_a( record => 'biblio::record_entry' );
411         
412         metabib::title_field_entry->has_a( source => 'biblio::record_entry' );
413         metabib::title_field_entry->has_a( field => 'config::metabib_field' );
414         
415         metabib::author_field_entry->has_a( source => 'biblio::record_entry' );
416         metabib::author_field_entry->has_a( field => 'config::metabib_field' );
417         
418         metabib::subject_field_entry->has_a( source => 'biblio::record_entry' );
419         metabib::subject_field_entry->has_a( field => 'config::metabib_field' );
420         
421         metabib::keyword_field_entry->has_a( source => 'biblio::record_entry' );
422         metabib::keyword_field_entry->has_a( field => 'config::metabib_field' );
423         
424         metabib::series_field_entry->has_a( source => 'biblio::record_entry' );
425         metabib::series_field_entry->has_a( field => 'config::metabib_field' );
426         
427         metabib::metarecord_source_map->has_a( metarecord => 'metabib::metarecord' );
428         metabib::metarecord_source_map->has_a( source => 'biblio::record_entry' );
429
430         action::circulation->has_a( usr => 'actor::user' );
431         action::circulation->has_a( target_copy => 'asset::copy' );
432         action::circulation->has_a( circ_lib => 'actor::org_unit' );
433
434         money::billable_transaction->has_a( usr => 'actor::user' );
435         
436         
437         #-------------------------------------------------------------------------------
438         actor::user->has_many( survey_responses => 'action::survey_response' );
439         actor::user->has_many( addresses => 'actor::user_address' );
440         actor::user->has_many( cards => 'actor::card' );
441
442         actor::org_unit->has_many( users => 'actor::user' );
443         actor::profile->has_many( users => 'actor::user' );
444
445         action::survey->has_many( questions => 'action::survey_question' );
446         action::survey->has_many( responses => 'action::survey_response' );
447         
448         action::survey_question->has_many( answers => 'action::survey_answer' );
449         action::survey_question->has_many( responses => 'action::survey_response' );
450
451         action::survey_answer->has_many( responses => 'action::survey_response' );
452
453         asset::copy->has_many( notes => 'asset::copy_note' );
454         asset::call_number->has_many( copies => 'asset::copy' );
455         asset::call_number->has_many( notes => 'asset::call_number_note' );
456
457         biblio::record_entry->has_many( record_descriptor => 'metabib::record_descriptor' );
458         biblio::record_entry->has_many( notes => 'biblio::record_note' );
459         biblio::record_entry->has_many( call_numbers => 'asset::call_number' );
460         biblio::record_entry->has_many( full_record_entries => 'metabib::full_rec' );
461         biblio::record_entry->has_many( title_field_entries => 'metabib::title_field_entry' );
462         biblio::record_entry->has_many( author_field_entries => 'metabib::author_field_entry' );
463         biblio::record_entry->has_many( subject_field_entries => 'metabib::subject_field_entry' );
464         biblio::record_entry->has_many( keyword_field_entries => 'metabib::keyword_field_entry' );
465         biblio::record_entry->has_many( series_field_entries => 'metabib::series_field_entry' );
466
467         metabib::metarecord->has_many( source_records => [ 'metabib::metarecord_source_map' => 'source'] );
468
469         money::billable_transaction->has_many( billings => 'money::billing' );
470         money::billable_transaction->has_many( payments => 'money::payment' );
471
472         money::billing->has_a( xact => 'money::billable_transaction' );
473         money::payment->has_a( xact => 'money::billable_transaction' );
474
475         money::cash_payment->has_a( xact => 'money::billable_transaction' );
476         money::cash_payment->has_a( accepting_usr => 'actor::user' );
477
478         money::check_payment->has_a( xact => 'money::billable_transaction' );
479         money::check_payment->has_a( accepting_usr => 'actor::user' );
480
481         money::credit_card_payment->has_a( xact => 'money::billable_transaction' );
482         money::credit_card_payment->has_a( accepting_usr => 'actor::user' );
483
484         money::forgive_payment->has_a( xact => 'money::billable_transaction' );
485         money::forgive_payment->has_a( accepting_usr => 'actor::user' );
486
487         money::work_payment->has_a( xact => 'money::billable_transaction' );
488         money::work_payment->has_a( accepting_usr => 'actor::user' );
489
490         money::credit_payment->has_a( xact => 'money::billable_transaction' );
491         money::credit_payment->has_a( accepting_usr => 'actor::user' );
492
493         permission::grp_tree->has_a( parent => 'permission::grp_tree' );
494
495         permission::grp_perm_map->has_a( grp => 'permission::grp_tree' );
496         permission::grp_perm_map->has_a(  perm => 'permission::perm_list' );
497         permission::grp_perm_map->has_a(  depth => 'actor::org_unit_type' );
498         
499         permission::usr_perm_map->has_a( usr => 'actor::user' );
500         permission::usr_perm_map->has_a(  perm => 'permission::perm_list' );
501         permission::usr_perm_map->has_a(  depth => 'actor::org_unit_type' );
502         
503         permission::usr_grp_map->has_a(  usr => 'actor::user' );
504         permission::usr_grp_map->has_a(  grp => 'permission::grp_tree' );
505
506         action::hold_notification->has_a(  hold => 'action::hold_request' );
507         
508         action::hold_copy_map->has_a(  hold => 'action::hold_request' );
509         action::hold_copy_map->has_a(  target_copy => 'asset::copy' );
510
511         action::hold_request->has_a(  current_copy => 'asset::copy' );
512         action::hold_request->has_a(  requestor => 'actor::user' );
513         action::hold_request->has_a(  usr => 'actor::user' );
514         action::hold_request->has_a(  pickup_lib => 'actor::org_unit' );
515
516         action::hold_request->has_many(  notifications => 'action::hold_notification' );
517         action::hold_request->has_many(  copy_maps => 'action::hold_copy_map' );
518
519         asset::copy->has_many(  hold_maps => 'action::hold_copy_map' );
520
521 1;