Connected Content Runtime and API

This developer note documents the BricksMembers Connected Content runtime: cross-post relation storage, primary resolution, native Bricks Posts query filtering, connected dynamic tag projection, directed field synchronization, provider boundaries, extension points, and AI/MCP abilities.

Since version 1.4.0.

Scope and Non-Goals

Connected Content relates two eligible local WordPress posts under a configured type. It can project source fields live through Bricks, constrain native post loops to related posts, and optionally materialize selected source values into a target field.

  • It does not replace WooCommerce, FluentCart, or SureCart product-to-level mapping. Commerce integrations remain the access-assignment owners.
  • It does not register a new Bricks query type. It adds one control to native Posts queries.
  • It does not infer arbitrary ACF relationship fields or scan structure metadata at runtime.
  • It does not write remote commerce data or pricing, stock, checkout, subscription, variation, or payment state.

Runtime Owners

  • ConnectedContentFeatureRegistrar owns feature-local bootstrap, fail-closed runtime hook registration, active-module adapters, post-deletion cleanup, and builder/admin registration.
  • ConnectedContentSettingsService exclusively owns brm_connected_content_settings: connection types, aliases, directed mappings, revision checks, graph validation, and target-owner conflict validation.
  • ConnectedContentConnectionWriteService exclusively owns brm_content_connections writes and primary-selection invariants.
  • ConnectedContentConnectionReadService owns bounded reads, current/top-level structure anchor resolution, primary selection, request memoization, and object-cache invalidation.
  • ConnectedContentDynamicTagIntegration parses aliases and expert syntax, resolves a primary related post, applies exposure policy, and delegates the inner tag to Bricks with the source post ID.
  • ConnectedContentFieldSyncService owns save observation, ID-only job enqueueing, cursor backfills, endpoint resolution, identical-value no-ops, and recursion guards.
  • ConnectedContentFieldWriteService owns the materialized-write adapter and delegates to WordPress, WooCommerce, ACF, or ContentFieldValueService.

Bootstrap and Disable Contract

  1. BootstrapCoordinator::boot_modules() calls ConnectedContentFeatureRegistrar::boot().
  2. The registrar always installs tag/query fail-closed adapters and post-deletion cleanup.
  3. When ModuleRegistry::is_active( 'connected_content' ) is false, saved tags render empty and saved connected queries return no posts.
  4. When active, the registrar ensures schema and adds admin handlers, authenticated lookup AJAX, builder picker entries, save observers, and the internal queue handler.

Module disablement preserves definitions and relations. Full-data uninstall deletes the option, table, module flag, and schema flags.

Storage Contract

  • Definitions: brm_connected_content_settings
  • Relations: {prefix}brm_content_connections
  • Schema flags: brm_connected_content_tables_created and brm_connected_content_schema_version
  • Relation uniqueness: connection type plus Owner/Connected post IDs
  • Nullable SHA-256 primary keys enforce at most one primary relation per endpoint, post, and connection type.

Anchor and Primary Resolution

resolve_connection_anchor_post_id() checks the current post first. If no relation exists, it calls StructureNavigationService::resolve_scope_post_id( ..., 'toplevel' ) once and checks that post. The reader does not walk arbitrary ancestors or scan _brm_parent_level_*.

One matching relation resolves without a primary marker. With multiple relations, all rows must place the context post on the same endpoint and one row must be marked primary for that endpoint. Ambiguous mixed-side or unpromoted sets resolve to zero.

Native Bricks Posts Query Contract

The builder control key is brmConnectedContentFilter. Its value is a configured connection-type key. ConnectedContentBricksPostQueryVarBuilder validates the module, type, schema, and resolved anchor, then adds only:

The control registrar attaches that key to native post loops on Section, Container, Block, Div, Accordion, Slider, and Posts elements. An omitted query.objectType is Bricks’ default Posts query and is handled like the explicit post value; explicitly non-post query types are not registered.

brmConnectedContentActive
brmConnectedContentType
brmConnectedContentAnchorId
bricks_force_run

ConnectedContentBricksPostQuerySqlFilter consumes those flags and adds two-direction indexed EXISTS constraints. Pagination, ordering, post type, and other native query behavior remain owned by Bricks and WordPress. An invalid saved state sets post__in to [0].

