Goal
When a user submits a Gravity Form on the WordPress site (not in the app), create or update a custom-post-type post, save the submitted values as post meta keyed by your own slugs, and call a feature handler with the resulting post ID. After this recipe, web submissions will produce the same content shape as app submissions, so a single downstream feature plugin can consume both flows.
This is the path that mam-special-offers and mam-geodirectory use to back their “edit on web, edit in app” flows.
Prerequisites
- The form is wired into the app per Recipe: Add a Gravity Form to your app
- You can write and deploy a small custom plugin (or a
functions.phpsnippet) — this is a developer recipe, not an admin one - A target custom post type already registered (or you’re comfortable registering one)
- The form’s Form ID (e.g.
42) - A list of the GF field IDs you want to expose, paired with the post-meta slugs you want them stored under
How the flow works
The plugin listens to gform_after_submission. On every web submission it does the following:
- Gate. It checks
has_filter( 'mam_for_gravity_forms_web_form_result_form_{form_id}' ). If no filter is registered for this form ID, it exits silently. No consumer = no work. - Look up the field mapping. It calls
apply_filters( 'mam_gf_get_form_settings', [] )and finds the entry whoseidoption resolves to this submission’sform_id. That entry tells the plugin: which CPT to use, which fields make uppost_title, which make uppost_content, and the slug → GF-field-index mapping. - Map values. It walks the mapping, reading values out of
$entryby GF field index and writing them to$values[$index]['value']keyed by your slug. - Create or update the post. It builds
$post_titleand$post_contentfrom the configured fields, setspost_typeto the configured CPT, and either inserts a new post or updates an existing one (ifpost-idis among the slugs and is non-empty, or if$_REQUEST['post_id']is set). - Save meta. It calls
update_post_meta( $post_id, $value['slug'], $value['value'] )for every mapped value. - Dispatch. It calls
apply_filters( 'mam_for_gravity_forms_web_form_result_form_{form_id}', $form_id, $post_id )so your consumer can finish the job (set status, geocode, send a custom email, etc.).
The pivotal data structure is the mam_gf_get_form_settings entry. Get its shape right and the rest works.
Steps
1. Register your form mapping
Add a callback to mam_gf_get_form_settings. The callback returns an array of “form settings” entries, each describing one form your code knows about.
add_filter( 'mam_gf_get_form_settings', 'my_app_register_offers_form' );
function my_app_register_offers_form( $settings ) {
$root = 'my_app_offers_';
$slug = 'offers_form';
$fields = [
[ 'id' => 1, 'slug' => 'offer-title', 'title' => 'Offer Title', 'type' => 'text' ],
[ 'id' => 2, 'slug' => 'description', 'title' => 'Description', 'type' => 'textarea' ],
[ 'id' => 3, 'slug' => 'start-date', 'title' => 'Start Date', 'type' => 'date' ],
[ 'id' => 4, 'slug' => 'end-date', 'title' => 'End Date', 'type' => 'date' ],
[ 'id' => 5, 'slug' => 'post-id', 'title' => 'Offer ID', 'type' => 'hidden' ],
];
// Each field's option key holds the GF field index for THIS site's form.
// The admin (or your install routine) sets these once.
foreach ( $fields as $field ) {
update_option( $root . $slug . '_' . $field['slug'], $field['id'] );
}
$settings[] = [
'category' => 'general',
'title' => __( 'My App: Offers', 'my-app' ),
'variable' => 'none',
'type' => 'gravity_form',
'id' => $root . $slug, // option key holding the GF form ID
'environment' => 'backend',
'data' => [
'cpt' => 'offer', // ← the CPT to write to
'post_title' => [ 'offer-title' ], // ← which slugs build post_title
'post_content' => [ 'description' ], // ← which slugs build post_content
'fields' => $fields,
],
];
return $settings;
}
Two things to notice:
- The
idis an option key, not a form ID. The plugin readsget_option( $entry['id'] )and compares the result to the GFform_idof the incoming submission. Whoever installs your plugin must set that option to the correct GF form ID — typically through your own admin settings page, orupdate_option( 'my_app_offers_offers_form', 42 )in an install routine. - Each field’s GF index is also stored in an option (
{root}{slug}_{field-slug}). The same install routine sets these. The values youupdate_option()to should be the actual GF field IDs in the form on this site.
2. Set the form ID and field indexes for this install
If you’re shipping a feature plugin, expose an admin settings page that lets the site admin pick the form and map fields. If you’re writing this for one site, just set the options directly:
update_option( 'my_app_offers_offers_form', 42 ); // GF form ID
update_option( 'my_app_offers_offers_form_offer-title', 1 ); // GF field IDs
update_option( 'my_app_offers_offers_form_description', 2 );
update_option( 'my_app_offers_offers_form_start-date', 3 );
update_option( 'my_app_offers_offers_form_end-date', 4 );
update_option( 'my_app_offers_offers_form_post-id', 5 );
These can be inspected and edited from Tools → Options if you’ve enabled the options page, or from WP-CLI with wp option get.
3. Subscribe to the result filter
Add a callback to mam_for_gravity_forms_web_form_result_form_{form_id}. The plugin calls this after the post is created or updated and meta is saved. Your callback receives the form ID and the post ID; it returns the form ID (the return value is ignored by the plugin, but you must return something because apply_filters() is the dispatcher — see TD-GF-005).
add_filter( 'mam_for_gravity_forms_web_form_result_form_42', 'my_app_offer_submitted', 10, 2 );
function my_app_offer_submitted( $form_id, $post_id ) {
// Post is created/updated and meta is saved by mam-gravity-forms-manager.
// Do anything that should happen *after* — set post status, geocode an address,
// send a custom email, mark the user as having submitted, etc.
if ( ! get_post( $post_id ) ) {
return $form_id;
}
// Example: pull a value back out of the meta we just stored.
$title = get_post_meta( $post_id, 'offer-title', true );
// Example: trigger your own pipeline.
do_action( 'my_app_offer_submitted', $post_id, $title );
return $form_id;
}
4. (Optional) Allow the form to update an existing post
If your form should be able to edit a post that already exists — say, an admin re-edit flow — include the post ID in the submission. Two ways:
- Add a hidden field to the GF form, mapped to the
post-idslug. The plugin sees$values['post-id']['value']and uses it as the update target. - Pass
?post_id=123on the URL when serving the form. The plugin reads$_REQUEST['post_id']and runsabsint()on it before use.
Either method causes wp_update_post() instead of wp_insert_post().
5. Verify
- Submit the GF form on the WP site.
- In WP admin, open the CPT list (
Offers, in the example above) and confirm a new post appears with the title built from yourpost_titleslugs and the content from yourpost_contentslugs. - Open the post and scroll to Custom Fields — confirm each mapped slug is stored as a meta key with the submitted value.
- Confirm your
mam_for_gravity_forms_web_form_result_form_{form_id}callback ran (add a temporarymamdebugline, or check whatever your callback’s side effects are). - Re-submit with
?post_id={existing-id}on the URL and confirm the existing post updates rather than a new one being created.
Common gotchas
- The gate is
has_filter(). If no callback is attached at any priority, the entireafter_submissionbody short-circuits. If your post isn’t being created, first check that youradd_filter()line is actually running (a typo in the form ID is the usual culprit). - The
idinmam_gf_get_form_settingsis an option key, not a form ID. This is the most common confusion when reading existing consumer code. Search forupdate_option( $root . $slug, ... )calls to see how the option gets populated. mam_gf_get_form_settingsruns twice in the same submission. The plugin applies the filter once to read the field mapping and a second time to read the CPT/title/content composition. Make sure your callback is idempotent and cheap — see Hook: mam_gf_get_form_settings.update_post_metais called for every mapped slug, includingpost-id. That means the post ID is also stored in its own post meta. Harmless but worth knowing if you grep for orphan meta.apply_filters(), notdo_action(). The dispatch hook usesapply_filters()even though it’s used for side effects. Alwaysreturn $form_idfrom your callback so a future migration todo_action()(planned in TD-GF-005) won’t break.- App submissions don’t trigger this flow. App submissions go through
mam_form_manager_send_notifications, notgform_after_submission. If you need to react to both, register a callback on each.
Variations
Compose post_title from multiple fields
post_title accepts an array of slugs. The plugin concatenates the values with spaces and trims the result. Example: 'post_title' => [ 'first-name', 'last-name' ].
Use the same callback for multiple forms
Register the same callback against each form’s ID. The plugin passes $form_id as the first argument so you can branch:
add_filter( 'mam_for_gravity_forms_web_form_result_form_42', 'my_app_dispatch', 10, 2 );
add_filter( 'mam_for_gravity_forms_web_form_result_form_43', 'my_app_dispatch', 10, 2 );
function my_app_dispatch( $form_id, $post_id ) {
switch ( (int) $form_id ) {
case 42: // Offers
// …
break;
case 43: // Listings
// …
break;
}
return $form_id;
}
Verification
This article was last verified against:
- Plugin:
mam-gravity-forms-managerv2.3 - Source:
mam-gravity-forms-manager/includes/submit-web-form.php - Reference consumer:
mam-special-offers/includes/mam_special_offers_form_manager.php(mam_gf_get_form_settings())
Re-verify whenever the gform_after_submission handler in mam_gf_submit_web_form::after_submission() changes the order of post creation, meta save, and result dispatch; the mam_gf_get_form_settings entry shape (id, data.cpt, data.post_title, data.post_content, data.fields) changes; or the $_REQUEST['post_id'] precedence over the post-id mapped field changes.
Related articles
- Plugin overview: mam-gravity-forms-manager
- Recipe: Activate the plugin
- Recipe: Add a Gravity Form to your app
- Hook: mam_gf_get_form_settings
- Hook: mam_for_gravity_forms_web_form_resultform{form_id}
Metadata
| Field | Value |
|---|---|
| Article type | Recipe (Developer) |
| Plugin slug | mam-gravity-forms-manager |
| Applies to plugin version | 2.3+ |
| Category | Extending MAM Suite |
| Audience | PHP developer |
| Estimated time | 30 minutes |
| Prerequisites article | Recipe: Add a Gravity Form to your app |
| Last verified | 2026-05-01 |
