Problem/Motivation

Business Rules makes it possible to configure a dependency between two fields in a node, i.e.: when the "triggering" field changes, new options can be selected from the "target" because it is updated in an AJAX call.

As a site builder, I might want to set up a dependent field inside of a paragraph, whose widget is embedded in a node. It is currently possible to configure this; but it does not work. More specifically, no JavaScript is listening for the triggering field to change, which is different when the two fields are in the node itself.

Steps to reproduce

  1. Clone drupal, branch 8.9.x - I was at commit 485d2a305f when I tried this.
  2. Install Drupal using the standard install profile.
  3. Download dBug-2.0.0, entity_reference_revisions-8.x-1.9, and paragraphs-8.x-1.12.
  4. Clone business_rules, branch 2.x - I was at commit bec01f4 when I tried this.
  5. Create a vocabulary named vocab1. Add 2 terms, named vocab1term1 and vocab1term2.
  6. Add a term reference field named field_user_term1 to the user entity. Configure it to use the Default Reference method, and accept terms from vocab1. Configure the form display to show this field as a Select list instead of an Autocomplete.
  7. Add a view named view1 that shows Users sorted by Unsorted. Add an Entity Reference display...
    1. Edit Format -> Format -> Entity reference list -> Settings, and check User: Name in Search fields.
    2. Add a contextual filter on field_user_term1. When the filter value is not available, Display contents of "No results found". When the filter value is available or a default is provided, check Specify validation criteria, set Validator to Taxonomy term ID, and check Vocabulary vocab1 (leave the remaining fields at their default).
  8. Create a paragraph type named paratype1...
    1. Add a term reference field named field_paratype1_termfield1 to the paragraph type. Configure it to use the Default Reference method, and accept terms from vocab1. Configure the form display to show this field as a Select list instead of an Autocomplete.
    2. Add a user reference field named field_paratype1_user1 to the paragraph type. Configure it to use the Business Rules: Make field dependent using views. Set View used to select the entities to view1 - Entity Reference and the Parent field to Entity reference: paratype1_termfield1 [field_paratype_termfield1] (leave the remaining fields at their default). Configure the form display to show this field as a Select list instead of an Autocomplete.
  9. Create a content type named nodetype1. Add a paragraph reference field named field_nodetype1_parafield1.
  10. Create four users: user1 and user3 referencing vocab1term1; and user2 and user4 referencing vocab1term2.
  11. Go to /node/add/nodetype1. You see the nodetype1_parafield1 field, with an empty paratype1 in it. You see the triggering paratype1_termfield1 field containing vocab1term1 and vocab1term2, and the target paratype1_user1 field, which is empty.
  12. Select vocab1term1 from paratype1_termfield1.
    • Expected behavior: paratype1_user1 changes to contain user1 and user3
    • Actual behavior: nothing happens
  13. Select vocab1term2 from paratype1_termfield1
    • Expected behavior: paratype1_user1 changes to contain user2 and user4
    • Actual behavior: nothing happens

... note that after applying the patch in #29, an AJAX spinner appears after changing the value of paratype1_termfield1, but paratype1_user1 remains empty.

Proposed resolution

Modify src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php and js/update-options-command.js to identify and properly handle subforms.

Remaining tasks

  1. Document a test case
  2. Fix test failures in the branch
  3. Write a working patch
  4. Review and feedback
  5. RTBC and feedback
  6. Commit
  7. Release

User interface changes

None.

API changes

  1. A required boolean $multiple parameter has been added to the signature of \Drupal\business_rules\Ajax\UpdateOptionsCommand::__construct()

Data model changes

None.

Original report by @lucius_nick

Following the guidelines in https://www.drupal.org/docs/8/modules/business-rules/advanced-usage/depe... I have created a dependent field in a paragraph.

The business rule works if I save the paragraph but changing the parent field does not trigger ajax.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

lucius_nick created an issue. See original summary.

C.E.A’s picture

Can you please elaborate with any logging errors for this issue ?
Copy and paste the error here please

lucius_nick’s picture

Hi, we would like to elaborate but we have no errors, I'm sorry. It seems like the the ajax doesn't get attached to the paragraph at all. Maybe the patch supplied in https://www.drupal.org/project/business_rules/issues/2973571 will solve it. I don't have the time to check it right now. I will try to find some time this week so I can try and apply that patch.

