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.

CommentFileSizeAuthor
#247 2458223-247.patch2.03 KBcatch
#245 test.patch1011 bytescatch
#235 interdiff.txt8.31 KBdawehner
#235 2458223-235.patch43.27 KBdawehner
#226 2458223-226.patch42.83 KBdawehner
#226 interdiff.txt588 bytesdawehner
#224 interdiff.txt1.39 KBdawehner
#224 2458223-224.patch42.87 KBdawehner
#219 2458223-219.patch42.91 KBplach
#219 2458223-219.interdiff.txt540 bytesplach
#197 interdiff.txt5.59 KBdawehner
#197 2458223-197.patch42.9 KBdawehner
#194 interdiff.txt1.95 KBdawehner
#194 2458223-194.patch40.37 KBdawehner
#192 interdiff.txt1.65 KBdawehner
#192 2458223-192.patch39.46 KBdawehner
#190 2458223-190.patch39.67 KBGábor Hojtsy
#190 interdiff.txt1.86 KBGábor Hojtsy
#187 patch_187_add_view_page.png210.47 KBgeertvd
#185 2458223-185.patch40.48 KBGábor Hojtsy
#185 interdiff.txt1.31 KBGábor Hojtsy
#183 interdiff.txt736 bytesGábor Hojtsy
#183 2458223-183.patch41.79 KBGábor Hojtsy
#180 Content (Content) | drupal 8.0.0-rc3 2015-11-12 11-34-12.png165.21 KBGábor Hojtsy
#179 2458223-179.patch41.07 KBGábor Hojtsy
#168 interdiff-2458223-168.txt3.15 KBlokapujya
#168 2458223-168.patch41.07 KBlokapujya
#163 interdiff-158-163.txt3.92 KBgeertvd
#163 2458223-163.patch39.38 KBgeertvd
#158 interdiff-142-158.txt8.33 KBgeertvd
#158 2458223-158-complete.patch35.46 KBgeertvd
#158 2458223-158-test.patch2.53 KBgeertvd
#151 add_relationship_no_patch_applied.png39.4 KBeiriksm
#151 add_relationship_patch_applied.png39.01 KBeiriksm
#142 2458223-142.patch33.3 KBgeertvd
#142 interdiff-2458223-138-142.txt1.54 KBgeertvd
#138 2458223-138.patch33.15 KBgeertvd
#138 2458223-137-138.txt1.55 KBgeertvd
#137 2458223-137.patch32.98 KBgeertvd
#137 interdiff-2458223-130-137.txt6.98 KBgeertvd
#130 2458223-130.patch30.74 KBgeertvd
#130 interdiff-2458223-128-130.txt778 bytesgeertvd
#128 interdiff-2458223-120-128.txt2.65 KBgeertvd
#128 2458223-128.patch30.26 KBgeertvd
#120 interdiff-2458223-117-119.txt684 bytesgeertvd
#120 2458223-119.patch29.82 KBgeertvd
#117 interdiff-2458223-115-117.txt8.91 KBgeertvd
#117 2458223-117.patch29.76 KBgeertvd
#115 2458223-115.patch38.66 KBgeertvd
#115 interdiff-2458223-99-115.txt3.5 KBgeertvd
#99 2458223-99.patch29.31 KBlokapujya
#99 2458223-99-minus-upgrade-path.patch26.4 KBlokapujya
#99 interdiff-2458223-99.txt668 byteslokapujya
#95 2458223-95.patch29.3 KBlokapujya
#95 2458223-95-minus-upgrade-path.patch26.39 KBlokapujya
#95 interdiff-2458223-95.txt668 byteslokapujya
#94 2458223-94.patch29.31 KBgeertvd
#92 2458223-93.patch29.27 KBgeertvd
#92 interdiff-2458223-88-83.txt4.58 KBgeertvd
#88 interdiff-2458223-88.txt1.26 KBlokapujya
#88 2458223-88.patch29 KBlokapujya
#83 interdiff-2458223-83.txt7.71 KBlokapujya
#83 2458223-83.patch29.24 KBlokapujya
#82 multiple_duplicated-2458223-82.patch21.47 KBeiriksm
#65 interdiff-2458223-61-65.txt9.49 KBjeqq
#65 interdiff-2458223-63-65.txt4 KBjeqq
#65 multiple_duplicated-2458223-65.patch18.6 KBjeqq
#63 interdiff-2458223-61-63.txt6.08 KBjeqq
#63 multiple_duplicated-2458223-63.patch13 KBjeqq
#61 interdiff-2458223-55-61.txt782 byteseiriksm
#61 multiple_duplicated-2458223-61.patch14.14 KBeiriksm
#55 interdiff52-55.txt2.29 KBeiriksm
#55 multiple_duplicated-2458223-55.patch14.16 KBeiriksm
#52 interdiff43-52.txt5.09 KBeiriksm
#52 multiple_duplicated-2458223-52.patch11.87 KBeiriksm
#43 Screen Shot 2015-05-09 at 14.37.27.png42.83 KBeiriksm
#43 interdiff37-43.txt4.4 KBeiriksm
#43 multiple_duplicated-2458223-43.patch8.66 KBeiriksm
#37 interdiff33-36.txt2.08 KBeiriksm
#37 multiple_duplicated-2458223-37-test-only.patch4.21 KBeiriksm
#37 multiple_duplicated-2458223-37.patch8.72 KBeiriksm
#36 interdiff33-36.txt2.08 KBeiriksm
#36 multiple_duplicated-2458223-36-test-only.patch4.21 KBeiriksm
#36 multiple_duplicated-2458223-36.patch8.72 KBeiriksm
#33 2458223.33.patch8.85 KBeiriksm
#33 interdiff-2458223-31-33.txt801 byteseiriksm
#31 2458223-30.patch8.63 KBeiriksm
#31 interdiff-2458223-27-30.txt1.18 KBeiriksm
#27 2458223-27.patch8.61 KBdawehner
#26 2458223-26.patch8.61 KBdawehner
#26 interdiff.txt690 bytesdawehner
#22 interdiff.txt3.69 KBdawehner
#22 2458223-22.patch8.61 KBdawehner
#20 interdiff.txt3.24 KBdawehner
#20 2458223-20.patch6.93 KBdawehner
#18 interdiff.txt5.48 KBdawehner
#18 2458223-16.patch5.06 KBdawehner
#7 fields in view.png11.71 KBDom.
#5 views.view_.content_type_display_view.yml.txt7.91 KBDom.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Dom.’s picture

Issue summary: View changes
dawehner’s picture

Its 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'?

Dom.’s picture

But 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.

jhodgdon’s picture

Dom: 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?

Dom.’s picture

My 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.

catch’s picture

Title: Content type field in view does not display for none admin permission » Two different fields called 'Content: type'
Priority: Critical » Normal

Bumping to normal, but the duplicate naming definitely looks confusing.

Dom.’s picture

FileSize
11.71 KB

It looks like the two fields in Views UI are created from this code in \Drupal\node\Entity\Node:

