Query & AJAX
How dynamic blocks fetch posts — the AJAX endpoints, WP_Query construction, transient caching, and the editor-side
useApiDatahook.
Dynamic blocks such as Post Grid need to query posts both in the editor (live preview) and on the frontend (initial render plus AJAX filtering/pagination). Styble centralises this in two PHP classes and one React hook.
The pieces
| Component | File | Role |
|---|---|---|
Blocks_Query | blocks/Includes/Blocks_Query.php | Registers and handles the wp_ajax_* endpoints. |
PostQueryHandler | blocks/Includes/PostQueryHandler.php | Builds the WP_Query arguments from block data. |
Transient_Registry | blocks/Includes/Utils/Transient_Registry.php | Tracks query transients per post for cache clearing. |
useApiData | src/hooks/useApiData.js | Editor-side hook that fetches query results with debouncing. |
Blocks_Query and the AJAX endpoints
Blocks_Query (namespace ShapedPlugin\StyblePro\Includes) is a singleton booted from Block_Init. Its init() registers the AJAX actions:
public function init() {
Transient_Registry::maybe_migrate_legacy_option();
// Editor-only: authenticated users with edit_posts.
add_action( 'wp_ajax_styble_post_block_post_query', array( $this, 'styble_post_block_post_query' ) );
if ( is_admin() ) {
add_action( 'wp_ajax_styble_post_block_all_post_query', array( $this, 'styble_post_block_all_post_query' ) );
}
add_action( 'wp_ajax_styble_post_block_meta_data_query', array( $this, 'styble_post_block_meta_data_query' ) );
// Public post-grid live filter (nonce required; no postmeta exposure).
add_action( 'wp_ajax_styble_ajax_filter_posts', array( $this, 'styble_ajax_filter_posts' ) );
add_action( 'wp_ajax_nopriv_styble_ajax_filter_posts', array( $this, 'styble_ajax_filter_posts' ) );
}| Action | Access | Purpose |
|---|---|---|
styble_post_block_post_query | wp_ajax_ (logged-in) | Editor post query for previews. |
styble_post_block_all_post_query | wp_ajax_ (admin only) | Fetch all posts for selection controls. |
styble_post_block_meta_data_query | wp_ajax_ (logged-in) | Metadata/taxonomy queries for controls. |
styble_ajax_filter_posts | wp_ajax_ + wp_ajax_nopriv_ | Public live filtering & pagination of the Post Grid. |
Security
The authenticated handlers verify a nonce and editor capability before running. require_authenticated_query_access() checks the sp_styble_block_nonce nonce and rejects invalid requests:
if ( ! wp_verify_nonce( $nonce, 'sp_styble_block_nonce' ) ) {
wp_send_json_error( array(
'code' => 'invalid_nonce',
'message' => __( 'Session expired. Please reload the page.', 'styble-pro' ),
) );
}The public filter endpoint also requires a nonce and deliberately does not expose post-meta. Responses use wp_send_json_success() / wp_send_json_error().
PostQueryHandler
PostQueryHandler (blocks/Includes/PostQueryHandler.php) turns block query data into a WP_Query. Its static query() method is the entry point:
public static function query( $query_data, $type = null, $block_id = '' ) {
// … build $args (post type, taxonomy, meta, order, pagination) …
$args = apply_filters( 'styble_pro_query_args', $args, $block_name, $query_data );
// … run WP_Query and shape the response …
}The styble_pro_query_args filter lets you adjust the final query arguments for any block before the query runs — see Hooks & Internationalization. It supports custom post types, taxonomy filtering, metadata queries, ordering, and pagination.
Transient caching with key tracking
Query results are cached in WordPress transients per block instance. So that the right caches can be purged when content changes, each transient is registered against the post it belongs to via Transient_Registry::track_for_post(). The registry stores the mapping of transient keys to post IDs.
On save_post and deleted_post, Block_Init::clear_cache() calls into the registry to clear that post's transients (skipping autosaves and revisions):
// blocks/Block_Init.php
add_action( 'save_post', array( $this, 'clear_cache' ), 10, 1 );
add_action( 'deleted_post', array( $this, 'clear_cache' ), 10, 1 );Transient_Registry also exposes targeted helpers such as clear_post_grid_query_transients() and clear_all_post_grid_query_transients() for finer-grained invalidation. The same registry backs the dynamic CSS cache.
The useApiData hook
On the editor side, useApiData (src/hooks/useApiData.js, exported from src/hooks/index.js) fetches query results based on the block's attributes and keeps the preview in sync as settings change. It debounces requests so dragging a slider or typing does not fire a request per keystroke:
/**
* Debounce delay in ms — prevents rapid fire Ajax requests
* when the user drags a slider or types quickly.
*/
const DEBOUNCE_MS = 300;
const useApiData = ( attributes, options = {} ) => {
const { enabled = true } = options;
// … derives a dependency object from query-related attributes,
// debounces, calls the AJAX endpoint, and returns posts + state.
};It returns the fetched posts together with state such as isLoading, totalPages, and postCount, which the block uses to render previews and to decide whether to attach pagination.
Pagination as a child block
Pagination attaches conditionally: only when totalPages > 1 and pagination is enabled. The useAddChildBlock hook handles attaching the pagination child block based on the loaded data. See the user-facing Ajax Pagination page.
Related
- Post Grid and Ajax Pagination — the user-facing blocks this powers.
- Dynamic CSS Generation — shares the transient registry for caching.
- Hooks & Internationalization —
styble_pro_query_argsand related filters.