API page: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21...
I am creating my custom module that extends ConfigEntityBase and overriding ConfigEntityListBuilder::render() method. I found that the following didn't work for table sort:
$keys = $query->tableSort($header)->sort($order, 'asc')->execute();
However the following works:
$keys = $query->tableSort($header)->sort($order, 'ASC')->execute();
It looks 'sort' method only accepts all capital 'ASC' or 'DESC' against a small letter parameter 'asc' or 'desc' if I use tablesort_get_sort($header) method to get a parameter because tablesort_get_sort($header) returns a small letter keyword 'asc' or 'desc'. I am wondering this is a specification or not. I did grep all Drupal core source code and 'ASC' or 'DESC' look being used mainly but also small letter keywords such as 'asc' or 'desc' look widely used. As DX, I think it may confuse a developer like me. Hope the API documentation clearly describes what kind of parameters can accept in this sort function.
As a reference, here is my complete code for ConfigEntityListBuilder::render() method to create a table with sortable header:
public function render() { // Overrides ConfigEntityListBuilder::render()
$header = $this->buildHeader();
$storage = $this->getStorage();
$query = $storage->getQuery();
$order = tablesort_get_order($header);
$sort = \Drupal\Component\Utility\Unicode::strtoupper(
tablesort_get_sort($header));
// $query->execute() below only returns Entity IDs.
$keys = $query->tableSort($header)
->sort($order, $sort)
->execute();
// You need to get all entity objects by calling loadMultiple.
$entities = $storage->loadMultiple($keys);
foreach ($entities as $entity) {
// You need to implement buildRow method.
$rows[] = $this->buildRow($entity);
}
$build['pager'] = array(
'#type' => 'pager',
);
$build['tablesort_table'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => $this->t('There is no @label yet.', array(
'@label' => $this->entityType->getLabel())),
);
return $build;
}
| Comment | File | Size | Author |
|---|---|---|---|
| #28 | 2498137_sort_by_uppercase-2498137_sort_by_uppercase-with_fix-28.patch | 3.57 KB | yas |
| #28 | 2498137_sort_by_uppercase-2498137_sort_by_uppercase-without_fix-28.patch | 3.01 KB | yas |
Comments
Comment #1
yasComment #2
yasI would suggest a patch since QueryBase::tableSort() is calling tablesort_get_sort() and then sort(); tablesort_get_sort() returns lowercase 'asc' or 'desc', and one of those is passed to sort(). tablesort_get_sort() should return uppercase 'ASC or 'DESC', but historically the function looks returning a lowercase value. By considering that, the parameter 'asc' or 'desc' to sort() can be converted to uppercase inside sort() function.
tablesort API page: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21...
Comment #3
dawehnerI would have used just strtoupper, because we are dealing basically with a simple ascii string always, right?
Comment #4
yas@dawehner
I tried to use both EntityListBuilder and ConfigListBuilder. The problem here is that tableSort function doesn't work without converting uppercase. Maybe I am wrong to use *ListBuilder but after my deep investigation, I needed to pass an uppercase parameter value such as 'ASC' and 'DESC' to sort() function. When we grep entire Drupal 8 Core code, we can find it includes mixed upper- or lower-case for 'ASC' or 'DESC'. I think it causes an incompatible parameter value issue among the internal function calls. What I identified is that there has a problem in between tablesort_get_sort and sort in tableSort. If sort function can internally convert a parameter value to uppercase like my patch, we can write the following code:
Before:
After:
Thus, "After" is what I expect as DX.
Comment #5
yas@dawehner
Uh... I am sorry I think I missed your point. I thought that we should always use Drupal-defined functions for code consistency. I felt \Drupal\Component\Utility\Unicode... was redundant. Let me put a patch to simply this with strtoupper().
Comment #6
jhodgdonWow, thanks for reporting this.
Questions:
- Why can't lower-case be used here? I don't get it.
- If this patch is the right thing to do (as opposed to changing the docs)... well in either case, this looks like a Database System component, not Docs.
If the database system maintainers decide that requiring upper-case is the correct thing, then you can move this back to Documentation and we need a different patch that documents this requirement, which is not evident currently.
Technicality: When you upload a patch, you should set the issue status to Needs Review to alert human and bot reviewers that a patch is ready.
Comment #9
sumitmadan commentedNow lower case can be used. Hope this is the right thing to do.
Comment #10
yas@jhodgdon
After my one hour debugging core code, now I can clearly answer to your question by attaching a new patch. (sorry my previous patch anyhow couldn't passed the testing due to non "unix-style line endings".)
Here is my findings:
- A sort() method simply returns an array. (Therefore it is not a candidate of the root cause.)
- tableSort _does_ work for ContentEntity (no case-sensitve) but doesn't work in case of ConfigEntity.
- An implementation of execute() method is totally different in between ContentEntity and ConfigEntity (see below to compare).
1) ContentEntity - Drupal\Core\Entity\Query\Sql\Query::execute()
2) ConfigEntity - Drupal\Core\Config\Entity\Query\Query::execute()
I think strtoupper() is required for the following code in a variable $sort ['direction'].
PS Thank you for your advice to interact this issue queue.
Comment #12
daffie commented@yas: Thank you for your patch. I think that your patch is the right solution for the bug. To make sure that the bug does not return we create tests. Can you make a test for your solution. Can your post two patches. The first with only the test and that should fail the testbot. The second patch with your test and your solution and that one should pass.
In the file core/lib/Drupal/Core/Entity/KeyValueStore/Query/Query.php on line 56 the is the same line:
$direction = $sort['direction'] == 'ASC' ? -1 : 1;. Maybe the same problem. ;-)Comment #13
yas@daffie
I am trying to create test cases based on Drupal\config\Tests\ConfigEntityListTest for tabeSort function in ConfigEntity but I am facing an issue because ConfigTestListBuilder::buildHeader() method doesn't have a data structure w/ sorting. The following is the difference.
Current:
Expected:
When I modify from the current to expected one, it affects existing test cases. I tried to put a flag to switch in between the current and expected ones (above) but It looks we cannot change the header as long as we use the same config_test entity. Once SimpleTest starts, it looks we cannot change the buildHeader ($header + parent::buildHeader()). (I am not sure if it is possible or not but I couldn't make it although I tried a variety of angles) How can I change the table header with or without sorting attributes inside Drupal\config\Tests\ConfigEntityListTest class?
I could create my test cases based on the expected data structure above but it will change the existing test cases such as:
- ConfigEntityListTest::testList()
- ConfigEntityListTest::testListUI()
- ConfigEntityListTest::testPager()
Anyhow, to change buildHader() method will affect the existing test case implementation. Please advise us about how we can take an approach as follows:
I think we need to test with or without tableSort function. So my question is: How we can handle both test requirements (with or without tableSort) in one ConfigEntityListTest?
Comment #14
yasFinally I came up with a test by extending Drupal\config\Tests\ConfigEntityListTest. I tried to extend the existing test code w/ minimum addition/modification. Actually I added a testTableSort() method to ConfigEntityListTest class.
I am attaching two patches as @daffie suggested:
Comment #15
yasComment #16
dawehnerIs there a specific reason why all this test code is added? I would have expected that its totally enough to just add some test code in
core/modules/system/src/Tests/Entity/ConfigEntityQueryTest.phpComment #21
yas@dawehner
It might be virtually enough to add a test code 'testTableSort' in core/modules/system/src/Tests/Entity/ConfigEntityQueryTest.php as the attached patch, however still it tests the following code. For example:
However, for DX, we would like to test without sort function as follows:
In order to test the latter one, I thought we needed to modify the buildHeader in core/modules/config/tests/config_test/src/ConfigTestListBuilder.php. Then, back to my comment #13, I couldn't find any sophisticated solution to add/modify the existing test cases, that's why I introduced core/modules/config/tests/config_test/src/ConfigTestTableSortListBuilder.php
Comment #24
yasSorry, comments in the patches were not correct. Please let me re-submit the patches.
Comment #27
daffie commented@yas: It looks good now. Great work!
Can we move the comment so that is looks like this:
Comment #28
yas@daffie
1. Moved the comments, please review.
2. For the last 4 tests, the error was "Link Label does not exist on http://localhost/checkout/admin/structure/config_test_table_sort". It looks beyond strtoupper tests. It has some table header and the link problem? I don't know why because it was successful to test on my local D8 installation.
Comment #29
dawehnerThank you!
Comment #30
daffie commented+1 for RTBC. Good work!
Comment #33
webchickSeems like this would screw people up easily, so worth getting in.
Committed and pushed to 8.0.x. Thanks!