Reading order
Each hook is listed in the order it fires during a full form lifecycle. Within each hook:
- Type — filter (transforms value) vs action (side-effect only)
- Signature — callback signature,
(int, int) priority/argcount where the framework registers with non-default
- Fires — what triggers it
- Return — expected shape if a filter; ignored if an action
- Use it when — what a sibling plugin would do here
Phase 1 — Plugin init / registration
These run when plugins load. They’re not in a single sequence — order depends on which plugin registers first.
Two parallel entry points, one per form source:
- Code-based (programmatic) forms →
mam_form_manager_get_forms_from_plugins. Forms defined entirely in PHP. No Gravity Forms installation required. This is mam-main’s first-class native path
- Gravity-Forms-backed forms →
mam_gf_get_form_settings. Forms built in the GF admin UI, bridged in by mam-gravity-forms-manager. Use when admins need to edit forms without code or when integrating with existing GF records
Pick whichever fits the use case; downstream phases (cache, fetch, prefill, submission) are identical for both.
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_form_manager_get_forms_from_plugins', array $forms ) |
| Fires |
When the framework collects code-based (non-Gravity-Forms) forms |
| Return |
Array of form definitions in the standard form-info shape |
| Use it when |
Your plugin defines a programmatic form fully in PHP — no Gravity Forms record exists or is needed |
add_filter( 'mam_form_manager_get_forms_from_plugins', function ( $forms ) {
$forms['my_internal_form'] = [
'slug' => 'my_internal_form',
'fields' => [ /* ... */ ],
];
return $forms;
} );
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_gf_get_form_settings', array $settings ) |
| Fires |
When the framework needs the slug-mapped form catalog (in the admin UI and during cache pre-warm) |
| Return |
Settings array with one entry per form, in the {category, title, variable, type, id, environment, data} envelope. See end-to-end cookbook |
| Use it when |
The form is backed by a Gravity Forms record (admin-editable or pre-existing). Requires mam-gravity-forms-manager + Gravity Forms. For PHP-only forms, use mam_form_manager_get_forms_from_plugins instead |
|
|
| Type |
Filter (registration only — fires later in Phase 4) |
| Signature |
add_filter( 'mam_for_gravity_forms_form_result_form_' . get_option( '<form_settings_id>' ), $callback ) |
| Fires (registration) |
At plugin init |
| Fires (callback) |
When the app submits this specific form |
| Gotcha |
get_option() must already be set when add_filter() runs. If your option is set later, register the filter on a later hook or use mam_gf_get_form_from_cache lazily |
Phase 2 — Cache pre-warm
The framework hooks setup_forms_for_caching() onto mam_initial_phone_data. That triggers when the app first asks for phone data.
mam_initial_phone_data
|
|
| Type |
Action |
| Signature |
do_action( 'mam_initial_phone_data' ) |
| Fires |
Early in the phone-data build, before content classes run |
| Use it when |
You need to do prep work before any content class executes — e.g., warm your own caches alongside the form cache |
Inside the framework’s handler (mam_for_gravity_forms_forms_manager::setup_forms_for_caching), per declared form:
form_id = get_option( form_def.id )
if form_id > 0:
build slug → field_id map from <form_def.id>_<slug> options
form = get_data_for_app(form_id) ← fires Phase 3 hooks
for each cached field:
field = apply_filters( 'mam_form_manager_precached_field', field, form_id )
if slug map has this field's id:
field.populate_with_key = slug
$local_app_form_array[] = form
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_form_manager_precached_field', array $field, int $form_id ) |
| Fires |
During cache pre-warm only, per field, after get_data_for_app has returned |
| Return |
The (possibly modified) field array |
| Use it when |
You need to tweak fields only during the pre-warm pass — rare. Most integrations should use mam_populate_gravity_form instead, which fires for both pre-warm and cache-miss paths |
| Gotcha |
Does not fire when a form is loaded via the cache-miss path of mam_gf_get_form_from_cache. If you depend on this hook, your changes silently disappear when the cache wasn’t pre-warmed |
local_app_gravity_forms::get_data_for_app( $form_id ) is the main shaper. It runs during pre-warm AND during cache-miss fetches. Hooks fire in the order below.
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_gf_get_form', array $form, int $form_id ) |
| Fires |
At the very start of get_data_for_app — to fetch the raw GF form |
| Return |
The form array (['id' => …, 'fields' => […]]). Returning empty array short-circuits |
| Default callback |
mam_for_gravity_forms_forms_manager::get_gravity_form (reads from GFAPI) |
| Use it when |
You’re providing a custom form source — e.g., synthetic forms not backed by Gravity Forms records |
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_gf_get_custom_form', array $form, int $form_id ) |
| Fires |
Inside get_gravity_form if the standard GF lookup didn’t find anything |
| Use it when |
You want a fallback source for forms that aren’t real GF records |
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_populate_gravity_form', array $form, int $form_id ) |
| Fires |
Inside get_data_for_app, right after the raw form is fetched, before field processing |
| Return |
The form array |
| Use it when |
The primary spot for runtime injection. Inject dropdown choices, set defaults, stamp populate_with_key, mutate field properties. Works for both pre-warm and cache-miss paths |
| Notable |
The framework’s web-only gform_pre_render does NOT fire for app-bound forms. This is its replacement |
mam_form_manager_content_class_<type>
|
|
| Type |
Filter |
| Signature |
apply_filters( "mam_form_manager_content_class_{$form_type}", array $field, $rawField, $form, $isRequired ) |
| Fires |
Per field, after standard shape is built |
| Return |
The field array |
| Use it when |
You’re owning a custom field type and need to control its on-wire shape. mam-gravity-forms-manager uses mam_form_manager_content_class_form for nested-form fields |
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_populate_gravity_form_final', array $form ) |
| Fires |
Last per-form transformation in get_data_for_app, after all per-field work |
| Use it when |
Final cleanup or whole-form tweaks |
mam_gf_populate_form_data_with_post_meta
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_gf_populate_form_data_with_post_meta', array $form, array $post_meta ) |
| Fires |
When the form is being rendered against a specific post’s meta |
| Use it when |
Form is editing a CPT and field defaults should come from get_post_meta |
|
|
| Type |
Filter |
| Signature |
apply_filters( "mam_update_form_before_sending_{$form_id}", array $form ) |
| Fires |
Just before the form payload exits get_data_for_app |
| Use it when |
Form-id-specific last-mile transform |
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_update_form_before_sending', array $form ) |
| Fires |
Final transform before return |
| Use it when |
Universal last-mile transform (not form-id-specific) |
Content classes call this when they’re publishing a row that references a form.
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_gf_get_form_from_cache', $current_value, int $form_id ) |
| Fires |
When a content class asks for a form’s cache index |
| Return |
1-based index into $local_app_form_array, or false if the form couldn’t be loaded |
| Gotcha |
If the form isn’t already cached, this triggers a get_data_for_app call — and the result lands in $local_app_form_array without populate_with_key stamping (that only happens in the pre-warm path). Use mam_populate_gravity_form to stamp populate keys yourself for cache-miss resilience |
$form_index = apply_filters( 'mam_gf_get_form_from_cache', 0, $form_id );
if ( $form_index ) {
$row['form_id'] = (string) $form_index;
}
Phase 5 — Submission handling
When the app POSTs a submission, mam_gf_submit_app_form processes it. The form’s handler runs through a form-id-keyed filter.
|
|
| Type |
Filter |
| Signature |
add_filter( "mam_for_gravity_forms_form_result_form_{$gf_form_id}", $callback ) — handler receives ( $result = null, $entry = null, $form = null ) |
| Fires |
Once per submission for that specific form ID |
| Return |
The standard result envelope (see end-to-end cookbook §5b) |
| Use it when |
Always — this is the submission entry point |
|
|
| Type |
Filter |
| Signature |
apply_filters( 'mam_gf_get_form_data', array $fields, string $slug_root_with_underscore ) |
| Fires |
Called from inside your submission handler |
| Return |
[ slug => [ 'value' => x, ...meta ], ... ] |
| Use it when |
Always — easiest way to pull submission values keyed by your slugs instead of GF field IDs |
$values = apply_filters( 'mam_gf_get_form_data', $fields, 'mam_aquaman_day_settings_' );
$booking_date = $values['booking_date']['value'] ?? '';
Note the trailing underscore — the framework expects it. Forgetting it makes every lookup miss.
|
|
| Type |
Action |
| Signature |
do_action( 'mam_form_manager_send_notifications', $entry, $form ) |
| Fires |
After your handler returns, only if send_notifications => true in the result envelope |
| Use it when |
You want to plug into the standard notification dispatcher |
|
|
| Type |
Action |
| Signature |
do_action( 'mam_gravity_forms_after_form_processed', $entry, $form ) |
| Fires |
After the full submission pipeline completes, regardless of handler outcome |
| Use it when |
Side-effect work (logging, cleanup) that always runs after every submission |
|
|
| Type |
Filter |
| Signature |
apply_filters( "mam_for_gravity_forms_form_submitted_{$gf_form_id}", $entry ) |
| Fires |
After the result handler, before the notification dispatch |
| Use it when |
You need to mutate the entry between handler return and notifications firing |
The cache. A simple indexed array of cached form arrays. Three rules:
- Push, don’t insert — always append; don’t reorder. Existing rows reference forms by 1-based index
- 1-based indexing on the wire —
$local_app_form_array[0] is reachable via form_id: "1". get_form_from_cache returns (string) ( $index + 1 )
- One copy per form_id —
get_form_from_cache deduplicates by $form['formid']. Don’t push the same form_id twice
Forms in this array carry their fully-shaped payload, including choices, populate_with_key, and any custom keys you added via mam_populate_gravity_form. After phone-data assembly, this global is serialized into the mobile JSON’s form_data array.
Option-key conventions
| Option key |
Holds |
Set by |
<form_settings_id> |
GF form ID for this declaration (e.g., 3) |
App admin UI’s form-settings page |
<form_settings_id>_<field_slug> |
GF field ID for that slug (e.g., 1) |
Same page, per declared field |
mam-form-cache-* |
Serialized form-definition cache |
mam_form_manager_cache_manager, invalidated on form save / option change |
The first two are the load-bearing convention. If populate_with_key isn’t appearing, walk through these options and verify each one has a value > 0.
Common firing-order gotchas
Submission handler isn’t firing
add_filter( 'mam_for_gravity_forms_form_result_form_' . get_option( 'foo' ), ... ) — when this line runs, get_option('foo') must already return the form ID. If your plugin loads before the option is set, the filter is registered with an empty form ID and never fires. Two fixes:
- Set the option (via the admin UI) before deploying the plugin
- Register late: hook your form-manager init onto
init or plugins_loaded priority 20+, after the option is reliably set
Three checks, in order:
- Is
<form_settings_id>_<slug> set to a positive GF field ID? If no, the framework can’t resolve the slug
- Did the pre-warm run?
mam_initial_phone_data must fire before anyone calls mam_gf_get_form_from_cache. If your content class is invoked from an earlier hook, the form gets loaded via the cache-miss path which skips populate-key stamping
- Are you stamping
populate_with_key yourself in mam_populate_gravity_form as belt-and-suspenders? Recommended for any new form
Choices keep getting overwritten
gform_pre_render and related GF web hooks do NOT fire for app-bound forms. If you set choices in those hooks they only apply on the web side. Move the logic to mam_populate_gravity_form.
Field arrives as object vs array
Some code paths hand you a GF_Field object; others hand you an array. Defensive code:
$fid = (int) ( is_object( $field ) ? ( $field->id ?? 0 ) : ( $field['id'] ?? 0 ) );
That hook only fires on the pre-warm pass. Cache misses skip it. Move to mam_populate_gravity_form.
See also
| Field |
Value |
| Article type |
Reference |
| Plugin slug |
mam-main |
| Applies to plugin version |
2.1.11+ |
| Category |
Forms Manager |
| Audience |
PHP developer |
| Last verified |
2026-05-20 |
| Related |
End-to-end cookbook, Forms manager overview |