Skip to content

Relationship Block

The Relationship block lets editors pull records from any registered Eloquent model into a page or post and render them as a list — for example "the 5 latest blog posts", a curated set of team members, or related pages.

Query Sources in the admin

Learn by doing

For practical step-by-step instructions, check out our How to Use Relationship Blocks recipe — it walks through the generic setup and ends with a complete Team Members Section example.

It supports two modes:

  • Automatic — Query a source by ordering + limit (a dynamic list).
  • Manual — Hand-pick specific records via a dual-list picker with search and drag-and-drop ordering (a curated list).

Which sources are available, and how each record is rendered on the frontend, is fully driven by configuration — no block code changes are needed to add a new source.


Using the block (step by step)

This is the everyday workflow once at least one source exists. If no source is available yet, jump to Setting up a source first.

1. Add the block to a page

In the page/post builder, open the block palette and add a Relationship block (teal database icon). The block has no datafields — its whole configuration lives in the inline edit panel that appears on the block.

2. Choose a source

In the block's edit panel, open the Source dropdown and pick what to pull in (e.g. Blog posts, Pages, Team). The list comes from the registered sources (/admin/query-sources). Nothing else shows until a source is selected.

3. Pick a mode

A Mode toggle appears with two options:

ModeUse it whenWhat you configure
AutomaticThe list should stay current on its own ("latest 5 posts").Order field, direction, limit.
ManualYou want an exact, hand-curated set in a specific order.Which records, and their order.

You can switch modes at any time; each mode keeps its own settings.

4a. Automatic mode

Three controls appear, plus a live preview of the matched records:

  • Order by — A field from the source's allowed order fields (e.g. created_at, title).
  • Direction — The ascending / descending icons toggle the sort order (default: descending).
  • Limit — How many records to show (1–100, default 5).

The preview list updates immediately so you can see exactly what visitors will get. Records are also filtered by the source's status/scope (e.g. only published posts) — that part is fixed by the source, not editable per block.

4b. Manual mode

Two lists sit side by side:

  • Available (left) — Every selectable record. Type in the search box to filter server-side (handy when there are many records); a spinner shows while it searches. Click a record (or the +) to add it.
  • Selected (right) — Your chosen records, in the order they'll render.
    • Reorder: Drag the grip handle (⋮⋮) to move a record up or down.
    • Remove: Click the to send it back to Available.

The order on the right is exactly the order on the frontend.

5. Save and view

The block saves as you edit (each change writes block meta immediately). Publish / view the page and the records render through the source's configured item view — e.g. blog posts as cards with thumbnail and category, team members with photo and contact details. If a source has no item view, records render as plain title links.

Data Persistence

Switching auto → manual (or back) never loses data: your manual selection and your auto settings are stored under separate meta keys, so toggling just changes which one is used.


Setting up a source

Before editors can pick a source, an admin registers it once. Two steps:

  1. Allow-list the model in config/kompass.php under query_source_models (a key → model class map). This is a security guard — only allow-listed models can ever be queried.
  2. Create the source at Admin → Query sources (/admin/query-sources, admin role only): click Add, choose the allow-listed model, set the display fields, ordering, optional filters (status / scope), the frontend item view, and the wrapper layout. Drag rows to set their order in the editor's Source dropdown.

The full end-to-end walkthrough — migration, model, allow-list, source row, and item view — is the Team members worked example in the how-to guide.


Architecture at a glance

ConcernWhere
Block type registrationconfig/kompass.phpblock_types.relationship
Allow-listed modelsconfig/kompass.phpquery_source_models (key → FQCN)
Selectable sourcesquery_sources table, managed at /admin/query-sources
Query execution / helperssrc/Helpers/helpers.php
Admin edit UI (inline)resources/views/components/block/relationship.blade.php
Admin builder dispatchresources/views/components/blocks-datafield.blade.php
Editor actions (Livewire)src/Livewire/PagesData.php, src/Livewire/PostsData.php
Frontend renderingresources/views/components/blocks/relationship.blade.php
Per-source item viewsresources/views/components/relations/*.blade.php

The configuration (chosen source, mode, ordering, limit, manual selection) is stored entirely in block meta — the block has no datafields.


Data model (block meta keys)

Meta keyTypeMeaning
query-modelstringThe chosen source's key (resolved via query_models()).
query-modestringauto (default) or manual.
query-orderstringColumn to order by (auto mode).
query-directionstringasc or desc (auto mode, default desc).
query-limitintMax records to return (auto mode, clamped 1–100, default 5).
query-idsint[]Selected record IDs in display order (manual mode). Stored as JSON.

Configuration

Registering a source

Sources are database-managed: each row in the query_sources table is one selectable source, created and edited at Admin → Query sources. Before a source can point at a model, that model must be allow-listed in config/kompass.php:

php
// config/kompass.php
'query_source_models' => [
    'pages' => \Secondnetwork\Kompass\Models\Page::class,
    'posts' => \Secondnetwork\Kompass\Models\Post::class,
    // 'teams' => \App\Models\TeamMember::class,
],

Optional config sources

You can still hardcode a source in config('kompass.query_models'). Config sources are merged with DB rows and win on key collision.

Source Definition Fields

The Edit source drawer (at Admin → Query sources) exposes these fields. The Key is locked once saved blocks reference the source, so existing blocks never break:

Edit source

KeyRequiredDescription
labelyesHuman label shown in the source <select>.
modelyesEloquent model class, resolved from the row's model_key via the allow-list.
display_fieldsyesList of attributes shown in the picker/preview and searched server-side.
order_fieldsyesColumns offered in the "Order by" select (auto mode).
url_patternnoURL built per record; {slug} is replaced with the record's slug.
statusnoSimple where('status', <value>) filter (auto mode only).
scopenoEloquent local scope applied to the query (e.g. active).
item_viewnoAnonymous Blade component rendering one record on the frontend.
wrapper_classnoCSS classes for the element wrapping the rendered items.
withnoRelations eager-loaded on every queried record (Eager loads).

Helper API

The Relationship block is driven by a family of global helpers — query_models(), kompass_query(), kompass_query_candidates(), kompass_query_url(), and kompass_query_label(). Their full signatures and behaviour are documented on the Helper Functions reference page.


Frontend rendering

The published block component resources/views/components/blocks/relationship.blade.php resolves the source's item_view and renders each record through it:

blade
@php
    $records     = $selected ? kompass_query($item) : collect();
    $itemView    = $selected['item_view'] ?? null;
    $hasItemView = $itemView && view()->exists('components.' . $itemView);
@endphp

@if ($records->isNotEmpty())
    <div class="{{ $wrapper }}">
        @foreach ($records as $record)
            @php $url = kompass_query_url($modelKey, $record); @endphp
            @if ($hasItemView)
                <x-dynamic-component :component="$itemView" :record="$record" :url="$url" :model-key="$modelKey" />
            @else
                {{-- plain title link fallback --}}
            @endif
        @endforeach
    </div>
@endif

Item view contract

An item view receives:

  • $record: The Eloquent model instance.
  • $url: The record's URL or null.
  • $modelKey: The source key (e.g. posts).

For a complete end-to-end walkthrough — migration, model, allow-list, source, and item view — see the Team members worked example in the how-to guide.

Released under the MIT License.