Problem/Motivation

When a relationship is removed there is no check whether other handlers depend on it. Thus it is possible to save a view with inconsistent data. This results in fatal errors when trying to process the view.

Proposed resolution

  • Check for dependent handlers on form level when relationship is removed. If dependent handlers found, deny removal.
  • Add check to DisplayPluginBase:validate().
  • Add check to HandlerBase::access() so that if a required relationship doesn't exist, exclude handler from view processing.

Steps to reproduce:

  1. create a view to display a list of fields
  2. add a relationship
  3. add a field based on the relationship described above
  4. delete the relationship
  5. save the view
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

er.pushpinderrana’s picture

Unable to reproduce this issue with drupal 8.0-alpha10 release.

netw3rker’s picture

I just tried to reproduce this as well and couldn't. the steps i attempted were:

1) fresh 8.x-dev install
2) create content view
3) add the user-created relationship
4) add the fields user->created and user->last login
5) delete the relatinship
6) preview

I'm not sure that deleting the view is what you actually meant for step 4 since you can't delete it, then click save (its already gone). If there's a step missing please let us know.

My steps to reproduce weren't without error though. What does happen however is a SQL error is thrown because the table the fields need doesn't exist anymore. It might make sense to have the relationship not be removable until fields that depend on it (and only it) are removed.

Thoughts?

dawehner’s picture

Version: 8.0-alpha9 » 8.x-dev
Priority: Minor » Major
Issue summary: View changes

We should do two things here:

  • Fix Field::access() to not fail completly when the exception is thrown but rather return FALSE. This is access related code, so we have to be safe
  • Add a validation to DisplayPluginBase::validate() to check that there is no field with an invalid relationship. This will block the user from saving the view
kpv’s picture

Assigned: Unassigned » kpv

here's what I get when following #2:

Drupal\Core\Database\DatabaseExceptionWrapper:
Exception in issue2204037[issue2204037]: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'login' in 'field list': SELECT node_field_data.title AS node_field_data_title, node.nid AS nid, node_field_data.created AS node_field_data_created, created AS created, login AS login FROM {node} node INNER JOIN {node_field_data} node_field_data ON node.nid = node_field_data.nid WHERE (( (node_field_data.status = :db_condition_placeholder_0) AND (node.type IN (:db_condition_placeholder_1)) )) ORDER BY node_field_data_created DESC LIMIT 10 OFFSET 0; Array ( [:db_condition_placeholder_0] => 1 [:db_condition_placeholder_1] => page ) in Drupal\views\Plugin\views\query\Sql->execute() (line 1446 of core/modules/views/src/Plugin/views/query/Sql.php).
kpv’s picture

I wonder if this is ok:

- '#limit_validation_errors' => array(array('override')),
+ '#limit_validation_errors' => ($type == 'relationship') ? array(array('override'), array(NULL)) : array(array('override')),

Also noticed a strange behaviour:
Set views as described in #2 and then remove the relationship - "last login" will be broken but "created" will show content's created date. Obviously it happens because content and user entities both have "created" property.

kpv’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 5: fix-missing-relationship-2204037-5.patch, failed testing.

kpv’s picture

Status: Needs work » Needs review
FileSize
4.27 KB

