Problem/Motivation
Content types are ordered by machine name on /node/add page. This is a problem especially on multilingual sites because machine names and content type labels are often in different languages. Content types on /node/add page often end up being in a seemingly random order.
Content types list on /node/add page is also inconsistent with the list on /admin/structure/types which is ordered by the content type labels.
In addition to the content type specific problem, the scope of this issue has been extended (#5) to include other similar use-cases as well:
- Block Content "add" page (BlockContentController:add())
- Generic entity "add" page (EntityController::addPage())
Page /node/add:

Page /admin/structure/types:

Proposed resolution
- Order content types by label on /node/add page
- Order block content types by label on /block/add page
- Order bundles of a custom entity by using the sort() method of the bundle entity type definition class (if such exists)
Tests for all three use-cases (#32). Proposing to add functional tests only because adding unit tests would extend the scope if this issue siginificantly (#106). The "add" page methods of those three controllers do not currently have any unit tests.
For reference, issue #2736377 presents an alternative approach for solving this problem: https://www.drupal.org/project/drupal/issues/2736377
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #144 | interdiff.2693485.139-144.txt | 3.5 KB | abhisekmazumdar |
| #144 | 2693485-144.patch | 6.11 KB | abhisekmazumdar |
| #142 | test_4.png | 33.12 KB | adalbertov |
| #142 | test_3.png | 18 KB | adalbertov |
| #142 | test_2.png | 18.29 KB | adalbertov |
Comments
Comment #2
cilefen commentedComment #3
tim.plunkettMaybe this?
Comment #4
cilefen commentedComment #5
cilefen commented#3 fixes it.
Comment #6
mo86 commented#3 works for me
Comment #7
dawehnerI like the idea. IMHO though we should adapt
\Drupal\Core\Entity\Controller\EntityController::addPageas well as\Drupal\block_content\Controller\BlockContentController::addOn top of that I'm wondering whether the sorting belongs onto the theme layer or the controller
Comment #8
tim.plunkettThe second screenshot in #4 made me think, why is that also not wrong?
Because
\Drupal\Core\Config\Entity\ConfigEntityListBuilder::load()does this:uasort($entities, array($this->entityType->getClass(), 'sort'));We should probably just use that.
If this were earlier days of D8, I'd add a
, $sort = FALSEto loadMultiple, but it's too late for that.Comment #9
dawehnerYeah in an ideal world we would not sort at all.
Comment #10
mo86 commentedSo what is the status?
Comment #11
mo86 commentedSo its still in Drupal 8.1. Any chance to get it fixed?
Comment #12
cilefen commentedIssues get fixed in the dev branches. Please do not change that.
Comment #13
Mirroar commentedI agree we should use the Entity Type's sort method as mentioned in #8. See attached patch. No interdiff since it isn't based on #3.
Comment #14
tim.plunkettI think this comment is redundant.
Might as well use entityTypeManager(). Could even assign to a local variable and reuse.
use [] instead of array()
Comment #15
Mirroar commentedI would agree and removed it. It was in
\Drupal\Core\Config\Entity\ConfigEntityListBuilder::load(), which is why I copied it over in the first place.Good point, though we're breaking consistency with the rest of the file, which uses
entityManager()everywhere. I guess we can switch to it in this function. Thinking about switching away from EntityManager for the rest of the file is probably out of scope for this issue.I wanted to use
array()for consistency, but I see now that[]is also used in some spots, so it isn't consistent anyway.So the patch grew a bit for consistency withing the addPage-method, but it still does the same thing.
I'll have to read up on creating tests before I can include that as well.
Comment #16
caspervoogt commentedGlad to see this issue taken up. Was about to post a patch I had made based on this post but thankfully I found this issue in the queue instead. I won't post my patch because the ones in this thread are already better tested.
Comment #17
traviscarden commentedCan't we can't just do this? (Re-posting attached from duplicate issue #2734573-2: Content types not sorted correctly on node/add page.) It's much simpler and it entirely unifies the code path between the two disparate pages. The
node/addpage is a list. Seems to make sense to use the list builder. :)Comment #18
Kumar Kundan commented# 17(@TravisCarden) working fine and it is nice !!!!.
Comment #19
tim.plunkettIf you have 51 content types (it could happen!) this approach will fail.
This is because the list builders are paged, by default to 50 entries.
Comment #20
traviscarden commentedWhat? And there's no way to dynamically override it? Well, thanks for pointing that out, @tim.plunkett. I've created #2736377: Add ability to dynamically set limit on EntityListBuilder, which would allow us to do this:
But until such a method is available, perhaps we continue here with the previously discussed approaches.
Comment #21
dawehnerIt indeed feels like a weird implementation detail that the load method is actually public. Looking from far away a list builder should simply return the rendered list of stuff, potentially.
Comment #24
jhuhta commentedModified patch #15 to apply against 8.3.x-dev.
Comment #26
timmillwoodDoes block_content have the same issue? Could we fix both in own patch?
Comment #27
mohit1604 commentedBefore applying patch
After applying patch
Comment #28
mohit1604 commentedPatch #24 works for both drupal 8.4.x and 8.5.x branch.
Comment #30
john cook commentedI've tested patch from #24 against 8.6.x and the patch works as expected.
As there are already screenshots by Mohit in #27 and the code has been updated from the comments from Tim Plunkett in #14, I'm going to set this as RTBC.
There is a question of if the issue gets postponed until #2736377: Add ability to dynamically set limit on EntityListBuilder gets fixed. There's also the question if a fix can be implemented for block_content at the same time (if it exists).
Comment #31
larowlan#7 and #26 haven't been addressed/answered
Comment #32
timmillwoodAlso the 'Needs tests' tag added in #3 hasn't been addressed. I'd hate for this to get fixed then an accidental regression gets in unchecked later.
Based on #7 and #26 I'm moving this to the 'entity system' component.
Comment #33
juhog commentedI've tried to add unit tests for NodeController in the patch above.
A couple of comments:
1) NodeController gets UrlGenerator via dependency injection
The original code uses UrlGeneratorTrait. I changed the code because I couldn't figure out how to mock this part:
2) NodeControllerTest has helper class TestConfigEntityBase
I used the same approach as SearchPageRepositoryTest uses. A helper class which extends the original class which has the sort method. I couldn't figure out other way to mock.
Comment #35
idebr commentedClosed #2991874: Drupal Content sorted by machine name not title as a duplicate issue.
Comment #36
idebr commentedClosed #3007618: Allow Node Views to be sorted on the Node type label instead of the the Node type machine name as a duplicate issue.
Comment #37
juhog commentedI've been working on this some more lately. I have a patch coming up soon. My patch will address #7 and #26, and will also add tests for the all three controllers.
Comment #38
jmarkel commentedPlease note that #3007618 has been re-opened, with a revamped title and summary. It describes a related, similar, issue in which Views results which are sorted by Content Type are ordered incorrectly by content type machine name rather than content type title.
Comment #39
juhog commentedThis patch addresses #7 and #26. All three controllers get unit tests. NodeController and BlockContentController get functional tests. Also, I can create new issues if the tests are too extensive for this issue's scope.
(Edit: Details about the patch removed. I will add details after fixing the failing tests.)
Comment #40
juhog commentedComment #42
juhog commentedThis patch fixes the problems in the previous patch.
Summary
Node
NodeControllerTest (unit tests)
NodeCreationTest (functional tests)
Block Content
BlockContentController: Dependency injections refactored. Inject only themeHandler and urlGenerator. Removed injecting entityStorage because ControllerBase provides entityTypeManager already.
BlockContentControllerTest (unit test)
BlockContentTestBase (functional tests)
BlockContentTypeTest (functional tests)
Entity
EntityController: Refactored because the actual entity objects must be loaded to use the sort() method. Previously the controller used EntityTypeBundleInfo which gives only entity IDs and labels.
EntityControllerTest (unit tests)
EntityAddUITest (functional tests)
PS. Thanks @xano for helping with the questions I had while doing this patch.
Comment #43
juhog commentedComment #45
cilefen commented@juhog: Could you please post a tests-only patch?
Comment #46
juhog commented@cilefen Here's a tests-only patch for #42.
Comment #48
juhog commentedTests-only patch for #42 again (incorrect diff root in the previous one).
Comment #49
juhog commentedComment #51
juhog commentedComment #52
brightboldThe patch in #42 is full of rainbows and happiness. My Add Content page no longer looks like someone shook up all the content types in a Yahtzee cup and dumped them out at random. Thanks to all the contributors who have been working on this,
Comment #54
pancho#42 passed all tests, so back to “needs review.”
Comment #56
lapek commentedIt seems 8.7 has broken the patch. Quickly looking it looks like just the line numbers may be off.
Comment #57
lapek commentedAdjusted the patch so that it can apply with 8.7
Comment #58
yesct commentedfixed (tried to fix) the linting on 8.7
thanks @samuelmortenson for helping me out in slack :)
Comment #59
yesct commentedand rerolled for 8.8.x
Comment #60
yesct commentedhm. I think I didn't need to change the issue version, and probably could have uploaded both patches in the same comment. I wonder what 8.8.x should run though, maybe php 7.2 not sure which mysql.
Comment #61
yesct commentedThis has tests, removing tag, until a review can give feedback on if it needs more :)
Comment #62
gogowitsch commentedI thoroughly went through the last patch. Here is what I found:
MockBlockContentType: This class was embedded at the end of the test file. In 1000's of test files, there is just one other *Test.php file containing more than one class. I went ahead and extractedMockBlockContentType,MockNodeType, andMockRendererinto separate files.Removed the deprecated. Undone, see next patch.UrlGeneratorTraitfromEntityController$content_entity_idto$bundle_entity_type_id.EntityController: Made the diff slightly smaller by moving the$form_route_nameline in front ofiflike it currently is in Core.$bundle_nameinstead of the technically correct, but less digestible$entity_type_id.uasortdown to where we know that there are multiple bundles. I had to change the call counts in the tests as a result.BlockContentTestBase::setUp: keep the block type label lowercase as it is currently in Core to prevent other people's tests from failing.BlockContentTestBase::createBlockContentType: Use$idas default value for $label, so we have to change less lines (read: smaller diff).assertUrl()accepts only one parameter - I have rewritten the message as a comment.Thanks to everyone on this issue for your enthusiasm in getting this fixed.
Comment #63
gogowitsch commentedChanges since the last patch:
UrlGeneratorTraittoEntityControllerbecause it broke tests.Comment #64
panchoWow, but there‘s something wrong with patch #63...
Comment #65
gogowitsch commentedI messed the last patch up and added a file "foo.patch" into the patch 😕. Here comes my next attempt to get the tests green. Unfortunately, I could not reproduce the test failures locally. I wonder if the "foo.patch" confused the test runner?
My changes since the last patch:
Comment #66
gogowitsch commentedChanges since the last patch:
NodeCreationTest: UseentityTypeManagerinstead of deprecatedentityManager. This should get the failed tests green.BlockContentWizardTest: Removed unnecessary line change from diff.SIMPLETEST_BASE_URLbeing exactly 'http://localhost', which defeats the purpose of the variable.$keyfrom foreach loop.Comment #67
adriancidRerolling against the last 8.8.x branch changes
Comment #68
john cook commentedThank you everyone for working on this. :D
I've test against 8.8.x and the bundles are ordered alphabetically on the "Add content" page.
I've checked the code and it all looks good and there are new tests included.
So, I'm going to set this to RTBC!
Comment #70
juhog commentedI'm reverting the issue status change made by test bot a couple of days ago. It seems like an unrelated random failure. At the moment the last patch has ran tests successfully two times.
Comment #71
larowlanLooks good, but there is a regression here, we're dropping support for the two hooks that allow bundle definition - hook_entity_bundle_info and hook_entity_bundle_info_alter
We can't be sure this route exists or that the user has access. Are we sure this change is in scope? Even still, I don't think the code is doing what the comment says.
'Redirect if there are no bundles' implies we're checking that no bundles have been created or that there are none defined in the entity bundle info, but this condition is testing if the entity has a bundle entity.
I think we're confusing 'has an entity for bundles' with 'there are bundles' and 'supports bundles'.
i.e. just because an entity doesn't have a bundle entity type, doesn't mean there aren't multiple bundles, because there are other ways (besides config entities) to define bundles for an entity type.
we need to use the entity bundle info service here, not the entity type storage, config entities are not the only way to define bundles of an entity type, there are hooks -eg hook_entity_bundle_info and hook_entity_bundle_info_alter
also, we don't have test coverage for those scenarios
we can't use this with bundles that are defined in the hooks (as they're not guaranteed to be entities), so we'll need to add back the other values provided by the hooks after we sort here
out of scope?
this is an api change, we should be adding a new method instead of modifying the argument order of the existing one - because its a base class, its considered API
out of scope?
Comment #73
renrhafPlease note that the sorting of config entities having labels with specific characters like "É" won't work properly as they will appear at the end of the list.
Comment #74
oriol_e9g.
Comment #75
ckaotikHow did we go from a 1kb patch to almost 60kb :O Are we still on track, or are we doing unrelated changes?
Could we leave the sorting up to the entity class, i.e. run
uasort($node_types, [$definition->getClass(), 'sort']);?Comment #76
juhog commented"How did we go from a 1kb patch to almost 60kb"
Scope was extended to include NodeController, EntityController, and BlockContentController (#7 and #26). Also tests for all of those.
The current newest patch has some issues as pointed out in #71. One important thing that I personally missed while writing one of these patches is that bundles can be defined in multiple ways, not just config entities.
The problem with
uasort($node_types, [$definition->getClass(), 'sort'])is the one mentioned in #71 point number 3.Comment #77
gogowitsch commentedIn a minute, I'll go ahead and work on the concerns raised above. First, however, I will upload an unmodified patch. It only differs from comment #67 in that it cleanly applies to Drupal Core 8.9.x.
Comment #78
esolitosPatch from #77 applies cleanly on 8.8.2 as well, while #67 does not.
Comment #79
drupalisedmanish commentedThe patch #77 is working correctly on 8.8.5
Can someone confirm which Drupal version will incorporate this patch
Comment #80
cilefen commented@drupalisedmanish It will have to be reviewed and tested by the community to be incorporated in a release.
Comment #82
esolitosRe-Roll of #77 for 8.9.x as of commit 8ce62cd71f
Comment #83
longwaveComment #84
longwaveRerolled for 9.1.x.
Comment #86
hardik_patel_12 commentedworking on it
Comment #87
hardik_patel_12 commentedSolving test cases.
Comment #89
kishor_kolekar commentedComment #90
kishor_kolekar commentedSorry .I am stuck in another task which will take more time so i am un-assign this issue
Comment #91
Lal_Comment #92
Lal_Declaring ::setUp without a void return typehint
Comment #93
Lal_Comment #95
Lal_Comment #97
pameeela commentedAdded #2863292: Cannot manually set the sorting of content types on page /node/create as related and will close that as a duplicate, also added credit for leisurman who reported that issue.
Comment #98
ravi.shankar commentedWorking on this. Needs a re-roll.
Comment #99
ravi.shankar commentedHere I have re-rolled patch #92 and tried to fix failed test of patch #92.
Comment #101
hardik_patel_12 commentedSolving failed test case. Kindly follow a new patch.
Comment #102
juhog commentedI've been working on the problems pointed out in #71. I'm hoping get the patch ready during the weekend. The patch will also address some problems in the unit tests.
Comment #103
juhog commentedComment #104
juhog commentedThank you larowlan for the review and insights given on comment #71!
This patch is basically a complete rewrite of the changes needed in this issue.
Summary:
EntityController
Previous approach was based on some misunderstandings and caused regression issues mentioned in #71. This new solution avoids making any major changes to the addPage() method. The addPage() method gets the bundles from bundle info service.
If I've understood correctly, a content entity will get all of its bundles from just one source: the bundle entity types or the bundle info hooks. If a content entity were able to get bundles from both sources, I believe the addPage() would break when loadBundleDescriptions() method is called.
When bundles are defined by a bundle entity type, they are now being sorted with a new method sortBundleEntities(). The actual sorting is done by calling the static 'sort' method on the bundle entity type class (usually ConfigEntityBase). Having this wrapper method makes unit testing easier.
When bundles are defined without a bundle entity type, the controller doesn't do any sorting. Sorting doesn't seem necessary because the bundle info hooks allow the implementation to place the bundles in which ever order they want.
Other minor changes:
BlockContentController
New method sortBlockContentTypes() handles sorting the bundles. This is very similar to the new sort method in EntityController.
Minor change: url_generator service injected to make unit testing easier.
NodeController
New method sortNodeTypes() handles sorting the bundles. This is very similar to the new sort method in EntityController.
New method redirectNodeAddPage() to make unit testing easier.
Unit tests: BlockContentControllerTest
Static 'sort' method calls are the main problem to solve in these unit tests. Previous solution tried to handle those by mocking the block content type class.
This patch changes the approach completely and implements the idea described in Unit testing more complicated Drupal classes doc page. New class TestBlockContentController extends the BlockContentController and overrides the sortBlockContentTypes() method with a custom logic that doesn't require calling static methods. This method override also allows us to count the number of times the actual sort function is called. The way I see it, BlockContentController doesn't actually care how the bundles are sorted, it just wants the block content type class to do some sorting.
Other minor changes:
Side note: When only one block content type exists, would be nice to do a redirect, instead of printing out a form.
Unit tests: NodeControllerTest
In general, the problems and the solutions in this class are very similar to the ones in BlockContentControllerTest. In summary:
Unit tests: EntityControllerTest
In general, the problems and the solutions in this class are very similar to the ones in BlockContentControllerTest. In summary:
EntityController has quite a lot of different cases though because the bundles can be defined either by a bundle entity or by bundle info hooks.
This file covers the following cases:
Functional tests: EntityAddUITest
Functional test case testAddPageWithBundleEntities() fixed so that it passes only when the bundles are actually sorted by label.
Test case testAddPageWithoutBundleEntities() has been reverted to its original form. The changes in the previous patches were invalid, due to some misunderstanding.
Out-of-scope: Bundle IDs and labels edited to keep them consistent throughout the test case.
Functional tests: BlockContentTypeTest
New functional test case testBlockContentAddPageOrder(). This test needs to define both the ID and the label of the test block content types it creates. A new method createBlockContentTypeWithId() has been added to BlockContentTestBase to allow creating those block content types.
Out-of-scope: One assertRaw() usage fixed. See #42 for details.
Functional tests: NodeCreationTest
New functional test case testNodeAddPageOrder().
Other notes
I've used "@noinspection PhpUndefinedMethodInspection" in the unit test files. Prophecy objects tend to trigger a lot of these IDE inspection warnings. I will remove these if this isn't the proper way to deal with the warnings.
Comment #105
tim.plunkettWhy this change? It's not clear how this is related to fixing the bug.
Out of scope
Same question
Why remove this?
What kind of information? Machine names? Objects? This should be something like
string[]So the load is done on every sort comparison? That seems unnecessary. Why not load them all, key by machine name, sort the objects, then return the array keys?
Stopping my review here because the rest of the patch is more of the same confusing Url/urlGenerator switches.
Comment #106
juhog commentedThe changes in the previous patch that aren't directly related to sorting the bundles are related to making things easier in the unit tests. Apologies for any confusion caused.
The three controllers related to this issue currently don't have any unit tests. I suppose the options are as follows:
The previous patch is aiming for option 4.
Which option or approach would be preferred? I can modify the proposed solution accordingly.
Regarding the specific comments:
#105.1 - The motivation has been to make unit testing easier. I will look into reverting these kinds of changes, moving towards option 3 mentioned above.
#105.2 - Noted, next patch will revert this and other similar out of scope changes.
#105.3 - "add_bundle_message" simplified to make unit testing easier. I'm trying to avoid having to use Link::createFromRoute() inside the unit tests. I've seen that approach in some other core unit tests though. If that solution (or some other solution) is preferred, I will revert this change.
#105.4 - Noted, next patch will revert this and other similar out of scope changes. The comment seemed unnecessary because the method name is very descriptive already.
#105.5 - Noted, next patch will try to change the method to use a more simple parameter. In the current solution I tried to follow the example given by loadBundleDescriptions() method in the same class. This new method takes the same parameter. It's the bundle information returned from EntityBundleInfo::getBundleInfo().
#105.6 - Noted, next patch will implement the suggestion.
Thank you for the review tim.plunkett, the insights are much appreciated.
Comment #107
juhog commentedThis patch addresses the feedback given in comment #105.
Summary
EntityController:
New method sortBundleNames() implements the idea suggested in #105.6: Receives bundle names
string[], and returns sorted bundles namesstring[].However, all the logic in addPage() is currently assuming the data is in bundle information array format. So, some more processing is needed at addPage() to sort the bundle information array with the results given by sortBundleNames(). If we want to avoid this, we could give the bundle information array as a parameter to our custom sortBundleNames() method. Or addPage() could be refactored to not rely on the bundle information array format so much.
When bundles are defined without a bundle entity type, the controller doesn't do any sorting. Sorting doesn't seem necessary because the bundle info hooks allow the implementation to place the bundles in which ever order they want.
NodeController and BlockContentController:
Both controllers apply the same new logic. Bundles are provided by a new method: getNodeTypes() / getBlockContentTypes(). The new method handles loading the bundles and sorting them with the sort() method of the bundle entity type definition class.
Functional tests:
All three controllers have functional tests for checking the correct bundle sort order.
Unit tests:
This patch removes all the unit tests that were being suggested in previous patches. I'm assuming it's better to leave the unit testing to some other issue due to the reasons mentioned in comment #106.
If unit tests are eventually added in some issue, patch #104 can be a good starting point since it provides solutions for many mocking related problems in these three controllers (NodeController, BlockContentController, EntityController).
Comment #108
pameeela commentedComment #109
samiullah commentedTested the latest patch on simplytest.me
Worked nicely
Able to see content types alphabetically both on node/add screen and content type listing page
As well as dropdowns
This can be moved for RTBC
Comment #110
pameeela commented@samiullah thanks for testing! It isn't clear from your comment/screenshots how you confirmed that the content types are not sorted by machine name?
Are you able to update your comment with steps to reproduce and verify that the order is alpha by content type name rather than machine name?
Also the patch will need a code review before it can be marked RTBC.
Comment #111
samiullah commented@pameeela Thanks for feedback
Following are the steps to manually test this one
1. Install fresh instance of core 9.1 x branch and add patch file link on simplytest.me
2 Wait for the site to spin up
3. Login as admin (uname: admin pwd: admin)
4. By default we have two content types: Article and Basic Page
5. Machine name of Article is article and for Basic Page it is page
6. Add new content type example Zebra machine name would be zebra
7. Edit the Article content type and change Content type name to ZArticle, note that this step will not change the machine name
8. Check the content type listing and see if they are appearing alphabetically by name
9. Navigate to node/add page and observe if same order of content type is available here or not
10. One more test u can do is to rename zebra content type to example : Apple and see if it gets sorted by name or not
@pameeela yes this one needs to Code reviewed as well
Comment #112
samiullah commentedComment #114
gogowitsch commentedI have carefully reviewed @juhog's patch from #107. From my perspective, the code looks wonderful. I didn't see any code style violation or out-of-scope change. My manual tests based on
9.2.x-dev+ the patch didn't reveal any problems. PhpStorm inspections are clean. Thanks @juhog!From my perspective, this is ready to be commited.
Comment #115
douggreen commentedThis is a nice change! RTBC++.
It will also allow contrib to add a weight sort to other entity types, such as node_type. This small chunk of code adds the functionality, without adding the UI that will also be desired:
Comment #116
catchWe have bundles here.
We then convert it into an array of bundle hames (although is it not bundle machine names at this point?).
Then here, we're loading bundles again to sort them.
Why not a ::sortBundles() method without having to deconstruct then reconstruct the array? The block and node controllers appear to be doing this already. If we can't do that, I think we need to explain why we're doing all the array_flip/array_replace dance to avoid it - but can't see why.
Comment #117
juhog commentedThank you for the comments @catch, let me elaborate:
We have bundle information array and it has very minimal data. The actual bundle entity type classes are not available.
Bundle classes are being loaded for the first time. Loading is necessary to be able to use the sort() method of the bundle entity type definition class.
Node and Block use an bundle entity type to define the bundles. The situation in this method isn't the same.
The current approach for the sortBundleNames() method was suggested in #105 points 5 & 6 by tim.plunkett. I mentioned some more details in comment #107:
"Some more processing is needed at addPage() to sort the bundle information array with the results given by sortBundleNames(). If we want to avoid this, we could give the bundle information array as a parameter to our custom sortBundleNames() method. Or addPage() could be refactored to not rely on the bundle information array format so much."
In summary:
A decision needs to be made about what kind of parameter should the "sort" method receive. Orignal solution on #104 was to give the bundle information array as parameter (the same way as loadBundleDescriptions() does). This was changed in #107 to be just a
string[]. From my point of view, the options are limited, and each option has it's downsides, unless some overall refactoring is done for the addPage() method.If the current approach seems valid, I'm happy to add a code comment explaining the need for this solution.
Comment #118
catchAhh OK good point. However that brings up another problem I think. It's possible to define bundles entirely in code without using bundle entities at all, and the generic method is assuming that bundle entities exist. This is fine for the entity test module since it uses bundle entities, but it's not necessarily going to be for custom entity types. Given we don't have a case in core apart from entity test module where we can use it, should be adding the generic method at all? Would be tempted to fix node and block here, and move the generic method to a follow-up.
Comment #119
juhog commentedI'm not sure if I understood correctly. The "generic method" refers to EntityController::addPage()?
EntityController::addPage() does currently work in both situations. The method checks whether or not the current entity type is using a bundle entity type.
If bundle entity type is being used, the method checks which bundles are accessible, adds bundle descriptions, and with patch #107 sorts those bundles.
If there is no bundle entity type, the method doesn't do any of those things and just lists the bundles in the order that bundle information array has them. The addPage() method could apply some sorting in this situation as well but it seems unnecessary because the bundle info hooks allow the implementation to place the bundles in which ever order they want.
Node and Block Content are indeed more simple situations than the situation with EntityController.
Comment #120
alexpottWhy are we using forward_static_call_array() and not call_user_func_array()? It doesn't seem necessary as far as I can see. (aside - never seen forward_static_call_array() before - neat).
If it is necessary then a comment as to why would be great.
Comment #121
paulocsRe-rolled patch.
I also changed:
return forward_static_call_array([$bundle_entity_type_class, 'sort'], [$a, $b]);to
return call_user_func_array([$bundle_entity_type_class, 'sort'], [$a, $b]);Comment #123
paulocsComment #124
paulocsI changed the test cases.
Comment #125
longwaveThis is a slightly wordy set of variable names, maybe it explains what's going on but usually I think we would just say something shorter e.g:
Why do we even need the anonymous function here?
would seem to be equivalent?
Seems a bit odd that we have the storage injected (for which we need the entity type manager already) but not the definition, maybe not worth fixing here though. Looking at this I wonder if we should try to unify the node and block content controllers so they extend from EntityController instead, maybe there is an issue for this already?
Comment #126
abhijith s commentedApplied patch #124 and it works fine.Adding screenshots below.
Before patch:

After patch:

Comment #127
juhog commentedThis patch addresses the following comments:
#116.2
Code comment added. Another option would be to change the sort method, more details in #117.
#125.1
Variables have been modified as suggested.
#125.2
Modified as suggested. Indeed anonymous function seems unnecessary.
Comment #128
gogowitsch commentedI have tested the patch locally and read the full patch again. Looks good to me 🙂
Comment #129
juhog commentedComment #130
juhog commentedComment #131
juhog commentedComment #132
alexpottNot doing this as part of loadBundleDescriptions means we have to load the bundles twice which feels wasteful. It's tempting to make loadBundleDescriptions return the sorted list? If it did then contrib/custom usages would be fixed - see http://grep.xnddx.ru/search?text=loadBundleDescriptions&filename= But that has the downside of then becoming a really bad method name.
I wish \Drupal\Core\Entity\Controller\EntityController::addPage() was being dealt with in a follow-up issue. There are a number of things in it that make it tricky to handle. It has to deal with non config entity bundles and it already has a bad code flow (it does a redirect after doing unnecessary work if there is only 1 bundle). I think it would make it much easier to pull this part of the change and do it separately. Especially since the search of contrib shows that the current solution means that we need a CR that details changes contrib should make.
This should accept an array of values to be more flexible - like createContentType....
Ahh I see we already have \Drupal\Tests\block_content\Functional\BlockContentTestBase::createBlockContentType... I think we should overload the first argument $label and change it to $values - if it is a string treat it as the label otherwise treat it as an array of values. And then in a follow-up we should deprecate the string code path. So we can typehint with an array in Drupal 10.
Comment #133
paulocsComment #134
paulocsAddressed comment #132
Comment #135
paulocsComment #137
paulocsComment #138
paulocsCorrect patch and interdiff between patch #127 and #138.
Comment #139
alexpottThe documentation needs to change to explain what the array is...
For example
Plus we have to cope where id is not set in $values - see \Drupal\block_content\Tests\Views\BlockContentTestBase::createBlockContentType - for how to do this. Eventually we'll move this into a test trait and not have the duplicate code.
Comment #140
paulocsComment #141
paulocsI attached a new patch that address the comment #139 and interdiff.
Comment #142
adalbertov commentedApplied patch #141 and it looks good. Screenshots below.
before patch

after patch

Comment #143
alexpottI think we can make the change safer by not adding new protected methods that don't really do much anyway.
There's no need for the new protected method.
Not adding a protected method makes this a safer change to make.
There's no need for the new protected method.
Comment #144
abhisekmazumdarHi, thank you @alexpott
I have made the suggested changes. Kindly review.
Comment #145
longwaveThis feels inconsistent now, we should be getting the entity type definition class the same way in both cases I think? At least in the node case, the code that gets the definition could be closer to where it is actually used.
Comment #146
alexpott@longwave $this->entityTypeManager()->getDefinition('node_type') and $node_storage->getEntityType() returned different information then we'd have a tonne of problems. The issue is the NodeController doesn't inject the storage and we shouldn't be changing that here. Imo getting the entity type in the most convenient way in either class is fine.
Comment #147
longwaveFair enough, it does feel weird doing it two different ways in one patch but I guess it doesn't matter really and I agree fixing NodeController is out of scope.
Otherwise all points addressed and this patch has got much smaller and more focused so I think it is RTBC.
Comment #148
alexpottCreated the follow-up for the de-scoped work - #3196798: Fix EntityController::addPage so bundles are sorted by label and not ID
Comment #149
alexpottCommitted and pushed f0eaa97e13 to 9.2.x and bd41f89e84 to 9.1.x. Thanks!
Backported to 9.1.x since now this change only adds sorts inline to existing methods.
Comment #153
benjifisherI came here from the release notes for 9.1.x. It is a little confusing: I think the issue was re-scoped (and I see a follow-up issue) but the title and issue summary were not updated.