Problem/Motivation
EntityViewsData has the following code around line 252:
// Load all typed data definitions of all fields. This should cover each of
// the entity base, revision, data tables.
$field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id());
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
if ($table_mapping = $this->storage->getTableMapping($field_definitions)) {
The problem is that it asks for FieldDefinitionInterface[] and immediately treats it as FieldStorageDefinitionInterface[] by calling $this->storage->getTableMapping() which expects the latter. Further down the code it correctly treats the retrieved data as FDI[] when it calls $this->mapFieldDefinition().
So why is this a problem? Because it is only working by happy accident. We know that we are getting BaseFieldDefinition objects, which we know implement both FDI and FSDI. The problem becomes clear when we try to patch EntityViewsData to actually take care of bundle fields as well, as demonstrated in #2898635: bundleFieldDefinitions() are not added in EntityViewsData
Consider the following code be inserted right after the setting of $field_definitions:
// Add any bundle fields defined in code.
if (isset($this->entityType->getKeys()['bundle'])) {
$bundle_field_definitions = [];
foreach ($this->entityManager->getBundleInfo($this->entityType->id()) as $bundle_id => $bundle_info) {
$bundle_field_definitions += $this->entityManager->getFieldDefinitions($this->entityType->id(), $bundle_id);
}
$field_definitions += $bundle_field_definitions;
}
Now we might get BaseFieldDefinitions from the above, but we could also get FieldConfig items. Which is perfectly fine because both getFieldDefinitions() and getBaseFieldDefinitions() should return FDI[] and then be treated as such. Except, because we treat the returned data as both FDI[] and FSDI[], the above addition will lead to a crash because FieldConfig (among others) does not implement FSDI.
Proposed resolution
Properly treat the base field definitions as FDI[] only and rewrite EntityViewsData to properly retrieve the FSDI when needed.
Remaining tasks
- Write a patch
- Figure out whether this breaks BC
API changes
None, unless we have to change function signatures to ask for the right interface.
Data model changes
None.
Issue priority explanation
Marked as major because it is both a massive code smell and it blocks a useful patch that would allow bundle fields to show up in views.
| Comment | File | Size | Author |
|---|
Issue fork drupal-2930736
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
kristiaanvandeneyndeComment #3
kristiaanvandeneynde.
Comment #4
kristiaanvandeneyndeWhoops
Comment #6
joachim commentedIt gets messier: further down the code, we stop assuming that we have a FSDI, and go off to load them all:
We have a $field_definition at that point, so why not just call getFieldStorageDefinition() on it?
Comment #7
joachim commentedHere's a go at a patch.
Given an FDI, we can get its FSDI with getFieldStorageDefinition().
For BaseFieldDefinitions, since they implement both, that method trivially returns the same object, but that's ok.
Comment #9
joachim commentedHow can we have something that's not an array here????
Comment #10
joachim commentedTrying a quick fix in the entity field manager -- should be a separate issue if it's indeed the problem.
Comment #12
joachim commentedFigured it out -- the mocked entity manager should be returning an empty array for baseFieldDefinitions().
Comment #14
joachim commentedI went down the rest of the class and changed all the calls to FDI that actually are methods on FSDI.
Tests are still failing. I am pretty sure it's due to the mocking in the test now being insufficient, however, I am finding the test code extremely hard to read and figure out!
Comment #15
yogeshmpawarComment #17
mglamanI think the test failures actually helped uncover a bug.
it is failing here
The undefined is the `uid` field on the user entity as a base field.
Comment #18
mglamanConfirmed. When working in\Drupal\Tests\views\Unit\EntityViewsDataTest::testBaseTableFields the base fields are never actually able to be retrieved from the mocked entity manager.
The problem is due to the fact the method is already mocked in setUp
Comment #19
mglamanRerolling to get tests passing.
Comment #20
mglamanHere is a patch, worked on getting passing tests. It was mostly due to mocked data in the test itself. That, to me, shows this fixed some kind of bug if the mocking was incorrect in the test.
Comment #21
mglamanI don't know if we can make this change as it currently stands. I tried to install Commerce and it broke \Drupal\commerce\CommerceEntityViewsData.
Anything implementing this method will have a breaking change. I don't think we need to explicitly pass the storage definition, here, since it can be retrieved from the field definition?
Comment #22
mglamanSimplifying the patch. This is all we cared about from the initial summary
\Drupal\Core\Entity\EntityFieldManagerInterface::getBaseFieldDefinitions is expected to return FieldDefinitionInterface. Currently it's returning \Drupal\Core\Field\BaseFieldDefinition which satisfies both interfaces, so as original reporter mentioned: it just happened to work.
Comment #23
mglamanThis should, hopefully, be a more concise patch.
Comment #24
mglamanI wonder if this needs explaination that it is no longer used, and that is why it is deprecated? That anyone extending this class should retrieve the field storage definition from the field definition itself?
Comment #25
joachim commented> I wonder if this needs explaination that it is no longer used, and that is why it is deprecated?
IIRC a class such as this is considered internal, so anyone extending this should expect changes.
But from a core maintenance POV, it's a good idea to mark this, so that anyone making changes to this class in future knows they should avoid using these variables.
Comment #26
mglamanIt is not flagged
@internaland is a handler"views_data" = "Drupal\views\EntityViewsData",I'm assuming the message should be "get the field storage definition from the field definition, not this method." But I feel the case is small on anyone having used that method outside of core. But that is a maintainer call.
Comment #27
joachim commentedTurns out there's guidance at https://www.drupal.org/core/d8-bc-policy (though that document itself is not definitive...)
EntityViewsData is not a particular entity handler, but a default base class to be used generically. Therefore, this applies:
Comment #28
lendudeAs far as I know, 'Yes, it does'.
Internal or not, anything marked @deprecated still needs the explanation about what to do when you currently use this and a trigger_error(). It also needs a CR and the trigger error and the deprecation need a reference to that CR.
https://www.drupal.org/core/deprecation
While its a nice way to prove it's a bug, don't we want so more explicit test for this?
Comment #31
andypostfor 8.8.x deprecation format
Comment #32
vacho commentedOnly patch reroll
Comment #33
vacho commentedComment #34
vacho commentedAbout deprecation message:
I review this issue 3024461
The text should be:
@deprecated in drupal:8.8.0 and is removed from drupal:9.0.0
But @andypost suggest only "@deprecated" ? why?
Comment #35
catchComment #40
catchRe-rolled, with fixed deprecation messages, and s/entityManager/entityTypeManager/g.
Comment #41
joachim commentedThis should be postponed on #3116481: Convert EntityViewsDataTest from a unit test to a kernel test, as that rewrites the tests.
Comment #42
catchIn general we would not postpone a major bug fix on a test refactoring unless it was impossible to fix without it, but also I don't think there's any risk of this getting committed before that one. Trying to clean up the deprecation messages a bit more, and added a change record.
Comment #44
catchRe-rolled now that patch is in.
Comment #48
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #50
joachim commentedMade a MR on 10.1.
Comment #51
lendudeBack to NW for the update to the deprecation messages
Comment #53
ravi.shankar commentedUpdated deprecation messages.
Comment #54
lendudePer discussion around #26 if we should expand the deprecation message, I don't think we need it in the deprecation message, but I expanded the CR to at least provide some sort of example on how to replace this.
Looks nice and minimal otherwise.
Comment #55
catchThis looks like good clean-up.
My only question is that both this and #2898635: bundleFieldDefinitions() are not added in EntityViewsData which depends on it don't come with any test coverage. This issue is a good net reduction in code, and I'm sure the code that's executed is covered by existing tests, but at some point do we need something to show that bundle field definitions work so it doesn't eventually get regressed. Maybe that's for #2898635: bundleFieldDefinitions() are not added in EntityViewsData though?
Comment #56
mglamanThe problem is that Drupal doesn't have a bundle field paradigm yet, so I'm not sure how to test without adding that (like BundleFieldDefinition from Entity API module.)
Can we add the test coverage be added once Drupal adds that support? IMO #2898635: bundleFieldDefinitions() are not added in EntityViewsData needs another blocker – bringing BundleFieldDefinition into core which verifes this feature. Maybe that issue can handle that.
Comment #57
catchAdded some related issues on #2898635: bundleFieldDefinitions() are not added in EntityViewsData which are all heading in that direction, which also confirms we have plenty of other issues to add test coverage meaning I think it's fine for this to go in without and unblock them.
Comment #58
amateescu commented@mglaman, the BundleFieldDefinition problem is tackled in #2280639: Add the FieldStorageDefinition class to define field storage definitions in hook_entity_field_storage_info()
Comment #59
larowlanLeft a comment on the MR
Comment #60
smustgrave commentedOnly moving to NW for the open thread in the MR.
Comment #64
rodrigoaguileraDeprecatedServicePropertyTrait is used for when there is a \Drupal::service() replacement call
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Dependenc...
So deprecation with no replacement I think is the appropriate thing to do here.
I rebased against 11.x and created a new MR.
Hopefully this can be merged for Drupal 11.2.0
Comment #66
smustgrave commented11.2 is very feasible.
Probably should add deprecation test coverage though
Comment #67
rodrigoaguileraIsn't a bit too much adding deprecation tests for a protected method?
I'm curious about how to mark them so they will be removed along the deprecated code.
I tried to look for examples but couldn't find much. I guess it would be about setting up an artificial code path with a class that extends from EntityViewsData that calls the deprecated method.
Comment #68
nicxvan commentedI'm not sure about needing deprecations for protected methods.
However to answer your second question, the nature of the test will necessitate its removal when the method is removed. The test will no longer see the deprecation so it will fail when you remove the function.
You then have to remove the deprecation test, it takes care of itself.
Comment #69
kristiaanvandeneyndeYeah we could have a deprecation test here and then remove it when the deprecation is removed.
Generally speaking, I would agree that we don't need a deprecation test here, but we have way too much public API surface in core and I've been bitten before where it turned out we should have kept in mind that some people might be extending these classes.
Having said that, MR looks good to me and so does the CR. So that's great :)
Comment #70
rodrigoaguileraAdded test for the deprecation so back to NR
Comment #71
kristiaanvandeneyndeAwesome, thanks!
Comment #72
quietone commentedNo unanswered questions here, updated credit and hid files.
Comment #73
longwaveThe suggested changes make sense to me, and if anything it makes the code more readable anyway.
Committed 42fc80c and pushed to 11.x. Thanks!
Also published the change record.
Comment #76
longwaveComment #78
pixiekatIs there a backport for 10.6 for this? None of the patches work and upgrading from D9 is broken.