datagrid icon indicating copy to clipboard operation
datagrid copied to clipboard

setSortableCallback is used only with AJAX handleSort/handleRefreshState

Open ikeblaster opened this issue 5 years ago • 1 comments

Please see the first comment below first.


I think this is a very old problem, because I am finding similar problems mentioned from about 2016. Hopefully I am not doing something completely wrong.

Versions: v6.0.1 (playground), v6.2.5

How to reproduce

  1. Download and fire up https://github.com/planette/playground
  2. Add column into ColumnsPresenter::createComponentGrid:
$grid->addColumnText('notRealColumn', 'Something')
	->setRenderer(function(Row $row) {
		echo $row['id'] % 2 === 0 ? 'dělitelné 2' : 'nedělitelné 2';
	})
	->setSortable()			
	->setSortableCallback(function($source, $sort){
		$source->orderBy('MOD(id,2) ' . $sort['notRealColumn']);
	});			
	
  1. Open in browser
  2. Sort the column
  3. Press F5
  4. Observe Unknown column 'notRealColumn' in 'order clause'

The problem lies in not set DataGrid::$sortCallback. But even if we fix correct loading of this callback, it won't work with more than just one column with sortableCallback (=multisort wouldn't work). Personally I would get rid of DataGrid::$sortCallback. DataGrid::createSorting could return mixed array of columns and callbacks. IDataSource classes would then just call these callbacks. handleSort could be simplified. But it's a BC break if someone has own data source.

#840 Related problem (same cause, I think).

Workaround

TLDR: calling IDataSource::sort for every to-be-sorted column individually.

This is probably not a BC break per se (not sure - is anywhere mentioned that IDataSource::sort will be called at most once?). I feel this is more of a hack than proper solution, so I am posting it with hope it could help someone. I use it with Doctrine QueryBuilders, apparently works with dibi, didn't test anything else.

Edit: since then I've found a problematic case with the code below, probably with pure arrays (not sure now). I have returned to simpler fix from #860, since I don't use multisort

class SortingFixedDataGrid extends DataGrid {

	/**
	 * Fixes a problem when loading page with column.sortableCallback + sort is set in URL
	 *
	 * @param array $sort
	 * @param callable|null $sortCallback
	 * @return Sorting
	 */
	protected function createSorting(array $sort, ?callable $sortCallback = null): Sorting
	{
		$dataSource = $this->getDataSource();

		return new Sorting($sort, function ($qb, $sort) use ($dataSource) {
			foreach ($sort as $key => $order) {
				try {
					$column = $this->getColumn($key);
				} catch (DataGridColumnNotFoundException $e) {
					continue;
				}

				$columnSort = [$column->getSortingColumn() => $order];

				if ($column->getSortableCallback() !== null) {
					$dataSource->sort(new Sorting($columnSort, $column->getSortableCallback()));
				} else {
					$dataSource->sort(new Sorting($columnSort));
				}
			}
		});
	}
};

Btw, playground on windows has broken JS because of case sensitivity (Happy.js on drive vs happy.js in code). nette.ajax.js is never initialized. This makes the problem very visible, since every click (sort, page change, ...) leads to new page load.

ikeblaster avatar Jan 12 '20 20:01 ikeblaster

This is the real culprit. :( Missed that, sorry. https://github.com/contributte/datagrid/pull/860

I am leaving this open, since #860 doesn't have own issue.

Still as I mentiond - it won't work with multisort. My workaround allows that (but then again - it's too hackish to be merged into this repo).

ikeblaster avatar Jan 12 '20 20:01 ikeblaster