Regarding the previous test fail and the fix:
Is it a fair assumption that if: (!empty($this->options['relationship'])) {
then: $relationships = array_keys($this->view->display_handler->getHandlers('relationship')); won't be undefined, i.e. view and display_handler will be set for the current instance?

Status: Needs review » Needs work

The last submitted patch, 8: fix-missing-relationship-2204037-8.patch, failed testing.

kpv’s picture

Status: Needs work » Needs review
FileSize
5.14 KB

fixed views test

kpv’s picture

Issue summary: View changes
Status: Needs review » Needs work
Issue tags: +Needs tests
kpv’s picture

Title: Site broken when deleting a Views relationship required for a field » Views allows removal required relationships
kpv’s picture

Title: Views allows removal required relationships » Views allows removal of required relationships
Issue summary: View changes
kpv’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
14.32 KB
18.6 KB

The last submitted patch, 14: fix-missing-relationship-2204037-14-test_only.patch, failed testing.

dawehner’s picture

Issue tags: +VDC

Interesting work, will have a deep look in it tomorrow! Thank you for your great work!

kpv’s picture

Also have a look at notice at #5. Maybe it's a subject for a separate issue. I mean, it could point at some architectural drawbacks.

dawehner’s picture

  1. +++ b/core/modules/views/src/Plugin/views/HandlerBase.php
    @@ -473,6 +473,15 @@ public function showExposeForm(&$form, FormStateInterface $form_state) {
    +
    +    // Check for missing relationships.
    +    if (!empty($this->options['relationship'])) {
    +      $relationships = array_keys($this->view->display_handler->getHandlers('relationship'));
    +      if ($this->options['relationship'] != 'none' && !in_array($this->options['relationship'], $relationships)) {
    +        return FALSE;
    +      }
    +    }
    

    So in wonder whether we really have to do that on runtime or can't just always do that on save time.

  2. +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
    @@ -2423,6 +2423,17 @@ public function validate() {
    +          $errors[] = $this->t('You can not remove this relationship because it is used by other handlers.');
    

    Can we list which handlers are using it?

kpv’s picture

Status: Needs review » Needs work

The last submitted patch, 19: fix-missing-relationship-2204037-19.patch, failed testing.

kpv’s picture

FileSize
19.25 KB
adci_contributor’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 21: fix-missing-relationship-2204037-21.patch, failed testing.

kpv’s picture

Status: Needs work » Needs review
FileSize
19.52 KB

Status: Needs review » Needs work

The last submitted patch, 24: fix-missing-relationship-2204037-24.patch, failed testing.

kpv’s picture

Status: Needs work » Needs review
FileSize
19.58 KB

One of the tests in #21 (views/src/Tests/Plugin/DisplayTest::testDisplayPlugin()) failed because ViewExecutable::getHandlers($type, $display_id) sets current display to $display_id (if given), and doesn't restore it to the previous value before return. Currently this was fixed by changing the test. If it is a bug, I suppose, it should be fixed in a separate issue.

upd: Added a follow-up issue #2408613: ViewExecutable::getHandlers() should restore display_id before return.

kpv’s picture

FileSize
19.64 KB

added a minor comment

kpv’s picture

FileSize
19.37 KB

Removed test fix (since #2408613: ViewExecutable::getHandlers() should restore display_id before return was fixed).
Imo, this one is ready for final review.

dawehner’s picture

Its great to have some kind of test coverage here.

+++ b/core/modules/views/src/Entity/View.php
@@ -298,6 +298,32 @@ public function calculateDependencies() {
 

Honestly: I would rather move the logic into the display than into the view storage object, which once had the idea to just store stuff, and be kinda stupid otherwise. At least it would be nice to extract all the logic below into a method.

ivan.chavarro’s picture

Status: Needs review » Needs work
Issue tags: +LatinAmerica2015, +D8UX usability

Hi,

I have reviewed the patch and it works when I remove the relationship from the "Configure Relationship" Form.

Unfortunately In "Rearrange relationships" form it allows to remove, unless it has dependencies, so the user is forced to cancel the submit form to revert the relationship removal.

kpv’s picture

Status: Needs work » Needs review
FileSize
22.81 KB

@ivan.chavarro
Thanks, added "rearrange" form validation.

dawehner’s picture

The point in #29 though is still true :(

kpv’s picture

Assigned: kpv » Unassigned
givingxsharing’s picture

I applied the patch and it works for me in both the Rearrange Relationship form and the Configure Relationship form. Thanks!

kpv’s picture

@dawehner
What kind of test coverage did you mean in #29 ?

jcnventura’s picture

Reviewing this in the mentored core sprints. Contributor tasks:
https://www.drupal.org/contributor-tasks/review

akozma’s picture

Status: Needs review » Needs work

I applied the patch #31 but the result has a strange behavior which is not so user friendly.

Steps:

1) fresh 8.0.x install
2) create content view
3) add the "Content: Content author" relationship
4) add the field "User: Name"
5) delete the relationship

Result:
- the relationship has been removed
- the error message appears "You can not remove this relationship because it is used by other handlers (field: name)."

Problem:
- the message "You can not remove this relationship..." doesn't contains information about the removed relationship
- can't use the "Cancel" button in order to cancel the current changes (I got the same configuration + the error message mentioned above)

Tip:
- the solution would be more user friendly if the relationship could be deleted only if there are no other dependencies, in other case a validation message will popup

lokapujya’s picture

I think I performed the same steps, but got slightly different results.

After deleting the relationship and hitting apply, I got the message: You can not remove "uid" relationship because it is used by other handlers (field: name).
Cancel button works.

Lendude’s picture

Title: Views allows removal of required relationships » Views allows removal of required relationships and gives a fatal error on save
Issue summary: View changes
Status: Needs work » Needs review
FileSize
15.39 KB
11.28 KB

As @dawehner pointed out in #18, it would be better to check this at save. The number of scenarios that can lead to this (defaulted field vs overridden relationships, vice versa, multiple displays with different fields and relationships) makes checking this at run time pretty doomed I feel.
Also, when I'm editing my View I don't want to be bothered with "You can't do that" messages before I'm all the way done.

So, major overhaul. Just validation in the display at save. Lets see how this tests.

xjm’s picture

The core committers and Views maintainers (alexpott, xjm, effulgentsia, tim.plunkett, and dawehner) agreed that this is a major issue. A fatal is being caused under normal administrative site operation.

xjm’s picture

Issue tags: +Triaged core major
dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Looks reasonable for me

lokapujya’s picture

Status: Reviewed & tested by the community » Needs work

