Problem/Motivation
Whilst working with Layout Builder blocks, we noticed some of our blocks were not correctly ordered.
CategorizingPluginManagerTrait::getSortedDefinitions() performs a comparison between the category of $a and $b to figure out how to sort the two items relative to each other. If the category is different, then the items are ordered by category name, otherwise they are ordered by plugin admin label.
However, both the category name and the admin label are instances of Drupal\Core\StringTranslation\TranslatableMarkup
So the line:
if ($a['category'] != $b['category']) {
Actually compares if the TranslatableMarkup objects are the same, rather than the rendered, stringified value. In some circumstances, the translatedMarkup has not yet been rendered at the time of the comparison (its value is NULL), and so the two categories, despite the same label, are different and sorted as such.
This causes some block plugins to not sort properly.
Here is a screenshot of xdebugging the categories:
As you can see, the translatedMarkup is set to NULL at this point, because it has not yet been evaluated. But, in subsequent comparisons the value for this particular item will be set, but for other, as yet uncompared items, they will not - so the objects will differ.
Here you can see how the items are not alphabetically sorted:
In the subsequent calls to strnatcasecmp() those values do actually get cast as strings and rendered, but those happen after the category comparison, which is why some of the plugins have not had their category rendered yet.
Steps to reproduce
Create multiple block plugins with translatable categories, using different categories for some of the plugins annotations like below
@Block(
* id = "your_block_id",
* admin_label = @Translation("Block Label"),
* category = @Translation("Primary"),
* )
Then observe if the blocks are arranged alphabetically within those categories or not.
Proposed resolution
At the moment CategorizingPluginManagerTrait::getSortedDefinitions() compares $a['category'] and $b['category'] rather than the rendered output of those values. Simply casting those values as strings should fix the issue, as the __toString() magic method will render those values for us.
if ((string) $a['category'] != (string) $b['category']) { ... }
Remaining tasks
User interface changes
None
API changes
None
Data model changes
None
Release notes snippet
Fix issue with plugins not being ordered appropriately if TranslatableMarkup for the category names has not yet been rendered.
I came across #2824890: Convert uasort to array_multisort in \Drupal\Core\Plugin\CategorizingPluginManagerTrait::getSortedDefinitions() during investigating this issue, and the approach taken in that issue does also fix this problem but that issue seems to have stalled and was aimed at addressing a PHP7 problem, which was addressed elsewhere.
Comment | File | Size | Author |
---|---|---|---|
#15 | drupal-translated_plugin_ordering-3308449-7.patch | 3.13 KB | owenbush |
#9 | drupal-translated_plugin_ordering-3308449-7-TEST-ONLY.patch | 2.19 KB | owenbush |
Screen Shot 2022-09-07 at 12.35.48 PM.png | 77.46 KB | owenbush | |
Screen Shot 2022-09-07 at 12.39.20 PM.png | 20.11 KB | owenbush |
Issue fork drupal-3308449
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
owenbush CreditAttribution: owenbush at Lullabot commentedComment #3
owenbush CreditAttribution: owenbush at Lullabot commentedComment #4
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedComment #6
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedI have made the change and modified some BlockManagerTests which would fail without this change. Marking as Needs Review.
Comment #7
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedFor those wanting to safely use a patch with composer I've uploaded a copy of the diff as a patch.
Comment #8
Dave ReidI have confirmed this resolves the issue we see in block sorting.
Comment #9
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedAttaching a test-only patch that should fail, to show that this is an issue.
Comment #11
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedMoving back to RTBC, because it was automatically moved back to Needs Work, despite that being a test only patch designed to fail.
Comment #13
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedComment #15
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedReuploading the working patch to stop the issue being marked as needs work every time the test only patch is retested.
Comment #16
owenbush CreditAttribution: owenbush at Lullabot for Principal Financial Group commentedComment #17
alexpottNice fix and test. Backporting to 9.4.x as a low-risk bug fix.
Committed and pushed d3cf1e9277 to 10.1.x and 53fdb351ef to 10.0.x and 6de387a4ca to 9.5.x and cbdb894196 to 9.4.x. Thanks!