File Coverage

blib/lib/MyLibrary/Term.pm
Criterion Covered Total %
statement 27 488 5.5
branch 8 236 3.3
condition 0 93 0.0
subroutine 8 18 44.4
pod 15 15 100.0
total 58 850 6.8


line stmt bran cond sub pod time code
1             package MyLibrary::Term;
2              
3 4     4   30946 use MyLibrary::DB;
  4         13  
  4         114  
4 4     4   23 use Carp qw(croak);
  4         9  
  4         198  
5 4     4   20 use strict;
  4         7  
  4         38245  
6              
7             =head1 NAME
8              
9             MyLibrary::Term
10              
11             =head1 SYNOPSIS
12            
13             # require the necessary module
14             use MyLibrary::Term;
15              
16             # create a new Term object
17             my $term = MyLibrary::Term->new();
18              
19             # set the attributes of a Term object
20             $term->term_name('Term One');
21             $term->term_note('Sample note for a term');
22             $term->facet_id(9999);
23              
24             # delete the term note
25             $term->delete_term_note();
26              
27             # commit Term data to database
28             $term->commit();
29              
30             # get a list of all term objects
31             my @terms = MyLibrary::Term->get_terms();
32              
33             # get list of term objects based on criteria
34             my @terms = MyLibrary::Term->get_terms(field => 'name', value => 'Chemistry');
35              
36             # get a list of all related resource ids
37             my @related_resources = $term->related_resources();
38              
39             # delete relations between terms and resources
40             my @related_resources = $term->related_resources(del => [@resource_ids]);
41              
42             # set new relations between a term and resources
43             my @related_resources = $term->related_resources(new => [@resource_ids]);
44              
45             # sort a list of returned resource ids according to name
46             my @related_resources = $term->related_resources(sort => 'name');
47              
48             # get a list of all related suggested resource ids
49             my @suggested_resources = $term->suggested_resources();
50              
51             # retrieve a sorted list of related suggested resource ids
52             my @suggested_resources = $term->suggested_resources(sort => 'name');
53              
54             # return a list of related librarian objects
55             my @librarians = $term->librarians();
56              
57             # return a list of related librarian ids
58             my @librarians = $term->librarians(output => 'id');
59              
60             # add a list of librarians to this term, dismissing database relational integrity
61             $term->librarians(new => [@librarian_ids], strict => 'off');
62              
63             # sort a list of supplied term ids according to specific criteria
64             my @sorted_terms = MyLibrary::Term->sort(term_ids => [@term_ids], type => 'name');
65              
66             # return overlapping resources with this term (~30 term ids max)
67             my @overlap_resources = $term->overlap(term_ids => [@term_ids]);
68              
69             # return a distinct set of related terms within a resource group
70             my @distinct_terms = MyLibrary::Term->distinct_terms(resource_ids => [@resource_ids]);
71              
72             # delete a list of librarians from this term
73             $term->librarians(del => [@librarians_ids]);
74              
75             # delete a Term object from the database
76             $term->delete();
77              
78             =head1 DESCRIPTION
79              
80             Use this module to get and set the terms used to classify things in a MyLibrary database. You can also retrieve a list of all term objects in the database, as well as get, set or delete relations between term objects and resource objects.
81              
82             =head1 METHODS
83              
84             =head2 new()
85              
86             This method creates a new term object. Called with no input, this constructor will return a new, empty term object:
87              
88             # create empty term object
89             my $term = MyLibrary::Term->new();
90              
91             The constructor can also be called using a known term id or term name:
92              
93             # create a term object using a known term id
94             my $term = MyLibrary::Term->new(id => $id);
95              
96             # create a term object using a known term name
97             my $term = MyLibrary::Term->new(name => $name);
98              
99             =head2 term_id()
100              
101             This method can be used to retrieve the term id for the current term object. It cannot be used to set the id
102             for the term.
103              
104             # get term id
105             my $term_id = $term->term_id();
106              
107             =head2 term_name()
108              
109             This is an attribute method which allows you to either get or set the name attribute of a term. The names for terms
110             will be created by the institutional team tasked with the responsibility of designating the more specific categories under
111             which resources will be categorized. A term is related to one and only one parent facet. To retrieve the name attribute:
112              
113             # get the term name
114             my $term_name = $term->term_name();
115              
116             =head2 term_note()
117              
118             This method allows one to either retrieve or set the term descriptive note.
119              
120             # get the term note
121             my $term_note = $term->term_note();
122              
123             # set the term note
124             $term->term_note('This is a term note.');
125              
126             =head2 delete_term_note()
127              
128             Use this method to delete the term note
129              
130             # delete term note
131             $term->delete_term_note();
132              
133             =head2 facet_id()
134              
135             This method may be used to either set or retrieve the value of the related facet id for this term. When the term
136             is commited to the database, if the facet id is changed, the relation between this term and the facets will also
137             be changed.
138              
139             # get the related facet id
140             my $related_facet_id = $term->facet_id();
141              
142             # set the related facet id
143             $term->facet_id(25);
144              
145             =head2 commit()
146              
147             This object method is used to commit the current term object in memory to the database. If the term already exists in the database,
148             it will be updated. New terms will be inserted into the database.
149              
150             # commit the term
151             $term->commit();
152              
153             =head2 delete()
154              
155             This object method should be used with caution as it will delete an existing term from the database. Any associations
156             with the Resources will also be deleted with this method in order to maintain referential integrity. If an attempt is made to
157             delete a term which does not yet exist in the database, a return value of '0' will result. A successful deletion will result
158             in a return value of '1'.
159              
160             # delete the term
161             $term->delete();
162              
163             =head2 get_terms()
164              
165             This class method can be used to retrieve an array of all term objects. The array can then be used to sequentially process through all of the existing terms. This method can also be used to retrieve a list of objects based on object attributes such as name or description. This can be accomplished by supplying the field and value parameters to the method. Examples are demonstrated below.
166              
167             # get all the terms
168             my @terms = MyLibrary::Term->get_terms();
169              
170             # get all terms based on criteria
171             my @terms = MyLibrary::Term->get_terms(field => 'name', value => 'Biology and Life Sciences');
172              
173             =head2 related_resources()
174              
175             This object method can be used to retrieve an array (a list) of resource ids to which this term is related. This list can then be used to sequentially process through all related resources (for example in creating a list of related resources). No parameters are necessary for this method to retrieve related resources, however, new relatetions can be created by supplying a list of resource ids using the 'new' parameter. If the term is already related to a supplied resource id, that resource id will simply be discarded. Upon a term commit (e.g. $term->commit()), the new relations with resources will be created. Also, the input must be in the form of numeric digits. Care must be taken because false relationships could be created. A list of the currently related resources will always be returned (if such relations exist).
176              
177             # get all related resources
178             my @related_resources = $term->related_resources();
179              
180             # supply new related resources
181             $term->related_resources(new => [10, 12, 14]);
182             or
183             my @new_related_resource_list = $term->related_resources(new => [@new_resources]);
184              
185             The method will by default check to make sure that the new resources to which this term should be related exist in the database. This feature may be switched off by supplying the strict => 'off' parameter. Changing this parameter to 'off' will switch off the default behavior and allow bogus resource relations to be created.
186              
187             # supply new related resources with relational integrity switched off
188             $term->related_resources(new => [10, 12, 14], strict => 'off');
189              
190             Resources which do not exist in the database will simply be rejected if strict relational integrity is turned on.
191              
192             The method can also be used to delete a relationship between a term and a resource. This can be accomplished by supplying a list of resources via the 'del' parameter. The methodology is the same as the 'new' parameter with the primary difference being that referential integrity will be assumed (for example, that the resource being severed already exists in the database). This will not delete the resource itself, it will simply delete the relationship between the current term object and the list of resources supplied with the 'del' parameter.
193              
194             # sever the relationship between this term and a list of resource ids
195             $term->related_resources(del => [10, 11, 12]);
196              
197             or
198              
199             $term->related_resources(del => [@list_to_be_severed]);
200              
201             If the list includes resources to which the current term is not related, those resource ids will simply be ignored. Priority will be given to resource associations added to the object; deletions will occur during the commit() after new associations have been created.
202              
203             Finally, a returned list of related resources can be sorted.
204              
205             # sort a returned list of resource ids according to resource name
206             my @related_resources = $term->related_resources(sort => 'name');
207              
208             =head2 suggested_resources()
209              
210             This is an object method which can be used to retrieve, set or delete suggested resource relationships between terms and resources. The return set will always be an array of resource ids which can then be used to process through the resources to which the ids correspond. This method functions similarly to the related_resource() method and uses similar parameters to change method functionality. If no parameters are submitted, the method simply returns a list of resource_ids or undef if there are no suggested resources for this term. As with the related_resources() method, passing a sort parameter will sort the returned list according to the parameter value. Currently, only 'name' is acceptable as a parameter value.
211              
212             # get all suggested resources
213             my @suggested_resources = $term->suggested_resources();
214              
215             # get a sorted list of suggested resources
216             my @suggested_resources = $term->suggested_resources(sort => 'name');
217              
218             # supply new suggested resources
219             my @new_suggested_resource_list = $term->suggested_resources(new => [@new_suggested_resource_list]);
220              
221             As with related_resources(), this method will by default check to make sure that the new resources to which this term should be related exist in the database. The strict => 'off' parameter may also be supplied to the method to turn off relational integrity checks.
222              
223             # turn off relational integrity checks
224             $term->suggested_resources(new => [@new_suggested_resources], strict => 'off');
225              
226             Turning off this feature will allow for bogus relations to be created.
227              
228             The parameter to delete suggested resource relationships is del => [@set_to_delete]. The list supplied will be automatically deleted when the term is commited with commit(). This parameter does not delete the resources themselves, only their relationship as a 'suggested resource'. If the list includes resource ids to which the term is not related, they will simply be discarded and ignored.
229              
230             # remove suggested resource relationships
231             $term->suggested_resources(del => [@list_to_be_deleted]);
232              
233             Priority for processing the list will be given to resources associations added to the term, but the overall effect on the data should be transparent.
234              
235             =head2 librarians()
236              
237             This object method will return a list of related librarian objects/ids or undef if no librarians are associated with this term. The type of data returned is controlled by the 'output' parameter. If 'id' is chosen as the preferred output, a simple list of related librarian ids will be returned. If the output type of 'object' is preferred, the returned librarian object can be manipulated using the librarian object methods. This method can also be used to add or delete librarian associations with this term. The 'new' and 'del' parameters exist for this purpose (see examples below). A list of librarian ids should be provied for these parameters. Relational integrity can be abandoned by using the 'strict' parameter and giving it a value of off.
238              
239             # return a list of librarian objects
240             my @librarians = $term->librarians();
241              
242             # return a list of librarian ids
243             my @librarians = $term->librarians(output => 'id');
244              
245             # add a list of librarian associations to this term
246             $term->librarians(new => [@librarian_ids]);
247             $term->librarians(new => [@librarian_ids], strict => 'off');
248              
249             # remove a list of librarian associations from this term
250             $term->librarians(del => [@librarian_ids]);
251              
252             =head2 sort()
253              
254             This class method performs a simple sort on a supplied list of term ids according to specific criteria, which is indicated as a parameter value for the method.
255              
256             # sort term ids by term name
257             my @sorted_terms = MyLibrary::Term->sort(term_ids => [@term_ids], type => 'name');
258              
259             =head2 overlap()
260              
261             This object method returns a list of overlapping resources with a provided list of term ids. If there are no overlapping resources, the method returns null. If submitted term ids do not exist in the database, the method will ignore that input. Since some databases are limited by how many table joins they can perform in one query, limit the number of term ids to approximately 25-30 at a time. Otherwise, the method will likely fail.
262              
263             # return overlapping resources with this term
264             my @overlap_resources = $term->overlap(term_ids => [@term_ids]);
265              
266             =head2 distinct_terms()
267              
268             This class method returns a unique list of term ids (which can be sorted using the sort() method) that correspond to a specific group of resource ids.
269              
270             # return a distinct set of related terms within a resource group
271             my @distinct_terms = MyLibrary::Term->distinct_terms(resource_ids => [@resource_ids]);
272              
273             =head1 AUTHORS
274              
275             Eric Lease Morgan
276             Robert Fox
277              
278             =cut
279              
280              
281             sub new {
282              
283             # declare local variables
284 1     1 1 901 my ($class, %opts) = @_;
285 1         3 my $self = {};
286              
287             # check for an id
288 1 50       10 if ($opts{id}) {
    50          
289            
290             # get a handle
291 0         0 my $dbh = MyLibrary::DB->dbh();
292            
293             # find this record
294 0         0 my $rv = $dbh->selectrow_hashref('SELECT * FROM terms WHERE term_id = ?', undef, $opts{id});
295            
296             # check for success
297 0 0       0 if (ref($rv) eq "HASH") {
298 0         0 $self = $rv;
299 0         0 $self->{related_resources}= $dbh->selectall_arrayref('SELECT resource_id FROM terms_resources WHERE term_id =?', undef, $opts{id});
300 0         0 $self->{suggested_resources} = $dbh->selectall_arrayref('SELECT resource_id FROM suggestedResources WHERE term_id = ?', undef, $opts{id});
301             } else {
302 0         0 return;
303             }
304            
305             } elsif ($opts{name}) {
306              
307             # get a handle
308 0         0 my $dbh = MyLibrary::DB->dbh();
309              
310             # find this record
311 0         0 my $rv = $dbh->selectrow_hashref('SELECT * FROM terms WHERE term_name = ?', undef, $opts{name});
312              
313             # check for success
314 0 0       0 if (ref($rv) eq "HASH") {
315 0         0 $self = $rv;
316 0         0 $self->{related_resources}= $dbh->selectall_arrayref('SELECT resource_id FROM terms_resources WHERE term_id =?', undef, $self->{term_id});
317 0         0 $self->{suggested_resources} = $dbh->selectall_arrayref('SELECT resource_id FROM suggestedResources WHERE term_id = ?', undef, $self->{term_id});
318             } else {
319 0         0 return;
320             }
321             }
322            
323             # return the object
324 1         4 return bless $self, $class;
325            
326             }
327              
328              
329             sub term_id {
330              
331 0     0 1 0 my $self = shift;
332 0         0 return $self->{term_id};
333              
334             }
335              
336              
337             sub term_name {
338              
339             # declare local variables
340 2     2 1 583 my ($self, $term_name) = @_;
341            
342             # check for the existance of a term name
343 2 100       7 if ($term_name) { $self->{term_name} = $term_name }
  1         7  
344            
345             # return it
346 2         9 return $self->{term_name};
347            
348             }
349              
350              
351             sub term_note {
352              
353             # declare local variables
354 2     2 1 6 my ($self, $term_note) = @_;
355            
356             # check for the existance of a term note
357 2 100       9 if ($term_note) { $self->{term_note} = $term_note }
  1         2  
358            
359             # return it
360 2         34 return $self->{term_note};
361            
362             }
363              
364             sub delete_term_note {
365            
366 0     0 1 0 my $self = shift;
367 0         0 $self->{term_note} = undef;
368              
369             }
370              
371              
372             sub facet_id {
373              
374             # declare local variables
375 2     2 1 25 my ($self, $facet_id) = @_;
376            
377             # check for the existance of facet id
378 2 100       7 if ($facet_id) { $self->{facet_id} = $facet_id }
  1         3  
379            
380             # return it
381 2         6 return $self->{facet_id};
382            
383             }
384              
385              
386             sub commit {
387              
388             # get myself, :-)
389 1     1 1 3 my $self = shift;
390            
391             # get a database handle
392 1         6 my $dbh = MyLibrary::DB->dbh();
393            
394             # see if the object has an id
395 0 0         if ($self->term_id()) {
396            
397             # update the record with this id
398 0           my $return = $dbh->do('UPDATE terms SET term_name = ?, term_note = ?, facet_id = ? WHERE term_id = ?', undef, $self->term_name(), $self->term_note(), $self->facet_id(), $self->term_id());
399 0 0 0       if ($return > 1 || ! $return) {
400 0           croak "Terms update in commit() failed. $return records were updated.";
401             }
402             # update term=>resource relational integrity
403 0           my @related_resources = $self->related_resources();
404 0 0 0       if (scalar(@related_resources) > 0 && @related_resources) {
    0 0        
405 0           my $arr_ref = $dbh->selectall_arrayref('SELECT resource_id FROM terms_resources WHERE term_id =?', undef, $self->term_id());
406             # determine which resources stay put
407 0 0         if (scalar(@{$arr_ref}) > 0) {
  0            
408 0           foreach my $arr_val (@{$arr_ref}) {
  0            
409 0           my $j = scalar(@related_resources);
410 0           for (my $i = 0; $i < scalar(@related_resources); $i++) {
411 0 0         if ($arr_val->[0] == $related_resources[$i]) {
412 0           splice(@related_resources, $i, 1);
413 0           $i = $j;
414             }
415             }
416             }
417             }
418             # add the new associations
419 0           foreach my $related_resource (@related_resources) {
420 0           my $return = $dbh->do('INSERT INTO terms_resources (resource_id, term_id) VALUES (?,?)', undef, $related_resource, $self->term_id());
421 0 0 0       if ($return > 1 || ! $return) { croak "Unable to update term=>resource relational integrity. $return rows were inserted." }
  0            
422             }
423             # determine which resource associations to delete
424 0           my @del_related_resources;
425 0           my @related_resources = $self->related_resources();
426 0 0         if (scalar(@{$arr_ref}) > 0) {
  0            
427 0           foreach my $arr_val (@{$arr_ref}) {
  0            
428 0           my $found;
429 0           for (my $i = 0; $i < scalar(@related_resources); $i++) {
430 0 0         if ($arr_val->[0] == $related_resources[$i]) {
431 0           $found = 1;
432 0           last;
433             } else {
434 0           $found = 0;
435             }
436             }
437 0 0         if (!$found) {
438 0           push (@del_related_resources, $arr_val->[0]);
439             }
440             }
441             }
442             # delete removed associations
443 0           foreach my $del_rel_resource (@del_related_resources) {
444 0           my $return = $dbh->do('DELETE FROM terms_resources WHERE resource_id = ? AND term_id = ?', undef, $del_rel_resource, $self->term_id());
445 0 0 0       if ($return > 1 || ! $return) { croak "Unable to delete term=>resource association. $return rows were deleted." }
  0            
446             }
447             } elsif (scalar(@related_resources) <= 0 || !@related_resources) {
448             # remove any remaining resource associations
449 0           my $return = $dbh->do('DELETE FROM terms_resources WHERE term_id = ?', undef, $self->term_id());
450             }
451             # update suggested resource relational integrity
452 0           my @suggested_resources = $self->suggested_resources();
453 0 0 0       if (scalar(@suggested_resources) > 0 && @suggested_resources) {
    0          
454 0           my $arr_ref = $dbh->selectall_arrayref('SELECT resource_id FROM suggestedResources WHERE term_id =?', undef, $self->term_id());
455             # determine which suggested stay put
456 0 0         if (scalar(@{$arr_ref}) > 0) {
  0            
457 0           foreach my $arr_val (@{$arr_ref}) {
  0            
458 0           my $j = scalar(@suggested_resources);
459 0           for (my $i = 0; $i < scalar(@suggested_resources); $i++) {
460 0 0         if ($arr_val->[0] == $suggested_resources[$i]) {
461 0           splice(@suggested_resources, $i, 1);
462 0           $i = $j;
463             }
464             }
465             }
466             }
467             # add the new associations
468 0           foreach my $suggested_resource (@suggested_resources) {
469 0           my $return = $dbh->do('INSERT INTO suggestedResources (term_id, resource_id) VALUES (?,?)', undef, $self->term_id(), $suggested_resource);
470 0 0 0       if ($return > 1 || ! $return) { croak "Unable to update term=>suggested_resource relational integrity. $return rows were inserted." }
  0            
471             }
472             # determine which resource associations to delete
473 0           my @del_suggested_resources;
474 0           my @suggested_resources = $self->suggested_resources();
475 0 0         if (scalar(@{$arr_ref}) > 0) {
  0            
476 0           foreach my $arr_val (@{$arr_ref}) {
  0            
477 0           my $found;
478 0           for (my $i = 0; $i < scalar(@suggested_resources); $i++) {
479 0 0         if ($arr_val->[0] == $suggested_resources[$i]) {
480 0           $found = 1;
481 0           last;
482             } else {
483 0           $found = 0;
484             }
485             }
486 0 0         if (!$found) {
487 0           push (@del_suggested_resources, $arr_val->[0]);
488             }
489             }
490             }
491             # delete removed associations
492 0           foreach my $del_sug_resource (@del_suggested_resources) {
493 0           my $return = $dbh->do('DELETE FROM suggestedResources WHERE resource_id = ? AND term_id = ?', undef, $del_sug_resource, $self->term_id());
494 0 0 0       if ($return > 1 || ! $return) { croak "Unable to delete term=>suggested_resource association. $return rows were deleted." }
  0            
495             }
496             } elsif (scalar(@suggested_resources) == 0) {
497 0           my $arr_ref = $dbh->selectall_arrayref('SELECT resource_id FROM suggestedResources WHERE term_id =?', undef, $self->term_id());
498             # delete remainder of suggested resources for this term
499 0 0         if (scalar(@{$arr_ref}) > 0) {
  0            
500 0           my $return = $dbh->do('DELETE FROM suggestedResources WHERE term_id = ?', undef, $self->term_id());
501 0 0         if ($return eq undef) { croak "Unable to delete remainder of term suggested resource associations. $return rows were deleted." }
  0            
502             }
503              
504             }
505            
506             } else {
507            
508             # get a new sequence
509 0           my $id = MyLibrary::DB->nextID();
510            
511             # create a new record
512 0           my $return = $dbh->do('INSERT INTO terms (term_id, term_name, term_note, facet_id) VALUES (?, ?, ?, ?)', undef, $id, $self->term_name(), $self->term_note(), $self->facet_id());
513 0 0 0       if ($return > 1 || ! $return) {
514 0           croak 'Terms commit() failed.';
515             }
516 0           $self->{term_id} = $id;
517             # update term=>resource relational integrity
518 0           my @related_resources = $self->related_resources();
519 0 0 0       if (scalar(@related_resources) > 0 && @related_resources) {
520 0           foreach my $related_resource (@related_resources) {
521 0           my $return = $dbh->do('INSERT INTO terms_resources (resource_id, term_id) VALUES (?,?)', undef, $related_resource, $self->term_id());
522 0 0 0       if ($return > 1 || ! $return) { croak "Unable to update term=>resource relational integrity. $return rows were inserted." }
  0            
523             }
524             }
525             # update term=>suggested_resource relational integrity
526 0           my @suggested_resources = $self->suggested_resources();
527 0 0 0       if (scalar(@suggested_resources) > 0 && @suggested_resources) {
528 0           foreach my $suggested_resource (@suggested_resources) {
529 0           my $return = $dbh->do('INSERT INTO suggestedResources (term_id, resource_id) VALUES (?,?)', undef, $self->term_id(), $suggested_resource);
530 0 0 0       if ($return > 1 || ! $return) { croak "Unable to update term=>suggested_resource relational integrity. $return rows were inserted." }
  0            
531             }
532             }
533             }
534            
535             # done
536 0           return 1;
537             }
538              
539              
540             sub delete {
541              
542 0     0 1   my $self = shift;
543              
544 0 0         if ($self->{term_id}) {
545              
546 0           my $dbh = MyLibrary::DB->dbh();
547 0           my $rv = $dbh->do('DELETE FROM terms WHERE term_id = ?', undef, $self->{term_id});
548 0 0         if ($rv != 1) {croak ("Deleted $rv records. I'll bet this isn't what you wanted.");}
  0            
549             # delete resource associations
550 0           $rv = $dbh->do('SELECT * FROM terms_resources WHERE term_id = ?', undef, $self->{term_id});
551 0 0         if ($rv > 0) {
552 0           $rv = $dbh->do('DELETE FROM terms_resources WHERE term_id = ?', undef, $self->{term_id});
553 0 0 0       if ($rv < 1 || ! $rv) {croak ("Term => Resource associations could not be deleted. Referential integrity may be compromised.");}
  0            
554             }
555             # delete suggested resource associations
556 0           $rv = $dbh->do('SELECT * FROM suggestedResources WHERE term_id = ?', undef, $self->{term_id});
557 0 0         if ($rv > 0) {
558 0           $rv = $dbh->do('DELETE FROM suggestedResources WHERE term_id = ?', undef, $self->{term_id});
559 0 0 0       if ($rv < 1 || ! $rv) {croak ("Term => Suggested resource associations could not be deleted. Referential integrity may be compromised.");}
  0            
560             }
561             # delete the librarian associations
562 0           $rv = $dbh->do('SELECT * FROM terms_librarians WHERE term_id = ?', undef, $self->{term_id});
563 0 0         if ($rv > 0) {
564 0           $rv = $dbh->do('DELETE FROM terms_librarians WHERE term_id = ?', undef, $self->{term_id});
565 0 0 0       if ($rv < 1 || ! $rv) {croak ("Term => Librarian associations could not be deleted. Referential integrity may be compromised.");}
  0            
566             }
567              
568             # delete patron associations
569 0           $dbh->do('DELETE FROM patron_term WHERE term_id = ?', undef, $self->term_id());
570              
571 0           return 1;
572              
573             }
574              
575 0           return 0;
576              
577             }
578              
579              
580             sub get_terms {
581              
582 0     0 1   my $self = shift;
583 0           my %opts = @_;
584 0           my @rv = ();
585              
586 0           my ($field, $value, $sort, $limit_clause, $sort_clause, $query);
587              
588 0 0         if ($opts{sort}) {
589 0 0         if ($opts{sort} eq 'name') {
590 0           $sort_clause = 'ORDER BY term_name';
591             }
592             }
593              
594 0 0 0       if ($opts{field} && $opts{value}) {
595 0           $field = $opts{'field'};
596 0           $value = $opts{'value'};
597 0 0         if ($field eq 'name') {
    0          
598 0           $limit_clause = "WHERE term_name LIKE \'%$value%\'";
599             } elsif ($field eq 'description') {
600 0           $limit_clause = "WHERE term_note LIKE \'%$value%\'";
601             }
602             }
603              
604 0           $query = 'SELECT term_id FROM terms';
605              
606             # the order is important here
607 0 0         if ($limit_clause) {
608 0           $query .= " $limit_clause";
609             }
610 0 0         if ($sort_clause) {
611 0           $query .= " $sort_clause";
612             }
613            
614             # create and execute a query
615 0           my $dbh = MyLibrary::DB->dbh();
616 0           my $term_ids = $dbh->selectcol_arrayref("$query");
617              
618 0           foreach my $term_id (@$term_ids) {
619 0           push (@rv, MyLibrary::Term->new(id => $term_id));
620             }
621            
622 0           return @rv;
623            
624             }
625              
626             sub related_resources {
627              
628 0     0 1   my $self = shift;
629 0           my %opts = @_;
630 0           my @new_related_resources;
631 0 0         if ($opts{new}) {
632 0           @new_related_resources = @{$opts{new}};
  0            
633             }
634 0           my @del_related_resources;
635 0 0         if ($opts{del}) {
636 0           @del_related_resources = @{$opts{del}};
  0            
637             }
638 0           my $sort;
639 0 0         if ($opts{sort}) {
640 0           $sort = $opts{sort};
641             }
642 0           my @related_resources;
643             my $strict_relations;
644 0 0         if ($opts{strict}) {
645 0 0 0       if ($opts{strict} == 1) {
    0 0        
    0 0        
      0        
646 0           $strict_relations = 'on';
647             } elsif ($opts{strict} == 0) {
648 0           $strict_relations = 'off';
649             } elsif (($opts{strict} !~ /^\d$/ && ($opts{strict} == 1 || $opts{strict} == 0)) || $opts{strict} ne 'off' || $opts{strict} ne 'on') {
650 0           $strict_relations = 'on';
651             } else {
652 0           $strict_relations = $opts{strict};
653             }
654             } else {
655 0           $strict_relations = 'on';
656             }
657 0 0         if (@new_related_resources) {
658 0           RESOURCES: foreach my $new_related_resource (@new_related_resources) {
659 0 0         if ($new_related_resource !~ /^\d+$/) {
660 0           croak "Only numeric digits may be submitted as resource ids for term relations. $new_related_resource submitted.";
661             }
662 0 0         if ($strict_relations eq 'on') {
663 0           my $dbh = MyLibrary::DB->dbh();
664 0           my $resource_list = $dbh->selectcol_arrayref('SELECT resource_id FROM resources');
665 0           my $found_resource;
666 0           RESOURCE_VAL: foreach my $resource_list_val (@$resource_list) {
667 0 0         if ($resource_list_val == $new_related_resource) {
668 0           $found_resource = 1;
669 0           last RESOURCE_VAL;
670             } else {
671 0           $found_resource = 0;
672             }
673             }
674 0 0         if ($found_resource == 0) {
675 0           next RESOURCES;
676             }
677             }
678 0           my $found;
679 0 0         if ($self->{related_resources}) {
680 0           RESOURCES_PRESENT: foreach my $related_resource (@{$self->{related_resources}}) {
  0            
681 0 0         if ($new_related_resource == @$related_resource[0]) {
682 0           $found = 1;
683 0           last RESOURCES_PRESENT;
684             } else {
685 0           $found = 0;
686             }
687             }
688             } else {
689 0           $found = 0;
690             }
691 0 0         if ($found) {
692 0           next RESOURCES;
693             } else {
694 0           my @related_resource_num = ();
695 0           my $related_resource_num = \@related_resource_num;
696 0           $related_resource_num->[0] = $new_related_resource;
697 0           push(@{$self->{related_resources}}, $related_resource_num);
  0            
698             }
699             }
700             }
701 0 0         if (@del_related_resources) {
702 0           foreach my $del_related_resource (@del_related_resources) {
703 0           my $j = scalar(@{$self->{related_resources}});
  0            
704 0           for (my $i = 0; $i < scalar(@{$self->{related_resources}}); $i++) {
  0            
705 0 0         if ($self->{related_resources}->[$i]->[0] == $del_related_resource) {
706 0           splice(@{$self->{related_resources}}, $i, 1);
  0            
707 0           $i = $j;
708             }
709             }
710             }
711             }
712            
713            
714 0           foreach my $related_resource (@{$self->{related_resources}}) {
  0            
715 0           push(@related_resources, $related_resource->[0]);
716             }
717              
718 0 0         if ($sort) {
719 0 0         if ($sort eq 'name') {
720 0           my $dbh = MyLibrary::DB->dbh();
721 0           my $resource_id_string;
722 0           foreach my $resource_id (@related_resources) {
723 0           $resource_id_string .= "$resource_id, ";
724             }
725 0           chop($resource_id_string);
726 0           chop($resource_id_string);
727 0           my $resource_ids = $dbh->selectcol_arrayref("SELECT resource_id FROM resources WHERE resource_id IN ($resource_id_string) ORDER BY resource_name");
728 0           @related_resources = ();
729 0           foreach my $resource_id (@$resource_ids) {
730 0           push (@related_resources, $resource_id);
731             }
732             }
733             }
734              
735 0           return @related_resources;
736             }
737              
738             sub suggested_resources {
739              
740 0     0 1   my $self = shift;
741 0           my %opts = @_;
742 0           my @new_suggested_resources;
743 0 0         if ($opts{new}) {
744 0           @new_suggested_resources = @{$opts{new}};
  0            
745             }
746 0           my @del_suggested_resources;
747 0 0         if ($opts{del}) {
748 0           @del_suggested_resources = @{$opts{del}};
  0            
749             }
750 0           my $sort;
751 0 0         if ($opts{sort}) {
752 0           $sort = $opts{sort};
753             }
754 0           my @suggested_resources;
755             my $strict_relations;
756 0 0         if ($opts{strict}) {
757 0 0 0       if ($opts{strict} == 1) {
    0 0        
    0 0        
      0        
758 0           $strict_relations = 'on';
759             } elsif ($opts{strict} == 0) {
760 0           $strict_relations = 'off';
761             } elsif (($opts{strict} =~ /^\d$/ && ($opts{strict} != 1 || $opts{strict} != 0)) || $opts{strict} ne 'off' || $opts{strict} ne 'on') {
762 0           $strict_relations = 'on';
763             } else {
764 0           $strict_relations = $opts{strict};
765             }
766             } else {
767 0           $strict_relations = 'on';
768             }
769             # debug
770 0 0         if (@new_suggested_resources) {
771 0           SUGGESTED: foreach my $new_suggested_resource (@new_suggested_resources) {
772 0 0         if ($new_suggested_resource !~ /^\d+$/) {
773 0           croak "Only numeric digits may be submitted as resource ids for term relations. $new_suggested_resource submitted.";
774             }
775 0 0         if ($strict_relations eq 'on') {
776 0           my $dbh = MyLibrary::DB->dbh();
777 0           my $resource_list = $dbh->selectcol_arrayref('SELECT resource_id FROM resources');
778 0           my $found_resource;
779 0           RESOURCE_VAL: foreach my $resource_list_val (@$resource_list) {
780 0 0         if ($resource_list_val == $new_suggested_resource) {
781 0           $found_resource = 1;
782 0           last RESOURCE_VAL;
783             } else {
784 0           $found_resource = 0;
785             }
786             }
787 0 0         if ($found_resource == 0) {
788 0           next SUGGESTED;
789             }
790             }
791 0           my $found;
792 0 0         if ($self->{suggested_resources}) {
793 0           SUGGESTED_PRESENT: foreach my $suggested_resource (@{$self->{suggested_resources}}) {
  0            
794 0 0         if ($new_suggested_resource == @$suggested_resource[0]) {
795 0           $found = 1;
796 0           last SUGGESTED_PRESENT;
797             } else {
798 0           $found = 0;
799             }
800             }
801             } else {
802 0           $found = 0;
803             }
804 0 0         if ($found) {
805 0           next SUGGESTED;
806             } else {
807 0           my @suggested_resource_num = ();
808 0           my $suggested_resource_num = \@suggested_resource_num;
809 0           $suggested_resource_num->[0] = $new_suggested_resource;
810 0           push(@{$self->{suggested_resources}}, $suggested_resource_num);
  0            
811             }
812             }
813             }
814 0 0         if (@del_suggested_resources) {
815 0           foreach my $del_suggested_resource (@del_suggested_resources) {
816 0           my $j = scalar(@{$self->{suggested_resources}});
  0            
817 0           for (my $i = 0; $i < scalar(@{$self->{suggested_resources}}); $i++) {
  0            
818 0 0         if ($self->{suggested_resources}->[$i]->[0] == $del_suggested_resource) {
819 0           splice(@{$self->{suggested_resources}}, $i, 1);
  0            
820 0           $i = $j;
821             }
822             }
823             }
824             }
825            
826 0           foreach my $suggested_resource (@{$self->{suggested_resources}}) {
  0            
827 0           push(@suggested_resources, $suggested_resource->[0]);
828             }
829              
830 0 0         if ($sort) {
831 0 0         if ($sort eq 'name') {
832 0           my $dbh = MyLibrary::DB->dbh();
833 0           my $resource_id_string;
834 0           foreach my $resource_id (@suggested_resources) {
835 0           $resource_id_string .= "$resource_id, ";
836             }
837 0           chop($resource_id_string);
838 0           chop($resource_id_string);
839 0           my $resource_ids = $dbh->selectcol_arrayref("SELECT resource_id FROM resources WHERE resource_id IN ($resource_id_string) ORDER BY resource_name");
840 0           @suggested_resources = ();
841 0           foreach my $resource_id (@$resource_ids) {
842 0           push (@suggested_resources, $resource_id);
843             }
844             }
845             }
846              
847 0           return @suggested_resources;
848             }
849              
850             sub librarians {
851              
852 0     0 1   my $self = shift;
853 0           my %opts = @_;
854 0           my @new_librarians = ();
855              
856 0           my $output;
857 0 0         if ($opts{'output'}) {
858 0           $output = $opts{'output'};
859             } else {
860 0           $output = 'object';
861             }
862              
863 0 0         if ($opts{'new'}) {
864 0           @new_librarians = @{$opts{new}};
  0            
865             }
866 0           my @del_librarians = ();
867 0 0         if ($opts{'del'}) {
868 0           @del_librarians = @{$opts{del}};
  0            
869             }
870              
871 0           my $strict_relations;
872 0 0         if ($opts{'strict'}) {
873 0 0 0       if ($opts{strict} == 1) {
    0 0        
    0 0        
      0        
874 0           $strict_relations = 'on';
875             } elsif ($opts{strict} == 0) {
876 0           $strict_relations = 'off';
877             } elsif (($opts{strict} =~ /^\d$/ && ($opts{strict} != 1 || $opts{strict} != 0)) || $opts{strict} ne 'off' || $opts{strict} ne 'on') {
878 0           $strict_relations = 'on';
879             } else {
880 0           $strict_relations = $opts{strict};
881             }
882             } else {
883 0           $strict_relations = 'on';
884             }
885            
886 0           my $dbh = MyLibrary::DB->dbh();
887              
888 0           my $librarians = $dbh->selectcol_arrayref('SELECT librarian_id FROM terms_librarians WHERE term_id = ?', undef, $self->term_id());
889 0 0         if (@new_librarians) {
890 0           NEW_LIBRARIAN: foreach my $new_librarian (@new_librarians) {
891 0 0         if ($new_librarian !~ /^\d+$/) {
892 0           croak "Only numeric digits may be submitted as librarian ids for term relations. $new_librarian submitted.";
893             }
894 0 0         if ($strict_relations eq 'on') {
895 0           my $librarian_list = $dbh->selectcol_arrayref('SELECT librarian_id FROM librarians');
896 0           my $found_librarian;
897 0           LIBRARIAN: foreach my $librarian_list_val (@$librarian_list) {
898 0 0         if ($librarian_list_val == $new_librarian) {
899 0           $found_librarian = 1;
900 0           last LIBRARIAN;
901             } else {
902 0           $found_librarian = 0;
903             }
904             }
905 0 0         if ($found_librarian == 0) {
906 0           next NEW_LIBRARIAN;
907             }
908             }
909 0           my $found;
910 0 0         if ($librarians) {
911 0           LIBRARIAN_PRESENT: foreach my $librarian_present (@{$librarians}) {
  0            
912 0 0         if ($new_librarian == $librarian_present) {
913 0           $found = 1;
914 0           last LIBRARIAN_PRESENT;
915             } else {
916 0           $found = 0;
917             }
918             }
919             } else {
920 0           $found = 0;
921             }
922 0 0         if ($found) {
923             # librarian association already exists
924 0           next NEW_LIBRARIAN;
925             } else {
926             # add new librarian association to database
927 0           my $rv = $dbh->do('INSERT INTO terms_librarians (term_id, librarian_id) VALUES (?,?)', undef, $self->term_id(), $new_librarian);
928 0 0 0       if ($rv > 1 || ! $rv) {
929 0           croak("Librarian could not be added to term. $rv values inserted");
930             }
931             }
932             }
933             }
934 0 0         if (@del_librarians) {
935 0           foreach my $del_librarian_id (@del_librarians) {
936 0           my $j = scalar(@{$librarians});
  0            
937 0           for (my $i = 0; $i < scalar(@{$librarians}); $i++) {
  0            
938 0 0         if ($librarians->[$i] == $del_librarian_id) {
939             # librarian found, delete association
940 0           my $rv = $dbh->do('DELETE FROM terms_librarians WHERE term_id = ? AND librarian_id = ?', undef, $self->term_id(), $del_librarian_id);
941 0 0 0       if ($rv > 1 || ! $rv) {
942 0           croak("Could not delete librarian association from term. $rv database rows deleted.");
943             }
944 0           $i = $j;
945             }
946             }
947             }
948             }
949            
950             # get final list of librarians after additions and deletions
951 0           my @librarian_objects = ();
952 0           $librarians = $dbh->selectcol_arrayref('SELECT librarian_id FROM terms_librarians WHERE term_id = ?', undef, $self->term_id());
953 0 0         if ($output eq 'object') {
954 0           require MyLibrary::Librarian;
955             }
956              
957 0           foreach my $librarian_id (@{$librarians}) {
  0            
958 0 0         if ($output eq 'object') {
    0          
959 0           my $librarian = MyLibrary::Librarian->new(id => $librarian_id);
960 0           push(@librarian_objects, $librarian);
961             } elsif ($output eq 'id') {
962 0           push(@librarian_objects, $librarian_id);
963             }
964             }
965              
966 0 0         if (scalar(@librarian_objects) >= 1) {
967 0           return @librarian_objects;
968             } else {
969 0           return;
970             }
971              
972             }
973              
974             sub sort {
975              
976 0     0 1   my $class = shift;
977 0           my $dbh = MyLibrary::DB->dbh();
978 0           my %opts = @_;
979 0           my $sort_option = $opts{'type'};
980 0 0         unless ($sort_option) {
981 0           croak ("Missing parameter: type");
982             }
983 0           my @term_ids = @{$opts{'term_ids'}};
  0            
984 0           my $return_term_ids;
985              
986             my $term_id_string;
987 0           foreach my $term_id (@term_ids) {
988 0           $term_id_string .= "$term_id, ";
989             }
990 0           chop($term_id_string);
991 0           chop($term_id_string);
992              
993 0 0         if ($sort_option eq 'name') {
994 0           $return_term_ids = $dbh->selectcol_arrayref("SELECT term_id FROM terms WHERE term_id IN ($term_id_string) ORDER BY term_name");
995             }
996              
997 0           return @{$return_term_ids};
  0            
998              
999             }
1000              
1001             sub overlap {
1002              
1003             # THIS QUERY IS LIMITED BY HOW MANY TERMS ARE JOINED; FOR EXAMPLE, MYSQL CAN ONLY HANDLE 31 TABLE JOINS MAX
1004            
1005 0     0 1   my $term = shift;
1006 0           my %opts = @_;
1007 0           my @overlap_ids = @{$opts{'term_ids'}};
  0            
1008              
1009 0 0         unless (scalar(@overlap_ids) >= 1) {
1010 0           return;
1011             }
1012              
1013 0           my $current_term_id = $term->term_id();
1014              
1015 0           my $statement_prefix = 'SELECT r.resource_id FROM resources r';
1016 0           my $where_statement = 'WHERE';
1017 0           my $n = 1;
1018              
1019 0           my $sql_statement;
1020 0           foreach my $overlap_id (@overlap_ids) {
1021              
1022 0           my $current_number = $n + 1;
1023 0           my $last_number = $n - 1;
1024              
1025 0 0         unless (MyLibrary::Term->new(id => $overlap_id)) {
1026              
1027 0           next;
1028              
1029             }
1030              
1031 0 0         if ($n == 1) {
1032              
1033 0           $statement_prefix .= ", terms_resources tr1, terms_resources tr${current_number}";
1034              
1035             } else {
1036              
1037 0           $statement_prefix .= ", terms_resources tr${current_number}";
1038              
1039             }
1040              
1041 0 0         if ($n == 1) {
1042              
1043 0           $where_statement .= " tr1.resource_id = tr${current_number}.resource_id";
1044 0           $where_statement .= " AND tr1.term_id = $current_term_id";
1045 0           $where_statement .= " AND tr${current_number}.term_id = $overlap_id";
1046              
1047             } else {
1048              
1049 0           $where_statement .= " AND tr${last_number}.resource_id = tr${current_number}.resource_id";
1050 0           $where_statement .= " AND tr${current_number}.term_id = $overlap_id";
1051              
1052             }
1053              
1054 0           $where_statement .= ' AND r.resource_id = tr1.resource_id';
1055 0           $sql_statement = $statement_prefix . ' ' . $where_statement;
1056            
1057 0           $n++;
1058             }
1059              
1060             # execute query
1061 0           my $dbh = MyLibrary::DB->dbh();
1062 0           my $resource_ids = $dbh->selectcol_arrayref("$sql_statement");
1063              
1064 0           return @{$resource_ids};
  0            
1065              
1066             }
1067              
1068             sub distinct_terms {
1069              
1070 0     0 1   my $class = shift;
1071 0           my %opts = @_;
1072              
1073 0 0         unless ($opts{'resource_ids'}) {
1074 0           return;
1075             }
1076              
1077 0           my @resource_ids = @{$opts{'resource_ids'}};
  0            
1078              
1079 0 0         unless (scalar(@resource_ids) >= 1) {
1080 0           return;
1081             }
1082              
1083 0           my $in_list;
1084              
1085 0           foreach my $resource_id (@resource_ids) {
1086 0           $in_list .= "$resource_id, ";
1087             }
1088 0           chop($in_list);
1089 0           chop($in_list);
1090              
1091 0           my $sql = "SELECT DISTINCT term_id FROM terms_resources WHERE resource_id IN ($in_list)";
1092              
1093             # execute query
1094 0           my $dbh = MyLibrary::DB->dbh();
1095 0           my $term_ids = $dbh->selectcol_arrayref("$sql");
1096              
1097 0           return @{$term_ids};
  0            
1098              
1099             }
1100              
1101             # return true
1102             1;