$fields['type'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Type'))
      ->setDescription(t('The node type.'))
      ->setSetting('target_type', 'node_type')
      ->setReadOnly(TRUE);

Changing the label and description here changes both field name in views :
Fields in Views UI

Anyone can help me on that to give me a clue on how to solve this ?

jhodgdon’s picture

By 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...

jhodgdon’s picture

Talked 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?

Dom.’s picture

I guess this is not something I can work on as per a Novice tag ?

dawehner’s picture

I guess this is not something I can work on as per a Novice tag ?

You 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.

jhodgdon’s picture

We 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.

Dom.’s picture

@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."

xjm’s picture

Priority: Normal » Major
Issue tags: +VDC

Hm, I think this is major.

dawehner’s picture

However, isn't therefore this issue related ? (see todo in code)

Yeah I think its related but not necessarily the same issue.

I'll give that issue a try.

Hm, I think this is major.

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.

xjm’s picture

Title: Two different fields called 'Content: type' » Multiple duplicated field handlers in Views UI

Retitling to the scope that I think is major.

jhodgdon’s picture

Yeah, and given it is not just Node that presumably has them...

dawehner’s picture

Status: Active » Needs review
FileSize
5.06 KB
5.48 KB

This removes the duplicates for now, let's see whether this causes some failures.

Status: Needs review » Needs work

The last submitted patch, 18: 2458223-16.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
6.93 KB
3.24 KB

I guess this is it.

jhodgdon’s picture

Status: Needs review » Needs work

Looking good! A few comments:

a) Suggested rewrite of the comment in EntityViewsData about skipping fields... currently says:

+          // Some fields, like for example the entity ID appear on both the base
+          // table and data table. Given that we want to skip the field on
+          // the bsae table, as otherwise the user has duplicate entries in the
+          // UI.
+          // On top of that we have to special case the langcode, because on the
+          // base table it is the default langcode and on the data table, it is
+          // the langcode of the

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:

          if ($field_name !== 'langcode' && $data_table && $table === $base_table && array_search($field_name, $field_names_per_table[$data_table]) !== FALSE) {
+            continue;
+          }
+          if ($field_name !== 'langcode' && $revision_data_table && $table === $revision_table && array_search($field_name, $field_names_per_table[$revision_data_table]) !== FALSE) {
+            continue;
+          }

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.

dawehner’s picture

Status: Needs work » Needs review
FileSize
8.61 KB
3.69 KB

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.

This indeed sounds way more fluent to read. Thanks a lot!

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.

Ha, while working on it I stumbled upon the return value of array_search() and wondered why I never had problems with it.

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.

Fair point! It was more of a organic growing of the conditions.

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.

Sure, let's do that.

jhodgdon’s picture

Regarding the test, I was thinking about something more systematic. Would it be possible to do something like this, conceptually:

foreach entity in (node, user, taxonomy term, ...) {
  foreach add_type in (fields, filters, sorts, arguments) {
     generate the Add add_type dialog for entitity that you'd get in Views UI, or its list of options at least
     foreach item in list {
       calculate the displayed item title and help that the user would see 
       verify that it doesn't match one previous calculated in this loop
       throw a fail if it does match
     }
   }
}

Status: Needs review » Needs work

The last submitted patch, 22: 2458223-22.patch, failed testing.

andypost’s picture

dawehner’s picture

Status: Needs work » Needs review
FileSize
690 bytes
8.61 KB

Regarding the test, I was thinking about something more systematic. Would it be possible to do something like this, conceptually:

Well, 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.

dawehner’s picture

FileSize
8.61 KB

Rerolled ...

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.

xjm’s picture

jhodgdon’s picture

If we had unit tests that covered this problem, why did it happen?

dawehner’s picture

If we had unit tests that covered this problem, why did it happen?

Well, because we introduced the unit tests as part of this issue ...

eiriksm’s picture

Tested 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.

jibran’s picture

Status: Needs review » Needs work

I agree with #27. This is ready just a minor confirmation for sanity.

+++ b/core/modules/views_ui/src/Tests/HandlerTest.php
@@ -172,4 +185,18 @@ public function testBrokenHandlers() {
+    $this->assertEqual(1, substr_count($content, 'Content: Type'), 'Content: Type appears just once.');
+    $this->assertEqual(1, substr_count($content, 'Content: Node ID'), 'Content: Node ID appears just once.');

Can we please add a quick assert about uuid because it's the only field which we still get from base table?

eiriksm’s picture

Status: Needs work » Needs review
FileSize
801 bytes
8.85 KB

Since I already had the file open.

Can we please add a quick assert about uuid because it's the only field which we still get from base table?

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.

plach’s picture

+++ b/core/modules/views/src/EntityViewsData.php
@@ -213,8 +213,27 @@ public function getViewsData() {
+        $field_names_per_table[$table] = $table_mapping->getFieldNames($table);
+      }
+
+      foreach ($field_names_per_table as $table => $fields) {
+        foreach ($fields as $field_name) {
+          // 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.
+          if ($data_table && $table === $base_table && $field_name !== 'langcode' && in_array($field_name, $field_names_per_table[$data_table])) {
+            continue;
+          }
+          if ($revision_data_table && $table === $revision_table && $field_name !== 'langcode' && in_array($field_name, $field_names_per_table[$revision_data_table])) {
+            continue;
+          }
+
           $this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]);

I think we can use TableMappingInterface::getFieldTableName() for this.

dawehner’s picture

I think we can use TableMappingInterface::getFieldTableName() for this.

Sounds like a good idea! I guess we still have to special case 'langcode', but the rest should be alright!

eiriksm’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
8.72 KB
4.21 KB
2.08 KB

I think we can use TableMappingInterface::getFieldTableName() for this.

Like this?

Attached also interdiff and test-only patch

eiriksm’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
8.72 KB
4.21 KB
2.08 KB

Heh, 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 :)

plach’s picture

Yes, 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 :)

The last submitted patch, 36: multiple_duplicated-2458223-36.patch, failed testing.

The last submitted patch, 36: multiple_duplicated-2458223-36-test-only.patch, failed testing.

The last submitted patch, 37: multiple_duplicated-2458223-37.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 37: multiple_duplicated-2458223-37-test-only.patch, failed testing.

eiriksm’s picture

Status: Needs work » Needs review
FileSize
8.66 KB
4.4 KB
42.83 KB

Here 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).

dawehner’s picture

