Hooks

Hooks: Forms

6 min read

These hooks cover the Forms module (app/Modules/Forms/) added in Notifal 3.0.0. Use them to validate, block, mutate, or react to public form submissions without editing core services.

Index: Hook Reference Hub.

User guides: Notifal Forms Overview.

Prefer PHP constants from Notifal\Infrastructure\WordPress\Hooks\ActionHooks and FilterHooks. Scoped hooks are fired through FormsHookDispatcher with sprintf().

Conventions

TopicDetail
Global hooksRun for every form (notifal/forms/...)
Form-scoped hooksAppend CPT ID: sprintf(FilterHooks::FORMS_SUBMIT_FIELDS_FORM, $form_id) → notifal/forms/submit/fields/42
Field-scoped hooksAppend field machine name, or form ID + field name
Mutating dataUse filters. Actions (FORMS_SUBMIT_BEFORE) are for side effects only
SecretsNever add webhook keys, Mailchimp secrets, or tokens to FORMS_SUBMIT_RESPONSE
Security gatesCore spam and integrity checks run before extension hooks. Do not rely on hooks to replace those checks

Submit and validation pipeline (order)

After core security checks succeed, extension hooks run in this order:

  1. FORMS_SUBMIT_ALLOW (+ form-scoped)
  2. FORMS_SUBMIT_FIELDS (+ form-scoped)
  3. FORMS_SUBMIT_BEFORE (+ form-scoped)
  4. FORMS_VALIDATE_BEFORE (+ form-scoped)
  5. Per field: FORMS_VALIDATE_FIELD (+ field / form+field scoped)
  6. FORMS_VALIDATE_RESULT (+ form-scoped)
  7. FORMS_VALIDATE_AFTER (+ form-scoped)
  8. Unique-value check (FORMS_UNIQUE_VALUE_DUPLICATE_MESSAGE on collision)
  9. FORMS_SUBMISSION_DATA (+ form-scoped) then DB insert
  10. Emails and integrations
  11. FORMS_SUBMIT_AFTER (+ form-scoped)
  12. FORMS_SUBMIT_RESPONSE (+ form-scoped)

On failure after a verified request: FORMS_SUBMIT_ERROR (+ form-scoped) and FORMS_SUBMIT_FAILED (+ form-scoped).

Submit filters

HookParametersDescription
notifal/forms/submit/allow`bool\array $allowed, array $schema, array $fields`Return true to continue. Return false or ['message' => '...', 'code' => '...', 'field' => '...'] to block
notifal/forms/submit/allow/%dsameForm CPT scoped
notifal/forms/submit/fieldsarray $fields, array $schemaMutate raw submitted fields before validation
notifal/forms/submit/fields/%dsameForm CPT scoped
notifal/forms/submission/dataarray $data, array $schema, array $fieldsSubmission row before DB insert
notifal/forms/submission/data/%dsameForm CPT scoped
notifal/forms/submit_responsearray $response, int $submission_idPublic AJAX success payload (no secrets)
notifal/forms/submit_response/%dsameForm CPT scoped
notifal/forms/submit/errorarray $error, array $schema, array $contextPublic AJAX error payload
notifal/forms/submit/error/%dsameForm CPT scoped
notifal/forms/unique_value_duplicate_messagestring $message, string $field_name, string $label, int $form_id, array $fieldDuplicate unique-value message

Submit and validation actions

HookParametersDescription
notifal/forms/submit/beforearray $schema, array $fieldsSide effects before validation
notifal/forms/submit/before/%dsameForm CPT scoped
notifal/forms/validate/beforearray $schema, array $fieldsBefore the validation field loop
notifal/forms/validate/before/%dsameForm CPT scoped
notifal/forms/validate/afterarray $result, array $schema, array $fieldsAfter validation completes
notifal/forms/validate/after/%dsameForm CPT scoped
notifal/forms/submit/afterint $submission_id, array $schema, array $fieldsAfter save, emails, and integrations
notifal/forms/submit/after/%dsameForm CPT scoped
notifal/forms/submit/failedarray $error, array $schema, array $contextSubmit failed after a verified request
notifal/forms/submit/failed/%dsameForm CPT scoped

Validation filters

HookParametersDescription
notifal/forms/validate/fieldstring[] $field_errors, array $field, mixed $value, array $input, array $schemaPer-field error slugs (required, type, regex, or custom). Non-empty list fails the field
notifal/forms/validate/field/%ssameField machine name scoped
notifal/forms/validate/field/%d/%ssameForm CPT + field machine name scoped
notifal/forms/validate/resultarray $result, array $schema, array $inputFull result: valid, fields, errors
notifal/forms/validate/result/%dsameForm CPT scoped

Schema, fields, emails, integrations

HookTypeDescription
notifal/forms/schema_parsedfilterParsed schema DTO after HTML parse ($schema, $html)
notifal/forms/field_definitionsfilterSanitized field definitions before CPT persistence
notifal/forms/fields/syncedactionAfter fields sync ($form_id, $fields, $source)
notifal/forms/email_system_variablesfilterSystem merge tags in email UI
notifal/forms/email_field_variablesfilterField merge tags in email UI
notifal/forms/email_contextfilterMerge tag values before rendering ($context, $form_id, $submission_id, $fields)
notifal/forms/email_template_renderedfilterSubject/body after merge tag replacement
notifal/forms/email_admin_default_bodyfilterDefault admin body ($template, $format)
notifal/forms/email_visitor_default_bodyfilterDefault visitor body ($template, $format)
notifal/forms/integration_adaptersfilterAdapter class map (webhook, mailchimp, custom)
notifal/forms/integrations/dispatchedactionAfter all adapters run ($form_id, $submission_id, $fields)
notifal/forms/servicesfilterForms module service class list

Admin lifecycle actions

HookParameters
notifal/forms/form/savedint $form_id, array $data
notifal/forms/form/duplicatedint $source_id, int $new_id
notifal/forms/form/trashedint $form_id, string $status
notifal/forms/form/deletedint $form_id, string $status
notifal/forms/trash/emptiedint $deleted_count
notifal/forms/submission/status_updatedint $submission_id, string $status
notifal/forms/submission/note_updatedint $submission_id, string $note
notifal/forms/submission/deletedint $submission_id
notifal/forms/submissions/bulk_marked_readint[] $ids, int $updated
notifal/forms/submissions/bulk_deletedint[] $ids, int $deleted

Admin list filters: notifal/forms/submission_list/columns, notifal/forms/submission_list/bulk_actions.

Frontend JavaScript events

Events bubble from the form root. Each fires twice: notifal:form:{name} and notifal:form:{name}:{formId}.

EventCancelableWhenDetail
initnoAfter form is bound{ formId }
validateyesBefore submit / next step{ valid, fields } (set detail.valid = false or preventDefault())
before-submityesBefore AJAX starts{ fields } (mutable)
submitnoWhen AJAX request starts{ fields }
successnoAfter successful AJAX{ message, redirect, behavior }
errornoClient or server failure{ message, code, field }
step-changenoMulti-step navigation{ from, to }

OnPage notifications listen for notifal:form:success for close-on-submit behavior.

Hooks series

  1. Hook Reference Hub
  2. Hooks: Forms (you are here)
  3. Hooks: Templates and Rendering
  4. Hooks: Pro Integration Bridge

Cookbook and user guides