Problem/Motivation

Recently the Content lock module removed its field plugins. This had the unexpected effect of making views that had data from the content_lock table no longer dependent on the content_lock module.

Proposed resolution

Ensure that views handlers depend on the provider of any views data.

Remaining tasks

User interface changes

None

API changes

None

Data model changes

New dependencies in views.

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Status: Active » Needs review
Issue tags: +Needs tests
StatusFileSize
new838 bytes

Something like this.

Status: Needs review » Needs work

The last submitted patch, 2: 2942986-2.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new5.36 KB
new5.21 KB

So yay as expected existing tests show that we have missing coverage.

If you look at core/modules/views/tests/modules/views_test_config/test_views/views.view.test_page_display.yml this should depend on views_test_data because it links to a table provided by that module. It currently doesn't in HEAD. With this patch it does.

alexpott’s picture

From \Drupal\views\ViewsData::getData():

      foreach ($modules as $module) {
        $views_data = $this->moduleHandler->invoke($module, 'views_data');
        // Set the provider key for each base table.
        foreach ($views_data as &$table) {
          if (isset($table['table']) && !isset($table['table']['provider'])) {
            $table['table']['provider'] = $module;
          }
        }
        $data = NestedArray::mergeDeep($data, $views_data);
      }

So we're already ensuring that the provider is set which is really nice.

Status: Needs review » Needs work

The last submitted patch, 4: 2942986-4.patch, failed testing. View results

alexpott’s picture

Status: Needs work » Needs review
StatusFileSize
new654 bytes
new5.54 KB

Missed one.

dawehner’s picture

