Problem/Motivation

The content_first_audit submodule processes heading audits using procedural hooks (hook_node_insert, hook_node_update,
hook_node_delete) that run synchronously on every node save. This approach has several limitations:

  • No tracking or retry logic — if an audit fails (e.g. rendering error, database issue), the failure is silent and unrecoverable without manually re-saving the node.
  • No bulk coverage — there is no way to audit existing content created before the module was installed, or to re-audit all content after analyzer improvements.
  • Synchronous rendering on every save — full node render + DOMDocument parsing runs in the request lifecycle, adding latency to every node save.
  • No admin visibility — administrators cannot see which nodes have been audited, which failed, or trigger re-processing.

Steps to reproduce

  1. Install content_first_audit on a site with existing content.
  2. Observe that only newly saved nodes get heading audits — existing content has no audit data.
  3. Cause a transient error during a node save (e.g. database timeout) — the audit silently fails with no way to retry.

Proposed resolution

Integrate content_first_audit with the Entity Registry module by:

  1. Creating a ContentAuditConsumer plugin (#[EntityRegistryConsumer]) that implements processItem() (load entity → get translation → render → XPath headings → analyze → upsert) and deleteItem() (remove audit data for deleted translations).
  2. Removing the procedural hooks (hook_node_insert, hook_node_update, hook_node_delete) and the _content_first_audit_process_node()
    helper — entity lifecycle tracking is now handled by Entity Registry.
  3. Setting processing_mode: 'async' so heading audits run via cron queue instead of synchronously on every save.
  4. Adding a deleteByEntityAndLanguage() method to AuditRepositoryInterface / AuditRepository for per-translation cleanup.
  5. Adding a migration update hook (content_first_audit_update_10001) that seeds the entity_registry tracker table with PROCESSED records for all existing audit
    data, so Entity Registry knows which nodes have already been audited.

Remaining tasks

  • Review and commit the implementation.
  • Test migration update hook on a site with existing audit data.

User interface changes

None in content_first_audit itself. Administrators can use the Entity Registry admin UI at /admin/config/system/entity-registry to see the Content Audit consumer status (pending/processed/failed counts), trigger bulk populate, reindex, or retry failed items.

API changes

  • AuditRepositoryInterface — new method: deleteByEntityAndLanguage(int $entity_id, string $entity_type, string $language): void
  • New dependency: entity_registry:entity_registry
  • Removed: hook_node_insert(), hook_node_update(), hook_node_delete(), _content_first_audit_process_node()

Data model changes

No schema changes to the content_first_audit table. The migration update hook (content_first_audit_update_10001) inserts records into the entity_registry table (provided by the Entity Registry module) with status PROCESSED for all existing audit entries, using cursor-based pagination in chunks of 1000.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

eduardo morales alberti’s picture

Issue summary: View changes

eduardo morales alberti’s picture

Status: Active » Needs review

Ready to review

eduardo morales alberti’s picture

Assigned: eduardo morales alberti » Unassigned

eduardo morales alberti’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

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