I noticed that I was unable to cancel after removing the relationship (as described in #37). Without the patch, I can cancel.

lokapujya’s picture

Is there some way to check if the Action is Cancel and skip validation?

For example:

In: core/lib/Drupal/Core/Form/FormBuilder.php
-      $this->formValidator->validateForm($form_id, $form, $form_state);
+      if ($form_state->getUserInput()['op'] != 'Cancel') {
+        $this->formValidator->validateForm($form_id, $form, $form_state);
+      }

Notice that without the patch Cancel brings you out of the edit and back to the views list.

Lendude’s picture

@lokapujya I haven't tested this yet but 'Views validates displays on Cancel' sound like an issue unrelated to this patch. So if it is indeed a more generic issue, shouldn't we just open an issue for that and RTBC this one?

dawehner’s picture

Status: Needs work » Reviewed & tested by the community

@lokapujya I haven't tested this yet but 'Views validates displays on Cancel' sound like an issue unrelated to this patch. So if it is indeed a more generic issue, shouldn't we just open an issue for that and RTBC this one?

+1

This patch solves a problem and doesn't introduce a new one.

lokapujya’s picture

OK, created a new issue: https://www.drupal.org/node/2671182

catch’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2443,6 +2443,16 @@ public function validate() {
 
+    // Check for missing relationships.
+    $relationships = array_keys($this->getHandlers('relationship'));
+    foreach (ViewExecutable::getHandlerTypes() as $type => $handler_type_info) {
+      foreach ($this->getHandlers($type) as $handler_id => $handler) {
+        if (!empty($handler->options['relationship']) && $handler->options['relationship'] != 'none' && !in_array($handler->options['relationship'], $relationships)) {
+          $errors[] = $this->t('The %handler_type %handler uses a relationship that has been removed.', array('%handler_type' => $handler_type_info['lstitle'], '%handler' => $handler->adminLabel()));
+        }
+      }
+    }
+

This validates when a relationship has been removed and the view is saved.

But could we not validate when the relationship itself is removed or at least warn then? When you get here it's a bit late.

I can't decide whether it's more annoying to not be able to remove the relationship or not be able to save the view, but feels like the latter is probably more annoying. Obviously both are less annoying than fatal errors so if that would be much harder to implement could move to a follow-up - we already have the validation meta.

Lendude’s picture

@dawehner argued against that in #18 and me in #39

I personally feel that not being able to remove the relationship is more annoying because it forces a workflow on me (remove all references to relationship first then remove relationship), where I feel that its a good thing that the UI doesn't bother me with errors while I'm mucking about until I tell it I'm actually done with all my changes, but that might just be me.

kpv’s picture

@Lendude
this reverts a test fix which wouldn't pass one of the checks you removed in #39

diff --git a/core/modules/views/src/Tests/Handler/HandlerTest.php b/core/modules/views/src/Tests/Handler/HandlerTest.php
index ae61c7b..6507e23 100644
--- a/core/modules/views/src/Tests/Handler/HandlerTest.php
+++ b/core/modules/views/src/Tests/Handler/HandlerTest.php
@@ -28,7 +28,7 @@ class HandlerTest extends ViewTestBase {
    *
    * @var array
    */
-  public static $testViews = array('test_view', 'test_view_handler_weight', 'test_handler_relationships', 'test_handler_test_access', 'test_filter_in_operator_ui', 'test_exposed_relationship_admin_ui');
+  public static $testViews = array('test_view', 'test_view_handler_weight', 'test_handler_relationships', 'test_handler_test_access', 'test_filter_in_operator_ui');
Lendude’s picture

@kpv sorry, I don't follow. The test that was in HandlerTest is basically the same as the one that is now in DisplayTest. Not sure what you are referring to.

lokapujya’s picture

But could we not validate when the relationship itself is removed or at least warn then?

Validating would force the site builder to remove the fields before removing the relationship. Validating on save doesn't force a workflow.

How would a warning work? Maybe this discussion should happen in the views validation meta?

this reverts a test fix which wouldn't pass one of the checks you removed in #39

Doesn't that test only make sense when validating the relationship, but not when validating on save.

swetashahi’s picture

I tested this using Simplytest.me on a chrome browser using the patch in #39

Steps followed
1. Created a content type and added a content
2. Created a view and added fields from new content type
3. Created a relationship author
4. Added another field to the view User: Name
Screenshot here
image
5. Now deleted the relationship by clicking Rearrange->Remove
6. In Preview section, the warning showed up that "The field User: Name uses a relationship that has been removed. "
7. On clicking Save, got the error "The field User: Name uses a relationship that has been removed."
Screenshot here
image
8. I was able to cancel the relationship deletion at relationship screen and also edit view screen

Thanks @alexpott for the "ask-to-test"

swetashahi’s picture

Status: Needs review » Reviewed & tested by the community
catch’s picture

Status: Reviewed & tested by the community » Fixed

In Preview section, the warning showed up that "The field User: Name uses a relationship that has been removed. "

That warning showing up is enough to cover my concern from #48, great that this already happens when the relationship gets removed.

Committed/pushed to 8.1.x and cherry-picked to 8.0.x. Thanks!

I added issue credit for dawehner, swetashahi and lokapujya.

  • catch committed 49db644 on 8.1.x
    Issue #2204037 by kpv, Lendude: Views allows removal of required...

  • catch committed d63155c on 8.0.x
    Issue #2204037 by kpv, Lendude: Views allows removal of required...

Status: Fixed » Closed (fixed)

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