diff --git a/Netdisco/lib/App/Netdisco/DB/ResultSet.pm b/Netdisco/lib/App/Netdisco/DB/ResultSet.pm index 42a9d439..44d89c67 100644 --- a/Netdisco/lib/App/Netdisco/DB/ResultSet.pm +++ b/Netdisco/lib/App/Netdisco/DB/ResultSet.pm @@ -8,6 +8,8 @@ use base 'DBIx::Class::ResultSet'; __PACKAGE__->load_components( qw{Helper::ResultSet::SetOperations Helper::ResultSet::Shortcut}); +=head1 ADDITIONAL METHODS + =head2 get_distinct_col( $column ) Returns an asciibetical sorted list of the distinct values in the given column @@ -17,16 +19,175 @@ drop-down list of possible options. =cut sub get_distinct_col { - my ($rs, $col) = @_; - return $rs unless $col; + my ( $rs, $col ) = @_; + return $rs unless $col; - return $rs->search({}, - { - columns => [$col], - order_by => $col, - distinct => 1 + return $rs->search( + {}, + { columns => [$col], + order_by => $col, + distinct => 1 + } + )->get_column($col)->all; +} + +=head2 get_datatables_data( $params ) + +Returns a ResultSet for DataTables Server-side processing which populates +the displayed table. Evaluates the supplied query parameters for filtering, +paging, and ordering information. Note: query paramters are expected to be +passed as a reference to an expanded hash of hashes. + +Filtering if present, will generate simple LIKE matching conditions for each +searchable column (searchability indicated by query parameters) after each +column is casted to text. Conditions are combined as disjunction (OR). +Note: this does not match the built-in DataTables filtering which does it +word by word on any field. + +=cut + +sub get_datatables_data { + my $rs = shift; + my $params = shift; + my $attrs = shift; + + die "condition parameter to search_by_field must be hashref\n" + if ref {} ne ref $params + or 0 == scalar keys %$params; + + # -- Paging + $rs = $rs->_with_datatables_paging($params); + + # -- Ordering + $rs = $rs->_with_datatables_order_clause($params); + + # -- Filtering + $rs = $rs->_with_datatables_where_clause($params); + + return $rs; +} + +=head2 get_datatables_filtered_count( $params ) + +Returns the total records, after filtering (i.e. the total number of +records after filtering has been applied - not just the number of records +being returned for this page of data) for a datatables ResultSet and +query parameters. Note: query paramters are expected to be passed as a +reference to an expanded hash of hashes. + +=cut + +sub get_datatables_filtered_count { + my $rs = shift; + my $params = shift; + + return $rs->_with_datatables_where_clause($params)->count; + +} + +sub _with_datatables_order_clause { + my $rs = shift; + my $params = shift; + my $attrs = shift; + + my @order = (); + + if ( defined $params->{'order'}{0} ) { + for ( my $i = 0; $i < keys $params->{'order'}; $i++ ) { + + # build direction, must be '-asc' or '-desc' (cf. SQL::Abstract) + # we only get 'asc' or 'desc', so they have to be prefixed with '-' + my $direction = '-' . $params->{'order'}{$i}{'dir'}; + + # We only get the column index (starting from 0), so we have to + # translate the index into a column name. + my $column_name = _datatables_index_to_column( $params, + $params->{'order'}{$i}{'column'} ); + + # Prefix with table alias if no prefix + my $csa = $rs->current_source_alias; + $column_name =~ s/^(\w+)$/$csa\.$1/x; + push @order, { $direction => $column_name }; + } } - )->get_column($col)->all; + + $rs = $rs->order_by( \@order ); + return $rs; +} + +# NOTE this does not match the built-in DataTables filtering which does it +# word by word on any field. +# +# General filtering using LIKE, this will not be efficient as is will not +# be able to use indexes. + +sub _with_datatables_where_clause { + my $rs = shift; + my $params = shift; + my $attrs = shift; + + my %where = (); + + if ( defined $params->{'search'}{'value'} + && $params->{'search'}{'value'} ) + { + my $search_string = $params->{'search'}{'value'}; + for ( my $i = 0; $i < keys $params->{'columns'}; $i++ ) { + + # Iterate over each column and check if it is searchable. + # If so, add a constraint to the where clause restricting the given + # column. In the query, the column is identified by it's index, we + # need to translate the index to the column name. + if ( $params->{'columns'}{$i}{'searchable'} + and $params->{'columns'}{$i}{'searchable'} eq 'true' ) + { + my $column = _datatables_index_to_column( $params, $i ); + + # Cast everything to text for LIKE search + $column = $column . '::text'; + push @{ $where{'-or'} }, + { $column => { -like => '%' . $search_string . '%' } }; + } + } + } + + $rs = $rs->search( \%where, $attrs ); + return $rs; +} + +sub _with_datatables_paging { + my $rs = shift; + my $params = shift; + my $attrs = shift; + + my $limit = $params->{'length'}; + + my $offset = 0; + if ( defined $params->{'start'} && $params->{'start'} ) { + $offset = $params->{'start'}; + } + $attrs->{'offset'} = $offset; + + $rs = $rs->search( {}, $attrs ); + $rs = $rs->limit($limit); + + return $rs; +} + +# Use the DataTables columns.data definition to derive the column +# name from the index. + +sub _datatables_index_to_column { + my $params = shift; + my $i = shift; + + my $field; + + if ( !defined($i) ) { + $i = 0; + } + $field = $params->{'columns'}{$i}{'data'}; + return $field; } 1;