Problem/Motivation
When creating or editing a view, there are duplicated field handlers in the UI, making the adding of fields more difficult / confusing than necessary for the user. The reason for this is that some fields (for example nid and revision ID for nodes) are both in the base table and the data table.
Just need to edit a view (such as the front page) and hit the "Add" button on fields. Notice the duplicate fields.
Proposed resolution
Filter out fields that are both in the base and data tables and only add them for the data table.
Remaining tasks
Review patch. Fix tests. Commit.
User interface changes
No duplicate fields.
API changes
The fields in views that may have been used from the base table are now always used from the data table. Updates included for views fields.
Comment | File | Size | Author |
---|---|---|---|
#247 | 2458223-247.patch | 2.03 KB | catch |
#245 | test.patch | 1011 bytes | catch |
#235 | interdiff.txt | 8.31 KB | dawehner |
#235 | 2458223-235.patch | 43.27 KB | dawehner |
| |||
#226 | 2458223-226.patch | 42.83 KB | dawehner |
|
Comments
Comment #1
Dom. CreditAttribution: Dom. commentedComment #2
dawehnerIts kinda what
core/modules/node/src/NodeAccessControlHandler.php:135
was built for. I guess we need to add the node type there as well to check for 'administer nodes'?Comment #3
Dom. CreditAttribution: Dom. commentedBut then a field view that displays the content type will not render the same for admin and none admin. This looks like a regression to me.
Comment #4
jhodgdonDom: in Drupal 8, views of entities must obey the entity access permissions, so yes they can look different for admin and non-admin users. That is not a regression, that is a security decision.
But I think the node type is a field that should be visible to everyone, not just administrators... Let's see... Hm, it looks like NodeAccessControlHandler::checkFieldAccess is only special-casing Edit operations... so I don't know why regular users would be blocked from View on the Node Type field?
Comment #5
Dom. CreditAttribution: Dom. commentedMy bad, there are actually two fields named "Content: type" :
first one is formattable (label, full entity, ...) and is not accessible to non admin.
second one is not formattable and consists in the content type display name. This one is accessible to users.
(see attached example view at path: /test-path)
Therefore, a correct solution for user experience would be to rename the first one to "content type entity" and the second to "node type" or something like this.
Comment #6
catchBumping to normal, but the duplicate naming definitely looks confusing.
Comment #7
Dom. CreditAttribution: Dom. commentedIt looks like the two fields in Views UI are created from this code in \Drupal\node\Entity\Node:
Changing the label and description here changes both field name in views :
Anyone can help me on that to give me a clue on how to solve this ?
Comment #8
jhodgdonBy looking at the checkbox HTML code, I determined that one of these is the database field node.type. The other is the database field node_field_data.type.
We also have more duplicates:
- both node.nid and node_field_data.nid showing up as "Content: Node ID".
- both node.vid and node_field_data.vid showing up as "Content: Revision ID".
I thought that on another issue we got rid of the database table fields from the base table, so I'm not sure why this is still there. @dawenher, any idea? Is that issue still open? Did its patch actually get committed? Am I hallucinating?
I just tested this on simplytest.me using the latest -dev...
Comment #9
jhodgdonTalked to @dawehner on IRC about this.
On #2429447: Use data table as views base table, if available., we changed Views to use the entity data table as the base table in Views. I thought we had also removed the duplicate fields there, and made it so the base table isn't joined unless it's actually needed, but apparently we didn't?
Comment #10
Dom. CreditAttribution: Dom. commentedI guess this is not something I can work on as per a Novice tag ?
Comment #11
dawehnerYou can certainly try to work on it. The novice tag is just an indicator for issues which are good for getting started.
The code which defines those fields is in
core/modules/views/src/EntityViewsData.php:209
.There you would have to exclude the fields on the base table, which also exists on the data table,
not sure how to exactly do that.
Comment #12
jhodgdonWe do not have any rules on who can work on which issues! If you would like to work on it, and think you can solve the problem, please do. But this issue is probably a bit complex, so it may not be the best choice if you are new to Drupal 8 development.
Comment #13
Dom. CreditAttribution: Dom. commented@jhodgdon yes indeed I'm totally newbie at D8 Core dev. Thus maybe not a good point I work on that.
However, isn't therefore this issue related ? (see todo in code)
#2337511: EntityViewsData: "@todo We should better just rely on information coming from the entity storage."
Comment #14
xjmHm, I think this is major.
Comment #15
dawehnerYeah I think its related but not necessarily the same issue.
I'll give that issue a try.
It is certainly a user experience improvement, but I don't really see yet, why the initial report claims that one of the two ones works different than the other one.
Comment #16
xjmRetitling to the scope that I think is major.
Comment #17
jhodgdonYeah, and given it is not just Node that presumably has them...
Comment #18
dawehnerThis removes the duplicates for now, let's see whether this causes some failures.
Comment #20
dawehnerI guess this is it.
Comment #21
jhodgdonLooking good! A few comments:
a) Suggested rewrite of the comment in EntityViewsData about skipping fields... currently says:
My suggestion:
Some fields (for example, Entity ID) appear in both the base table and data table, or in both the revision table and revision data table. With the exception of the langcode field (which is different in the two tables -- base language vs. translation language), these fields have the same values in both tables, corresponding to an entity base field. To avoid confusing duplication, only add them in the data tables.
And just below that:
b) I think you can use in_array instead of array_search -- should be a bit more efficient and it just returns TRUE/FALSE which is what you need here.
c) To me the if() statements would be clearer (and possibly slightly more efficient) if they were reordered and multi-line. I'd go: if ($data_table && table === $base_table && [field name is not langcode and is in the field names array]) rather than the order here, and similar for the other one. This seems slightly more organized. But, not a big deal.
d) A UI test that verifies there aren't duplicates in the Field or Filter or Sort lists would make this patch more convincing. Not sure we need that, but if we don't have that, we need to look at the UI for Node and other entities manually and verify there are not any remaining duplicates to be addressed.
Comment #22
dawehnerThis indeed sounds way more fluent to read. Thanks a lot!
Ha, while working on it I stumbled upon the return value of
array_search()
and wondered why I never had problems with it.Fair point! It was more of a organic growing of the conditions.
Sure, let's do that.
Comment #23
jhodgdonRegarding the test, I was thinking about something more systematic. Would it be possible to do something like this, conceptually:
Comment #25
andypostJust faced that user:id is duplicated too in #2456691-3: User email field need to use Field-Entity-aware formatters in Views
Comment #26
dawehnerWell, possible is everything, but the question is whether we want to do that. For me tests are about catching problems ... but these problems will be always catched already by the unit test, which have a way better developer experience due to its faster feedback cycle.
Comment #27
dawehnerRerolled ...
Well, I'm not convinced that having that super crazy test coverage of #23 is worth, as explained before.
We do have now unit tests which cover that problem, which allows for a very fast feedback cycle, compared to a slow UI test.
Comment #28
xjmComment #29
jhodgdonIf we had unit tests that covered this problem, why did it happen?
Comment #30
dawehnerWell, because we introduced the unit tests as part of this issue ...
Comment #31
eiriksmTested the patch and it works great.
I just redid a couple extremely minor coding style changes:
- Use same syntax for arrays at least in the same file.
- Use consistent string format (single quotes instead of double quotes).
If you think that is unnecessary (or wrong), then I won't be the one to complain. No offence taken :)
Other than that, it looks good.
Comment #32
jibranI agree with #27. This is ready just a minor confirmation for sanity.
Can we please add a quick assert about uuid because it's the only field which we still get from base table?
Comment #33
eiriksmSince I already had the file open.
Added that, although that were not duplicated before the patch. On the other hand, revision ID was actually duplicated and with no tests. So I added that as well.
Comment #34
plachI think we can use
TableMappingInterface::getFieldTableName()
for this.Comment #35
dawehnerSounds like a good idea! I guess we still have to special case 'langcode', but the rest should be alright!
Comment #36
eiriksmLike this?
Attached also interdiff and test-only patch
Comment #37
eiriksmHeh, diffing the wrong way around. And setting it to RTBC on top of that. Sorry about that. The interdiff was correct though. Still uploading it here for better clarity :)
Comment #38
plachYes, that's idea but
TableMappingInterface::getFieldTableName()
already wraps all that logic and will return the data table if defined, so I suspect that code can be simplified even more.Btw, you aren't supposed to RTBC your own patches :)Comment #43
eiriksmHere is a new patch. It's a little bit shorter again.
To me it seems we do not need to take care of fields that are in "both the revision table and revision data table" as this is not duplicated in the GUI for me. (screenshot attached without patch applied). So I also removed those assertion changes from the unit tests (and updated the comment about it).
Comment #44
dawehnerThat honestly is confusing, don't $table_mapping->getFieldNames($table) already ensure the table we are dealing with?
Comment #45
eiriksmHm, you are of course right.
However. If we simplify it to only
Then it will no longer return nid and vid and so on duplicated. But it will also not return other base fields either. Like type and uuid. Actually the last patch would in theory also exclude type if it were not in the data table.
So I am a bit at a loss here. Are you saying we should not use getFieldTableName as suggested in #34?
To me the most "robust" solution is still in #33, but I would love to simplify some more if someone could give me some additional pointers? :)
Comment #46
plachIsn't the following enough?
Comment #47
eiriksmYou mean replace
with
?
Because that does not work so well.
Sorry if I misunderstood you.
Comment #48
plachI think I meant something like this:
Comment #49
dawehnerNote: This potential has potential, but it seems that the support for revisions will cause more problems.
@eiriksm
So it seems to be that we should basically
$field_table = $table_mapping->getFieldTableName($field_name);
and always use
$table
instead of$field_table
.In case you are at the sprint, feel free to come over at the sprint at any point.
Comment #50
eiriksmNot at the sprint, no.
Tried to apply the logic in #48 but that also fails to add the field mapping for all base type fields (for example sticky). That is, if I understood you correctly this time... :)
I will be afk until at least tomorrow, so feel free to take over at any time :)
Comment #51
jhodgdonStatus update due to recent comments.
Comment #52
eiriksmHere is a patch that also adds back the logic for revisions. I changed the unit tests so they don't expect the type to be returned from the base table, but from the data table.
Comment #53
jhodgdonThis issue desperately needs an issue summary update. The current summary has very little to do with the current patch.
Comment #55
eiriksmUpdated patch that also should fix the test failures.
Will update the IS now.
Comment #56
eiriksmComment #57
alexpottComment #58
dawehnerUUID is not part of the revision table
Comment #59
gokulnk CreditAttribution: gokulnk at Azri Solutions commentedUpdated the Issue summary as description about content type and permissions was confusing.
For site-developers who would come to this issue page, I think we should add a message saying "Using either of the displayed fields should be fine and shouldn't affect the functionality as such."
Comment #60
jhodgdonThat is not true golunk. Some of those fields are going away.
Comment #61
eiriksmI meant to post this sooner, but the reminder disappeared in my inbox :)
Removed the check for uuid in revision table
Comment #62
jhodgdonI'm glad to see this making progress again!
I tested this patch using simplytest.me, and have verified that it fixes the problem. Great!
I also took a look through the entire patch, and I had some questions/comments:
This change to several views doesn't seem correct -- it was previously the ID of the *revision* and now I think it is getting the ID of the custom block instead of the revision ID? Unsure, but changing from block_content_revision to block_content_field_data doesn't seem like the right change?
Coding standards: should be a , after 'uuid' right?
I am not a huge fan of the asymmetry here. Why not use in_array() for the revision table too? I guess it is OK as it is...
How come 'type' and 'id' don't have to be removed from entity_test_mul? I think the fact that they're in both the base table and the data table is precisely what this patch is supposed to be fixing? This makes me not trust the test, since it passed apparently.
This looks wrong to me. Aren't most of the fields supposed to be on the property_data table? looks like it's just looking for 'id' there? If this passes, I think we have a problem.
In the first bit here, it looks good -- we take out the tests asserting the 'id' field is on the base table, and add an assertFalse instead.
Maybe we should also add an assertFalse to the second one too, to assert that the 'type' field doesn't exist on entity_test_mul?
Again I don't see how this can be right.
Again should we add an assertFalse for the 'type' field here?
and assertFalse() on the 'id' field?
Comment #63
jeqqComment #65
jeqqUpdated the patch.
Comment #66
dawehnerThe ID of the revision of stored in the 'revision_id' column, so this is not the problem, but I agree we should change just the lines we actually have to fix.
Yeah
I like that argument :)
Yeah that code doesn't run often, no need to micro-optimize
Well, the thing is that this test tests ONE of many possible implementation of the table data ... this one is storing the type as part of the property_data table, I mean there is no requirement that has to be that way.
EVD should not depend on that internal detail ... the table mapping defines where those fields are stored, we just ensure that its not appearing twice.
Comment #67
jhodgdonBut look at the code in item #4 that I was referring to in "don't type and id have to be removed"....
It looks like type and id are in both the tables. Why is that not wrong?
Comment #69
jhodgdonI really do think this patch has problems, as per my previous reviews... no one has answered #67 yet and explained why this is not wrong. I don't trust the tests...
Comment #70
jeqqComment #71
dawehnerAs written before, there is no reason for us here to mock the exact same behaviour of core. The views integration was not written against THE way we store those fields,
but rather against the interface. The interface doesn't disallow you to have the same fields in multiple tables.
Maybe the next point is more convincing, the node type is actually in both tables:
Comment #72
alexpottWe're going to need a hook_update_N() for this right?
Comment #73
dawehnerAbsolutely, people might have used the wrong field.
Comment #74
jhodgdonRegarding those test classes, I was under the impression that they were using the EntityViewsData classes. If they aren't, then I take back everything I said. :)
Comment #75
jhodgdonAnd if they are not using the base EntityViewsData classes, then I don't know why they are in this patch?
Comment #76
dawehnerThey are using this class
Comment #77
jhodgdonHumph. So I must have been looking at the interdiff or an earlier patch rather than the final patch here and gotten confused about this. Sorry for the noise.
So I think this is ready to go, except that it definitely needs an update function, and a test for the update function.
Comment #79
catchThe API change here for exported views feels worth doing to minimise confusion up until RC (assuming we have an upgrade path).
Tagging RC target since I don't see the patch getting in as-is after release candidate, we'd have to provide a BC layer for existing views - but could hopefully still not show the options in the UI or allow them to be configured for new Views.
Comment #80
jhodgdonOK, this is Needs Work because there are no update functions in the patch.
Also tagging Usability, because the UI is really really really confusing at this point.
Comment #81
eiriksmI started the upgrade path some weeks ago, will take another stab at it at the sprints today.
Comment #82
eiriksmFirst pass at an update function. Works for me at least. I saw earlier someone mentioned a test for the update function. Is there any good examples of this, if this is needed?
Comment #83
lokapujyaHere is a start for an update test extending UpdatePathTestBase.
Comment #84
lokapujyaThis is going to fail.
Instead of this block of code that extracts the base table, the base table can probably just be hardcoded.
The test view can be simplified.
Comment #86
catchTagging rc deadline. Not sure what we can do about this after rc if it's not in before.
Comment #88
lokapujyaThe name of the view in the update test was wrong.
I get an error in the test locally when calling: $view->get('display').
Comment #91
geertvd CreditAttribution: geertvd at XIO commentedLooking into those fails.
Comment #92
geertvd CreditAttribution: geertvd at XIO commentedComment #94
geertvd CreditAttribution: geertvd at XIO commentedreroll
Comment #95
lokapujyaShould the test view needs to start with node as the base table in order to properly test the upgrade path?
Comment #96
eiriksmNo, the _field_ should use the node table, which it does. The problem is that you can both add fields for the nid field in the base table and in the data table. And we want to convert fields that are added with the base table as a table, to using the data table as a table.
Although, pretty sure this will prove the point as well. But it is less correct IMO :)
Comment #99
lokapujyaOops, I agree. Reverting that last change.
Comment #102
jhodgdonThis is looking pretty good, thanks for all the work!
However, there are several problems in this patch that definitely need to be addressed:
It seems to me that the logic should be something like:
If we are doing the base table or revision table, and the field exists in both the table I'm doing and the corresponding data table (data table for base, revision data table for revision), then skip this field.
But the logic here is instead:
If we are doing the base table or revision table, and there is a data table defined, and the field is not 'uuid', then skip it.
This seems wrong to me. Can we fix it to actually do the right logic?
By the way, I'm not actually sure that if there is a data table and a revision table, there is necessarily also a revision data table... is that always true?
This comment needs a bit of adjustment. How about:
Update views that use previously-duplicated fields that have been removed.
This is wrong. It needs to move it to the revision data table instead, as you can see in some of the YML files in the patch, such as:
It would probably be illuminating to run the update function here on all the core Views and see what it does... right now it would not produce this output, as it would move block_content_revision to be the data table, which doesn't have a revision_id field and this would be broken.
Comment #103
lokapujya1.
vs.
I think what you mean is that just because the entity has a data table, the field doesn't necessarily have a data table. So we need to check if the field has a data table? Possibly, this can section can be refactored in this other issue? #2337511: EntityViewsData: "@todo We should better just rely on information coming from the entity storage."
Comment #104
jhodgdonAh, I see what you mean. Disregard comment #102, item 1... however, I think if $table is the revision table, we should only skip the field if the revision data table exists, right?
Also, one other thing. The langcode field is actually different between the base table and the field table -- it has a different meaning. So we need to have 'langcode' in all 4 tables. It is not actually a duplicate... We had that in a patch around #62, but it apparently got lost. The tests that removed 'langcode' from various stuff probably shouldn't have either.
Comment #105
lokapujyaI think it's always true that if there is a data table and a revision table, that there is also a revision data table. At least it's supposed to be that way unless something gets messed up. But since we setting $revision_data_table, it wouldn't hurt to check it. So, what you want is the following?:
Comment #106
lokapujyaFor 3,4, it's probably better to write the test first. I think the steps are: import the test view into a site. Then add a relationship to revisions and add a field from the revisions table. Export the view to replace the test view. Assert that the field gets updated to the revision data table.
Comment #107
jhodgdonRE #105, right, but also we need to make an exception for 'langcode'.
Comment #108
lokapujyaI was going to do #106, but revision table fields are not duplicated in the UI and I don't know how to create a view (using the UI) that has a field using the node_revision table. Somehow, the test_block_content_revision_id test does have one though.
Comment #109
jhodgdonIf you look at the screen shot in the issue summary, it sure looks to me as though revision fields are duplicated in the UI just like base fields... oh wait, that is Revision ID but it's a field on the base/data table, right. So, let's see. You're right! The revision fields are not duplicated in the UI. Interesting. I wonder why that is?
Hm... So I looked at the Views UI for adding a new field when the base table on a view is node revisions, and it looks like the fields from node_revision are not present at all. Only the fields from node_field_revision are present.
Is that a bug?
Comment #110
dawehnerThanks all for the really important work on this issue!!
I'm curious how the entity system decides which fields are just in the base table. Do we have that hardcoded to be just the UUID? Otherwise we might need some more complex logic.
Urgs, that feels fragile. What would happen if any custom / contrib code adds new fields to the base table as well? What about using the views data to always ensure that the destination field also exists in the views data?
Comment #111
jhodgdonHm. Well for one thing, there are the entity keys, like 'id' and 'uuid' and 'bundle', but any entity can assign these to be different database table names in the entity annotation (for instance, Node uses "type" not "bundle"). So the database table field name might not be 'uuid' for all entities.
So to get more dteails, I looked at SqlContentEntityStorageSchema::getEntitySchema(), which is what sets up the entity schema tables.
This calls EntityManager::getFieldStorageDefinitions [although note that technically any entity type can use its own manager instead of the default EntityManager and could override this method! In practice, in Core there are no overrides.]. This method:
a) First finds the base field definitions, which (again in the default manager) come from the baseFieldDefinitions() method on the entity type, plus any that are provided by hook_entity_base_field_info(), and then lets modules alter them via hook_entity_base_field_info_alter().
b) Then calls buildFieldStorageDefinitions(), which (again in the default entity manager) calls hook_entity_field_storage_info() to get storage definitions for any of the fields added by the hooks in (a), and then lets modules alter the storage definitions via hook_entity_base_field_info_alter().
So back to SqlContentEntityStorageSchema::getEntitySchema(), the next thing it does is to call the method getTableMapping() on the SqlContentEntityStorage class that is injected... let's see what SqlContentEntityStorage::getTableMapping() does:
a) Calls EntityManager::getFieldStorageDefinitions() to get the field storage definitions (see above). Filters this down to only non-multiple base fields that don't have custom storage defined in their definitions.
b) From this list, makes a list of all fields, key fields (defined as the entity keys for ID, revision, bundle, uuid, and language code), and revisionable fields (those marked as revisionable by the entity in its baseFieldDefinitions method presumably and/or altered to be such). Also makes a list of "revision metadata fields", which are hard-wired to be revision_timestamp, revision_uid, revision_log (if they exist on the entity).
c) Then does some logic:
- If the entity has no revisions and is not translatable, all fields go on the base table.
- If it is revisionable but not translatable, all fields except revision metadata fields go on the base table, and the revision table gets the revisionable fields plus ID and revision ID. [Hopefully the revision meta-data fields are marked as revisionable!! Otherwise they might not get added. This is most likely a logic bug in this function I think, because the revisionable/translatable case below explicitly adds the metadata fields and this case doesn't.]
- If it is translatable but not revisionable, the base table gets the key fields and the data table gets everything but UUID.
- If it is revisionable and translatable, the base table gets the key fields; the data table gets everything but UUID and the revision metadata fields; the revision table gets ID, revision ID, language code, and revision metadata fields; the revision data table gets ID, revision ID, language code, plus all revisionalbe fields except revision meta data fields.
So back to SqlContentEntityStorageSchema::getEntitySchema(), it takes this mapping and finds the column names defined for each field from the field type definitions, and adds them to the table schema for each table.
So. In short: no, we cannot assume anything. However, Views is already making some assumptions about storage I think, so assuming something here may not be much worse. But we should definitely NOT assume that the database column names are specifically uuid or langcode, because entities can easily override this in their entity key meta-data.
Hope this helps...
Comment #112
jhodgdonOK. That was kind of a long comment. Here is what I think we need to do:
a) For sure, don't assume that 'uuid' is the actual database field name. We should at least get this from the entity definition's entity keys.
b) We don't have to worry about the "not translatable" cases in my previous comment because in that case we don't have a data table or a revision data table, and there is no duplication.
c) It looks like only the "key" fields can be duplicated between base table and data table. These are defined as the entity keys for ID, revision ID, bundle, uuid, and language code. However, UUID is never on the data table, and we don't want to remove langcode because it has a different meaning on the two tables. So really all we need to remove is the base table fields for ID, revision ID, bundle. Those are all that can be duplicated between the two tables.
d) Between revision and revision data, it looks like ID and revision ID can be duplicated (plus language code, but this has different meanings and needs to be in both). I still do not understand why we are not getting this duplication in the views UI.
Comment #113
dawehnerThis sounds really promising! Great research!
I think the trick is that the implicit (automatic) join from {node_revision} to anything is missing.
vs.
see
for the code which adds the implicit join from 'node_field_revision' to 'node_field_data'.
Comment #114
jhodgdonHm, that seems wrong then. I think that if we are making a view of revisions, we need the base table to be the revision field data table (just like if the view is of entities, the base table is the field data table), and we also need the automatic join to the revision table to be there, so that we can reference the language code field if nothing else (which again is NOT the same between revision data and revision tables).
Comment #115
geertvd CreditAttribution: geertvd at XIO commentedLots of info in these last few comments, is this patch heading in the right direction?
Comment #117
geertvd CreditAttribution: geertvd at XIO commentedwoops
Comment #119
jhodgdonThe patch to EntityViewsData looks like the right thing to do, yes.
Comment #120
geertvd CreditAttribution: geertvd at XIO commentednow it should be ok.
Comment #125
jhodgdonAside from the tests failing... ;)
Comment #126
lokapujyareminder for next patch: missing a word here.
Comment #127
dawehnerIts great that you all work on this!
Comment #128
geertvd CreditAttribution: geertvd at XIO commentedFails in
Views_ui.Drupal\views_ui\Tests\HandlerTest
were related to #1832862: Make views add field scannable.Haven't figured out why
Drupal\Tests\views\Unit\EntityViewsDataTest
is failing yet (it's not failing locally)Comment #130
geertvd CreditAttribution: geertvd at XIO commentedThis should fix it
Comment #132
jhodgdonThis is looking pretty good! I reviewed the code carefully and I have 1 big/important thing, and a bunch of docs nipticks to report.
Also I tested it using simplytest.me and it does fix the field duplication problem. However, filters, sorts, etc. are still duplicated (see below).
This comment was confusing to me when I read it.
How about:
To avoid confusing duplication in the user interface, for fields that are on both base and data tables, only add them on the data table (same for revision vs. revision data).
So... We're only testing updates on fields. Do we also need to update and/or test filters and sorts? They have the same problems, I think? [see below for more]
id => ID [in text, "id" is like ego, superego, id; ID means identifier]
is => are
But actually, this is not *verifying*. It is *updating*.
This text will appear in the UI when someone runs update.php, so it should be clear.
How about:
Update some views fields that were previously duplicated.
Yeah, so here it looks like we are only updating fields.
I think we also need to update filters, sorts, arguments, relationships... anything else (@dawehner???)?
And we should have tests for the UI for all of those, and tests for the update of all of them. Right?
Comment #133
geertvd CreditAttribution: geertvd at XIO commentedThanks for the review. I'll work on this later today.
Comment #134
catchfwiw considering how bad the UX issue is, we've discussed this between the core committers and decided it is probably OK to land during RC without a backwards compatibility layer for exported views.
This might break some exported views, but should be a simple update. If it turned out not to be, the main change and upgrade path here is still important to do.
Comment #135
lokapujya1. Another option, but don't care that much (it is just a comment):
Avoid duplicate fields in the user interface. Only add the field from the data table.
edit: I meant for the text in the comment in #132.1, but NM.
Comment #136
jhodgdonRE #135, That is an interesting idea, but how would you even do that? That would require a huge overhaul of Views UI, which knows nothing about what the tables are. It just figures out all available fields from the tables involved in the view (plus automatic joined tables) and displays them. It doesn't know what "entities" are, much less base tables vs. data tables, etc.
Comment #137
geertvd CreditAttribution: geertvd at XIO commentedSo this patch should demonstrate the problem @jhodgdon describes in #132.5
Comment #138
geertvd CreditAttribution: geertvd at XIO commentedAnd this fixes #132.5
Comment #140
geertvd CreditAttribution: geertvd at XIO commentedComment #141
jhodgdonGood. I think we also need to add to the UI test (in core/modules/views_ui/src/Tests/HandlerTest.php ) so that we also verify that the Fields, Sorts, etc. UIs don't have duplicates?
Comment #142
geertvd CreditAttribution: geertvd at XIO commentedThis covers that.
Comment #143
jhodgdonGreat! We may need to add other handler types... not sure. Maybe 'relationship' as well? @dawehner, any thoughts there?
Comment #144
dawehnerWell, I doubt that relationship really makes sense, given that those fields should not have a relationship anyway right? For now the test coverage tests the problem the issue is describing.
Thank you @geertvd to stick with that.
Comment #146
alexpottI might be missing something but I don't see how the update could ever make this type of fix.
I think we need to add a test view to test updating a table which references the wrong revision table.
Comment #147
lokapujyaIs it even possible to create a view that mistakenly uses the node_revision table?
Comment #148
jhodgdonIt's possible to use the node revision tables... In the UI if you create a Node view, add go to add fields, you'll be given several options from node revision tables. It's auto-joined to Node.
There is also a NodeRevision wizard class, so you should be able to create one in the UI with base table being node revisions. I guess we've probably made that be node_revision_field_data as the base table by now, but presumably the base node revision table is auto-joined? Maybe not. If not that is probably a bug... actually I vaguely remember mentioning that in one of the previous 147 comments on this issue.
Not sure if that is exactly what you are asking... Anyway the Node module provides table/field data for this table, so in principle if you did it programmatically you could definitely create a view on this table.
Comment #149
dawehnerSo yeah I think it would be better safe and migrate revision information as well, than sorry
Comment #150
catchTagging this as minor version target because if it's not fixed during RC we'd have to do it in 8.1.
Would of course still be great to fix during RC.
Comment #151
eiriksmOK, seems the latest patch actually breaks quite a few different things. Since it is a good point made in #146 that we do not test why we actually have to do this change, I decided to try to replicate the view attached in the block test. It is not possible anymore with this patch attached. Also, the interface bugs out again in a different way with this patch applied.
Attached are the popup for "add relationship" for a "Custom block revision" view (before and after).
Have not tried to find out why this is yet, but just wanted to post my findings before I go home from work :)
Comment #152
jhodgdonYou said "the interface bugs out" and "this patch breaks things"... ??!?
Comment #153
eiriksmHeh, sorry about that. That was not very elaborate. And on top of that, I named the screenshots wrong (so that no_patch is really patch_applied and so on). As an excuse, I was in a hurry, and coincidentally I am not at that particular computer for the next week, so i felt had to post it quickly before I left. Anyway, enough about me :)
What I was trying to say:
Reading through comment #146 I decided to try to create one of the views touched in the latest patch (#142) manually through clicking in the interface. The particular view was a view of "Custom block revisions".
So first I did this with no patch applied (clean checkout of 8.0.x branch). Everything seems fine. Among the things I do is add a relationship (the view I wanted to "mimic" had this). Precise steps to reproduce:
- Go to admin/structure/views
- Add new view
- Select custom block revisions.
- Click "add relationship".
This is a screenshot from when I clicked "Add relationship":
That screenshot also seems fine.
Now, following this logic, I wanted to create the same view with the patch applied, and then just export the view, and inspect the differences. Among the things I need to do (again) is click "Add relationship". This is a screenshot from that:
So to reiterate: The screenshot above depicts a buggy interface, even though I applied the patch in #142.
So what I meant with "the interface bugging out in a different way" was this:
1. Yes, with the patch applied it no longer has the bug with duplicated node ids (and so on, as reported).
2. With the patch applied it suddenly has other bug(s) (duplicated relationship fields being at least one of them).
So what I meant with "this patch breaks things" was point 2 above.
So what I am trying to say is that the patch in #142 (that we previously RTBCed) indeed needs work (and not only extra tests).
Also, I have not had the time to dig into why this happens yet, just thought it might be helpful to post while it was still fresh in my memory.
Also, thanks for pointing out what an unhelpful comment my last comment was @jhodgdon :)
Comment #154
lokapujyaThe screenshot in #153 doesn't show a duplicate block. The problem I see in that screenshot is that the Description says "Error:missing help." Is the patch causing that? We should confirm that.
Comment #155
jhodgdonYeah, it looks like with/without the patch, the Relationship list is the same in both cases, with two fields having identical Names but different Help; but with the patch, the help is labeled as "missing".
That's odd... just to be sure did you apply the patch to an already-running site? If so might need to clear a cache or rebuild the something or other, just to make sure.
Let's see. No, you're right; I made a clean site on simplytest.me with the patch installed and got the same "Error: missing help" message, which does seem to be introduced by this patch.
This happens when the views data doesn't have a 'help' component on (in this case) the relationship.
Comment #156
geertvd CreditAttribution: geertvd at XIO commentedI will be on a local sprint tomorrow, so I would have time to work on this, unless @eiriksm wanted to pick this up.
Comment #157
eiriksmGo for it :)
I won't have the time needed in the next few days anyway.
Comment #158
geertvd CreditAttribution: geertvd at XIO commentedRegarding #153, I think that's related to #2410275: EntityViewsData should add relationships to revision tables so I just had to make some changes
BlockContentViewsData::EntityViewsData()
for that, which can be removed once #2410275 is fixed.We might need extra test coverage for this part.
For #146 I'm not actually sure if we need to make that change there, since IDs and revision IDs don't actually have duplicate handlers if used with a revision table. Running
\Drupal\views_ui\Tests\HandlerTest::testNoDuplicateFields()
without the patch applied should actually demonstrates that.I'm totally not sure if that's the right thing to do so I'd love to hear someone else's opinion on that.
The update_hook was failing when it encountered a view that has a revision table as base table, since it couldn't find the entity type matching it's base table. For now I fixed this with a simple
isset()
.Comment #161
geertvd CreditAttribution: geertvd at XIO commentedDisregard this for now, the handlers mentioned don't actually have a revision table as base table. I'm going to work some more on this tomorrow.
Comment #162
geertvd CreditAttribution: geertvd at XIO commentededit: wrong ticket
Comment #163
geertvd CreditAttribution: geertvd at XIO commentedNot a lot of progress today, won't be able to work on this the next couple of days.
Comment #165
lokapujyaWhat if we include both relationships?
Comment #166
lokapujyaOr, maybe we just need to update the tests to expect: block_content_block_content_revision_id ? Actually, it's the expected result that has block_content_block_content_revision_id; the actual result is missing that column.
Comment #167
lokapujyaComment #168
lokapujyaReroll + Possible solution to failing tests.
For the reroll, I needed to increment the update function to views_update_8004(). Actually, the reroll change shows up in the interdiff.
Comment #171
lokapujyaComment #179
Gábor HojtsyRerolled again.
Comment #180
Gábor HojtsyComment #181
Gábor HojtsyComment #183
Gábor HojtsyAdd block_content update path to ensure that the added revision data table is added to the definition properly as well.
Comment #185
Gábor Hojtsy@plach pointed out that the revision data table is automatically added with the same name already, so we don't need to manually define it. (The entity definition update checker uses the raw info data, so it does not actually considers the automatically added table). We can avoid branching out to solve that preexisting problem by not adding the table name manually.
Comment #187
geertvd CreditAttribution: geertvd at XIO commented@Gábor Hojtsy I think I manually defined it in #158 to avoid this from happening.
Comment #188
Gábor Hojtsy@geertvd: right, the problem with manually defining it is it conflicts with how the entity definition update system thinks the definition is and then it says it needs updating, while it is not, because it was already implicitly defined. Just implicitly defining it works well with entity updates, because the already implicitly present revision data table just keeps being there. However Views uses the entity data directly, instead of the processed data, so it does not have the revision table for block_content. That would require fixing in QueryPluginBase::getEntityTableInfo() and possibly elsewhere AFAIS. The question is whether we want to futz with how views uses entity definitions or how the entity definition updater uses entity definitions :)
Comment #189
Gábor HojtsyI think the entity update manager is right in considering the explicit data only because it needs to work well in update functions, and the implicit behavior may change, the explicit data is the only given. So probably views need to be fixed to use the processed entity information.
Comment #190
Gábor HojtsySo I don't have time anymore to figure that out BUT @plach said we should just use the table mapping to decide on the table, so giving that a try. That would just (hopefully) do the same thing with less code, not fix the other outstanding issues.
Comment #192
dawehnerWhile @plach is probably right, this is something we can still work on later, can't we?
Worked on some minor bits + fixed the failure in a different way than #190. Using the table mapping should be IMHO done consistently throughout the entire file.
Comment #194
dawehnerThis patch is no longer allowed to be red, okay?
Comment #195
plachFound a few minor things and a couple of more concerning items.
Why don't we get the table names directly from
SqlContentEntityStorage
? It has all the required methods, this way we don't have to replicate the logic here.Missing
get
afterwhich
, I think.I don't know why my suggestion failed so badly, but I agree it can be applied in later patch, since there's much more to fix in this code wrt Table Mapping API adoption.
Shouldn't this match the update function name, that is
testViewsUpdate8004
? Maybe it's not a strict rule :)This is not enough, we should replicate the logic of
SqlContentEntityStorage::initTableLayout()
here. Setting to NW for this.Comment #196
Gábor Hojtsy@plach/@dawehner: I applied @plach's suggestion about using the table mapping because if we are to fix it later to use the right table based on the table mapping (and not our custom code), then it may change tables again if the two parts of the code don't agree on the table picked. Sounds like that is a possible further API change.
Comment #197
dawehnerTalked with IRC, we will open a follow up to deal with the table mapping and OR use SqlContentEntityStorage
Yeah I totally agree with using the table mapping, but on the other hand, I really want this issue to be fixed before the release.
Comment #198
dawehnerOpened up a followup: #2614746: Convert EntityViewsData to use the table mapping
Comment #199
plachLooks ready to me, thanks
Comment #212
plach@Gabor:
We've agreed to copy the code from
SqlContentEntityStorage
, which is what the table mapping currently relies on, so doing #2614746: Convert EntityViewsData to use the table mapping should imply no BC break / API change.Comment #213
Gábor Hojtsy@dawehner: I think an rc target major is not eligible anymore given the last RC being out, so the only chance for this would be if its critical and if it is really well explained that it will *really* not break anything, given no chance of pushing this out anymore in an RC.
Comment #214
catch@Gabor we may still commit a very limited number of RC targets between RC4 and 8.0.0, but everything's going to have to be individually looked at.
This issue is definitely on the list for discussion.
Comment #215
Gábor HojtsyYay, #fingerscrossed.
Comment #216
tim.plunkettComment #217
dawehnerThis issue also makes it MUCH easier for contrib modules to argue about views. In case they integrate with views and it provides this super odd behaviour, they
might make bad decisions based upon that.
Comment #218
xjmThis should be after the update hook, not before.
Comment #219
plachFixed
Comment #220
xjmHm, so I had posted a very detailed review that seems to be completely missing here. :( Darn.
Setting NR and assigning to myself to try to reconstruct my review.
Comment #221
dawehnerxjm++
Comment #222
xjmSo overall this is looking pretty good. Much of the size of the patch is just from re-exporting the views with the new correct base table. The main content of the patch is the fix in EntityViewsData, test coverage for it, and an upgrade path and upgrade path tests (both of which look sound).
@catch and I agreed on making this change before 8.0.0 as an exception to the general freeze because of its impact on users and site configuration. Ideally we will get it in within the next day or so.
Most of my feedback is minor, though there is one point of concern (edit: point 6 below).
So do these many changes to BlockContentViewsData indicate a bug in HEAD other than the main bug with duplicated handlers? I saw the class discussed in #158 but it did not seem to answer this. There is a lot about content blocks on the issue so maybe this is covered already -- sorry if I missed it.
Edit: or has it to do instead with #2410275: EntityViewsData should add relationships to revision tables that there is so much of the data provider that needs to be updated?
Somewhat similar to the block change, but just the one field. Is the reason that only content blocks and nodes needed their individual data handlers updated that they are the only revisionable entities in core?
Nit: missing parens on the method name.
Looks like there's a typo here with a missing word or two. Maybe this would be clearer:
Why do these only get set if the entity is translatable?
...Hmm, I swear the last time I reviewed the patch, there were some hunks like that last in the update hook as well. I remember because a not-insignificant part of my review was trying to disentangle the somewhat confusing code flow/logic.
Yes indeed. I checked #197 and there are changes to the update hook that are not included in the interdiff. @plach and/or @dawehner, can you figure out what happened there?
Let's remove this. We don't need to reference the issue that added a fix or test because that's what git blame is for. If clarification were needed, we would want to write out that clarification in the docblock, but I don't think that's necessary in this case. Also, if we did this consistently there would be thousands of lines like this littered through core.
Nit: This needs parens.
Comment #223
xjmComment #224
dawehnerI totally agree that we should work on the other issue as well, but really, these changes are related to things we are doing as part of this issue. When the views data changes, we no longer have the fields defined on the base table, so without the interdiff in https://www.drupal.org/files/issues/interdiff-142-158.txt we would have relationships on the wrong table ...
The changes in BlockContentViewsData are now basically the same as we did for node in #2429447: Use data table as views base table, if available..
The general problem is basically #2410275: EntityViewsData should add relationships to revision tables. So ideally we would have autogenerated all this stuff, but currently in HEAD we hardcode the relationships for both node and block_content.
See comment above :) We copy the logic from
\Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout()
Did a manual interdiff, and there has been no changes. Given that the hunk you posted in point 5, you might have looked onto an old patch (so before #197, because this is something which got changed in https://www.drupal.org/files/issues/interdiff_17859.txt )
Right, we have git for that!
Comment #225
jibranI think this would break the contrib default views which exist on file system if they are using old fields. We should show some kind of warning message after the update function.
Do we need a uuid here?
Comment #226
dawehnerWell, they can be updated.
Comment #227
alexpottI think we need a change record to tell module developers that any default configuration that provides a view that uses fields might need fixing.
Comment #228
alexpottApart from the missing CR this patch looks pretty good to me. Couple of comments about the update function...
Given that we're relying on the entity manager and entity type definitions - shouldn't this be a post update function?
This could be done once per entity type in the loop above - rather than once per view.
Comment #229
catchYes that ought to be a post-update function, or if there's really a reason it shouldn't be, then it needs to save the config object not the view as such.
Comment #230
effulgentsia CreditAttribution: effulgentsia at Acquia commentedWe're in the final home stretch of preparing 8.0.0, and we won't be committing the majority of "rc target" issues any more, but still leaving the tag on them to triage them for patch-release or minor-release post 8.0.0 release. Meanwhile, switching to "8.0.0 target" for issues we'd still love to get in before 8.0.0.
Comment #231
xjmThanks @dawehner and @alexpott.
But that still doesn't answer the question. Why would we not need the data table if the entity is not translatable? It seems like we would always need it? This made me nervous that we would not get the right base table when the entity was neither revisionable nor translatable. There were no comments justifying it. The issue was the same in the update hook but yet even more so. From #197:
This is a whole lot of conditional logic that could be much simpler. Sorry, don't have time to review more than that. If someone else is comfortable with it I won't block it; it's just that there are a whole lot of different cases and combinations and it made me nervous that not all combinations would receive the correct base table.
Comment #232
plachIt's the entity type that is not translatable, in that case there is no data table or, put in a different way, the base table is the data table.
Comment #233
lokapujyaI think it's because: if the content is not translatable, then it won't have a data table.
Comment #234
lokapujya#222.7.) Agreed that we can remove the reference to the issue. The UpdatePath test was cloned from EntityViewsDataUpdateTest.php (which had similar documentation.)
Comment #235
dawehnerWorked on the feedback, now about to create a change record.
Comment #236
dawehnerAdded a change record.
Comment #237
plachI think #235 addresses Jess' pending concerns.
Comment #238
xjmThat helps a good bit, yes. I tested this manually on an existing site and the upgrade was successful and resolved the issue. The CR should be sufficient I think.
I'm still nervous though about translatable or revisionable views. (Didn't have any views with revisions or translations on my site.) There are so many different code paths in the update hook that it makes it difficult to prove the right base table is picked by the update in each case. If the update somehow corrupted an existing View that would be really bad.
That said, it's better to take this risk now rather than after 8.0.0. So committed and pushed to 8.0.x. Thanks for getting this ready in time for 8.0.0 after so many months and so much work!
Comment #240
xjmAnd right as I pushed I noticed the update hook docblocks were wrong again, so here is a small followup:
#2617742: Followup: fix Views post-update hook doc groups
Comment #241
xjmAlso tweaked the CR a little, including correcting for @dawehner's mysterious ability to create CRs without a project set, and published it.
Comment #242
dawehnerEveryone has their own superpower. Mine is a bit ridiculous.
Comment #243
xjmUhoh, I think this broke Postgres:
https://www.drupal.org/pift-ci-job/84294
Comment #244
catchMIssing ordering on the test view. I don't have postgres locally but patch up in a patch testing issue: https://www.drupal.org/node/933404 if that comes back green will post here.
Comment #245
catchhttps://dispatcher.drupalci.org/job/default/38606/console is encouraging enough to post a patch here.
Comment #246
dawehnerDo you mind fixing
core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_revision_revision_id.yml
as well?Comment #247
catchAdded that.
Comment #248
dawehnerAre you awake already?
This is the list of views without sort criteria:
see https://gist.github.com/dawehner/8503816b25e24dd8eea0 but to be fair, they maybe just not have more than 1 row
Comment #249
xjmThanks @dawehner, that's helpful.
If you save a View through the UI with no sort on Postgres, does it
fatalfail or is it just ordered differently? I wonder if we should provide some sort of fallback sort if none is specified, serial ID for the given table or something, in order to have predictable ordering. I think the hotfix here will be sufficient for now just to get postgres passing, but maybe a followup.Edit: Also, we should make a habit of testing Views patches on all databases before commit.
Comment #250
plachThe PHP 7 fail looks unrelated, should we RTBC this?
Comment #251
xjmI'll commit it if you do. ;) Would like that followup though.
Comment #252
alexpott@xjm
Just a different order - it does not fail - exactly like the test.
Comment #253
alexpottI don't think a follow is necessary tbh. Postgres views needs sorts often to have expected order - we could add default sorts or we could not. I'd be in favour of not cause more magic in the views UI doesn't feel like the right direction - though otoh we add the default thing for node access so adding a default sort on the primary key could be considered the same. We definitely should try to remember to test all patches that add test views on multiple dbs.
Comment #254
alexpottCreated #2618132: When creating a view add a default sort on the primary key of the thing being viewed
Comment #255
xjmThanks!
Comment #257
xjmComment #265
tim.plunkett