Problem/Motivation
I'm currently in the process of making an entity type translatable through an update. That entity type has views. I noticed that the view does a very nice job of updating the table of all the field, sort, relationship, ... handlers but it doesn't update the base table, so the result is a completely broken view.
Proposed resolution
Similar to baes table rename, update the base_table in the view.
Remaining tasks
Pretty sure that this won't work for revision views and the logic there in general might not work, as it would need to use different table mappings. I think that could be a separate issue.
User interface changes
API changes
Data model changes
Comments
Comment #2
berdirComment #3
berdirComment #5
amateescu commentedI looked a bit at the failures and I think that at least the
test_view_entity_test_dataview used by\Drupal\Tests\views\Kernel\EventSubscriber\ViewsEntitySchemaSubscriberIntegrationTest::testDataTableRename()is wrong.It is supposed to be a view of a translatable entity type, so I just tested manually by creating a view of nodes through the UI and the base table of the view is indeed the data table of the entity type, not its base table.
$this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']);this assertion from that test is also wrong. In the same view that I created earlier I added the ID field and, surely enough, it was using the data table to retrieve it, not the base table :)In conclusion, the "needs tests" part of this issue is actually "fix broken tests" :)
Comment #6
berdirRight, that's what I thought. And for the same reason, the logic about keeping base fields on the base table is also wrong, we need to move that to the data table as well, the only exception are fields that only exist on the base table. Which is only the UUID.
Had to hold myself back *really* hard to not change all those assertEqual() to assertEquals() because they already use the new argument order of expected, actual and then the errors are vey confusing.
Comment #7
amateescu commentedHow come we can change this assertion before running the updates and not get any fails? The actual view has
base_table: entity_test_updatein the yml file.Comment #8
berdir@amateescu: The first thing in that test is to make the entity type translatable and apply updates, then we check. By default, it is not translatable, so using the base table in the view yml is correct.
Comment #9
amateescu commentedAhh, you're right, I wrote #7 from memory, without looking at the actual test method.
Comment #10
dawehnerDoes this bug basically mean we need some more kind of integration level testing?
Comment #11
berdirI'll try to extend the test by executing the view, as without the correct conversion, it is completely broken.
Comment #14
berdirOne year later :)
So this was fun. I added code to the test to actually execute the view and it failed horribly, completely broken query, both with and without the fix, there were no fields, so it was a SELECT FROM ... query. There is a field but it did a fallback to the broken plugin and didn't add any fields to the query.
First thought it was because of a stale views data cache, which also was the case but that wasn't enough. Turns out, EntityTestUpdate did *not* have a views_data handler. So we've been testing views with an entity type that did not actually have any views integration ;)
Adding that fixed it for some test methods, but it started to fail on the crazier ones like testVariousTableUpdates(), because we actually started to run code that we didn't before, for example the views cacheability calculation. And in test test, we do several iterations of entity definition changes and then reseting it again. Including enabling and disabling revisions and somehow, that results in base_table of revision views to be NULL and then the next time they are saved (after updateEntityTypeToRevisionable()), it fails with an exception on save.
Stay tuned for part 2.
Comment #15
berdirAh, found the problems I think.
The first was a test problem. updateEntityTypeToTranslatable() also sets the revision_data_table key if the entity type is revisionable, but updateEntityTypeToRevisionable() didn't do that if the entity type was translatable already, so calling it in that order resulted setting an empty base_table as having a revision data table is required/expected when being translatable.
The second was an actual bug. When going from revisionable + translatable to neither, we detected that as a REVISION_TABLE_REMOVAL *and* REVISION_DATA_TABLE_REMOVAL. But the revision data table tried to switch it back to the revision table and runs after disabling those views. revisionRemoval() already looks at the revision *and* revision data table, so I changed the conditions so that REVISION_TABLE_REMOVAL is only triggered if an entity type is still revisionable.
A test only patch is likely not very useful, it would fail on the first wrong condition already and/or fail completely without a bunch of the necessary fixes.
Comment #16
dawehnerInstead of saying what happens, could we document why we need to invalidate?
Comment #17
berdirFair point, something like this?
Comment #18
dawehnerThe comment talks about the concept of revisioan-ble, is this a new concept just berdir understands?
Nice, this does make sense.
Shouldn't we check somehow the result of that as well? Maybe check whether
$view->build_info['fail']wasn't set?Comment #20
berdirComment #21
berdir#18.1 That's revisionable with a moan. As in, the sound that Drupal makes when it has has to update all those views.
#18.3 Will have a look that, but preview() explodes with an exception when a query fails, not sure when fail would be set.
Did a reroll, pretty ugly conflicts and I can only assume that a bunch more will come with the current changes and refactorings on that handler. Had to add a lot of extra views to the changed-views assertion because we're now updating things that we didn't do before.
As pointed out earlier, on HEAD, we are still testing all those changes *without a views data handler* on this entity type, which means that many things aren't reflecting how it really works. One specific example is that I wasn't yet able to fix \Drupal\Tests\views\Kernel\EventSubscriber\ViewsEntitySchemaSubscriberIntegrationTest::testRevisionDisabling(). What's happening is that the view doesn't get saved, because as part of saving, we build the entity views data that is now being cleared as part of doing this change. However, in \Drupal\views\EntityViewsData::getViewsData(), we have the old problem that the table mapping returns confusing data, specifically, because the entity type hasn't been updated yet, it still returns a revision_id, that then results in a PHP notice on this line:
$this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]);. The problem is that we fetch that information as part of the event, which happens before the information is saved *and* before the instantiated entity handlers are dropped.Adding an isset() there seems like a hack, changing \Drupal\Core\Entity\Sql\DefaultTableMapping::getFieldNames() to only return fields that actually exist as field definition, even if they are keys, could mask some other errors/inconsistencies. I tried updating \Drupal\Core\Entity\Sql\SqlContentEntityStorage::onFieldableEntityTypeUpdate() so that it updates the stored entity type and table mapping like onEntityTypeUpdate does (no idea why it doesn't do that right now), but that didn't help.
Comment #23
amateescu commentedFinally managed to take a look at this, sorry @Berdir for the delay :/ The patch looks very very good to me, it clearly shows all the deficiencies of this subscriber and its test coverage in HEAD.
For the problem detailed in #21, the fix is to make
\Drupal\views\EntityViewsDataandviews_views_data()use the "active" entity type and field storage definitions.That means we're blocked on #3056816: Installing a new field storage definition during a fieldable entity type update is not possible, so posting a combined patch as well to prove that everything works nicely together :)
Comment #26
amateescu commented#3056816: Installing a new field storage definition during a fieldable entity type update is not possible was committed (only to 8.8.x for now, but it should be backported to 8.7.x), so here's a fix for the remaining test failures.
Comment #27
amateescu commented#3056816: Installing a new field storage definition during a fieldable entity type update is not possible has been reverted and the patch that will be re-committed won't have the cache clears needed by this issue, so we need to add them here.
Comment #29
amateescu commentedA new test method was added to
EntityViewsDataTestin #2973137: EntityViewsData missed revisionable validation so we need to apply the same changes as the ones from the interdiff in #26.Comment #30
amateescu commented#3056816: Installing a new field storage definition during a fieldable entity type update is not possible was re-committed so the non-combined patch should be good to go for 8.8.x at least.
Comment #31
amateescu commentedRerolled after #3056816: Installing a new field storage definition during a fieldable entity type update is not possible, which was re-re-committed with the necessary cache clears.
Comment #33
andrew answer commentedPatch #31 re-rolled.
Comment #35
andrew answer commentedPatch #33 re-rolled for D 8.7.x.
Comment #36
andrew answer commentedFixed inconsistences (tested on live site).
Comment #37
hardik_patel_12 commentedPatch #36 re-rolled for 8.9.x-dev.
Comment #40
amateescu commentedThe changes in #36 were wrong, the entity manager was deprecated and shouldn't be used anymore.
Here's a proper reroll for 9.1.x. Some tests are still failing and needs to be looked at properly so leaving at NW.