Sort Columns

Add a sort option to the top of a table column.

To allow a column to be sortable, we need to add " :sort  " after the title of the header of the column in the $td_titles array. Then we specify the column name of the databse that will be used to sort the table in the new query.
 

array_push($td_titles,"140-CLIENT:sort:NAME");

 

Advanced Options
We can add more properties like Title, Sort and Width by passing an array on each $td_titles value

 

array_push($td_titles,array(
   'name'=> 'Client',
   'title'=> 'Client Name', //hover title attribute (optional)
   'width'=> 140, //(optional)
   'sort'=>'NAME' //(optional)
));


or inline version:

 

array_push($td_titles,array('name'=> 'CLIENT','title'=> 'CLIENT NAME','width'=> 140,'sort'=>'NAME'));

 

In this example 140 is the width of the column, CLIENT is the title to show and sort:NAME indicates that it will sort the table by the databse column name NAME.

Then we need to add the conditional before our query that will modify the order of the results,  $orderby and $asde are predefined variables, in the first part we define our defaults, in this case order by REG_DATE DESC:

 

$allowedSorts = ['REG_DATE', 'NAME', 'EMAIL', 'TEL'];
$requestedSort = $ob ?? 'REG_DATE';
$orderby = in_array($requestedSort, $allowedSorts, true) ? $requestedSort : 'REG_DATE';
$asde = strtoupper($asde ?? 'DESC');
$asde = in_array($asde, ['ASC', 'DESC'], true) ? $asde : 'DESC';
 
 
And finally incorporate these variables to our query:
 
 
$query = "SELECT CID, NAME, EMAIL, TEL, REG_DATE FROM $dbtable %%WHERE%% order by $orderby $asde";
 

Query and count compatibility

The header produces native ob/asde links; it does not change a hardcoded ORDER BY. Allowlist sort identifiers and normalize the direction before SQL interpolation. For a lookup label, map the header sort key to an actual joined column or expression, rather than the numeric lookup ID. The pagination count query replaces the SELECT list while retaining ORDER BY, so a SELECT-only alias may no longer exist. Use an expression supported by both queries and test both directions with search and pagination. Option P manages row positions and is independent of header-column sorting.

Native positioned records

Use the existing pos integer column and P option for native positioning. The renderer defaults $posCol to pos, marks rows sortable and emits a SORT,table,idfield,posColumn descriptor. The native SORT handler writes submitted row indexes starting at zero; zero is a valid position. The existing sortable_functions.js also saves the displayed sortable rows on load, normalizing their positions to these indexes. Order the management query and every lookup with ORDER BY pos, primaryKey for deterministic ties, including any temporarily missing positions. Sorting/filtering can suppress positioning controls, so validate the intended unfiltered management view.

For an Add form, include P in its option string and set $nextpos = 'last' before opening the native form. It submits max(pos)+1, or 1 for an empty table. The default is first; explicitly choose the intended behavior. Client Categories uses last, orders all choices by pos, CCID, and fills only null initial positions in ascending CCID order in update 158. Re-running that migration preserves saved positions, including zero. After deployment, run logged-out initialization for the changed menu option flags; do not run an entire updater case just to test this migration.