my $sort_dir = defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{default}->[1];
my $sort_dir_str = $sort_dir ? 'ASC' : 'DESC';
- my $sort_by = $params{sort_by};
+ my $sort_by = $params{sort_by} || { };
$sort_by = $sort_spec->{default}->[0] unless $sort_spec->{columns}->{$sort_by};
my $nulls_str = '';
my $sort_by_str = $sort_spec->{columns}->{$sort_by};
$sort_by_str = [ $sort_by_str ] unless ref($sort_by_str) eq 'ARRAY';
+
+ # generaate tiebreaker
+ push @$sort_by_str, @{ $sort_spec->{tiebreaker} };
+
$sort_by_str = join(', ', map { "${_} ${sort_dir_str}${nulls_str}" } @{ $sort_by_str });
return wantarray ? ($sort_by, $sort_dir, $sort_by_str) : $sort_by_str;
sub _make_sort_spec {
my ($class) = @_;
- my %sort_spec = $class->_sort_spec if defined &{ "${class}::_sort_spec" };
+ my %sort_spec = defined &{ "${class}::_sort_spec" } ? $class->_sort_spec : ();
my $meta = $class->object_class->meta;
+ my $table = $meta->table;
if (!$sort_spec{default}) {
my @primary_keys = $meta->primary_key;
$sort_spec{columns} ||= { SIMPLE => [ map { "$_" } $meta->columns ] };
if ($sort_spec{columns}->{SIMPLE}) {
- my $table = $meta->table;
-
if (!ref($sort_spec{columns}->{SIMPLE}) && ($sort_spec{columns}->{SIMPLE} eq 'ALL')) {
map { $sort_spec{columns}->{"$_"} ||= "${table}.${_}"} @{ $meta->columns };
delete $sort_spec{columns}->{SIMPLE};
}
}
+ $sort_spec{tiebreaker} ||= [ map { "${table}.${_}" } $meta->primary_key ];
+
return \%sort_spec;
}
Evaluates C<$params{sort_by}> and C<$params{sort_dir}> and returns an
SQL string suitable for sorting. The package this package is mixed
into has to provide a method L</_sort_spec> that returns a hash whose
-structure is explained below. That hash is authoritive in which
+structure is explained below. That hash is authoritative in which
columns may be sorted, which column to sort by by default and how to
handle C<NULL> values.
customer_name => 'lower(customer.name)',
},
-If sorting by a column is requested that is not a key in this hash
+If sorting is requested for a column that is not a key in this hash
then the default column name will be used.
The value can be either a scalar or an array reference. If it's the
will be sorted. If undefined then the decision is left to the
database.
-If it is a scalar then all the same value will be used for all
+If it is a scalar then the same value will be used for all
classes. The value is either C<FIRST> or C<LAST>.
If it is a hash reference then its keys are column names (not SQL
names). The values are either C<FIRST> or C<LAST>. If a column name is
-not found in this hash then the special keu C<default> will be looked
+not found in this hash then the special key C<default> will be looked
up and used if it is found.
Example:
default => 'LAST',
},
+=item C<tiebreaker>
+
+Optional tiebreaker sorting that gets appended to any user requested sorting.
+Needed to make sorting by non unique columns deterministic.
+
+If present must be an arrayref of column sort specs (see C<column>).
+
+Defaults to primary keys.
+
=back
=back