Skip to main content

Data Filters

This page documents filters for customizing data providers, meta values, and expression extensions.

FilterDescription
unblock/data/providersRegister custom data providers
unblock/data/provider_globalTurn a global provider on or off
unblock/data/preview_fixturesPlaceholder values in pattern library previews
unblock/data/preview_overridesReplace a field value in the editor preview
unblock/data/filtersRegister custom expression filters
unblock/data/functionsRegister custom expression functions
unblock/data/post_metaFilter post meta values
unblock/data/user_metaFilter user meta values
unblock/data/term_metaFilter term meta values
unblock/data/option_metaPre-filter for option retrieval from Site provider
unblock/data/allowed_optionsWhitelist accessible WordPress options
unblock/data/allowed_meta_keysWhitelist protected meta keys

data/providers​

Register custom data providers to expose new data sources in expressions.

ParameterTypeDescription
$providersarrayRegistered providers configuration

Each provider is an array of fields. Each field requires:

  • label — Display label for the field
  • callback — Callable that returns the field value. It receives ( $context, $args ): the execution context, and the arguments passed in the expression as an indexed array.

Optional field properties:

  • escape — Escape function (false, 'esc_url', etc.). Default: 'esc_html'.
  • returns — Provider key of the value the callback returns ('post', 'user', 'term', 'image', a post type or another custom provider), so its fields can be chained.
  • capability — Capability required to read the field. Users who have neither this capability nor unfiltered_html can't save an expression that reads it: it is removed on save.

Example: Register Custom Provider

add_filter( 'unblock/data/providers', function( $providers ) {

$providers['shop'] = [
'name' => [
'label' => __( 'Shop Name', 'flavor' ),
'callback' => fn( $context, $args ) => get_option( 'blogname' ),
],
'url' => [
'label' => __( 'Shop URL', 'flavor' ),
'escape' => 'esc_url',
'callback' => fn( $context, $args ) => home_url( '/shop' ),
],
'phone' => [
'label' => __( 'Phone', 'flavor' ),
'callback' => fn( $context, $args ) => get_option( 'shop_phone', '+1 555-0100' ),
],
];

return $providers;

} );

Use in expressions: {{ shop.name }}, {{ shop.url }}, {{ shop.phone }}

A custom provider is a global root: it works in any expression, and the data picker offers it everywhere. Register it from a plugin or your theme's functions.php: providers are read once, the first time an expression renders.

A provider named after a post type is not a root. It adds fields to the posts of that type: {{ post.price }} on a product.

Common mistake

Name the provider with letters, digits and underscores only, and avoid the words the expression language uses (and, or, not, in, is, true, false, null, loop) and the built-in names (post, user, term, site, author…). A loop alias or a variable with the same name as your provider takes precedence inside its scope.

data/provider_global​

Decide whether a provider is a global root. Custom providers are global by default, except those named after a post type. Return false to turn one off.

apply_filters( 'unblock/data/provider_global', bool $global, string $provider );
ParameterTypeDescription
$globalboolWhether the provider is a global root
$providerstringProvider key

Example: Disable the Request Provider

add_filter( 'unblock/data/provider_global', function( $global, $provider ) {

return 'request' === $provider ? false : $global;

}, 10, 2 );

site and user always resolve: for them, this filter only affects the data picker.

data/preview_fixtures​

Pattern library previews never show your site's real data. There, a global provider shows its placeholder values, or nothing. Declare placeholders for your provider's fields:

add_filter( 'unblock/data/preview_fixtures', function( $fixtures ) {

$fixtures['shop'] = [
'name' => 'Example Shop',
'phone' => '+1 555-0100',
];

return $fixtures;

} );

Values are read as they are: field arguments are ignored.

data/preview_overrides​

In the editor preview, global providers show your site's real values. Replace a field's value there, keyed provider.field:

add_filter( 'unblock/data/preview_overrides', function( $overrides ) {

$overrides['shop.phone'] = fn( $context, $args ) => '+1 555-0100';

return $overrides;

} );

data/filters​

Register custom expression filters for data transformation.

ParameterTypeDescription
$filtersarrayRegistered filters configuration

Example: Reading Time Filter

The first argument is always the piped value. Arguments passed in the expression follow as an indexed array: $args[0] is the first one, $args[1] the second, etc.:

add_filter( 'unblock/data/filters', function( $filters ) {

$filters['reading_time'] = [
'callback' => fn( $value, $args ) => max( 1, (int) round( str_word_count( strip_tags( $value ) ) / ( $args[0] ?? 200 ) ) ),
];

return $filters;

} );

Use in expressions: {{ post.content|reading_time }} or {{ post.content|reading_time(150) }}

data/functions​

Register custom functions callable directly in expressions.

ParameterTypeDescription
$functionsarrayRegistered functions configuration

Example: Current Year Function

Arguments are passed as an indexed array — $args[0] is the first argument, $args[1] the second, etc.:

add_filter( 'unblock/data/functions', function( $functions ) {

$functions['year'] = [
'callback' => fn( $args ) => gmdate( $args[0] ?? 'Y' ),
];

return $functions;

} );

