Webform Node and other entity references

Created on 8 September 2025, 2 days ago

Problem/Motivation

On sites where the Webform Node module is used, the goal is usually that each Webform has been embedded on at least one accompanying node, but there is no easy way for non-technical website administrators to see this.

Proposed resolution

When Webform Node is enabled:

  • create a report page that lists all Webforms and the node or nodes each one is listed on.
  • create a warning on the Drupal Status page stating that "X out of Y Webforms are not associated with a Webform Node".

This information can be obtained, roughly, with an SQL query like this:

SELECT `webform`.`webform_id`, `node__webform`.`entity_id` as node_id FROM `webform`
LEFT JOIN `node__webform` ON `node__webform`.`webform_target_id` = `webform`.`webform_id`
ORDER BY node_id;

It would also be useful to know when a Webform is referenced by any entity, not just nodes, so there might be room for generalization here, or at least a follow-up to create another report page to list out each Webform and any top-level entities that reference them. By top-level entities, I mean any entity that has a canonical URI. (Paragraphs notably do not have a canonical URI.)

Remaining tasks

User interface changes

API changes

Data model changes

✨ Feature request
Status

Active

Version

6.3

Component

Miscellaneous

Created by

πŸ‡ͺπŸ‡¨Ecuador jwilson3

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @jwilson3
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3

    It is probably also useful to see when any Webform Nodes exist but do not reference any webform. A report going in both directions is akin to a more proper Correspondence or Bijection graph, where we're trying to ensure each entity has a matching partner, a natural 1-to-1 mapping.

    The basic information can be obtained, roughly, with a simple query like this:

    SELECT `webform`.`webform_id`, `node__webform`.`entity_id` as node_id FROM `webform`
    LEFT JOIN `node__webform` ON `node__webform`.`webform_target_id` = `webform`.`webform_id`
    ORDER BY node_id;
    

    But this only accounts for webforms that either have or don't have Webform Nodes, it doesn't account for Webform Nodes that don't link to webforms, or Webforms referenced by multiple Webform Nodes.

    For a more complete picture, we need a more complex query:

    -- Show pairs + orphans + cardinality checks in one result.
    SELECT
      x.webform_id,
      x.node_id,
      CASE
        WHEN x.webform_id IS NULL THEN '1_NODE_WITHOUT_WEBFORM'
        WHEN x.node_id   IS NULL THEN '2_UNREFERENCED_WEBFORM'
        WHEN wn.cnt_nodes_for_form = 1 AND nw.cnt_forms_for_node = 1
             THEN '5_PAIRED_1_TO_1'
        WHEN wn.cnt_nodes_for_form > 1 THEN '3_FORM_REFERENCED_BY_MULTIPLE_NODES'
        WHEN nw.cnt_forms_for_node > 1 THEN '4_NODE_REFERENCES_MULTIPLE_FORMS'
        ELSE 'CHECK'
      END AS status,
      wn.cnt_nodes_for_form,
      nw.cnt_forms_for_node
    FROM (
      -- A) all webforms, with their referencing node if any
      SELECT w.webform_id, n.entity_id AS node_id
      FROM webform AS w
      LEFT JOIN node__webform AS n
        ON n.webform_target_id = w.webform_id
       AND n.deleted = 0
      /* optionally constrain to the node bundle that should carry the field:
         AND n.bundle = 'webform_node' */
      UNION
      -- C) nodes whose field points to a webform id that doesn't exist
      SELECT w.webform_id, n.entity_id AS node_id
      FROM node__webform AS n
      LEFT JOIN webform AS w
        ON w.webform_id = n.webform_target_id
      WHERE w.webform_id IS NULL
        AND n.deleted = 0
      /* optionally: AND n.bundle = 'webform_node' */
    ) AS x
    LEFT JOIN (
      -- count how many nodes reference each webform
      SELECT webform_target_id, COUNT(*) AS cnt_nodes_for_form
      FROM node__webform
      WHERE deleted = 0
      GROUP BY webform_target_id
    ) AS wn
      ON wn.webform_target_id = x.webform_id
    LEFT JOIN (
      -- count how many webforms each node references (should be 0 or 1)
      SELECT entity_id, COUNT(*) AS cnt_forms_for_node
      FROM node__webform
      WHERE deleted = 0
      GROUP BY entity_id
    ) AS nw
      ON nw.entity_id = x.node_id
    ORDER BY status, x.node_id, x.webform_id;
    
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3
  • πŸ‡ͺπŸ‡¨Ecuador jwilson3
  • πŸ‡ΊπŸ‡ΈUnited States jrockowitz Brooklyn, NY

    This is a great idea. It would be handy for tracking nodes and paragraphs that reference webforms.

    I think it should be in a dedicated contrib module called something like webform_audit or webform_usage.

    I might opt not to have a query build the report, but to track webform usage in a table when an entity is saved.

    When saving an entity, review the field definitions to check for a webform reference field. If a webform reference field exists, then store the entity type, entity ID, field name, delta, and webform id in the webform_usage table. For paragraphs, you can also look up the main entity. I would store the delta just in case there is a multiple-value webform reference field.

    BTW, I think the entity usage module has some support for webforms.
    @see https://www.drupal.org/project/issues/entity_usage?text=webform&status=All β†’

    I hope this helps.

Production build 0.71.5 2024