Sort varcher type as number in Laravel (column-sortable)

Aug 29, 2020 PHP MySQL Laravel

Thing you want to do

I want to sort the column defined by varchar in MySQL as a numeric type.

What do you mean?

If you order by varchar column, it will be sorted in dictionary order. For example, suppose you have data 100, 2, 99, 11, and you want to arrange them in ascending order. Normally, it is expected to be lined up with 2, 11, 99, 100, but in the case of varchar, the order is 100, 11, 2, 99 .. So I want to arrange them in the order I expect.

Possible solutions

  1. Change to column number type
  2. Zero padding (example: 100, 002, 099, 011)
  3. Cast the target data to a numeric type

This paper deals with 3.

Assumptions

environment

column-sortable

It is recommended that column-sortable is already introduced as an extension for handling Laravel sort. However, the idea and basic methods are for Laravel, so you can use them without this extension.

How to install and use column-sortable is not covered here.

Table you want to sort

Column name: amount Type: varcher

Implementation

add sort method to model

public function amountNumericSortable($query, $direction)
{
    return $query->orderByRaw('CAST(amount AS DECIMAL)'. $direction);
}

The method name must have the suffix Sortable and be named in camel case. You can follow this rule, but don’t duplicate existing columns.

If you don’t use column-sortable, attach orderByRaw('CAST(amount AS DECIMAL)'. $direction) to existing query. Specify ASC/DESC in place of $direction

Add as a sortable column to the model

public $sortable = [
    'amount_numeric',
];

If the $sortable variable already exists, add amount_numeric to the array. Naming is done by removing the Sortable of the method added earlier as a snake case.

Not needed for those who are not using column-sortable.

Change sort settings on View side

Change before

@sortablelink('amount', __('Amount'))

After change

@sortablelink('amount_numeric', __('Amount'))

bonus

null support

If you sort a table that allows nulls in ascending order, nulls come first, so you want to avoid this. In that case, use orderByRaw('amount IS NULL ASC'). The combination is as follows.

public function amountNumericSortable($query, $direction)
{
    return $query
        ->orderByRaw('amount IS NULL ASC')
        ->orderByRaw('CAST(amount AS DECIMAL)'. $direction);
}

0 support

A mystery requirement that you want to be behind when the value is 0. Use orderByRaw('amount = 0 ASC').

Why does it work?

amount IS NULL becomes 1 when amount is null and becomes 0 when character is present (treated as true = 1, false = 0). So if you put them in ascending order, the characters line is smaller than the null lines, so the characters line comes first. amount = 0 has the same principle.