1 package SL::DB::Helper::Sorted;
6 our @ISA = qw(Exporter);
7 our @EXPORT = qw(get_all_sorted make_sort_string);
11 sub make_sort_string {
12 my ($class, %params) = @_;
14 my $sort_spec = _get_sort_spec($class);
16 my $sort_dir = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
17 my $sort_dir_str = $sort_dir ? 'ASC' : 'DESC';
19 my $sort_by = $params{sort_by} || { };
20 $sort_by = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
23 if ($sort_spec->{nulls}) {
24 $nulls_str = ref($sort_spec->{nulls}) ? ($sort_spec->{nulls}->{$sort_by} || $sort_spec->{nulls}->{default}) : $sort_spec->{nulls};
25 $nulls_str = " NULLS ${nulls_str}" if $nulls_str;
28 my $sort_by_str = $sort_spec->{columns}->{$sort_by};
29 $sort_by_str = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
32 push @$sort_by_str, @{ $sort_spec->{tiebreaker} };
34 $sort_by_str = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
36 return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
40 my ($class, %params) = @_;
41 my $sort_str = $class->make_sort_string(sort_by => delete($params{sort_by}), sort_dir => delete($params{sort_dir}));
43 return $class->get_all(sort_by => $sort_str, %params);
48 return $sort_spec{$class} ||= _make_sort_spec($class);
54 my %sort_spec = defined &{ "${class}::_sort_spec" } ? $class->_sort_spec : ();
56 my $meta = $class->object_class->meta;
57 my $table = $meta->table;
59 if (!$sort_spec{default}) {
60 my @primary_keys = $meta->primary_key;
61 $sort_spec{default} = [ "" . $primary_keys[0], 1 ];
64 $sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
66 if ($sort_spec{columns}->{SIMPLE}) {
67 if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
68 map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
69 delete $sort_spec{columns}->{SIMPLE};
71 map { $sort_spec{columns}->{$_} = "${table}.${_}" } @{ delete($sort_spec{columns}->{SIMPLE}) };
75 $sort_spec{tiebreaker} ||= [ map { "${table}.${_}" } $meta->primary_key ];
88 SL::DB::Helper::Sorted - Mixin for a manager class that handles
89 sorting of database records
93 package SL::DB::Manager::Message;
95 use SL::DB::Helper::Sorted;
98 return ( columns => { recipient_id => [ 'CASE
99 WHEN recipient_group_id IS NULL THEN lower(recipient.name)
100 ELSE lower(recipient_group.name)
102 sender_id => [ 'lower(sender.name)', ],
103 created_at => [ 'created_at', ],
104 subject => [ 'lower(subject)', ],
105 status => [ 'NOT COALESCE(unread, FALSE)', 'created_at' ],
107 default => [ 'status', 1 ],
108 nulls => { default => 'LAST',
114 package SL::Controller::Message;
117 my $messages = SL::DB::Manager::Message->get_all_sorted(sort_by => $::form->{sort_by},
118 sort_dir => $::form->{sort_dir});
121 =head1 CLASS FUNCTIONS
125 =item C<make_sort_string %params>
127 Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
128 SQL string suitable for sorting. The package this package is mixed
129 into has to provide a method L</_sort_spec> that returns a hash whose
130 structure is explained below. That hash is authoritative in which
131 columns may be sorted, which column to sort by by default and how to
132 handle C<NULL> values.
134 Returns the SQL string in scalar context. In array context it returns
135 three values: the actual column it sorts by (suitable for another call
136 to L</make_sort_string>), the actual sort direction (either 0 or 1)
139 =item C<get_all_sorted %params>
141 Returns C<< $class->get_all >> with C<sort_by> set to the value
142 returned by c<< $class->make_sort_string(%params) >>.
146 =head1 CLASS FUNCTIONS PROVIDED BY THE MIXING PACKAGE
152 This method is actually not part of this package but can be provided
153 by the package this helper is mixed into. If it isn't then all columns
154 of the corresponding table (as returned by the model's meta data) will
155 be eligible for sorting.
157 Returns a hash with the following keys:
163 A two-element array containing the name and direction by which to sort
164 in default cases. Example:
166 default => [ 'name', 1 ],
168 Defaults to the table's primary key column (the first column if the
169 primary key is composited).
173 A hash reference. Its keys are column names, and its values are SQL
174 strings by which to sort. Example:
176 columns => { SIMPLE => [ 'transaction_description', 'orddate' ],
177 the_date => 'CASE WHEN oe.quotation THEN oe.quodate ELSE oe.orddate END',
178 customer_name => 'lower(customer.name)',
181 If sorting is requested for a column that is not a key in this hash
182 then the default column name will be used.
184 The value can be either a scalar or an array reference. If it's the
185 latter then both the sort direction as well as the null handling will
186 be appended to each of its members.
188 The special key C<SIMPLE> can be a scalar or an array reference. If it
189 is an array reference then it contains column names that are mapped
190 1:1 onto the table's columns. If it is the scalar 'ALL' then all
191 columns in that model's meta data are mapped 1:1 unless the C<columns>
192 hash already contains a key for that column.
194 If C<columns> is missing then all columns of the model will be
195 eligible for sorting. The list of columns is looked up in the model's
200 Either a scalar or a hash reference determining where C<NULL> values
201 will be sorted. If undefined then the decision is left to the
204 If it is a scalar then the same value will be used for all
205 classes. The value is either C<FIRST> or C<LAST>.
207 If it is a hash reference then its keys are column names (not SQL
208 names). The values are either C<FIRST> or C<LAST>. If a column name is
209 not found in this hash then the special key C<default> will be looked
210 up and used if it is found.
214 nulls => { transaction_description => 'FIRST',
215 customer_name => 'FIRST',
221 Optional tiebreaker sorting that gets appended to any user requested sorting.
222 Needed to make sorting by non unique columns deterministic.
224 If present must be an arrayref of column sort specs (see C<column>).
226 Defaults to primary keys.
238 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>