brm-community-link with linkDirection=show_content is intentionally outside the Connected Content module. It is a Community-owned nestable that exposes the authorized WordPress post already attached to the active Community Item or Reply through CommunityBricksAttachedTargetContextBuilder and CommunityBricksLinkedPostContext. It does not read brm_content_connections, and it must not be implemented through brmConnectedContentFilter or a second Community Area.

Dynamic Tag Contract

{brm_connected:alias_key}
{brm_connected:connection_type:endpoint:inner_bricks_tag}

Endpoint values are owner, connected, or other. The inner value must be an allowed post-scoped Bricks tag. WordPress post fields, featured images, ACF/custom fields, native Content Fields, and the allowlisted WooCommerce tag family are supported. Unsafe modifiers, nested brm_connected tags, action execution, and non-post-scoped families are rejected.

Expert tags always inherit source access. Aliases default to inherit; an explicitly public alias bypasses the source-post gate only for reviewed public marketing output. Native Content Fields still require their own public flag.

Field Sync Contract

A mapping identifies one connection type, a source endpoint/reference, a different target endpoint/reference, an enabled state, and the on_save trigger. The settings owner rejects endpoint self-writes, graph cycles, unavailable fields, protected metadata, and competing target owners.

  • WordPress fields: title, excerpt, content, slug, featured_image
  • ACF: an existing field key resolved through the ACF API
  • BRM Content Fields: an existing field key delegated to ContentFieldValueService
  • Safe meta: an explicit key and one of text, textarea, rich_text, number, boolean, url, or media_id

save_post enqueues only relation ID, mapping ID, cursor, and reason. Values never enter the queue payload. Backfill jobs process 50 relation IDs at a time, mapping jobs allow three attempts, and identical normalized values are no-ops.

An enabled mapping delegates target-edit authority to source editors. Administrators must connect only source post types and fields whose editors are trusted to update the selected target.

Commerce Provider Boundaries

  • WooCommerce: local product post fields route through WC_Product setters for name, description, short description, slug, and image ID. Pricing, inventory, variations, checkout, subscription, and payment state remain blocked.
  • FluentCart: fc_product and fluent-products participate as local posts through WordPress, ACF, Content Fields, or safe local metadata.
  • SureCart: local sc_product posts may participate in relations and local-field projection. Remote SureCart cloud models are outside this workflow, and WordPress post-field writes to sc_product are not Field Sync targets.

Events and Filters

do_action(
    'brm_connected_content_field_synced',
    $connection_id,
    $mapping_id,
    $target_post_id
);

apply_filters(
    'brm_connected_content_field_options',
    $options,
    $post_types
);

The successful-sync action intentionally omits the field value. Extensions adding field options are responsible for maintaining the same safe read/write and sanitization boundary.

Authenticated Admin Lookups

  • brm_connected_content_search_posts requires the centralized admin nonce, CAP_MANAGE_CONNECTED_CONTENT, a request throttle, eligible post types, and object-level edit_post. It returns at most 50 rows.
  • brm_connected_content_field_options uses the same capability/nonce/throttle boundary and returns definitions only, never stored values or secrets.

AI/MCP Abilities

  • bricksmembers/connected-content-catalog-get
  • bricksmembers/connected-content-connections-get
  • bricksmembers/connected-content-settings-plan
  • bricksmembers/connected-content-connection-plan
  • bricksmembers/connected-content-sync-plan

Read abilities return definitions and relation metadata, not field values or secrets. Plans are dry-run-first. Approved commits delegate through AiConnectedContentCommitService to the canonical settings, relation, or sync owner.

Operational Guidance

  • Prefer Dynamic Aliases over Field Sync when the value is only rendered in Bricks.
  • Keep product-to-level mapping in the provider integration. Do not infer access from a content relation.
  • Keep one canonical source for every synchronized field and avoid bidirectional ownership.
  • Use the top-level course as the relation anchor when lesson templates need the same connected product.
  • Start maintenance work from the Connected Content feature map in the plugin repo.
Get BricksMembers

Start Building Your Membership Site Today

Create, sell, and manage your content without limits. BricksMembers gives you everything you need to build membership and LMS sites with Bricks Builder.

Lifetime updates & bug fixes • Premium support • 0% transaction fees • 60-day money-back guarantee