+++ b/core/modules/views/src/EntityViewsData.php
@@ -217,6 +217,16 @@ public function getViewsData() {
       foreach ($table_mapping->getTableNames() as $table) {
...
+          $field_table = $table_mapping->getFieldTableName($field_name);

That honestly is confusing, don't $table_mapping->getFieldNames($table) already ensure the table we are dealing with?

eiriksm’s picture

Hm, you are of course right.

However. If we simplify it to only

if ($data_table && $table === $base_table && $field_name !== 'langcode')

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? :)

plach’s picture

Isn't the following enough?

if ($table !== $field_table)
eiriksm’s picture

You mean replace

if ($data_table && $data_table == $field_table && $table === $base_table && $field_name !== 'langcode') {

with

if ($table !== $field_table) {

?

Because that does not work so well.

Sorry if I misunderstood you.

plach’s picture

I think I meant something like this:

    // Load all typed data definitions of all fields. This should cover each of
    // the entity base, revision, data tables.
    $table_mapping = $this->storage->getTableMapping();
    $definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id());
    $definitions = array_filter($definitions, [$table_mapping, 'allowsSharedTableStorage']);
    $langcode_key = $this->entityType->getKey('langcode');
    /** @var \Drupal\Core\Field\FieldDefinitionInterface $definition */
    foreach ($definitions as $definition) {
      $field_name = $definition->getName();
      $table = $table_mapping->getFieldTableName($field_name);
      $this->mapFieldDefinition($table, $field_name, $definition, $table_mapping, $data[$table]);
      // TODO ...
      if ($field_name == $langcode_key && $table == $data_table) {
        $this->mapFieldDefinition($base_table, $field_name, $definition, $table_mapping, $data[$base_table]);
      }
    }
dawehner’s picture

Note: 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.

eiriksm’s picture

Not 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 :)

jhodgdon’s picture

Status: Needs review » Needs work

Status update due to recent comments.

eiriksm’s picture

Status: Needs work » Needs review
FileSize
11.87 KB
5.09 KB

Here 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.

jhodgdon’s picture

This issue desperately needs an issue summary update. The current summary has very little to do with the current patch.

Status: Needs review » Needs work

The last submitted patch, 52: multiple_duplicated-2458223-52.patch, failed testing.

eiriksm’s picture

Updated patch that also should fix the test failures.

Will update the IS now.

eiriksm’s picture

Issue summary: View changes
Status: Needs work » Needs review
alexpott’s picture

Issue tags: +D8 upgrade path
dawehner’s picture

Status: Needs review » Needs work
+++ b/core/modules/views/src/EntityViewsData.php
@@ -217,6 +217,23 @@ public function getViewsData() {
+          if ($data_table && $table === $revision_table && !in_array($field_name, $base_table_exceptions)) {

UUID is not part of the revision table

mysql> describe node_field_revision;
+------------------+------------------+------+-----+---------+-------+
| Field            | Type             | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| nid              | int(10) unsigned | NO   |     | NULL    |       |
| vid              | int(10) unsigned | NO   | PRI | NULL    |       |
| langcode         | varchar(12)      | NO   | PRI | NULL    |       |
| title            | varchar(255)     | YES  |     | NULL    |       |
| uid              | int(10) unsigned | YES  | MUL | NULL    |       |
| status           | tinyint(4)       | YES  |     | NULL    |       |
| created          | int(11)          | YES  |     | NULL    |       |
| changed          | int(11)          | YES  |     | NULL    |       |
| promote          | tinyint(4)       | YES  |     | NULL    |       |
| sticky           | tinyint(4)       | YES  |     | NULL    |       |
| default_langcode | tinyint(4)       | NO   | MUL | NULL    |       |
+------------------+------------------+------+-----+---------+-------+
11 rows in set (0.01 sec)
gokulnk’s picture

Issue summary: View changes

Updated 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."

jhodgdon’s picture

That is not true golunk. Some of those fields are going away.

eiriksm’s picture

Status: Needs work » Needs review
FileSize
14.14 KB
782 bytes

I meant to post this sooner, but the reminder disappeared in my inbox :)

Removed the check for uuid in revision table

jhodgdon’s picture

Status: Needs review » Needs work

I'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:

  1. +++ b/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_revision_id.yml
    @@ -31,7 +31,7 @@ display:
               id: id_1
    -          table: block_content_revision
    +          table: block_content_field_data
               field: id
    

    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?

  2. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -217,6 +217,23 @@ public function getViewsData() {
    +          $base_table_exceptions = [
    +            'langcode',
    +            'uuid'
    +          ];
    +          if ($data_table && $table === $base_table && !in_array($field_name, $base_table_exceptions)) {
    

    Coding standards: should be a , after 'uuid' right?

  3. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -217,6 +217,23 @@ public function getViewsData() {
    +          if ($data_table && $table === $base_table && !in_array($field_name, $base_table_exceptions)) {
    +            continue;
    +          }
    +          if ($data_table && $table === $revision_table && $field_name != 'langcode') {
    +            continue;
    +          }
               $this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]);
    

    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...

  4. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -527,9 +527,18 @@ public function testDataTableFields() {
             ['entity_test_mul', ['id', 'uuid', 'type', 'langcode']],
    -        ['entity_test_mul_property_data', ['id', 'langcode', 'name', 'description', 'homepage', 'user_id']],
    +        ['entity_test_mul_property_data', ['id', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']],
    

    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.

  5. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -527,9 +527,18 @@ public function testDataTableFields() {
    +    $table_mapping->expects($this->any())
    +      ->method('getFieldTableName')
    +      ->willReturnCallback(function($field) {
    +        if ($field == 'id') {
    +          return 'entity_test_mul_property_data';
    +        }
    +        return 'entity_test_mul';
    +      });
    +
    

    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.

  6. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -547,13 +556,10 @@ public function testDataTableFields() {
    -    $this->assertNumericField($data['entity_test_mul']['id']);
    -    $this->assertField($data['entity_test_mul']['id'], 'id');
    +    $this->assertFalse(isset($data['entity_test_mul']['id']));
         $this->assertUuidField($data['entity_test_mul']['uuid']);
         $this->assertField($data['entity_test_mul']['uuid'], 'uuid');
     
    -    $this->assertBundleField($data['entity_test_mul']['type']);
    -    $this->assertField($data['entity_test_mul']['type'], 'type');
         $this->assertFalse(isset($data['entity_test_mul']['type']['relationship']));
    

    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?

  7. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -645,6 +654,15 @@ public function testRevisionTableFields() {
    +    $table_mapping->expects($this->any())
    +      ->method('getFieldTableName')
    +      ->willReturnCallback(function($field) {
    +        if ($field == 'id' || $field == 'revision_id') {
    +          return 'entity_test_mulrev_property_data';
    +        }
    +        return 'entity_test_mulrev';
    +      });
    +
    

    Again I don't see how this can be right.

  8. Also just above there are arrays of which fields are expected to be on the revision and revision field data tables, and these were not patched?
  9. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -654,14 +672,10 @@ public function testRevisionTableFields() {
    -    $this->assertNumericField($data['entity_test_mulrev']['id']);
    -    $this->assertField($data['entity_test_mulrev']['id'], 'id');
    -    $this->assertNumericField($data['entity_test_mulrev']['revision_id']);
    -    $this->assertField($data['entity_test_mulrev']['revision_id'], 'revision_id');
    +    $this->assertFalse(isset($data['entity_test_mulrev']['id']));
    +    $this->assertFalse(isset($data['entity_test_mulrev']['revision_id']));
         $this->assertUuidField($data['entity_test_mulrev']['uuid']);
         $this->assertField($data['entity_test_mulrev']['uuid'], 'uuid');
    -    $this->assertStringField($data['entity_test_mulrev']['type']);
    -    $this->assertField($data['entity_test_mulrev']['type'], 'type');
     
    

    Again should we add an assertFalse for the 'type' field here?

  10. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -672,11 +686,9 @@ public function testRevisionTableFields() {
    -    // Check the revision fields.
    -    $this->assertNumericField($data['entity_test_mulrev_revision']['id']);
    -    $this->assertField($data['entity_test_mulrev_revision']['id'], 'id');
    -    $this->assertNumericField($data['entity_test_mulrev_revision']['revision_id']);
    -    $this->assertField($data['entity_test_mulrev_revision']['revision_id'], 'revision_id');
    +    // Check the revision fields. The revision id should only appear in the data
    +    // table.
    +    $this->assertFalse(isset($data['entity_test_mulrev_revision']['revision_id']));
     
    

    and assertFalse() on the 'id' field?

jeqq’s picture

Status: Needs review » Needs work

The last submitted patch, 63: multiple_duplicated-2458223-63.patch, failed testing.

jeqq’s picture

Updated the patch.

dawehner’s picture

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?

The 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.

Coding standards: should be a , after 'uuid' right?

Yeah

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...

I like that argument :)
Yeah that code doesn't run often, no need to micro-optimize

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.

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.

jhodgdon’s picture

But look at the code in item #4 that I was referring to in "don't type and id have to be removed"....

+++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
@@ -527,9 +527,18 @@ public function testDataTableFields() {
         ['entity_test_mul', ['id', 'uuid', 'type', 'langcode']],
-        ['entity_test_mul_property_data', ['id', 'langcode', 'name', 'description', 'homepage', 'user_id']],
+        ['entity_test_mul_property_data', ['id', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']],

It looks like type and id are in both the tables. Why is that not wrong?

jhodgdon’s picture

I 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...

jeqq’s picture

dawehner’s picture

It looks like type and id are in both the tables. Why is that not wrong?

As 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:

mysql> describe node;
+----------+------------------+------+-----+---------+----------------+
| Field    | Type             | Null | Key | Default | Extra          |
+----------+------------------+------+-----+---------+----------------+
| nid      | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| vid      | int(10) unsigned | YES  | UNI | NULL    |                |
| type     | varchar(32)      | NO   | MUL | NULL    |                |
| uuid     | varchar(128)     | NO   | UNI | NULL    |                |
describe node_field_data;
| langcode | varchar(12)      | NO   |     | NULL    |                |
+----------+------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> describe node_field_data;
+-------------------------------+------------------+------+-----+---------+-------+
| Field                         | Type             | Null | Key | Default | Extra |
+-------------------------------+------------------+------+-----+---------+-------+
| nid                           | int(10) unsigned | NO   | PRI | NULL    |       |
| vid                           | int(10) unsigned | NO   | MUL | NULL    |       |
| type                          | varchar(32)      | NO   | MUL | NULL    |       |
| langcode                      | varchar(12)      | NO   | PRI | NULL    |       |
| title                         | varchar(255)     | NO   | MUL | NULL    |       |
| uid                           | int(10) unsigned | NO   | MUL | NULL    |       |
| status                        | tinyint(4)       | NO   | MUL | NULL    |       |
| created                       | int(11)          | NO   | MUL | NULL    |       |
| changed                       | int(11)          | NO   | MUL | NULL    |       |
| promote                       | tinyint(4)       | NO   | MUL | NULL    |       |
| sticky                        | tinyint(4)       | NO   |     | NULL    |       |
| revision_translation_affected | tinyint(4)       | YES  |     | NULL    |       |
| default_langcode              | tinyint(4)       | NO   |     | NULL    |       |
+-------------------------------+------------------+------+-----+---------+-------+
13 rows in set (0.00 sec)
alexpott’s picture

We're going to need a hook_update_N() for this right?

dawehner’s picture

Absolutely, people might have used the wrong field.

jhodgdon’s picture

Regarding 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. :)

jhodgdon’s picture

And if they are not using the base EntityViewsData classes, then I don't know why they are in this patch?

dawehner’s picture

They are using this class

jhodgdon’s picture

Humph. 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.

catch’s picture

Issue tags: +rc target

The 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.

jhodgdon’s picture

Status: Needs review » Needs work
Issue tags: +Usability

OK, 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.

eiriksm’s picture

Assigned: Unassigned » eiriksm

I started the upgrade path some weeks ago, will take another stab at it at the sprints today.

eiriksm’s picture

Status: Needs work » Needs review
FileSize
21.47 KB

First 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?

lokapujya’s picture

Here is a start for an update test extending UpdatePathTestBase.

lokapujya’s picture

This is going to fail.

+++ b/core/modules/views/src/Tests/Update/FieldHandlersUpdateTest.php
@@ -0,0 +1,64 @@
+    // Get the expected base table name for the entity.
+    $test_entity = "";

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.

Status: Needs review » Needs work

The last submitted patch, 83: 2458223-83.patch, failed testing.

catch’s picture

Issue tags: +rc deadline

Tagging rc deadline. Not sure what we can do about this after rc if it's not in before.

The last submitted patch, 83: 2458223-83.patch, failed testing.

lokapujya’s picture

Status: Needs work » Needs review
FileSize
29 KB
1.26 KB

The name of the view in the update test was wrong.
I get an error in the test locally when calling: $view->get('display').

Status: Needs review » Needs work

The last submitted patch, 88: 2458223-88.patch, failed testing.

The last submitted patch, 88: 2458223-88.patch, failed testing.

geertvd’s picture

Assigned: eiriksm » geertvd
Issue tags: +Barcelona2015

Looking into those fails.

geertvd’s picture

Assigned: geertvd » Unassigned
Status: Needs work » Needs review
FileSize
4.58 KB
29.27 KB

Status: Needs review » Needs work

The last submitted patch, 92: 2458223-93.patch, failed testing.

geertvd’s picture

Status: Needs work » Needs review
FileSize
29.31 KB

reroll

lokapujya’s picture

Should the test view needs to start with node as the base table in order to properly test the upgrade path?

eiriksm’s picture

Should the test view needs to start with node as the base table in order to properly test the upgrade path?

No, 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 :)

The last submitted patch, 95: 2458223-95-minus-upgrade-path.patch, failed testing.

The last submitted patch, 95: 2458223-95-minus-upgrade-path.patch, failed testing.

lokapujya’s picture

No, the _field_ should use the node table, which it does.

Oops, I agree. Reverting that last change.

The last submitted patch, 99: 2458223-99-minus-upgrade-path.patch, failed testing.

The last submitted patch, 99: 2458223-99-minus-upgrade-path.patch, failed testing.

jhodgdon’s picture

Status: Needs review » Needs work

This is looking pretty good, thanks for all the work!

However, there are several problems in this patch that definitely need to be addressed:

  1. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -217,6 +217,12 @@ public function getViewsData() {
           // @todo https://www.drupal.org/node/2337511
           foreach ($table_mapping->getTableNames() as $table) {
             foreach ($table_mapping->getFieldNames($table) as $field_name) {
    +          // Some fields (for example, Entity ID) appear in both the base table
    +          // and data table. With the exception of the uuid field. To avoid
    +          // confusing duplication, only add them in the data tables.
    +          if ($data_table && ($table === $base_table || $table === $revision_table) && $field_name != 'uuid') {
    +            continue;
    +          }
    

    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?

  2. in views.install:
     /**
    + * Verify that no fields of the previous duplicated types is used in views.
    + */
    +function views_update_8003(&$sandbox) {
    

    This comment needs a bit of adjustment. How about:

    Update views that use previously-duplicated fields that have been removed.

  3. In that same update function, it looks like the code is taking something that was previously in the revision table and moving it to the data table:
    +            if (($table === $base_table || $table === $revision_table) && $field_name != 'uuid') {
    +              if (in_array($field['field'], $update_exceptions)) {
    +                continue;
    +              }
    +              $field['table'] = $data_table;
    

    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:

    --- a/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_revision_id.yml
    +++ b/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_revision_id.yml
    @@ -31,7 +31,7 @@ display:
               entity_field: revision_id
             id_1:
               id: id_1
    -          table: block_content_revision
    +          table: block_content_field_revision
     

    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.

  4. And we apparently need an update test for the revision data table stuff, because the existing tests are not catching this problem. They are only testing one field being updated in a base table, not a revision table.
lokapujya’s picture

1.

and the field exists in both the table I'm doing and the corresponding data table

vs.

and there is a data table defined

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."

jhodgdon’s picture

Ah, 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.

lokapujya’s picture

I 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?:

<?php
          if (($data_table && $table === $base_table) || ($revision_data_table && $table === $revision_table) && $field_name != 'uuid') {
?>
lokapujya’s picture

For 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.

jhodgdon’s picture

RE #105, right, but also we need to make an exception for 'langcode'.

lokapujya’s picture

I 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.

jhodgdon’s picture

If 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?

dawehner’s picture

Thanks all for the really important work on this issue!!

  1. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -217,6 +217,12 @@ public function getViewsData() {
    +          // Some fields (for example, Entity ID) appear in both the base table
    +          // and data table. With the exception of the uuid field. To avoid
    +          // confusing duplication, only add them in the data tables.
    +          if ($data_table && ($table === $base_table || $table === $revision_table) && $field_name != 'uuid') {
    +            continue;
    +          }
    

    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.

  2. +++ b/core/modules/views/views.install
    @@ -311,5 +311,80 @@ function _views_update_argument_map($displays) {
    +  // Some special fields are not relevant here.
    +  $update_exceptions = [
    +    'user_bulk_form',
    +    'operations',
    +    'node_bulk_form',
    +  ];
    

    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?

jhodgdon’s picture

Hm. 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...

jhodgdon’s picture

OK. 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.

dawehner’s picture

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.

This sounds really promising! Great research!

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.

I think the trick is that the implicit (automatic) join from {node_revision} to anything is missing.

>>> \Drupal\views\Views::viewsData()->get('node_revision')['table']
=> [
     "group" => Drupal\Core\StringTranslation\TranslatableString {#2635}
     "provider" => "node"
     "entity type" => "node"
   ]

vs.

>>> \Drupal\views\Views::viewsData()->get('node_field_revision')['table']
=> [
     "entity revision" => true
     "base" => [
       "field" => "vid"
       "title" => Drupal\Core\StringTranslation\TranslatableString {#2649}
       "help" => Drupal\Core\StringTranslation\TranslatableString {#2653}
       "defaults" => [
         "title" => "title"
       ]
     ]
     "join" => [
       "node_field_data" => [
         "left_field" => "vid"
         "field" => "vid"
         "type" => "INNER"
       ]
       "node_revision" => [
         "left_field" => "vid"
         "field" => "vid"
         "type" => "INNER"
       ]
     ]
     "group" => Drupal\Core\StringTranslation\TranslatableString {#2650}
     "entity type" => "node"
     "wizard_id" => "node_field_revision"
     "provider" => "views"
   ]

see

      // Join the revision table to the base table.
      $data[$views_revision_base_table]['table']['join'][$views_base_table] = array(
        'left_field' => $revision_field,
        'field' => $revision_field,
        'type' => 'INNER',
      );

for the code which adds the implicit join from 'node_field_revision' to 'node_field_data'.

jhodgdon’s picture

Hm, 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).

geertvd’s picture

Status: Needs work » Needs review
FileSize
3.5 KB
38.66 KB

Lots of info in these last few comments, is this patch heading in the right direction?

Status: Needs review » Needs work

The last submitted patch, 115: 2458223-115.patch, failed testing.

geertvd’s picture

Status: Needs work » Needs review
FileSize
29.76 KB
8.91 KB

woops

Status: Needs review » Needs work

The last submitted patch, 117: 2458223-117.patch, failed testing.

jhodgdon’s picture

The patch to EntityViewsData looks like the right thing to do, yes.

geertvd’s picture

Status: Needs work » Needs review
FileSize
29.82 KB
684 bytes

now it should be ok.

The last submitted patch, 115: 2458223-115.patch, failed testing.

The last submitted patch, 117: 2458223-117.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 120: 2458223-119.patch, failed testing.

The last submitted patch, 120: 2458223-119.patch, failed testing.

jhodgdon’s picture

Aside from the tests failing... ;)

lokapujya’s picture

+++ b/core/modules/views/src/EntityViewsData.php
@@ -217,10 +221,9 @@ public function getViewsData() {
+          // To avoid confusing duplication, only add duplicate fields the data
+          // tables.

reminder for next patch: missing a word here.

dawehner’s picture

Its great that you all work on this!

geertvd’s picture

Status: Needs work » Needs review
FileSize
30.26 KB
2.65 KB

Fails 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)

Status: Needs review » Needs work

The last submitted patch, 128: 2458223-128.patch, failed testing.

geertvd’s picture

Status: Needs work » Needs review
FileSize
778 bytes
30.74 KB

This should fix it

The last submitted patch, 128: 2458223-128.patch, failed testing.

jhodgdon’s picture

Status: Needs review » Needs work

This 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).

  1. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -217,6 +221,11 @@ public function getViewsData() {
    +          // To avoid confusing duplication, only add duplicate fields to the
    +          // data tables.
    

    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).

  2. +++ b/core/modules/views/src/Tests/Update/FieldHandlersUpdateTest.php
    @@ -0,0 +1,46 @@
    +    $this->assertEqual('node_field_data', $data['display']['default']['display_options']['fields']['nid']['table']);
    

    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]

  3. +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
    @@ -672,17 +691,12 @@ public function testRevisionTableFields() {
    +    // Check the revision fields. The revision id should only appear in the data
    

    id => ID [in text, "id" is like ego, superego, id; ID means identifier]

  4. +++ b/core/modules/views/views.install
    @@ -311,5 +311,73 @@ function _views_update_argument_map($displays) {
    + * Verify that no fields of the previous duplicated types is used in views.
    

    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.

  5. +++ b/core/modules/views/views.install
    @@ -311,5 +311,73 @@ function _views_update_argument_map($displays) {
    +        if (!empty($display['display_options']['fields'])) {
    +          foreach ($display['display_options']['fields'] as $field_name => &$field) {
    

    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?

geertvd’s picture

Assigned: Unassigned » geertvd

Thanks for the review. I'll work on this later today.

catch’s picture

fwiw 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.

lokapujya’s picture

1. 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.

jhodgdon’s picture

RE #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.

geertvd’s picture

Status: Needs work » Needs review
FileSize
6.98 KB
32.98 KB
  1. Fixed with your suggestion
  2. Extended the view a bit to include these duplicate fields in sorts/filters/arguments, couldn't find any other handlers that have duplicates (relationships didn't have any as far as I could see).
  3. Fixed
  4. Went with your suggestion
  5. Didn't fix this yet, will do so in my next patch

So this patch should demonstrate the problem @jhodgdon describes in #132.5

geertvd’s picture

And this fixes #132.5

The last submitted patch, 137: 2458223-137.patch, failed testing.

geertvd’s picture

Assigned: geertvd » Unassigned
jhodgdon’s picture

Good. 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?

geertvd’s picture

This covers that.

jhodgdon’s picture

Great! We may need to add other handler types... not sure. Maybe 'relationship' as well? @dawehner, any thoughts there?

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Well, 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.

The last submitted patch, 137: 2458223-137.patch, failed testing.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_revision_id.yml
@@ -31,7 +31,7 @@ display:
-          table: block_content_revision
+          table: block_content_field_revision

+++ b/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_revision_revision_id.yml
@@ -33,7 +33,7 @@ display:
-          table: block_content_revision
+          table: block_content_field_revision

I 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.

lokapujya’s picture

Is it even possible to create a view that mistakenly uses the node_revision table?

jhodgdon’s picture

It'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.

dawehner’s picture

+++ b/core/modules/views/views.install
@@ -311,5 +311,76 @@ function _views_update_argument_map($displays) {
+          if (!empty($display['display_options'][$handler_type['plural']])) {
+            foreach ($display['display_options'][$handler_type['plural']] as $field_name => &$field) {
+              $table = $field['table'];
+              if (($table === $base_table || $table === $revision_table) && in_array($field_name, $duplicate_fields)) {
+                $field['table'] = $data_table;
+                $changed = TRUE;
+              }

So yeah I think it would be better safe and migrate revision information as well, than sorry

catch’s picture

Tagging 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.

eiriksm’s picture

OK, 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 :)

jhodgdon’s picture

You said "the interface bugs out" and "this patch breaks things"... ??!?

eiriksm’s picture

Heh, 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 :)

lokapujya’s picture

The 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.

jhodgdon’s picture

Yeah, 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.

geertvd’s picture

I will be on a local sprint tomorrow, so I would have time to work on this, unless @eiriksm wanted to pick this up.

eiriksm’s picture

Go for it :)

I won't have the time needed in the next few days anyway.

geertvd’s picture

Regarding #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().

The last submitted patch, 158: 2458223-158-test.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 158: 2458223-158-complete.patch, failed testing.

geertvd’s picture

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.

Disregard 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.

geertvd’s picture

edit: wrong ticket

geertvd’s picture

Status: Needs work » Needs review
FileSize
39.38 KB
3.92 KB

Not a lot of progress today, won't be able to work on this the next couple of days.

Status: Needs review » Needs work

The last submitted patch, 163: 2458223-163.patch, failed testing.

lokapujya’s picture

+++ b/core/modules/block_content/src/BlockContentViewsData.php
@@ -36,25 +36,24 @@ public function getViewsData() {
-    $data['block_content_revision']['table']['base']['help'] = $this->t('Block Content revision is a history of changes to block content.');
-    $data['block_content_revision']['table']['base']['defaults']['title'] = 'info';
+    $data['block_content_field_revision']['table']['base']['help'] = $this->t('Block Content revision is a history of changes to block content.');
+    $data['block_content_field_revision']['table']['base']['defaults']['title'] = 'info';

What if we include both relationships?

lokapujya’s picture

Or, 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.

lokapujya’s picture

Issue tags: +Needs reroll
lokapujya’s picture

Reroll + 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.

Status: Needs review » Needs work

The last submitted patch, 168: 2458223-168.patch, failed testing.

The last submitted patch, 168: 2458223-168.patch, failed testing.

lokapujya’s picture

Status: Needs work » Needs review

The last submitted patch, 138: 2458223-138.patch, failed testing.

The last submitted patch, 142: 2458223-142.patch, failed testing.

The last submitted patch, 137: 2458223-137.patch, failed testing.

The last submitted patch, 158: 2458223-158-complete.patch, failed testing.

The last submitted patch, 163: 2458223-163.patch, failed testing.

The last submitted patch, 158: 2458223-158-test.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 168: 2458223-168.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
41.07 KB

Rerolled again.

Gábor Hojtsy’s picture

Title: Multiple duplicated field handlers in Views UI » Duplicated field handlers in field UI for some base table fields
Issue summary: View changes
FileSize
165.21 KB
Gábor Hojtsy’s picture

Status: Needs review » Needs work

The last submitted patch, 179: 2458223-179.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
41.79 KB
736 bytes

Add block_content update path to ensure that the added revision data table is added to the definition properly as well.

Status: Needs review » Needs work

The last submitted patch, 183: 2458223-183.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
1.31 KB
40.48 KB

@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.

Status: Needs review » Needs work

The last submitted patch, 185: 2458223-185.patch, failed testing.

geertvd’s picture

FileSize
210.47 KB

@Gábor Hojtsy I think I manually defined it in #158 to avoid this from happening.

Gábor Hojtsy’s picture

@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 :)

Gábor Hojtsy’s picture

I 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.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
1.86 KB
39.67 KB

So 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.

Status: Needs review » Needs work

The last submitted patch, 190: 2458223-190.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
39.46 KB
1.65 KB

So 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.

While @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.

Status: Needs review » Needs work

The last submitted patch, 192: 2458223-192.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
40.37 KB
1.95 KB

This patch is no longer allowed to be red, okay?

plach’s picture

Status: Needs review » Needs work

Found a few minor things and a couple of more concerning items.

  1. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -120,7 +120,14 @@ public function getViewsData() {
         $base_field = $this->entityType->getKey('id');
         $data_table = $this->entityType->getDataTable();
         $revision_table = $this->entityType->getRevisionTable();
    -    $revision_data_table = $this->entityType->getRevisionDataTable();
    ...
    +    if ($this->entityType->isRevisionable() && $this->entityType->isTranslatable()) {
    +      $revision_data_table = $this->entityType->getRevisionDataTable() ?: $this->entityType->id() . '_field_revision';
    +    }
    

    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.

  2. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -120,7 +120,14 @@ public function getViewsData() {
    +    // There are entity types which don't the revision data table defined but
    +    // they still got one from
    

    Missing get after which, I think.

  3. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -221,6 +232,12 @@ public function getViewsData() {
    +          if ($data_table && ($table === $base_table || $table === $revision_table) && in_array($field_name, $duplicate_fields)) {
    

    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.

  4. +++ b/core/modules/views/src/Tests/Update/FieldHandlersUpdateTest.php
    @@ -0,0 +1,49 @@
    +  public function testUpdateHookN() {
    

    Shouldn't this match the update function name, that is testViewsUpdate8004? Maybe it's not a strict rule :)

  5. +++ b/core/modules/views/views.install
    @@ -331,3 +333,77 @@ function views_update_8003() {
    +    if ($data_table = $entity_type->getDataTable()) {
    +      $entities_by_table[$data_table] = $entity_type;
    +    }
    +    if ($base_table = $entity_type->getBaseTable()) {
    +      $entities_by_table[$base_table] = $entity_type;
    +    }
    +    $data_tables[$entity_type_id] = $entity_type->getDataTable();
    +    $base_tables[$entity_type_id] = $entity_type->getBaseTable();
    +    $revision_tables[$entity_type_id] = $entity_type->getRevisionTable();
    

    This is not enough, we should replicate the logic of SqlContentEntityStorage::initTableLayout() here. Setting to NW for this.

Gábor Hojtsy’s picture

@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.

dawehner’s picture

Status: Needs work » Needs review
FileSize
42.9 KB
5.59 KB

Talked with IRC, we will open a follow up to deal with the table mapping and OR use SqlContentEntityStorage

@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.

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.

dawehner’s picture

plach’s picture

Status: Needs review » Reviewed & tested by the community

Looks ready to me, thanks

The last submitted patch, 137: 2458223-137.patch, failed testing.

The last submitted patch, 138: 2458223-138.patch, failed testing.

The last submitted patch, 142: 2458223-142.patch, failed testing.

The last submitted patch, 158: 2458223-158-complete.patch, failed testing.

The last submitted patch, 163: 2458223-163.patch, failed testing.

The last submitted patch, 190: 2458223-190.patch, failed testing.

The last submitted patch, 158: 2458223-158-test.patch, failed testing.

The last submitted patch, 168: 2458223-168.patch, failed testing.

The last submitted patch, 179: 2458223-179.patch, failed testing.

The last submitted patch, 183: 2458223-183.patch, failed testing.

The last submitted patch, 185: 2458223-185.patch, failed testing.

The last submitted patch, 192: 2458223-192.patch, failed testing.

plach’s picture

@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.

Gábor Hojtsy’s picture

@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.

catch’s picture

@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.

Gábor Hojtsy’s picture

Yay, #fingerscrossed.

tim.plunkett’s picture

dawehner’s picture

This 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.

xjm’s picture

+++ b/core/modules/views/views.install
@@ -331,3 +333,90 @@ function views_update_8003() {
 /**
  * @} End of "addtogroup updates-8.0.0-rc".
  */

This should be after the update hook, not before.

plach’s picture

xjm’s picture

Assigned: Unassigned » xjm
Status: Reviewed & tested by the community » Needs review

Hm, 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.

dawehner’s picture

xjm++

xjm’s picture

Status: Needs review » Needs work

So 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).

  1. +++ b/core/modules/block_content/src/BlockContentViewsData.php
    @@ -36,25 +36,24 @@ public function getViewsData() {
    -    $data['block_content_revision']['table']['base']['help'] = $this->t('Block Content revision is a history of changes to block content.');
    ...
     
         // @todo EntityViewsData should add these relationships by default.
         //   https://www.drupal.org/node/2410275
    

    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?

  2. +++ b/core/modules/node/src/NodeViewsData.php
    @@ -240,7 +240,7 @@ public function getViewsData() {
    -    ) + $data['node_revision']['vid'];
    +    ) + $data['node_field_revision']['vid'];
    

    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?

  3. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -120,7 +120,14 @@ public function getViewsData() {
    +    // There are entity types which don't the revision data table defined but
    ...
    +    // \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout so we
    

    Nit: missing parens on the method name.

  4. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -120,7 +120,14 @@ public function getViewsData() {
    +    // There are entity types which don't the revision data table defined but
    +    // they still got one from
    

    Looks like there's a typo here with a missing word or two. Maybe this would be clearer:

    Some entity types do not have a revision data table defined but still have a revision table name set in \Drupal::...

  5. +++ b/core/modules/views/src/EntityViewsData.php
    @@ -120,7 +120,14 @@ public function getViewsData() {
    +    $revision_data_table = '';
    +    if ($this->entityType->isRevisionable() && $this->entityType->isTranslatable()) {
    +      $revision_data_table = $this->entityType->getRevisionDataTable() ?: $this->entityType->id() . '_field_revision';
    +    }
    

    Why do these only get set if the entity is translatable?

  6. ...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?

  7. +++ b/core/modules/views/src/Tests/Update/FieldHandlersUpdateTest.php
    @@ -0,0 +1,49 @@
    + * @see https://www.drupal.org/node/2458223
    

    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.

  8. +++ b/core/modules/views/src/Tests/Update/FieldHandlersUpdateTest.php
    @@ -0,0 +1,49 @@
    + * @see views_update_8004
    

    Nit: This needs parens.

xjm’s picture

Assigned: xjm » Unassigned
dawehner’s picture

Status: Needs work » Needs review
FileSize
42.87 KB
1.39 KB

o 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?

I 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..

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?

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.

Why do these only get set if the entity is translatable?

See comment above :) We copy the logic from \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout()

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?

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 )

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.

Right, we have git for that!

jibran’s picture

I 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.

+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_duplicate_field_handlers.yml
@@ -1,11 +1,14 @@
+uuid: 98e7af3d-4601-4ec2-9204-4a39e9c6d370

Do we need a uuid here?

dawehner’s picture

I think this would break the contrib default views which exist on file system

Well, they can be updated.

alexpott’s picture

Issue tags: +Needs change record

I think we need a change record to tell module developers that any default configuration that provides a view that uses fields might need fixing.

alexpott’s picture

Status: Needs review » Needs work

Apart from the missing CR this patch looks pretty good to me. Couple of comments about the update function...

  1. +++ b/core/modules/views/views.install
    @@ -329,5 +331,92 @@ function views_update_8003() {
     /**
    + * Update some views fields that were previously duplicated.
    + */
    +function views_update_8004() {
    

    Given that we're relying on the entity manager and entity type definitions - shouldn't this be a post update function?

  2. +++ b/core/modules/views/views.install
    @@ -329,5 +331,92 @@ function views_update_8003() {
    +      $entity_keys = $entity_type->getKeys();
    +      $duplicate_fields = array_intersect_key($entity_keys, array_flip(['id', 'revision', 'bundle']));
    

    This could be done once per entity type in the loop above - rather than once per view.

catch’s picture

Yes 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.

effulgentsia’s picture

Issue tags: -rc target +8.0.0 target

We'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.

xjm’s picture

Thanks @dawehner and @alexpott.

Why do these only get set if the entity is translatable?
See comment above :) We copy the logic from \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout()

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:

++ b/core/modules/views/views.install
@@ -329,5 +331,92 @@ function views_update_8003() {
+    // Store the entity keyed by base table. If it has a data table, use that as
+    // well.
+    if ($data_table = $entity_type->getDataTable()) {
+      $entities_by_table[$data_table] = $entity_type;
+    }
+    if ($base_table = $entity_type->getBaseTable()) {
+      $entities_by_table[$base_table] = $entity_type;
+    }
+
+    $base_tables[$entity_type_id] = $entity_type->getBaseTable() ?: $entity_type->id();
+    $revisionable = $entity_type->isRevisionable();
+
+    $revision_table = '';
+    if ($revisionable) {
+      $revision_table = $entity_type->getRevisionTable() ?: $entity_type->id() . '_revision';
+    }
+    $revision_tables[$entity_type_id] = $revision_table;
+
+    $translatable = $entity_type->isTranslatable();
+    $data_table = '';
+    if ($translatable) {
+      $data_table = $entity_type->getDataTable() ?: $entity_type->id() . '_field_data';
+    }
+    $data_tables[$entity_type_id] = $data_table;
+  }

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.

plach’s picture

But that still doesn't answer the question. Why would we not need the data table if the entity is not translatable?

It'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.

lokapujya’s picture

Why would we not need the data table if the entity is not translatable?

I think it's because: if the content is not translatable, then it won't have a data table.

lokapujya’s picture

#222.7.) Agreed that we can remove the reference to the issue. The UpdatePath test was cloned from EntityViewsDataUpdateTest.php (which had similar documentation.)

dawehner’s picture

Status: Needs work » Needs review
FileSize
43.27 KB
8.31 KB

Worked on the feedback, now about to create a change record.

dawehner’s picture

Issue tags: -Needs change record

Added a change record.

plach’s picture

Status: Needs review » Reviewed & tested by the community

I think #235 addresses Jess' pending concerns.

xjm’s picture

Status: Reviewed & tested by the community » Fixed

That 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!

  • xjm committed 376fb61 on 8.0.x
    Issue #2458223 by geertvd, eiriksm, dawehner, lokapujya, Gábor Hojtsy,...
xjm’s picture

And 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

xjm’s picture

Also tweaked the CR a little, including correcting for @dawehner's mysterious ability to create CRs without a project set, and published it.

dawehner’s picture

including correcting for @dawehner's mysterious ability to create CRs without a project set

Everyone has their own superpower. Mine is a bit ridiculous.

xjm’s picture

Priority: Major » Critical
Status: Fixed » Needs work

Uhoh, I think this broke Postgres:
https://www.drupal.org/pift-ci-job/84294

catch’s picture

MIssing 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.

catch’s picture

https://dispatcher.drupalci.org/job/default/38606/console is encouraging enough to post a patch here.

dawehner’s picture

Do 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?

catch’s picture

dawehner’s picture

Are you awake already?

This is the list of views without sort criteria:

test_view_block
test_view_block2
test_block_content_revision_id
test_block_content_revision_revision_id
test_field_type
test_comment_user_uid
test_view_field_delete
test_view_fieldapi
test_file_user_file_data
test_image_user_image_data
test_contextual_links
test_node_revision_vid
test_serializer_node_display_field
test_serializer_node_exposed_filter
test_tracker_user_uid
test_access_perm
test_access_role
test_plugin_argument_default_current_user
test_user_changed
test_user_name
test_user_relationship
test_user_uid_argument
test_view_argument_validate_user
test_view_argument_validate_username
test_access_none
test_aggregate_count
test_alias
test_argument_default_current_user
test_argument_default_fixed
test_attachment_ui
test_click_sort
test_display_attachment
test_display_empty
test_display_invalid
test_entity_area
test_entity_row
test_entity_type_filter
test_example_area
test_example_area_access
test_executable_displays
test_exposed_block
test_exposed_form_buttons
test_field_argument_tokens
test_field_classes
test_field_output
test_field_tokens
test_filter
test_filter_date_between
test_filter_group_override
test_filter_in_operator_ui
test_glossary
test_group_by_count
test_group_by_in_filters
test_handler_relationships
test_menu_link
test_page_display
test_page_display_arguments
test_page_display_menu
test_page_display_path
test_page_display_route
test_page_view
test_pager_full
test_pager_none
test_pager_some
test_store_pager_settings
test_style_html_list
test_tag_cache
test_tokens
test_view_argument_validate_numeric
test_view_delete
test_view_empty
test_view_entity_test
test_view_entity_test_additional_base_field
test_view_entity_test_data
test_view_entity_test_revision
test_view_pager_full_zero_items_per_page
test_view_status
test_views_groupby_save
test_access_static

see https://gist.github.com/dawehner/8503816b25e24dd8eea0 but to be fair, they maybe just not have more than 1 row

xjm’s picture

Thanks @dawehner, that's helpful.

If you save a View through the UI with no sort on Postgres, does it fatal fail 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.

plach’s picture

The PHP 7 fail looks unrelated, should we RTBC this?

xjm’s picture

I'll commit it if you do. ;) Would like that followup though.

alexpott’s picture

@xjm

If you save a View through the UI with no sort on Postgres, does it fatal fail or is it just ordered differently?

Just a different order - it does not fail - exactly like the test.

alexpott’s picture

Status: Needs review » Reviewed & tested by the community

I 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.

alexpott’s picture

xjm’s picture

Status: Reviewed & tested by the community » Fixed

Thanks!

  • xjm committed 83fdfe9 on 8.0.x
    Issue #2458223 followup by catch, dawehner, alexpott, xjm: Fix block...
xjm’s picture

Priority: Critical » Major

The last submitted patch, 197: 2458223-197.patch, failed testing.

The last submitted patch, 219: 2458223-219.patch, failed testing.

The last submitted patch, 224: 2458223-224.patch, failed testing.

The last submitted patch, 226: 2458223-226.patch, failed testing.

The last submitted patch, 235: 2458223-235.patch, failed testing.

The last submitted patch, 245: test.patch, failed testing.

Status: Fixed » Needs work

The last submitted patch, 247: 2458223-247.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Fixed

  • xjm committed 376fb61 on 8.1.x
    Issue #2458223 by geertvd, eiriksm, dawehner, lokapujya, Gábor Hojtsy,...
  • xjm committed 83fdfe9 on 8.1.x
    Issue #2458223 followup by catch, dawehner, alexpott, xjm: Fix block...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.