+++ b/core/modules/views/src/Plugin/views/HandlerBase.php
@@ -845,4 +845,19 @@ public function submitFormCalculateOptions(array $options, array $form_state_opt
+        $dependencies['module'][] = $data['table']['provider'];

+++ b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php
@@ -169,11 +169,11 @@ public function query() {
+    $dependencies['module'][] = $table_data['table']['provider'];
+    return $dependencies;

Should we do some array_unique checking here?

alexpott’s picture

re #8 That's all handled in \Drupal\Core\Plugin\PluginDependencyTrait::calculatePluginDependencies() already. Which is called by \Drupal\views\Entity\View::calculateDependencies() to add each plugin's dependencies.

lendude’s picture

I think we need an update path to recalculate the dependencies on existing Views, right?

alexpott’s picture

Status: Needs review » Needs work
Issue tags: +Needs upgrade path

@Lendude++ indeed that is missing. On it.

alexpott’s picture

Status: Needs work » Needs review
Issue tags: -Needs upgrade path
StatusFileSize
new6.86 KB
new13.09 KB

Here's an upgrade path with a test. The update-test-ony.patch is interdiff.

The last submitted patch, 12: 2942986-12.update-test-ony.patch, failed testing. View results

lendude’s picture

+1 for the batching of the view updates, we should have done that for the other updates too.

+++ b/core/modules/views/views.post_update.php
@@ -321,3 +321,37 @@ function views_post_update_filter_placeholder_text() {
+  $count = 0;
+  foreach ($sandbox['views'] as $key => $view_id) {
...
+    unset($sandbox['views'][$key]);
+    $count++;
+    // Do 10 at a time.
+    if ($count == 10) {
+      break;
+    }

But wouldn't it be easier to just do:

for ($i = 0; $i < 10 && count($sandbox['views']); $i++) {
  $view_id = array_shift($sandbox['views']);

I don't really mind it being a little more verbose but this might get copy/pasted a lot in the future :)

alexpott’s picture

StatusFileSize
new1.1 KB
new13.01 KB

@Lendude++ like it! I copied from system_post_update_recalculate_configuration_entity_dependencies() but your idea is nice.

lendude’s picture

Status: Needs review » Reviewed & tested by the community

This looks great.

One thing I'm wondering, what happens after we recalculate the dependencies and it turns out the dependencies are no longer met? ie. the content lock views mentioned in the IS? Is there any way to detect that you now have orphaned/broken config floating around somewhere? Any way we can warn the user?

This is a much more generic question though, and goes for all dependency updates we do, so that shouldn't stop this from going in, I'm just wondering.

alexpott’s picture

@Lendude good question! Views handles this already - it's the plugins use the Broken plugin and that won't add the dependency :) so this patch doesn't make any change to the view because no dependencies change.

+++ b/core/modules/views/tests/fixtures/update/views-data-table-dependency.php
@@ -0,0 +1,71 @@
+// We need the views_test_data table to exist and state entries for
+// views_test_data_schema() and views_test_data_views_data().
+$schema = ViewTestData::schemaDefinition();
+$connection->schema()->createTable('views_test_data', $schema['views_test_data']);
+$connection->insert('key_value')
+  ->fields([
+    'collection',
+    'name',
+    'value',
+  ])
+  ->values([
+    'collection' => 'state',
+    'name' => 'views_test_data_schema',
+    'value' => serialize($schema),
+  ])
+  ->values([
+    'collection' => 'state',
+    'name' => 'views_test_data_views_data',
+    'value' => serialize(ViewTestData::viewsData()),
+  ])
+  ->execute();

All of this is to ensure that the field doesn't use the broken handler and therefore adds the dependency as expected.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 15: 2942986-15.patch, failed testing. View results

lendude’s picture

Status: Needs work » Needs review
StatusFileSize
new13.06 KB
chr.fritsch’s picture

Status: Needs review » Needs work

I checked the patch in combination with the content_lock module and can confirm that the dependencies are now correctly set.

Found one minor:

+++ b/core/modules/views/src/Plugin/views/HandlerBase.php
@@ -845,4 +845,19 @@ public function submitFormCalculateOptions(array $options, array $form_state_opt
+      $data = Views::viewsData()->get($this->table);

We could use $this->viewsData here

lendude’s picture

Status: Needs work » Needs review
StatusFileSize
new642 bytes
new13.06 KB

@chr.fritsch $this->viewsData might not be set so we need to use $this->getViewsData(), but yeah that is an improvement.

chr.fritsch’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. I think we are done here.

alexpott’s picture

Priority: Normal » Major

Missing dependencies is at least a major given how they impact data integrity.

alexpott’s picture

Priority: Major » Critical

This is actually a critical bug at least for Thunder. This is because without this fix views that use content_lock fields don't depend on content_lock. Therefore if you uninstall content_lock you break your site.

larowlan’s picture

  1. +++ b/core/modules/views/src/Plugin/views/HandlerBase.php
    @@ -845,4 +845,19 @@ public function submitFormCalculateOptions(array $options, array $form_state_opt
    +      if (isset($data['table']['provider'])) {
    +        $dependencies['module'][] = $data['table']['provider'];
    

    Do these go via array_unique at some stage? Is there a risk that we'd end up with the same module twice?

  2. +++ b/core/modules/views/views.post_update.php
    @@ -322,6 +322,34 @@ function views_post_update_filter_placeholder_text() {
    +  for ($i = 0; $i < 10 && count($sandbox['views']); $i++) {
    +    $view_id = array_shift($sandbox['views']);
    

    feels like we could use array_splice here

    foreach (array_splice($sandbox['views'], 0, 10) as $view_id)

alexpott’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new1.43 KB
new12.96 KB

@larowlan re #1 kinda they all eventually get added to config via \Drupal\Core\Entity\DependencyTrait::addDependency() which ensures things only get added once.

Agree re point 2. In fact we can do something even better. Hopefully this will become the new way we do this. Batching and mutli-loading ftw.

chr.fritsch’s picture

Status: Needs review » Reviewed & tested by the community

Changes are looking good to me

alexpott’s picture

I think we can generalise the update process see #2949351: Add a helper class to make updating configuration simple

catch’s picture

I don't see any updates to dependencies for views shipped with core, should they be re-exported?

Apart from that the patch looks fine.

alexpott’s picture

@catch they don't change because they are all have the dependencies by other means atm. If this did add a dependency we'd get an error in \Drupal\KernelTests\Config\DefaultConfigTest which explicitly re-saves configuration entities to ensure that the provided defaults match what would be there if you just re-save a configuration entity immediately after install.

plach’s picture

Looks good to me as well and #30 addresses #29, so it should be ok to commit this.

Checking credits.

  • plach committed 9a4cc55 on 8.5.x
    Issue #2942986 by alexpott, Lendude, chr.fritsch, larowlan, dawehner,...
plach’s picture

Status: Reviewed & tested by the community » Fixed

Committed 2d503f7 and pushed to 8.6.x and cherry-picked to 8.5.x. Thanks!

  • plach committed 2d503f7 on 8.6.x
    Issue #2942986 by alexpott, Lendude, chr.fritsch, larowlan, dawehner,...
plach’s picture

Sorry, I didn't mean to credit myself, I was fairly sure I had unchecked my checkbox :(

plach’s picture

Version: 8.6.x-dev » 8.5.x-dev

Status: Fixed » Closed (fixed)

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