line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DataTables;
|
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
147894
|
use 5.008008;
|
|
2
|
|
|
|
|
17
|
|
4
|
2
|
|
|
2
|
|
11
|
use strict;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
56
|
|
5
|
2
|
|
|
2
|
|
11
|
use warnings;
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
53
|
|
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
23
|
use Carp;
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
154
|
|
8
|
2
|
|
|
2
|
|
1905
|
use CGI::Simple;
|
|
2
|
|
|
|
|
32648
|
|
|
2
|
|
|
|
|
15
|
|
9
|
2
|
|
|
2
|
|
3591
|
use DBI;
|
|
2
|
|
|
|
|
37163
|
|
|
2
|
|
|
|
|
195
|
|
10
|
2
|
|
|
2
|
|
1754
|
use JSON::XS;
|
|
2
|
|
|
|
|
16163
|
|
|
2
|
|
|
|
|
144
|
|
11
|
2
|
|
|
2
|
|
1339
|
use SQL::Abstract::Limit;
|
|
2
|
|
|
|
|
63035
|
|
|
2
|
|
|
|
|
150
|
|
12
|
2
|
|
|
2
|
|
1357
|
use JQuery::DataTables::Request;
|
|
2
|
|
|
|
|
12893
|
|
|
2
|
|
|
|
|
14
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.08';
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# Preloaded methods go here.
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub new {
|
19
|
1
|
|
|
1
|
1
|
15673
|
my $invocant = shift;
|
20
|
1
|
|
33
|
|
|
9
|
my $class = ref($invocant) || $invocant;
|
21
|
1
|
|
|
|
|
10
|
my $self = {
|
22
|
|
|
|
|
|
|
tables => undef,
|
23
|
|
|
|
|
|
|
columns => undef,
|
24
|
|
|
|
|
|
|
dbh => undef,
|
25
|
|
|
|
|
|
|
query => CGI::Simple->new,
|
26
|
|
|
|
|
|
|
data_output_format => 'column-id', # or: key-value
|
27
|
|
|
|
|
|
|
patterns => {},
|
28
|
|
|
|
|
|
|
join_clause => '',
|
29
|
|
|
|
|
|
|
where_clause => '',
|
30
|
|
|
|
|
|
|
@_, # Override previous attributes
|
31
|
|
|
|
|
|
|
};
|
32
|
1
|
|
|
|
|
145
|
return bless $self, $class;
|
33
|
|
|
|
|
|
|
}
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub tables {
|
39
|
6
|
|
|
6
|
1
|
20
|
my $self = shift;
|
40
|
|
|
|
|
|
|
|
41
|
6
|
100
|
|
|
|
14
|
if (@_) {
|
42
|
1
|
|
|
|
|
11
|
my $a_ref = shift;
|
43
|
1
|
50
|
|
|
|
6
|
croak "tables must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
|
44
|
1
|
|
|
|
|
19
|
$self->{tables} = $a_ref;
|
45
|
|
|
|
|
|
|
}
|
46
|
6
|
|
|
|
|
31
|
return $self->{tables};
|
47
|
|
|
|
|
|
|
}
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub columns {
|
53
|
10
|
|
|
10
|
1
|
19
|
my $self = shift;
|
54
|
|
|
|
|
|
|
|
55
|
10
|
100
|
|
|
|
17
|
if (@_) {
|
56
|
1
|
|
|
|
|
1
|
my $ref = shift;
|
57
|
1
|
50
|
33
|
|
|
5
|
croak "columns_a must be an array or hash ref" unless UNIVERSAL::isa($ref,'ARRAY') or UNIVERSAL::isa($ref,'HASH');
|
58
|
1
|
|
|
|
|
3
|
$self->{columns} = $ref;
|
59
|
|
|
|
|
|
|
}
|
60
|
10
|
|
|
|
|
27
|
return $self->{columns};
|
61
|
|
|
|
|
|
|
}
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub dbh {
|
67
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
68
|
|
|
|
|
|
|
|
69
|
0
|
0
|
|
|
|
0
|
if (@_) {
|
70
|
0
|
|
|
|
|
0
|
my $ref = shift;
|
71
|
0
|
0
|
|
|
|
0
|
croak "dbh must be a DBI object" unless UNIVERSAL::can($ref,'prepare');
|
72
|
0
|
|
|
|
|
0
|
$self->{dbh} = $ref;
|
73
|
|
|
|
|
|
|
}
|
74
|
0
|
|
|
|
|
0
|
return $self->{dbh};
|
75
|
|
|
|
|
|
|
}
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub data_output_format {
|
81
|
3
|
|
|
3
|
1
|
5
|
my $self = shift;
|
82
|
3
|
|
|
|
|
3
|
my $new_val = shift;
|
83
|
|
|
|
|
|
|
|
84
|
3
|
50
|
|
|
|
7
|
if ($new_val) {
|
85
|
0
|
0
|
0
|
|
|
0
|
if( $new_val ne 'column-id' and $new_val ne 'key-value' ) {
|
86
|
0
|
|
|
|
|
0
|
croak "data_output_format must be one of: column-id, key-value; you sent: $new_val";
|
87
|
|
|
|
|
|
|
}else{
|
88
|
0
|
|
|
|
|
0
|
$self->{data_output_format} = $new_val;
|
89
|
|
|
|
|
|
|
}
|
90
|
|
|
|
|
|
|
}
|
91
|
3
|
|
|
|
|
8
|
return $self->{data_output_format};
|
92
|
|
|
|
|
|
|
}
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub patterns {
|
98
|
1
|
|
|
1
|
1
|
2
|
my $self = shift;
|
99
|
|
|
|
|
|
|
|
100
|
1
|
50
|
|
|
|
4
|
if (@_) {
|
101
|
0
|
|
|
|
|
0
|
my $h_ref = shift;
|
102
|
0
|
0
|
|
|
|
0
|
croak "patterns must be a hash ref" unless UNIVERSAL::isa($h_ref,'HASH');
|
103
|
0
|
|
|
|
|
0
|
$self->{patterns} = $h_ref;
|
104
|
|
|
|
|
|
|
}
|
105
|
1
|
|
|
|
|
2
|
return $self->{patterns};
|
106
|
|
|
|
|
|
|
}
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub join_clause {
|
112
|
1
|
|
|
1
|
1
|
2
|
my $self = shift;
|
113
|
|
|
|
|
|
|
|
114
|
1
|
50
|
|
|
|
3
|
if (@_) {
|
115
|
0
|
|
|
|
|
0
|
$self->{join_clause} = shift;
|
116
|
|
|
|
|
|
|
}
|
117
|
1
|
|
|
|
|
5
|
return $self->{join_clause};
|
118
|
|
|
|
|
|
|
}
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub where_clause {
|
124
|
1
|
|
|
1
|
1
|
2
|
my $self = shift;
|
125
|
|
|
|
|
|
|
|
126
|
1
|
50
|
|
|
|
3
|
if (@_) {
|
127
|
0
|
|
|
|
|
0
|
$self->{where_clause} = shift;
|
128
|
|
|
|
|
|
|
}
|
129
|
1
|
|
|
|
|
3
|
return $self->{where_clause};
|
130
|
|
|
|
|
|
|
}
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub _columns_arr {
|
136
|
3
|
|
|
3
|
|
5
|
my $self = shift;
|
137
|
3
|
|
|
|
|
9
|
my $aColumns;
|
138
|
|
|
|
|
|
|
my $regular_columns;
|
139
|
3
|
|
|
|
|
0
|
my $as_hash;
|
140
|
3
|
|
|
|
|
0
|
my $tables_hash;
|
141
|
|
|
|
|
|
|
|
142
|
3
|
50
|
|
|
|
6
|
if(UNIVERSAL::isa($self->columns,'HASH')) {
|
|
|
50
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
my $columns = $self->columns;
|
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
0
|
for my $key (sort {$a <=> $b} keys %{$columns}) { #here we sort by key so columns show in the same order as they on the page
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
146
|
0
|
|
|
|
|
0
|
my $as_exists = undef;
|
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
#if two keys, we assume user passed in AS as a key. We could check for as below in loop, but that limits users from having a column named AS
|
149
|
0
|
0
|
0
|
|
|
0
|
if(scalar(keys %{$columns->{$key}} == 2) and exists $columns->{$key}->{'AS'}) {
|
|
0
|
|
|
|
|
0
|
|
150
|
0
|
|
|
|
|
0
|
$as_exists = $columns->{$key}->{'AS'};
|
151
|
0
|
|
|
|
|
0
|
delete $columns->{$key}->{'AS'};
|
152
|
|
|
|
|
|
|
}
|
153
|
|
|
|
|
|
|
|
154
|
0
|
|
|
|
|
0
|
while(my ($column,$table) = each %{$columns->{$key}}) {
|
|
0
|
|
|
|
|
0
|
|
155
|
0
|
|
|
|
|
0
|
my $column_name = "$table.$column";
|
156
|
0
|
|
|
|
|
0
|
push @{$aColumns}, $column_name;
|
|
0
|
|
|
|
|
0
|
|
157
|
|
|
|
|
|
|
|
158
|
0
|
0
|
|
|
|
0
|
if($as_exists) {
|
159
|
0
|
0
|
|
|
|
0
|
$as_hash->{$column_name} = $as_exists if $as_exists; #add 'AS' value for this column if one exists
|
160
|
0
|
|
|
|
|
0
|
$column = $as_exists; # we want to change the column name to what it will be selected as out of database so we can do correct pattern matching
|
161
|
|
|
|
|
|
|
}
|
162
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
0
|
$tables_hash->{$table} = 1;
|
164
|
0
|
|
|
|
|
0
|
push @{$regular_columns}, $column;
|
|
0
|
|
|
|
|
0
|
|
165
|
|
|
|
|
|
|
}
|
166
|
|
|
|
|
|
|
}
|
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
0
|
my @tables = keys %$tables_hash;
|
169
|
0
|
|
|
|
|
0
|
$self->tables(\@tables);
|
170
|
|
|
|
|
|
|
}
|
171
|
|
|
|
|
|
|
elsif(UNIVERSAL::isa($self->columns,'ARRAY')) {
|
172
|
3
|
|
|
|
|
6
|
$aColumns = $self->columns;
|
173
|
3
|
|
|
|
|
5
|
$regular_columns = $aColumns;
|
174
|
|
|
|
|
|
|
}
|
175
|
|
|
|
|
|
|
else {
|
176
|
0
|
|
|
|
|
0
|
croak "columns must be a hash or an array ref";
|
177
|
|
|
|
|
|
|
}
|
178
|
|
|
|
|
|
|
|
179
|
3
|
|
|
|
|
7
|
return ($aColumns,$regular_columns,$as_hash);
|
180
|
|
|
|
|
|
|
}
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub print_json {
|
186
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
187
|
0
|
|
|
|
|
0
|
my $json = $self->json;
|
188
|
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
0
|
print "Content-type: application/json\n\n";
|
190
|
0
|
|
|
|
|
0
|
print $json;
|
191
|
|
|
|
|
|
|
}
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub table_data {
|
197
|
1
|
|
|
1
|
0
|
6
|
my $self = shift;
|
198
|
|
|
|
|
|
|
|
199
|
1
|
|
|
|
|
4
|
my %all_query_parameters = $self->_get_query_parameters();
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# may croak if client_params isn't recognized as containing DataTables parameters
|
202
|
1
|
|
|
|
|
15
|
my $dt_req = $self->_create_datatables_request( \%all_query_parameters );
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# DB HANDLE
|
205
|
1
|
|
|
|
|
394
|
my $dbh = $self->{dbh};
|
206
|
1
|
50
|
|
|
|
6
|
croak "Database handle not defined" unless defined $dbh;
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
#columns to use
|
209
|
1
|
|
|
|
|
5
|
my ($aColumns,$regular_columns,undef) = $self->_columns_arr;
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# check table name(s)
|
212
|
1
|
50
|
|
|
|
3
|
croak "Tables must be provided for the FROM clause" unless $self->tables;
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
#filtering
|
215
|
1
|
|
|
|
|
4
|
my $where_href = $self->_generate_where_clause($dt_req);
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
#ordering
|
218
|
1
|
|
|
|
|
5
|
my @order = $self->_generate_order_clause($dt_req);
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
#paging
|
221
|
1
|
|
50
|
|
|
4
|
my $limit = $dt_req->length || 10;
|
222
|
1
|
|
50
|
|
|
23
|
my $offset = $dt_req->start || 0;
|
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
#join
|
225
|
1
|
50
|
|
|
|
15
|
if($self->join_clause ne '') {
|
226
|
0
|
|
|
|
|
0
|
$where_href = $self->_add_where_clause($where_href, $self->join_clause);
|
227
|
|
|
|
|
|
|
}
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
#SQL queries
|
230
|
1
|
|
|
|
|
16
|
my $sql = SQL::Abstract::Limit->new( limit_dialect => $dbh );
|
231
|
|
|
|
|
|
|
|
232
|
1
|
|
|
|
|
321
|
my ($sQuery, @bind) = $sql->select($self->tables, $aColumns, $where_href, \@order, $limit, $offset );
|
233
|
|
|
|
|
|
|
#die("SQL: " . $sQuery);
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
#get columns out of db with query we created
|
236
|
1
|
|
|
|
|
2006
|
my $result_sth = $dbh->prepare($sQuery);
|
237
|
1
|
50
|
|
|
|
458
|
$result_sth->execute(@bind) or croak "error in mysql query: $!\n$sQuery";
|
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Data set length after filtering
|
240
|
1
|
|
|
|
|
7
|
my ($sQuery_cnt_filtered, @bind_cnt_filtered) = $sql->select($self->tables, 'COUNT(*)', $where_href );
|
241
|
|
|
|
|
|
|
|
242
|
1
|
|
|
|
|
825
|
my $sth_cnt_filtered = $dbh->prepare($sQuery_cnt_filtered);
|
243
|
1
|
50
|
|
|
|
100
|
$sth_cnt_filtered->execute(@bind_cnt_filtered) or croak "mysql error: $!";
|
244
|
|
|
|
|
|
|
|
245
|
1
|
|
|
|
|
14
|
my @aResultFilterTotal = $sth_cnt_filtered->fetchrow_array();
|
246
|
1
|
|
|
|
|
3
|
my $iFilteredTotal = $aResultFilterTotal[0];
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
|
249
|
1
|
|
|
|
|
2
|
my $num_tables = scalar(@{$self->tables});
|
|
1
|
|
|
|
|
3
|
|
250
|
|
|
|
|
|
|
|
251
|
1
|
|
|
|
|
14
|
my ($sQuery_cnt_total, @bind_cnt_total) = $sql->select($self->tables, 'COUNT(*)');
|
252
|
1
|
|
|
|
|
787
|
my $sth_cnt_total = $dbh->prepare($sQuery_cnt_total);
|
253
|
1
|
50
|
|
|
|
85
|
$sth_cnt_total->execute() or croak "error in query: $!\n$sQuery";
|
254
|
|
|
|
|
|
|
|
255
|
1
|
|
|
|
|
12
|
my @aResultTotal = $sth_cnt_total->fetchrow_array;
|
256
|
1
|
|
|
|
|
3
|
my $iTotal = $aResultTotal[0];
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# output hash
|
259
|
1
|
|
|
|
|
2
|
my %output = ();
|
260
|
1
|
|
|
|
|
4
|
my $sEcho = $dt_req->draw;
|
261
|
1
|
|
|
|
|
16
|
my $version = $dt_req->version( \%all_query_parameters );
|
262
|
|
|
|
|
|
|
|
263
|
1
|
|
|
|
|
15
|
my $data_key_name = 'aaData'; # defaults to v1.9
|
264
|
|
|
|
|
|
|
|
265
|
1
|
50
|
|
|
|
3
|
if( $version eq '1.10' ) {
|
266
|
|
|
|
|
|
|
# new interface
|
267
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
0
|
$data_key_name = 'data';
|
269
|
0
|
|
|
|
|
0
|
%output = (
|
270
|
|
|
|
|
|
|
"draw" => int($sEcho),
|
271
|
|
|
|
|
|
|
"recordsTotal" => int($iTotal),
|
272
|
|
|
|
|
|
|
"recordsFiltered" => int($iFilteredTotal),
|
273
|
|
|
|
|
|
|
$data_key_name => [],
|
274
|
|
|
|
|
|
|
);
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
}else{
|
277
|
|
|
|
|
|
|
# old interface
|
278
|
|
|
|
|
|
|
|
279
|
1
|
|
|
|
|
3
|
$data_key_name = 'aaData';
|
280
|
1
|
|
|
|
|
7
|
%output = (
|
281
|
|
|
|
|
|
|
"sEcho" => int($sEcho),
|
282
|
|
|
|
|
|
|
"iTotalRecords" => int($iTotal),
|
283
|
|
|
|
|
|
|
"iTotalDisplayRecords" => int($iFilteredTotal),
|
284
|
|
|
|
|
|
|
$data_key_name => [],
|
285
|
|
|
|
|
|
|
);
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
}
|
288
|
|
|
|
|
|
|
|
289
|
1
|
|
|
|
|
2
|
my $count = 0;
|
290
|
1
|
|
|
|
|
4
|
my $patterns = $self->patterns;
|
291
|
|
|
|
|
|
|
|
292
|
1
|
|
|
|
|
15
|
while(my @aRow = $result_sth->fetchrow_array) {
|
293
|
3
|
|
|
|
|
9
|
my @row = ();
|
294
|
3
|
|
|
|
|
9
|
for (my $i = 0; $i < @$aColumns; $i++) {
|
295
|
15
|
|
|
|
|
23
|
my $pat_name = $regular_columns->[$i]; #get out the name that would be used in the pattern
|
296
|
15
|
|
|
|
|
18
|
my $val = $aRow[$i];
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# apply user specified pattern for this column if one exists
|
299
|
15
|
50
|
|
|
|
26
|
if(exists $patterns->{$pat_name}) {
|
300
|
0
|
|
|
|
|
0
|
my $pattern = $patterns->{$pat_name};
|
301
|
0
|
|
|
|
|
0
|
$pattern =~ s/\[\%\s$pat_name\s\%\]/$val/g;
|
302
|
0
|
|
|
|
|
0
|
$val = $pattern;
|
303
|
|
|
|
|
|
|
}
|
304
|
|
|
|
|
|
|
|
305
|
15
|
|
|
|
|
41
|
push @row, $val;
|
306
|
|
|
|
|
|
|
}
|
307
|
|
|
|
|
|
|
|
308
|
3
|
50
|
|
|
|
14
|
if( $self->data_output_format eq 'column-id' ) {
|
309
|
3
|
|
|
|
|
8
|
@{$output{$data_key_name}}[$count] = [@row];
|
|
3
|
|
|
|
|
7
|
|
310
|
|
|
|
|
|
|
}else{
|
311
|
0
|
|
|
|
|
0
|
my %row = map { $aColumns->[$_] => $row[$_] } 0 .. $#row;
|
|
0
|
|
|
|
|
0
|
|
312
|
0
|
|
|
|
|
0
|
push @{$output{$data_key_name}}, \%row;
|
|
0
|
|
|
|
|
0
|
|
313
|
|
|
|
|
|
|
}
|
314
|
|
|
|
|
|
|
|
315
|
3
|
|
|
|
|
38
|
$count++;
|
316
|
|
|
|
|
|
|
}
|
317
|
|
|
|
|
|
|
|
318
|
1
|
|
|
|
|
42
|
return \%output;
|
319
|
|
|
|
|
|
|
} # /table_data
|
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub json {
|
325
|
0
|
|
|
0
|
1
|
0
|
my $self = shift;
|
326
|
|
|
|
|
|
|
|
327
|
0
|
|
|
|
|
0
|
my $output_href = $self->table_data;
|
328
|
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
0
|
return encode_json $output_href;
|
330
|
|
|
|
|
|
|
} # /json
|
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub _create_datatables_request {
|
336
|
1
|
|
|
1
|
|
3
|
my $self = shift;
|
337
|
1
|
|
|
|
|
2
|
my $query_params = shift;
|
338
|
1
|
|
|
|
|
12
|
return JQuery::DataTables::Request->new( client_params => $query_params );
|
339
|
|
|
|
|
|
|
}
|
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub _generate_where_clause {
|
345
|
1
|
|
|
1
|
|
3
|
my $self = shift;
|
346
|
1
|
|
|
|
|
2
|
my $dt_req = shift;
|
347
|
|
|
|
|
|
|
|
348
|
1
|
|
|
|
|
3
|
my ($aColumns,undef,undef) = $self->_columns_arr;
|
349
|
|
|
|
|
|
|
|
350
|
1
|
|
|
|
|
2
|
my $where_href = {};
|
351
|
|
|
|
|
|
|
|
352
|
1
|
50
|
33
|
|
|
13
|
if( $dt_req->search && defined $dt_req->search->{value} ) {
|
353
|
0
|
|
|
|
|
0
|
my $search_string = $dt_req->search->{value}; # the global search value
|
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# XXX: maybe use $dt_req->columns()?
|
356
|
0
|
|
|
|
|
0
|
for( my $i = 0; $i < @$aColumns; $i++ ) {
|
357
|
|
|
|
|
|
|
# Iterate over each column and check if it is searchable.
|
358
|
|
|
|
|
|
|
# If so, add a constraint to the where clause restricting the given
|
359
|
|
|
|
|
|
|
# column. In the query, the column is identified by it's index, we
|
360
|
|
|
|
|
|
|
# need to translates the index to the column name.
|
361
|
0
|
0
|
0
|
|
|
0
|
if ( defined $dt_req->column($i) and $dt_req->column($i)->{searchable} ) {
|
362
|
|
|
|
|
|
|
# XXX: maybe use $dt_req->column($i)->{name}?
|
363
|
0
|
|
|
|
|
0
|
my $column = $aColumns->[$i];
|
364
|
0
|
|
|
|
|
0
|
push @{$where_href->{'-or'}}, { $column => {-like => '%'.$search_string.'%' } };
|
|
0
|
|
|
|
|
0
|
|
365
|
|
|
|
|
|
|
}
|
366
|
|
|
|
|
|
|
}
|
367
|
|
|
|
|
|
|
}
|
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# XXX: merge with previous loop
|
370
|
|
|
|
|
|
|
#individual column filtering
|
371
|
1
|
|
|
|
|
34
|
for (my $i = 0; $i < @$aColumns; $i++) {
|
372
|
5
|
50
|
66
|
|
|
164
|
if( defined $dt_req->column($i) and $dt_req->column($i)->{searchable}
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
373
|
|
|
|
|
|
|
and ($dt_req->column($i)->{search}->{value} and $dt_req->column($i)->{search}->{value} ne '') ) {
|
374
|
0
|
|
|
|
|
0
|
my $individual_column_search = $dt_req->column($i)->{search}->{value};
|
375
|
0
|
|
|
|
|
0
|
$where_href->{$aColumns->[$i]} = {-like => '%'.$individual_column_search.'%'};
|
376
|
|
|
|
|
|
|
}
|
377
|
|
|
|
|
|
|
}
|
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# add user where if given
|
380
|
1
|
50
|
|
|
|
16
|
if( $self->where_clause ) {
|
381
|
0
|
|
|
|
|
0
|
$where_href = $self->_add_where_clause($where_href, $self->where_clause);
|
382
|
|
|
|
|
|
|
}
|
383
|
|
|
|
|
|
|
|
384
|
1
|
|
|
|
|
3
|
return $where_href;
|
385
|
|
|
|
|
|
|
} # /_generate_where_clause
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
#
|
391
|
|
|
|
|
|
|
# convert
|
392
|
|
|
|
|
|
|
# \%where = {key => value, -or => \@ }
|
393
|
|
|
|
|
|
|
# to
|
394
|
|
|
|
|
|
|
# \%where = {-and => [{key => value, -or => \@ }, $plus]}
|
395
|
|
|
|
|
|
|
#
|
396
|
|
|
|
|
|
|
# $plus can be a hashref for SQL::Abstract.
|
397
|
|
|
|
|
|
|
# $plus can also be scalarref (deprecated).
|
398
|
|
|
|
|
|
|
#
|
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub _add_where_clause {
|
401
|
0
|
|
|
0
|
|
0
|
my $self = shift;
|
402
|
0
|
0
|
|
|
|
0
|
my $existing_clauses_href = shift or croak('Missing where clause');
|
403
|
0
|
|
|
|
|
0
|
my $new_clause = shift;
|
404
|
|
|
|
|
|
|
|
405
|
0
|
0
|
|
|
|
0
|
return $existing_clauses_href unless $new_clause;
|
406
|
|
|
|
|
|
|
|
407
|
0
|
0
|
|
|
|
0
|
if( UNIVERSAL::isa($new_clause, 'HASH') ) {
|
408
|
|
|
|
|
|
|
return {
|
409
|
0
|
|
|
|
|
0
|
-and => [
|
410
|
|
|
|
|
|
|
$existing_clauses_href,
|
411
|
|
|
|
|
|
|
$new_clause,
|
412
|
|
|
|
|
|
|
],
|
413
|
|
|
|
|
|
|
};
|
414
|
|
|
|
|
|
|
}
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# Add arbitrary WHERE clause. This might be dangerous.
|
417
|
|
|
|
|
|
|
return {
|
418
|
0
|
|
|
|
|
0
|
-and => [
|
419
|
|
|
|
|
|
|
$existing_clauses_href,
|
420
|
|
|
|
|
|
|
\$new_clause
|
421
|
|
|
|
|
|
|
],
|
422
|
|
|
|
|
|
|
};
|
423
|
|
|
|
|
|
|
} # /_add_where_clause
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub _generate_order_clause {
|
429
|
1
|
|
|
1
|
|
3
|
my $self = shift;
|
430
|
1
|
|
|
|
|
2
|
my $dt_req = shift;
|
431
|
|
|
|
|
|
|
|
432
|
1
|
|
|
|
|
3
|
my ($aColumns,undef,undef) = $self->_columns_arr;
|
433
|
|
|
|
|
|
|
|
434
|
1
|
|
|
|
|
2
|
my @order = ();
|
435
|
|
|
|
|
|
|
|
436
|
1
|
|
|
|
|
2
|
foreach my $order_instruction ( @{$dt_req->orders()} ) {
|
|
1
|
|
|
|
|
6
|
|
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# build direction, must be '-asc' or '-desc' (cf. SQL::Abstract)
|
439
|
|
|
|
|
|
|
# we only get 'asc' or 'desc', so they have to be prefixed with '-'
|
440
|
0
|
|
|
|
|
0
|
my $sortable_column_nr = $order_instruction->{column};
|
441
|
0
|
|
|
|
|
0
|
my $direction = '-' . $order_instruction->{dir};
|
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# We only get the column index (starting from 0), so we have to
|
444
|
|
|
|
|
|
|
# translate the index into a column name.
|
445
|
0
|
|
|
|
|
0
|
my $column_name = $aColumns->[$sortable_column_nr];
|
446
|
0
|
|
|
|
|
0
|
push @order, { $direction => $column_name };
|
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
}
|
449
|
|
|
|
|
|
|
|
450
|
1
|
|
|
|
|
20
|
return @order;
|
451
|
|
|
|
|
|
|
} # /_generate_order_clause
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
sub _get_query_parameters {
|
457
|
1
|
|
|
1
|
|
3
|
my $self = shift;
|
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# CGI OBJECT
|
460
|
1
|
|
|
|
|
2
|
my $q = $self->{query};
|
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# TODO: available from Perl 5.20.0: get multiple key-value pairs in 1 request, e.g. my %new_hash = %hash{qw/a b/};
|
463
|
|
|
|
|
|
|
# XXX: encapsulate to make testing easier (re-use the encapsulated method in tests instead of custom code)
|
464
|
1
|
|
|
|
|
5
|
my %all_query_parameters = $q->Vars;
|
465
|
|
|
|
|
|
|
|
466
|
1
|
|
|
|
|
120
|
return %all_query_parameters;
|
467
|
|
|
|
|
|
|
} # /_get_query_parameters
|
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
1; # /DataTables
|
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
__END__
|