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';
30 $sort_by_str = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
32 return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
36 my ($class, %params) = @_;
37 my $sort_str = $class->make_sort_string(sort_by => delete($params{sort_by}), sort_dir => delete($params{sort_dir}));
39 return $class->get_all(sort_by => $sort_str, %params);
44 return $sort_spec{$class} ||= _make_sort_spec($class);
50 my %sort_spec = defined &{ "${class}::_sort_spec" } ? $class->_sort_spec : ();
52 my $meta = $class->object_class->meta;
54 if (!$sort_spec{default}) {
55 my @primary_keys = $meta->primary_key;
56 $sort_spec{default} = [ "" . $primary_keys[0], 1 ];
59 $sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
61 if ($sort_spec{columns}->{SIMPLE}) {
62 my $table = $meta->table;
64 if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
65 map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
66 delete $sort_spec{columns}->{SIMPLE};
68 map { $sort_spec{columns}->{$_} = "${table}.${_}" } @{ delete($sort_spec{columns}->{SIMPLE}) };
83 SL::DB::Helper::Sorted - Mixin for a manager class that handles
84 sorting of database records
88 package SL::DB::Manager::Message;
90 use SL::DB::Helper::Sorted;
93 return ( columns => { recipient_id => [ 'CASE
94 WHEN recipient_group_id IS NULL THEN lower(recipient.name)
95 ELSE lower(recipient_group.name)
97 sender_id => [ 'lower(sender.name)', ],
98 created_at => [ 'created_at', ],
99 subject => [ 'lower(subject)', ],
100 status => [ 'NOT COALESCE(unread, FALSE)', 'created_at' ],
102 default => [ 'status', 1 ],
103 nulls => { default => 'LAST',
109 package SL::Controller::Message;
112 my $messages = SL::DB::Manager::Message->get_all_sorted(sort_by => $::form->{sort_by},
113 sort_dir => $::form->{sort_dir});
116 =head1 CLASS FUNCTIONS
120 =item C<make_sort_string %params>
122 Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
123 SQL string suitable for sorting. The package this package is mixed
124 into has to provide a method L</_sort_spec> that returns a hash whose
125 structure is explained below. That hash is authoritive in which
126 columns may be sorted, which column to sort by by default and how to
127 handle C<NULL> values.
129 Returns the SQL string in scalar context. In array context it returns
130 three values: the actual column it sorts by (suitable for another call
131 to L</make_sort_string>), the actual sort direction (either 0 or 1)
134 =item C<get_all_sorted %params>
136 Returns C<< $class->get_all >> with C<sort_by> set to the value
137 returned by c<< $class->make_sort_string(%params) >>.
141 =head1 CLASS FUNCTIONS PROVIDED BY THE MIXING PACKAGE
147 This method is actually not part of this package but can be provided
148 by the package this helper is mixed into. If it isn't then all columns
149 of the corresponding table (as returned by the model's meta data) will
150 be eligible for sorting.
152 Returns a hash with the following keys:
158 A two-element array containing the name and direction by which to sort
159 in default cases. Example:
161 default => [ 'name', 1 ],
163 Defaults to the table's primary key column (the first column if the
164 primary key is composited).
168 A hash reference. Its keys are column names, and its values are SQL
169 strings by which to sort. Example:
171 columns => { SIMPLE => [ 'transaction_description', 'orddate' ],
172 the_date => 'CASE WHEN oe.quotation THEN oe.quodate ELSE oe.orddate END',
173 customer_name => 'lower(customer.name)',
176 If sorting by a column is requested that is not a key in this hash
177 then the default column name will be used.
179 The value can be either a scalar or an array reference. If it's the
180 latter then both the sort direction as well as the null handling will
181 be appended to each of its members.
183 The special key C<SIMPLE> can be a scalar or an array reference. If it
184 is an array reference then it contains column names that are mapped
185 1:1 onto the table's columns. If it is the scalar 'ALL' then all
186 columns in that model's meta data are mapped 1:1 unless the C<columns>
187 hash already contains a key for that column.
189 If C<columns> is missing then all columns of the model will be
190 eligible for sorting. The list of columns is looked up in the model's
195 Either a scalar or a hash reference determining where C<NULL> values
196 will be sorted. If undefined then the decision is left to the
199 If it is a scalar then all the same value will be used for all
200 classes. The value is either C<FIRST> or C<LAST>.
202 If it is a hash reference then its keys are column names (not SQL
203 names). The values are either C<FIRST> or C<LAST>. If a column name is
204 not found in this hash then the special keu C<default> will be looked
205 up and used if it is found.
209 nulls => { transaction_description => 'FIRST',
210 customer_name => 'FIRST',
224 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>