C.E.A’s picture

Sure!
Also please try to download the last dev release for testing because the beta 6 has been known will plenty issues already fixed in the latest dev release.

gun_dose’s picture

FileSize
7.14 KB

I encountered the same problem. There are no errors in logs, but module does not works with paragraphs: just nothing happens and fields are independent. I dig into this problem and made some changes:
1. First of all remove usage on 'Entity' class from .module file, because this class is deprecated and condition

if ($entity instanceof Entity) {

will never fires, because this class is not used now. (Proof)
And this was the reason why there are no errors in log.
2. When we deal with inline forms, we should check if field has parents or not. And we should use field definition from entity associated with subform.
3. When update selects, we should remember that ID's could be changed by AJAX, so we should get elements by 'data-drupal-selector' selector attribute instead of 'id'.
4. Also when field is multiple, but select is empty, it will be rendered as single-value select, so we should change this in according to field cardinality settings. This is the reason why I changed 'UpdateOptionsCommand' class.

And here is a patch, that allows to work with paragraphs. May this needs some work to work with another nested entities, but now it works well with Paragraphs.

gargsuchi’s picture

Status: Active » Reviewed & tested by the community

This patch worked perfectly for me! Thanks @gun_dose.

gun_dose’s picture

FileSize
8.03 KB

Sorry, I found one bug in my patch - it works good for new entities, but when edit existing entity, it causes validation error for dependent field in paragraph. I fixed it and here is new patch.

Kris77’s picture

The patch in #7 works great!

Thanks @gun_dose.

colan’s picture

Title: Business rules ajax does not trigger in paragraph » Add AJAX support to Paragraphs
Version: 8.x-1.0-beta6 » 8.x-1.x-dev
Category: Support request » Feature request
Status: Reviewed & tested by the community » Needs work

Thanks for working on this. Here's a code review:

+++ b/src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php
@@ -159,6 +160,27 @@ class BusinessRulesViewsSelection extends PluginBase implements SelectionInterfa
+    // Inline form marker.
+    $inline_form = FALSE;
+    if (isset($trigger_field['#parents'])) {
+      $parents = $trigger_field['#parents'];
+      if (in_array('subform', $parents, TRUE)) {
+        $parent_field_key = array_shift($parents);
+        /** @var \Drupal\field\Entity\FieldConfig $definition */
+        $definition = $entity->getFieldDefinition($parent_field_key);
+        if ($definition->getType() === 'entity_reference_revisions') {
+          $inline_form = TRUE;
+          $delta = array_shift($parents);
+          $widget = $form[$parent_field_key]['widget'][$delta];
+          if (isset($widget['#paragraph_type'])) {
+            // TODO: find way to extract real paragraph to avoid of empty paragraph creation.
+            $entity = \Drupal::entityTypeManager()->getStorage('paragraph')->create([
+              'type' => $widget['#paragraph_type'],
+            ]);
+          }
+        }
+      }
+    }

There's a lot of nesting here. Can we maybe move this code into one or more (protected?) methods with descriptive names? It would help make the code cleaner as it's unreadable currently.

+++ b/src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php
@@ -214,14 +236,26 @@ class BusinessRulesViewsSelection extends PluginBase implements SelectionInterfa
+
+

Extra line of whitespace here.

+++ b/src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php
@@ -214,14 +236,26 @@ class BusinessRulesViewsSelection extends PluginBase implements SelectionInterfa
+        // Check if field is miltiple or not.

Typo in "miltiple".

+++ b/src/Plugin/EntityReferenceSelection/BusinessRulesViewsSelection.php
@@ -510,6 +544,16 @@ class BusinessRulesViewsSelection extends PluginBase implements SelectionInterfa
+      // Try to extract values from nested entities.
+      if (isset($handler_settings['entity']) && $handler_settings['entity'] instanceof EntityInterface) {
+        // Handle paragraphs.
+        if ($handler_settings['entity']->getEntityTypeId() === 'paragraph') {
+          $value = $handler_settings['entity']->get($field)->getString();
+        }
+        // Here may be processors for another entity types.
+      }

I think it would be best to create a new method for this stuff so the current one doesn't get too big. Maybe something like extractValueFromNestedEntities()?

el1_1el’s picture

FileSize
7.71 KB

patch in 7 did not apply to beta8 or dev. reroll

pieterjandp’s picture

FileSize
8.19 KB

Patch 10 does not work for nested paragraphs. This patch fixes that problem.

pieterjandp’s picture

Status: Needs work » Needs review
colan’s picture

Status: Needs review » Needs work

Please include the missing interdiff. Thanks.

pieterjandp’s picture

Status: Needs work » Needs review
FileSize
3.46 KB
colan’s picture

Component: Code » Dependent fields
kevinkonsorr’s picture

Patch #11 is only kinda working for me (I am not sure if I am doing anything correctly..)
I am using beta10 of bussiness_rules and applying the patch via composer.json is not working. (Error says 'cannot apply patch [...]')
When I try applying it with phpStorm I notice that it is not able to process line 116 from the patch ($subform = $form;).
I apply every other block fine and add $subform = $form; to the file manually, skip the block and everything works fine.
Also I am not sure on how to use the interdiff – Maybe that is what I am doing wrong?

Tried to add the patch to composer.json like so like all my patches that work fine:

"drupal/business_rules": {
      "Bussines Rules Paragraphs Ajax Fix": "patches/paragraphs-3040253-11.patch"
}
kevinkonsorr’s picture

el1_1el’s picture

Patch rerolled for beta10. Also linked to Multi-valued dependent child fields are not saved since that requires this patch.

Murz’s picture

Thanks for the patch, it also fixes problem with not working dependent fields in Inline Entity Form widgets, issue about this is #2992273: Dependent fields not working in Inline Entity Forms?

Megha_kundar’s picture

FileSize
7.9 KB
499 bytes

As previous applied patch was failed to apply. Applying new updated patch.

Megha_kundar’s picture

FileSize
8.19 KB
216 bytes
Marceldeb’s picture

I can confirm #21 solves a lot of 'ajax hassle' with this module. Before the patch i could not open forms in a modal (ajax would not work and javascript would throw an error).

After patch it works.

Thanks a lot!

Megha_kundar’s picture

Version: 8.x-1.x-dev » 8.x-1.0-beta13
FileSize
19.37 KB

Status: Needs review » Needs work

The last submitted patch, 23: 3040253-23.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Megha_kundar’s picture

Assigned: Unassigned » Megha_kundar
Megha_kundar’s picture

Status: Needs work » Needs review
FileSize
20.59 KB

Status: Needs review » Needs work

The last submitted patch, 26: 3040253-24.patch, failed testing. View results

colan’s picture

Version: 8.x-1.0-beta13 » 2.x-dev

New features go into HEAD, and can then be backported if there's interest.

And please don't forget about interdiffs.

Megha_kundar’s picture

FileSize
18.37 KB
6.75 KB

Hi colan,
Attaching interdiff from #23 to #26 and also ported patch #26 to 2.0.
Please review.

Megha_kundar’s picture

Status: Needs work » Needs review
colan’s picture

If folks are interested in fixing tests (see the test results "Waiting for branch to pass" above), which would help with moving this issue along, please help with #3187013: Error: Call to a member function prepare() on null after updating to Drupal 9.1.0 / PHP 7.4.11.

mparker17’s picture

Issue summary: View changes
Status: Needs review » Needs work

Updated the issue summary to conform to the issue template; and added steps to reproduce, i.e.: a manual test case.

Note that when I perform the manual test case after applying the patch in #29, an AJAX spinner appears after changing the value of paratype1_termfield1, but paratype1_user1 remains empty, so I'm marking this as "Needs work" - but feel free to disagree if the manual test case doesn't match yours (I based my manual test case on my client site's desired behavior, but it may be more complex than it needs to be).

mparker17’s picture

Status: Needs work » Needs review
FileSize
19.01 KB
701 bytes

Ah! I figured out what was wrong by looking at the patch in #11... at some point, the line in \Drupal\business_rules\Plugin\EntityReferenceSelection\BusinessRulesViewsSelection::updateDependentField to set $subform went missing.

Here's a patch; reviews welcome.

mparker17’s picture

FileSize
45.87 KB
23.94 KB

Here's an initial attempt at codifying the test case in the issue summary into a FunctionalJavascript test. It was failing to find the values in the target field on my local for some reason - I suspect I'm not $page->waitFor()ing properly - but I'll have to figure that out on Monday.

***

On a related note, I found FunctionalJavascript tests to be failing with a PHP Fatal error: Uncaught PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal8.testNNNNNNNNkey_value_expire' doesn't exist

My gut says BusinessRulesProcessor::__destruct() runs after PHPUnit has deleted the table. Commenting out the line that tries to modify key_value_expire fixes the issue...

(EDIT: a code snippet used to be here, but was found to be a Really Bad Idea in #3187013-17: Error: Call to a member function prepare() on null after updating to Drupal 9.1.0 / PHP 7.4.11 so I've removed the code snippet for safety!)

... going to have to figure that one out on Monday too (and probably file another ticket to fix). The question is, why does that line exist and is it safe to delete? i.e.: will removing it cause key_value_expire to grow without constraints, like #2931611-11: Honeypot key value entries never expire (effectively)? 😱

mparker17’s picture

Issue summary: View changes
FileSize
46.75 KB
5.31 KB

Okay; here's a patch with a passing test (once $keyvalue->deleteAll(); is deleted/commented from src/Util/BusinessRulesProcessor.php::__destruct() - going to work on that next).

Several things were wrong; mainly that I was creating users and trying to assign values for the wrong field name (field_user_term) due to a typo when I originally captured the configuration for the test module. I also switched to using $this->assertSession()->assertWaitOnAjaxRequest(); instead of my own waitFor() logic (which was hard to get correct). And I captured the user's form display and modified the form display for the node type to make it easier when you install the test module for manual testing.

Reviews welcome.

mparker17’s picture

Note that #3133687: Wrap keyvalue delete in BusinessRulesProcessor destruct includes a similar test; if that gets merged first, we should probably update this patch so the test itself and the test module says, essentially, "go look over there for a similar test that doesn't involve paragraphs" and vice-versa.

The last submitted patch, 34: 3040253-34.patch, failed testing. View results

mparker17’s picture

Assigned: Megha_kundar » Unassigned
FileSize
46.66 KB
42.57 KB

It was merged; so here's an updated patch.

The last submitted patch, 35: 3040253-35.patch, failed testing. View results

mparker17’s picture

FileSize
3.15 KB

The interdiff in the last comment got mangled because I hadn't rebased my old branch; sorry about that....

Status: Needs review » Needs work

The last submitted patch, 38: 3040253-37.patch, failed testing. View results

mparker17’s picture

Status: Needs work » Needs review
FileSize
47.01 KB
308 bytes

Turns out I also need to tell Testbot to install the paragraphs module in the test environment so it is ready for the tests that involve it.

Status: Needs review » Needs work

The last submitted patch, 42: 3040253-41.patch, failed testing. View results

  • colan committed c244ede on 2.x authored by Megha_kundar
    Issue #3040253 by mparker17, Megha_kundar, gun_dose, el1_1el,...
mparker17’s picture

Status: Needs work » Needs review

Ah, yes, I'd forgotten about this... test_dependencies cannot be added in a patch...

From the documentation (emphasis mine)...

test_dependencies: a list of other modules (in the same format as dependencies) that are needed to run certain automated tests for your module on Drupal's automated test runner ("DrupalCI"), but not needed as module dependencies in general (or that are in development as module dependencies but not finalized yet). Note that you need to have the test_dependencies change committed to your Git repository before you try to run a test that depends on it – you cannot just put the info.yml change into the same patch as the new test. [...]

... so I daresay this is still cool, as it passes locally for me.

mparker17’s picture

Status: Needs review » Fixed

Marking as fixed as per @colan's commit.

Status: Fixed » Closed (fixed)

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

calinh’s picture

This current topic is about the parent field inside the same paragraph with the dynamically dependent child field.

I am very interested to have the parent field outside the paragraph - directly inside a node - and the dynamically dependent child field to be inside the paragraph, together with other normal fields.

Any idea on how this scenario can be solved? Using what other modules? Thank you.