Hooks & Filters
CAFE uses standard WordPress actions and filters. Every hook starts with cafe_.
Hooks that concern a value always receive the object ID and the object type
('post', 'term', 'user', 'comment', 'option') as their last two arguments.
Hooks from before the rename. Until 0.15.0 the plugin was called Advanced Field Core
(AFC) and these hooks started with afc_. Since 0.16.0 only the cafe_ names run:
rename afc_ hooks in your code (add_filter( 'afc_field_value', … ) becomes
add_filter( 'cafe_field_value', … )).
$field arguments are Cybo\AdvancedFieldEngine\Fields\Field objects. They work like arrays too:
$field['name'], $field['label'], $field['type'], and settings such as
$field['max_length'].
Order of events
Reading (cafe_get_field()):
- Stored value, or the field's Default Value
- Field type formatting
cafe_field_value_type_{type}→cafe_field_value→cafe_field_value_{name}
Saving (cafe_update_field()):
- Field type validation (+ Required when enforced)
cafe_validate_field- Field type sanitization
cafe_pre_save_valuecafe_before_save_field- Stored in the database (in a transaction), cache cleared
cafe_after_save_field
Filters
cafe_field_value
Filters every value returned by cafe_get_field(), cafe_get_fields() and cafe_get_option().
apply_filters( 'cafe_field_value', mixed $value, Field $field, int $object_id, string $object_type );
add_filter( 'cafe_field_value', function ( $value, $field, $object_id, $object_type ) {
if ( 'text' === $field['type'] && is_string( $value ) ) {
return trim( $value );
}
return $value;
}, 10, 4 );
cafe_field_value_{name}
Same as cafe_field_value, only for one field name. Runs last.
add_filter(
'cafe_field_value_price',
function ( $value ) {
return number_format( (float) $value, 2 );
}
);
cafe_field_value_type_{type}
Same as cafe_field_value, only for one field type (e.g. cafe_field_value_type_text). Runs first.
The
$default_valueyou pass tocafe_get_field()is returned as-is and is not filtered. A field's own Default Value setting is filtered.
cafe_validate_field
Accept or reject a value before it is saved. Receives the raw (unsanitized) value.
apply_filters( 'cafe_validate_field', true|WP_Error $valid, Field $field, mixed $value, int $object_id, string $object_type );
Return true to accept, or a WP_Error / an error message string to reject.
add_filter( 'cafe_validate_field', function ( $valid, $field, $value ) {
if ( true === $valid && 'price' === $field['name'] && (float) $value < 0 ) {
return new WP_Error( 'negative_price', 'Prices cannot be negative.' );
}
return $valid;
}, 10, 3 );
cafe_pre_save_value
Change a sanitized value right before it is stored.
apply_filters( 'cafe_pre_save_value', mixed $value, Field $field, int $object_id, string $object_type );
add_filter( 'cafe_pre_save_value', function ( $value, $field ) {
return 'sku' === $field['name'] ? strtoupper( $value ) : $value;
}, 10, 2 );
cafe_capability
The capability required to manage CAFE: field groups, fields, settings, import/export
and tools. Default manage_options. Invalid values fall back to the default.
Use it together with cafe()->capabilities()->can_manage( $user_id ) in your own entry points.
apply_filters( 'cafe_capability', string $capability );
// Let editors manage field groups.
add_filter( 'cafe_capability', fn () => 'edit_others_posts' );
cafe_video_providers
Known video providers, detected from the host of external Video URLs.
Subdomains match too (www.youtube.com → youtube).
apply_filters( 'cafe_video_providers', array $providers ); // [ 'youtube' => [ 'youtube.com', 'youtu.be', … ], 'vimeo' => [ 'vimeo.com' ] ]
add_filter( 'cafe_video_providers', function ( $providers ) {
$providers['wistia'] = [ 'wistia.com', 'wistia.net' ];
return $providers;
} );
cafe_auto_prime
Filters whether CAFE batches the objects of a query result (WP_Query posts and
get_terms() terms) for bulk loading. Return false to load each object's
fields on its own. See Performance.
add_filter( 'cafe_auto_prime', function ( bool $enabled, string $object_type, array $ids ) {
return 'term' === $object_type ? false : $enabled;
}, 10, 3 );
cafe_value_context
Filters the object a top-level field's value is read from or written to. The multilingual integration uses it to read "Copy" fields of a translation from the original, and Settings Page values from the current language's scope.
apply_filters( 'cafe_value_context', Cybo\AdvancedFieldEngine\Values\ObjectContext $context, Cybo\AdvancedFieldEngine\Fields\Field $field, string $operation ); // "read" or "write"
cafe_sub_value_source
Filters the object and row a sub-field's value is read from. The multilingual integration points a sub-field set to "Copy (locked)" at the same item of the original (same position, same section kind). A row below 0 reads as empty.
apply_filters( 'cafe_sub_value_source', ?array $source, Cybo\AdvancedFieldEngine\Fields\Field $field, Cybo\AdvancedFieldEngine\Values\ObjectContext $context, int $row_id ); // [ ObjectContext, int $row_id ] or null
cafe_field_view
Filters how a top-level field appears on an edit screen: state ("editable",
"readonly" or "hidden"), context (object the shown value comes from) and note
(text shown above a read-only field). Read-only and hidden fields are never saved
from the form.
apply_filters( 'cafe_field_view', array $view, Cybo\AdvancedFieldEngine\Fields\Field $field, Cybo\AdvancedFieldEngine\Values\ObjectContext $context );
cafe_prime_object_ids
Filters the objects loaded together in one bulk-loading batch. Integrations add objects the batch reads from (e.g. the originals of translations) so they load in the same queries.
apply_filters( 'cafe_prime_object_ids', array $object_ids, string $object_type );
cafe_display_object_id
Filters the ID of an object shown by a field: attachments (kind attachment), and
posts and terms of reference fields (kind = the post type or taxonomy). The WPML
integration swaps them for their translation in the current language. The stored ID
never changes.
apply_filters( 'cafe_display_object_id', int $id, string $kind ); // "attachment", "page", "category"…
cafe_google_maps_api_key
Filters the Google Maps API key used by map pickers of Google Map
fields on edit screens (default: the key in CAFE → Settings; '' = manual entry).
Keys must match [A-Za-z0-9_-]{20,100}. Front-end pages never call Google.
add_filter( 'cafe_google_maps_api_key', static fn () => MY_MAPS_BROWSER_KEY );
cafe_prime_references
Fires after the posts, terms and users referenced by the reference fields of a batch
(a loop, a REST collection, cafe_prime_fields()) were loaded: one WordPress call
per object type. Integrations prepare their own data for all of them at once; the
WPML integration loads the translations of those posts and terms here.
do_action( 'cafe_prime_references', array $by_type ); // [ 'post' => [ 12, 15 ], 'term' => [ 3 ], 'user' => [ 1 ] ]
cafe_language_provider
Filters the language provider CAFE uses: WPML's when detected, otherwise null.
Return an Cybo\AdvancedFieldEngine\Multilingual\LanguageProviderInterface to connect another
multilingual plugin. See WPML.
apply_filters( 'cafe_language_provider', ?Cybo\AdvancedFieldEngine\Multilingual\LanguageProviderInterface $provider );
cafe_field_label
Filters a field label shown on edit screens (WPML String Translation uses it). The field name never changes.
apply_filters( 'cafe_field_label', string $label, Cybo\AdvancedFieldEngine\Fields\Field $field );
cafe_field_instructions
Filters the instructions shown with a field on edit screens.
apply_filters( 'cafe_field_instructions', string $instructions, Cybo\AdvancedFieldEngine\Fields\Field $field );
cafe_field_group_title
Filters a field group title shown on edit screens and Settings Pages.
apply_filters( 'cafe_field_group_title', string $title, Cybo\AdvancedFieldEngine\Fields\FieldGroup $group );
cafe_settings
Filters CAFE's effective settings (for example to force a value per environment).
apply_filters( 'cafe_settings', array $settings );
cafe_edit_screen_css
Adds your own CSS after CAFE's styles on post, page and term edit screens. HTML tags are stripped, so only CSS is kept. Guide: Styling.
add_filter( 'cafe_edit_screen_css', function ( string $css ) {
return $css . '.cafe-scope { --cafe-input-radius: 4px; }';
} );
cafe_feedback_endpoint
The address CAFE sends rating and deactivation feedback to (only when someone presses
Send in the feedback form). Default: https://www.cybowebtechnologies.com/api/v1/plugin-feedback.
Only https:// addresses are used; anything else makes sending fail with a friendly message.
Useful for a test server.
add_filter( 'cafe_feedback_endpoint', function ( string $endpoint ) {
return 'https://staging.example.com/api/v1/plugin-feedback';
} );
cafe_location_rules
Adds or removes location rules (the "Show this field group if…" conditions).
Each rule implements Cybo\AdvancedFieldEngine\Location\LocationRuleInterface and is keyed by its
name. Rules with a wrong key, an invalid name or the wrong class are ignored.
Guide: Location rules.
add_filter( 'cafe_location_rules', function ( array $rules ) {
$rules['user_role'] = new My_User_Role_Rule();
return $rules;
} );
cafe_migration_database_tables
Filter. The database tables CAFE → Migration may read with the Table of an old system source. By default: this site's tables (with its table prefix) that are not WordPress or CAFE tables. A filter can remove tables or add other existing tables, but never WordPress or CAFE tables (password hashes and settings stay out of reach).
// Only offer the tables of the old system.
add_filter( 'cafe_migration_database_tables', function ( array $tables ) {
return array_values( array_filter( $tables, fn ( $table ) => str_contains( $table, '_old_' ) ) );
} );
See Connector: tables of an old system.
Actions
cafe_settings_page_saved
Fires after a Settings Page was saved (nonce and capability already checked).
add_action( 'cafe_settings_page_saved', function ( string $slug, array $errors ) {
if ( 'contact-settings' === $slug && [] === $errors ) {
delete_transient( 'my_theme_footer_html' );
}
}, 10, 2 );
$errors holds messages of fields that were not saved, keyed by field key.
cafe_field_groups_imported
Fires after field group definitions were imported (CAFE → Tools or
cafe_import_field_groups()). Does not fire for
dry runs, failed imports or files with nothing to change.
add_action( 'cafe_field_groups_imported', function ( Cybo\AdvancedFieldEngine\Tools\ImportResult $result ) {
$summary = $result->to_array()['summary'];
error_log( sprintf( 'CAFE import: %d fields created, %d updated', $summary['created']['fields'], $summary['updated']['fields'] ) );
} );
cafe_settings_page_header
Fires under the heading of a Settings Page, e.g. to say which language is edited.
do_action( 'cafe_settings_page_header', Cybo\AdvancedFieldEngine\SettingsPages\SettingsPage $page );
cafe_before_save_field
do_action( 'cafe_before_save_field', Field $field, mixed $value, int $object_id, string $object_type );
Fires after validation and sanitization, before the value is stored. $value is the sanitized value.
cafe_after_save_field
do_action( 'cafe_after_save_field', Field $field, mixed $value, int $object_id, string $object_type );
Fires after the value is stored and the cache is cleared: cafe_get_field() already returns the new value.
add_action( 'cafe_after_save_field', function ( $field, $value, $object_id, $object_type ) {
if ( 'post' === $object_type ) {
clean_post_cache( $object_id );
}
}, 10, 4 );
cafe_before_delete_field
do_action( 'cafe_before_delete_field', Field $field, int $object_id, string $object_type );
cafe_after_delete_field
do_action( 'cafe_after_delete_field', Field $field, int $object_id, string $object_type );
cafe_corrupted_value
Fires when a stored value cannot be decoded (malformed JSON in the database). The read returns the field as empty; nothing is overwritten. Use it for logging.
do_action( 'cafe_corrupted_value', Field $field, int $object_id, string $object_type, string $error );
cafe_object_purged
Fires after CAFE removed all data of a post, term, user or comment that WordPress deleted.
do_action( 'cafe_object_purged', int $object_id, string $object_type );
cafe_field_group_saved
do_action( 'cafe_field_group_saved', FieldGroup $group, bool $created );
cafe_field_group_deleted
Fires after a field group, its fields and all their values were deleted.
do_action( 'cafe_field_group_deleted', FieldGroup $group );
cafe_schema_changed
Fires after any field group or field is created, updated or deleted. Caches are already cleared.
do_action( 'cafe_schema_changed' );
cafe_loaded
Fires once when CAFE has booted. Use it to call cafe() safely.
do_action( 'cafe_loaded', Cybo\AdvancedFieldEngine\Core\Plugin $plugin );
cafe_init
Fires on WordPress init (priority 1) once CAFE is ready.
do_action( 'cafe_init', Cybo\AdvancedFieldEngine\Core\Plugin $plugin );
cafe_register_field_types
Register custom field types (or call cafe_register_field_type()). Guide:
Custom field types.
add_action( 'cafe_register_field_types', function ( $registry ) {
$registry->register( My_Icon_Field::class );
} );
cafe_register_object_types
Register custom object types (implement Cybo\AdvancedFieldEngine\Values\ObjectTypeInterface).
do_action( 'cafe_register_object_types', Cybo\AdvancedFieldEngine\Values\ObjectTypeRegistry $registry );
cafe_register_migration_sources
Fires once, the first time CAFE → Migration needs its sources. Register your own
source (an old CRM, an API export) with cafe_register_migration_source(). Guide:
Custom sources.
add_action( 'cafe_register_migration_sources', function () {
cafe_register_migration_source( new My_Crm_Source() );
} );
cafe_register_migration_transformers
Fires once, the first time migration transformations are needed. Add your own with
cafe_register_migration_transformer(). Guide:
Transformations.
add_action( 'cafe_register_migration_transformers', function () {
cafe_register_migration_transformer( 'status_label', 'Old status → sale status', function ( $value ) {
return '1' === (string) $value ? 'available' : 'sold';
} );
} );
cafe_migrated
Fires after database migrations ran on the current site.
do_action( 'cafe_migrated', int $from_version, int $to_version );
cafe_storage_renamed
Fires once per site after the data stored under the old plugin name (Advanced Field
Core: {prefix}cafe_* tables, cafe_* options, post types and meta keys) was moved to the
CAFE names. See Database.
do_action( 'cafe_storage_renamed' );
cafe_hide_admin_notices
Filter. Whether CAFE admin pages hide the admin notices of other plugins and themes
(for example "Welcome to …" or license banners). Default false: every notice
shows. CAFE's own notices always show.
// Hide other plugins' notices on CAFE pages.
add_filter( 'cafe_hide_admin_notices', '__return_true' );
Dynamic forms
Hooks of CAFE Forms (cafe_dynamic_form()), in the order they
run. $form is an Cybo\AdvancedFieldEngine\Forms\FormDefinition (read-only properties such as
$form->id, $form->object_type, $form->object_id, $form->mode). More
examples: Forms hooks.
cafe_dynamic_form_args
Filter. The arguments of a form before they are checked: on every render and submit. Use it to add defaults to all forms.
add_filter( 'cafe_dynamic_form_args', function ( array $args ) {
return $args + [ 'label_placement' => 'left' ];
} );
cafe_dynamic_form_can_submit
Filter. Whether the current user may use a form (see and submit it). Receives
true or a WP_Error; return either.
add_filter( 'cafe_dynamic_form_can_submit', function ( $allowed, $form, int $user_id ) {
if ( 'new-event' === $form->id && ! get_option( 'events_open' ) ) {
return new WP_Error( 'closed', 'Event submissions are closed.', [ 'status' => 403 ] );
}
return $allowed;
}, 10, 3 );
cafe_dynamic_form_fields
Filter. The fields a form shows and accepts, keyed by field key
(Cybo\AdvancedFieldEngine\Fields\Field). Removing a field here removes it from the form and
from what the server accepts.
apply_filters( 'cafe_dynamic_form_fields', array $fields, FormDefinition $form );
cafe_dynamic_form_before_fields
Filter. HTML inside the form, before the fields (escape it yourself).
apply_filters( 'cafe_dynamic_form_before_fields', string $html, FormDefinition $form );
cafe_dynamic_form_after_fields
Filter. HTML inside the form, after the fields and before the button.
apply_filters( 'cafe_dynamic_form_after_fields', string $html, FormDefinition $form );
cafe_dynamic_form_submit_button
Filter. The submit button HTML.
add_filter( 'cafe_dynamic_form_submit_button', function ( string $button, $form ) {
return '<button type="submit" class="cafe-form__submit my-button">Send</button>';
}, 10, 2 );
cafe_dynamic_form_html
Filter. The whole form HTML.
apply_filters( 'cafe_dynamic_form_html', string $html, FormDefinition $form, ?FormResult $result );
cafe_dynamic_form_field_description
Filter. How one field is described to headless front ends
(GET /cafe/v1/forms/{id}). Only display settings are included by default.
apply_filters( 'cafe_dynamic_form_field_description', array $item, Cybo\AdvancedFieldEngine\Fields\Field $field );
cafe_dynamic_form_before_validate
Action. Fires before a submission is validated. $values are the submitted
values as plain text (sanitize_textarea_field()), not the values that are
saved.
do_action( 'cafe_dynamic_form_before_validate', FormDefinition $form, array $values );
cafe_dynamic_form_validate
Filter. Validates a form as a whole: relations between fields. Receives the
messages so far (keyed by field key) and the valid values keyed by field
name. Add messages keyed by field name or key, or '' for a message about
the whole form. Any message stops the save.
add_filter( 'cafe_dynamic_form_validate', function ( array $errors, array $values, $form ) {
if ( 'new-event' === $form->id && ( $values['end_date'] ?? '' ) < ( $values['start_date'] ?? '' ) ) {
$errors['end_date'] = 'The end date must be after the start date.';
}
return $errors;
}, 10, 3 );
cafe_dynamic_form_failed
Action. Fires when a submission was not saved (invalid, or refused while saving: everything was rolled back).
do_action( 'cafe_dynamic_form_failed', FormResult $result, FormDefinition $form );
cafe_dynamic_form_before_save
Action. Fires after validation, right before saving.
do_action( 'cafe_dynamic_form_before_save', FormDefinition $form, array $values );
cafe_dynamic_form_post_data
Filter. The WordPress post data a form writes (arguments of wp_insert_post()
or wp_update_post()). $values are the submission's valid CAFE values, keyed
by field name.
add_filter( 'cafe_dynamic_form_post_data', function ( array $data, $form, array $values ) {
if ( 'new-event' === $form->id && empty( $data['post_title'] ) ) {
$data['post_title'] = trim( ( $values['venue'] ?? '' ) . ' ' . ( $values['event_date'] ?? '' ) );
}
return $data;
}, 10, 3 );
cafe_dynamic_form_saved
Action. Fires after a form saved everything. $values are keyed by field key.
add_action( 'cafe_dynamic_form_saved', function ( int $object_id, $form, array $values ) {
if ( 'new-event' === $form->id ) {
wp_mail( get_option( 'admin_email' ), 'New event', get_edit_post_link( $object_id, 'raw' ) );
}
}, 10, 3 );
cafe_dynamic_form_redirect
Filter. Where a form goes after saving ('' = stay on the page). The result
is always checked with wp_validate_redirect().
apply_filters( 'cafe_dynamic_form_redirect', string $url, int $object_id, FormDefinition $form );