File Coverage

blib/lib/HTML/DBForm/Search/DropDown.pm
Criterion Covered Total %
statement 12 92 13.0
branch 0 34 0.0
condition 0 2 0.0
subroutine 4 13 30.7
pod 2 10 20.0
total 18 151 11.9


line stmt bran cond sub pod time code
1             package HTML::DBForm::Search::DropDown;
2              
3 1     1   1030 use strict;
  1         3  
  1         45  
4 1     1   6 use warnings;
  1         3  
  1         51  
5 1     1   5 no warnings 'uninitialized';
  1         2  
  1         1403  
6              
7             our $VERSION = '1.05';
8              
9             =head1 NAME
10              
11             HTML::DBForm::Search::DropDown - Creates a web interface for searching database tables
12              
13             =head1 SYNOPSIS
14              
15             $search = HTML::DBForm::Search->new('dropdown', { column => 'name' });
16            
17             $editor->run(search => $search);
18              
19              
20             =head1 INTRODUCTION
21              
22             HTML::DBForm::Search::DropDown provides a web interface to search for rows
23             in a database to be updated by HTML::DBForm.
24              
25             =cut
26              
27             =head1 METHODS
28              
29             =over 4
30              
31             =cut
32              
33              
34             =head2 new
35              
36             Constructor inherited from HTML::DBForm::Search
37              
38             takes a scalar indicating the type of search module
39             to create (in this case 'dropdown'), and a list of
40             hash refs designating which columns to display as HTML
41             select form elements, and in which order.
42              
43             Each hash should have one of the following keys:
44             'column', 'columns', or 'sql'. 'column' should be the db
45             column to search, 'columns' should be two db columns, the
46             first of which will be the column to search, and the second of
47             which will be the values to display as option labels. 'sql'
48             can be used to populate the select options with an arbitrary SQL
49             statement. If one column is returned from the SQL statement, then
50             it will be used as choice values and lables. If two columns are
51             returned, then the first will be the specified column value, while
52             the second will be used as option labels.
53              
54              
55             B
56              
57             $search = HTML::DBForm::Search->new('dropdown',
58             { column => 'category' },
59             { columns => ['id', ' CONCAT(fname, ' ', lname) '] }
60             );
61              
62            
63              
64             This would create a two step search, the first screen would be a
65             selection of existing categories, and the next screen would be a
66             selection of names within the chosen categories. When picking
67             columns to display in the search, be aware that the final choice
68             should result in the primary key being chosen.
69              
70            
71             B
72              
73             $search = HTML::DBForm::Search->new('dropdown',
74             { sql => ['id','SELECT id, label FROM table ORDER BY label'] }
75             );
76              
77             This would create a simple one step search.
78              
79             You can use as many hashrefs as needed, each one will generate
80             a new search step, (e.g three hash references will create a three
81             step search). Just keep in mind that the last column chosen must be
82             the column given to DBForm->new() as a primary key.
83              
84              
85              
86             =cut
87              
88              
89             # implementation of this method is required
90             # constructor inherited from Class::Factory
91             # via HTML::DBForm::Search
92              
93             sub init {
94              
95 1     1 0 25 my $self = shift;
96 1         10 $self->{params} = \@_;
97            
98 1         5 return $self;
99             }
100              
101              
102              
103              
104             # implementation of this method is required
105             # main subroutine called by HTML::DBForm
106              
107             sub run {
108              
109 0     0 0   my ($self, $editor) = @_;
110              
111             my $tmpl_ref = $self->{'tmpl_file'}
112 0 0         ? do { open(FH, "< $self->{'tmpl_file'}"); local $/; }
  0            
  0            
  0            
113             : &TEMPLATE;
114              
115              
116 0           $self->{template} = HTML::Template->new(
117             scalarref => \$tmpl_ref,
118             die_on_bad_params => 0,
119             loop_context_vars => 1,
120             );
121              
122              
123 0           $self->{editor} = $editor;
124            
125             # find out what step we are on
126 0   0       $self->{step} = $self->{editor}->{query}->param('step') || 0;
127            
128 0           $self->{template}->param(STEP => $self->{step} + 1);
129            
130 0           $self->get_choices;
131              
132 0 0         return ($self->{editor}->{error}) ?
133             $self->{editor}->{template}->output :
134             $self->{template}->output ;
135            
136             }
137              
138              
139              
140             =head2 set_stylesheet
141              
142             Sets an optional css file
143              
144             Takes a scalar holding the path to a stylesheet.
145              
146              
147             B
148              
149             $search->set_stylesheet('/styles/site_styles.css');
150              
151             =cut
152              
153             sub set_stylesheet {
154              
155 0     0 1   my $self = shift;
156 0           $self->{css} = shift ;
157             }
158              
159              
160              
161             =head2 set_template
162              
163             Sets an optional template file
164              
165             Takes a scalar holding the path to an HTML::Template template.
166              
167             To get a template file to start with, you can do this:
168             perl -MHTML::DBForm::Search::DropDown -e 'print
169             HTML::DBForm::Search::DropDown::TEMPLATE()' > sample.tmpl
170              
171             B
172              
173             $search->set_template('/www/templates/my.tmpl');
174              
175             =cut
176              
177             sub set_template {
178              
179 0     0 1   my $self = shift;
180 0           $self->{tmpl_file} = shift ;
181             }
182              
183              
184              
185              
186             # get choices to display
187              
188             sub get_choices {
189              
190 0     0 0   my $self = shift;
191              
192 0 0         if ($self->{params}->[$self->{step}]->{sql}){
193              
194             # use sql parameter
195 0           $self->populate_search(
196             $self->{params}->[$self->{step}]->{sql}->[1]
197             );
198              
199             } else {
200              
201             # generate our own sql
202 0           $self->populate_search(
203             $self->get_select($self->parse_params($self->{step}))
204             );
205             }
206             }
207              
208              
209              
210             # parse search parameters
211            
212             sub parse_params {
213              
214 0     0 0   my $self = shift;
215 0           my $i = shift;
216              
217 0           my $c_param = $self->{params}->[$i];
218              
219 0 0         if ($c_param->{column}){
220 0           return ($c_param->{column}, $c_param->{column});
221             }
222              
223 0 0         if ($c_param->{columns}) {
224 0           return ($c_param->{columns}->[0], $c_param->{columns}->[1]);
225             }
226              
227 0 0         if ($c_param->{sql}) {
228 0           return ($c_param->{sql}->[0], $c_param->{sql}->[1]);
229             }
230             }
231              
232              
233              
234             # build a select statement
235            
236             sub get_select {
237              
238 0     0 0   my $self = shift;
239 0           my ($col1, $col2) = @_;
240              
241              
242 0           my $sql = qq( SELECT DISTINCT $col1, $col2
243             FROM $self->{editor}->{table}
244             );
245              
246 0 0         return $sql.' ORDER BY '.$col2 unless $self->{step};
247              
248 0           my (@values, $i);
249            
250 0           for my $step(0 .. $self->{step}-1){
251              
252 0 0         $sql .= ' WHERE ' unless $i++;
253 0           $sql .= ($self->parse_params($step))[0] ." = ?";
254 0 0         $sql .= ' AND ' unless $step >= $self->{step}-1;
255              
256 0           push @values,
257             $self->{editor}->{query}->param(($self->parse_params($step))[0]);
258             }
259              
260 0           $sql .= ' ORDER BY '. $col2;
261              
262              
263             # the sql is the first element
264             # the rest of the array is
265             # filled with placeholder vals
266              
267 0           unshift @values, $sql;
268 0           return @values;
269              
270             }
271              
272              
273              
274             # populate search choices
275              
276             sub populate_search {
277              
278 0     0 0   my $self = shift;
279 0           my ($sql, @params) = @_;
280 0           my (@tmpl_loop, $db_return);
281            
282 0 0         eval{
283 0           $db_return = $self->{editor}->{dbh}->selectall_arrayref($sql, undef, @params);
284 0           1 } or $self->{editor}->_err_msg($@, $sql);
285              
286             # workaround for servers that lack
287             # subqueries ( e.g mysql < 4.1 )
288              
289 0 0         if ($self->{params}->[$self->{step}]->{sql}){
290 0           $db_return = $self->constrain_results($db_return);
291             };
292              
293              
294 0           for my $row_ref(@$db_return){
295 0           my %row = (
296             VALUE => $row_ref->[0],
297             LABEL => $row_ref->[1],
298             );
299 0           push(@tmpl_loop, \%row);
300             }
301              
302              
303             # keep track of old choices
304 0           my @prev_vals;
305 0           for my $step(0 .. $self->{step}-1){
306 0           my %row;
307 0           $row{LABEL} = ($self->parse_params($step))[0];
308 0           $row{VALUE} = $self->{editor}->{query}->param(($self->parse_params($step))[0]);
309 0           push @prev_vals, \%row;
310             }
311              
312              
313             # is this the last step?
314 0 0         my $rm = (($self->{step} +1 ) >= scalar(@{$self->{params}})) ? 'display' :'';
  0            
315              
316              
317             # is it the first?
318 0 0         my $cancel = ($self->{step} > 0) ? 1 : 0;
319              
320              
321 0           $self->{template}->param( SEARCH_LOOP => \@tmpl_loop,
322             FORM => 1,
323             SELECT_NAME => ($self->parse_params($self->{step}))[0],
324             RUN_MODE => $rm,
325             CANCEL => $cancel,
326             PREV_VALS => \@prev_vals,
327             CUSTOM_CSS => $self->{css},
328             );
329             }
330              
331              
332              
333             # discard extra sql returns
334              
335             sub constrain_results {
336            
337             # this would be much cleaner
338             # but less portable using subqueries
339             # instead of two seperate queries
340              
341 0     0 0   my ($self, $list) = @_;
342              
343             # get a list of all results
344             # based on previous selections
345 0           my $editor = $self->{editor};
346              
347 0           my $sql = qq( SELECT DISTINCT
348             $self->{params}->[$self->{step}]->{sql}->[0]
349             FROM $editor->{table}
350             );
351              
352 0           my (@values, $i, @results);
353            
354 0           for my $step(0 .. $self->{step}-2){
355              
356 0 0         $sql .= ' WHERE ' unless $i++;
357 0           $sql .= ($self->parse_params($step))[0] ." = ?";
358 0 0         $sql .= ' AND ' unless $step >= $self->{step}-2;
359              
360 0           push @values,
361             $editor->{query}->param(($self->parse_params($step))[0]);
362             }
363              
364 0           my $selections;
365            
366            
367 0 0         eval{
368 0           $selections =
369 0           $editor->{dbh}->selectcol_arrayref($sql, undef, @values); 1
370             } or $editor->_err_msg($@, $sql);
371              
372 0           for my $lr(@$list){
373 0 0         push (@results, $lr) if grep{/^$lr->[0]$/} @$selections;
  0            
374             }
375            
376 0           return \@results;
377             }
378              
379              
380              
381              
382             sub TEMPLATE {
383              
384 0     0 0   qq(
385            
386            
387            
388            
389            
390            
428            
429            
430              
431            
432              
433            
434              
435            
436              
437            

438             Edit or Create a Record:
439            

440              
441            

442            
443            
444            
445            
446            
447            

448              
449            
450              
451            
452            
453            
454            
455            
456            
457              
458            
459              
460            

461            
462            
463            
464            
465             onclick='document.location="javascript:history.go(-1)"'
466             style="width:80;">
467            
468            
469            
470             onclick='document.location="?rm=display"'
471             style="width:80;">
472            

473            
474              
475            
476            
477             );
478             }
479              
480              
481             1;