Problem/Motivation
Currently, if an entity is revisionable, you are forced to make all non-ID base fields revisionable. Otherwise values of non-revisioned fields are not attached to loaded entities.
A bonus of this patch would be modifying entity definitions that include "created" properties from revisioned to non-revisioned. As values of created field types should not change between revisions.
Proposed resolution
Detect if any non-revisioned fields are on an entity and join the tables if any exist.
Remaining tasks
User interface changes
none
API changes
Original report by ivanjaros
The \Drupal\Core\Entity\ContentEntityDatabaseStorage::attachPropertyData() method is responsible for loading field data directly form entity's table.
If the entity is revisionable, it loads data from the {ENTITY_field_revision} table. If the entity is not revisionable, it loads data from {ENTITY_field_data} table.
However, not all fields has to be revisioned so there may be more fields in the {ENTITY_field_data} table than in {ENTITY_field_revision} table.
Previousely(before A13 and the #2259243: Database schema of content entities is automatically generated based on entity type and field definitions) such fields could have existed in the {ENTITY} table. Now, it is not possible anymore. That means that fields that are not revisioned has to be placed in the {ENTITY_field_data} table.
What all of this means is that such fields will never be loaded since attachPropertyData() will load the data from the {ENTITY_field_revision} table.
Comments
Comment #1
Anonymous (not verified) commentedComment #2
Anonymous (not verified) commentedComment #3
Anonymous (not verified) commentedThis patch does address this the issue by joining the data and revision table and using the data from the revision table for common(ie. revisioned) fields.
Comment #5
Anonymous (not verified) commentedbump
Comment #6
plachI'd say this is the correct solution. Maybe an optimization could be checking whether we do have non-revisionable fields, otherwise we would skip the join on the data table. Anyway, entity cache would avoid the performance penalty of the additional join most of the time.
Comment #7
plachLet's see what Berdir thinks about #6.
Comment #8
Anonymous (not verified) commentedI tihnk that we should indeed check if the entity has non-revisioned fields and store this information in static cache....but
I was just about to rewrite the patch but then I've noticed that there are these possible scenarios:
So that basically means this:
Scenario | field data table | field revision table
--------------------------------------------------------------
a | 1 | 0
b | 1 | 0
c1 | 1 | 1
c2 | 0 | 1
So in order to incorporate this into the code it could look messy(coditions) and unnecessarily long(DX).
So IMHO since we're (almost)always using the data table and joining the revision table only when needed(which takes care of overriding data from the latest revision stored in data table) I think one JOIN is not that harmful....or it's just 2AM and I'm too lazy to think about this :)
Comment #9
Anonymous (not verified) commentedThis is still present in the HEAD and it is critical for contrib modules with custom entities.
Comment #10
Anonymous (not verified) commentedThis patch works with HEAD.
Comment #11
plachComment #13
Anonymous (not verified) commentedComment #14
dpiSpent half a day chasing this one, what a DX disaster.
Updated. Needs tests.
Comment #15
dpiComment #17
dpiTrying a new approach.
Instead of always using dataTable. We will always use revisionDataTable, and include dataTable if any non-revisionable fields exist.
Comment #18
dpiComment #19
dpiMinor change to join condition.
Adding tests.
Comment #22
dpiFix other tests adding non-revisioned field to schemas and renaming due to pre-existing name.
Comment #24
plachThanks, the patch is looking good to me! Just a few minor remarks:
This line is misplaced also in HEAD, but I think the proper place would be right before the
foreachbelow.Can we expand this comment a bit? Also, the comment is not wrapping at column 80.
This 'static' terminology is not very clear to me. Can we use something more self-documenting, like
$revision_data_fieldsand$data_fields? Also, there's a surplus blank line.I'd replace "data_table" with "non-revisionable".
What about adding an underscore to improve readability? Also, the field is non revisionable, even a revisionable field could be "non revisioned" :)
Thus I'd suggest
non_revisionable.Can we use a more meaningful label here?
Comment #25
dpiThank you, I've addressed your feedback.
Changed terminology of "non-revisioned" to "persistent".
Aliases are now consistent on which tables are used.
Comment #26
dpiAs a separate issue, we should consider changing "created" properties of core entities to non-revisioned fields.
Comment #28
dpiApparently file upload order matters for patches.
Comment #29
plachThanks, even more minor nits:
This should say "entity keys" instead of "non-entity ID's". Also, missing trailing dot.
As above, please use "entity keys". Also, "data" should go in the previous line, it fits within 80 chars.
Mmh, I can't really connect
persistentto the revisions concept at first sight. What's wrong withnon_revisionable?Comment #30
chx commentedLoad and save such an entity and kaboom. Definitely critical:
Critical bugs include those that:
Comment #31
dpiHooray, a promotion!
"Non-revisionable fields are persistent through all revisions."
I feel calling non-something doesn't feel right. I really was hoping for an example from elsewhere.
For those looking on, looking for term for an entity property that is non-revisionable, and not an entity key.
Short list: base data, data, constant, persistent, persistent data, static.
Alternatively, give a name that describes the purpose of the field, rather than the function.
Comment #32
dpiScrap the last comment then. I have replaced the vague "persistent" integer field, with a non-revisionable "created" field type.
I have amended the comments
Comment #35
berdircreated makes sense to me. Maybe include that the field is not revisioned in the field definition description to make that clear?
Comment #36
dpiResolving conflicts due to #2358079: Require a specific placeholder format in db_query() in order to trigger argument expansion, and require explicit 'IN' parameter for conditions.
Comment #39
dpiFixed non translatable field...
Comment #41
plachLooks ready to go to me. @Berdir?
Comment #42
berdirLooks good to me as well :)
Comment #43
berdirComment #44
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed cf40fa1 and pushed to 8.0.x. Thanks!
Comment #46
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #47
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #48
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #49
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #50
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #51
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #52
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #53
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #54
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #55
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #56
Anonymous (not verified) commented7 months...finally this made it into the core. Great news.
Comment #57
dpiGo easy on the celebrating
Comment #58
plachlol