Use in expressions: © {{ year() }} or {{ year('y') }}

Example: Function with Provider Return

Functions that return an entity can declare its provider key in returns, so its fields can be chained:

add_filter( 'unblock/data/functions', function( $functions ) {

$functions['featured_post'] = [
'callback' => function( $args ) {

$id = (int) get_option( 'featured_post_id' );

return $id ? get_post( $id ) : null;

},
'returns' => 'post',
];

return $functions;

} );

Use in expressions: {{ featured_post().title }}

Common mistake

Return the object (get_post(), get_user_by(), get_term()), not its ID: an ID is a plain number and has no fields. returns takes a provider key ('post', 'user', 'term'), not a class name like 'WP_Post'.

data/post_meta​

Pre-filter for post meta retrieval. Return a non-null value to override get_metadata().

apply_filters( 'unblock/data/post_meta', null, string $meta_key, int $post_id, array $options );
ParameterTypeDescription
$valuemixedReturn non-null to override. Default: null
$meta_keystringMeta key name
$post_idintPost ID
$optionsarrayAdditional options from the expression
Common mistake

This filter receives null as the initial value — not the actual meta value. Return null to let WordPress handle the lookup. Returning any other value skips get_metadata() entirely.

Example: Type Conversion

add_filter( 'unblock/data/post_meta', function( $value, $meta_key, $post_id, $options ) {

if ( $meta_key === 'price' ) {
return floatval( get_post_meta( $post_id, $meta_key, true ) );
}

return null;

}, 10, 4 );

data/user_meta​

Pre-filter for user meta retrieval. Return a non-null value to override get_metadata().

apply_filters( 'unblock/data/user_meta', null, string $meta_key, int $user_id, array $options );
ParameterTypeDescription
$valuemixedReturn non-null to override. Default: null
$meta_keystringMeta key name
$user_idintUser ID
$optionsarrayAdditional options from the expression

Example: Type Conversion

add_filter( 'unblock/data/user_meta', function( $value, $meta_key, $user_id, $options ) {

if ( $meta_key === 'points' ) {
return intval( get_user_meta( $user_id, $meta_key, true ) );
}

return null;

}, 10, 4 );

data/term_meta​

Pre-filter for term meta retrieval. Return a non-null value to override get_metadata().

apply_filters( 'unblock/data/term_meta', null, string $meta_key, int $term_id, array $options );
ParameterTypeDescription
$valuemixedReturn non-null to override. Default: null
$meta_keystringMeta key name
$term_idintTerm ID
$optionsarrayAdditional options from the expression

Example: Fallback Value

add_filter( 'unblock/data/term_meta', function( $value, $meta_key, $term_id, $options ) {

$meta = get_term_meta( $term_id, $meta_key, true );

if ( $meta_key === 'icon_url' && empty( $meta ) ) {
return '/wp-content/themes/flavor/images/default-icon.svg';
}

return $meta ?: null;

}, 10, 4 );

data/option_meta​

Pre-filter for option retrieval from the Site provider. Return a non-null value to override get_option().

apply_filters( 'unblock/data/option_meta', null, string $key, array $options );
ParameterTypeDescription
$valuemixedReturn non-null to override. Default: null
$keystringOption key being retrieved
$optionsarrayAdditional options context

Example: ACF Options Page

add_filter( 'unblock/data/option_meta', function( $value, $key, $options ) {

if ( function_exists( 'get_field' ) ) {
return get_field( $key, 'option' );
}

return null;

}, 10, 3 );

data/allowed_options​

Whitelist WordPress options accessible via site.option().

ParameterTypeDescription
$allowedarrayAllowed option names

Default: ['date_format', 'time_format']

Example: Add Custom Option

add_filter( 'unblock/data/allowed_options', function( $allowed ) {

$allowed[] = 'my_custom_option';

return $allowed;

} );

Example: Theme Settings

add_filter( 'unblock/data/allowed_options', function( $allowed ) {

$allowed[] = 'theme_mods_flavor';
$allowed[] = 'flavor_settings';
$allowed[] = 'social_links';

return $allowed;

} );

Use in expressions: {{ site.option('social_links').twitter }}

data/allowed_meta_keys​

Whitelist metadata keys that bypass protection checks. By default, private meta keys (starting with _) are blocked for security.

ParameterTypeDescription
$allowedarrayAllowed meta keys
$meta_typestringMeta type: 'post', 'user', or 'term'

Default: [] (empty — all private meta keys blocked)

Example: Allow Private Field

add_filter( 'unblock/data/allowed_meta_keys', function( $allowed, $meta_type ) {

if ( $meta_type === 'post' ) {
$allowed[] = '_my_private_field';
}

return $allowed;

}, 10, 2 );

Example: Allow ACF Fields

add_filter( 'unblock/data/allowed_meta_keys', function( $allowed, $meta_type ) {

if ( $meta_type === 'post' ) {

$allowed[] = '_edit_lock';
$allowed[] = '_edit_last';

}

return $allowed;

}, 10, 2 );

Next steps​