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/meta_transform_valueConvert relational custom fields to objects site-wide
unblock/data/allowed_optionsWhitelist accessible WordPress options
unblock/data/allowed_meta_keysOpen meta keys: _ keys, user meta keys
unblock/data/allowed_fieldOpen a private field, for some objects
unblock/data/allowed_userOpen a user without a published post
unblock/data/allowed_post_typesOpen a post type WordPress shows nowhere to the public
unblock/data/allowed_postOpen or close one post

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 (a custom field is public: a callback returning personal data checks access itself):

  • 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.
  • args — The arguments, in order, each [ 'name' => …, 'type' => …, 'default' => … ]: the editor shows them in its hints, and an expression can give one by its name. Filters and functions take the same key.

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. Declared in args, an argument can also be given by its name: it reaches the callback at its position, and a skipped one is null:

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 ) ) ),
'args' => [ [ 'name' => 'words_per_minute', 'type' => 'number', 'default' => 200 ] ],
];

return $filters;

} );

Use in expressions: {{ post.content|reading_time }}, {{ post.content|reading_time(150) }} or {{ post.content|reading_time(words_per_minute: 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/meta_transform_value​

Default of the transform_value option of meta() and site.option(), as Timber's timber/meta/transform_value. When true, ACF, Meta Box, Pods and JetEngine relational fields (images, files, galleries, posts, terms, users) come as objects you can chain, and ACF dates as dates. An expression's own {transform_value: false} still wins.

apply_filters( 'unblock/data/meta_transform_value', bool $transform );
ParameterTypeDescription
$transformboolWhether to convert relational fields. Default: false

Example: convert everywhere

add_filter( 'unblock/data/meta_transform_value', '__return_true' );

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​

Open meta keys that are otherwise private: keys starting with _ (any meta type), and user meta keys, which are private except the names, nickname and bio.

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

Default: [] (empty: _ keys blocked, user meta keys closed unless opened in Settings > Privacy)

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 );

Example: Open a User Meta Key

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

if ( $meta_type === 'user' ) {
$allowed[] = 'job_title';
}

return $allowed;

}, 10, 2 );

data/allowed_field​

Decide whether a private field of an object can be read. Receives the answer of Settings > Privacy and has the final word: open a field for some objects only, or close what the settings opened.

ParameterTypeDescription
$allowedboolWhether Settings > Privacy opens the field
$providerstringProvider key: 'user', 'site', 'attachment', 'image'
$fieldstringField name: 'user_email', 'roles', 'admin_email'…
$objectmixedThe WordPress object the field is read on (WP_User, WP_Post…)

Example: Open the Email of Editors Only

add_filter( 'unblock/data/allowed_field', function( $allowed, $provider, $field, $user ) {

if ( 'user_email' === $field && in_array( 'editor', $user->roles, true ) ) {
return true;
}

return $allowed;

}, 10, 4 );

data/allowed_user​

Decide whether a user can be read at all. By default: the visitor, authors with a published post in a public post type, and members of the public roles (Settings > Privacy).

ParameterTypeDescription
$allowedboolWhether the user is readable by default
$user_idintUser ID

User loops and get_users() already list authors, or the members of the public roles they filter on; this filter then decides each user they return.

Example: Open Members Who Chose to Be Listed

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

return $allowed || get_user_meta( $user_id, 'show_in_directory', true );

}, 10, 2 );

data/allowed_post_types​

Open post types WordPress shows nowhere to the public (no pages of their own, left out of search): expressions read their published posts and loops list them. By default, the post types opened in Settings > Privacy.

ParameterTypeDescription
$post_typesstring[]Opened post types

Drafts and private posts of an opened post type still show only to users who can read them.

Example: Open Testimonials

add_filter( 'unblock/data/allowed_post_types', function( $post_types ) {

$post_types[] = 'testimonial';

return $post_types;

} );

data/allowed_post​

Decide whether a post can be read at all, with the last word. By default: a published post of a post type WordPress shows the public (its own pages, or in search) or opened, a post the viewer may read (read_post), and media.

ParameterTypeDescription
$allowedboolWhether the post is readable by default
$postWP_PostPost

A post that can't be read gives no field. The WooCommerce integration uses it to open a published variation of a readable product.

Example: Open Resources to Logged-in Visitors

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

return $allowed || ( 'resource' === $post->post_type && 'publish' === $post->post_status && is_user_logged_in() );

}, 10, 2 );

Next steps​