feat: feed the record and arbitrary data through to Brick::toHtml() - #57
Merged
Conversation
Brick::toHtml() has always taken a second `?array $data` parameter and nothing has ever passed anything to it: MasonRenderer hardcoded `data: []`, and both iframe renderers omitted the argument entirely. The hook existed in the signature, in the generator stub and in the docs, but was unreachable. MasonRenderer gains a data() setter (array or closure) forwarded to every brick, exposed as a third argument on the mason() helper. The @mason directive already forwards its whole expression to the helper, so it picks the argument up for free, named arguments included. MasonEntry defaults its data to ['record' => $this->getRecord()], so a brick that reads $data['record'] works in an infolist with no wiring, and data() overrides it. getRecord() is called with withContainerRecord tied to isset($this->container): it walks up to the schema container for its record, and that container is unset on a component not yet attached to one, which throws rather than returning null. The entry does not render in the Livewire request. It renders in an iframe fed by a POST to MasonController, so its data has to cross a request boundary. DataPayload reduces Eloquent models to a class-and-key marker, encrypts the JSON with Crypt, and re-resolves the model on arrival. Attributes never go over the wire, a payload we did not sign is treated as absent rather than fatal, and a record deleted between render and request arrives as null. Values that cannot survive the round trip are rejected at encode time rather than silently dropped, because json_encode() turns a closure into {} instead of failing: anything that is an object but neither JsonSerializable nor Arrayable throws, as does a resource. The editor preview is deliberately untouched. It POSTs to the same controller from mason.js with no record in scope, and what it should show is an open question — the last-saved row would contradict unsaved sibling fields, there is no record at all before the first save, and the preview re-renders on every state change with no debounce, so each one would re-resolve the model and any relations a brick touches. A record-dependent brick renders its null branch there for now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbLDdHUbgfXQ12eWn7zC68
Covers the second toHtml() argument from both ends: how a brick reads it, and how the helper, the directive, the renderer and MasonEntry supply it. Two notes that are easy to trip over are called out explicitly. The editor preview supplies no data, so a record-dependent brick needs a null branch written on purpose rather than one that renders nothing. And MasonEntry's data crosses a request boundary, so it must be JSON-serializable or an Eloquent model, and the record can come back null if it was deleted meanwhile. The README gains the @mason directive, which it has never documented at all — only the helper. Both forms take the same three arguments and both accept named arguments. Since the section now describes three ways to render the same content, the helper and the renderer get headings alongside the new directive one, matching how docs/bricks/rendering.md is already laid out. Anchors are deliberately avoided in the README cross-references. It has no internal links today, and readme-to-docs splits these sections into separate files, which would leave any #anchor pointing at the wrong document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbLDdHUbgfXQ12eWn7zC68
PageMeta renders from the record rather than from its own config — title, URL and last-updated — so the three call sites can be compared side by side in a running app: the front end passes the record explicitly, MasonEntry supplies its own, and the editor preview has none and falls through to the placeholder branch. Seeded into PageFactory so every generated page already contains one, which makes `composer serve` show the contrast without any setup: open a page on the frontend, then edit the same page and watch that brick change. The baseline gains a PHPStan entry for the new file. larastan cannot resolve view names in this setup, so every view() call in the repo is already baselined — Section, Hero, CardGrid, Divider, both iframe renderers and routes/web.php. Confirmed file-specific rather than a real defect by swapping view names between Hero and PageMeta: the error follows the file, not the string. Appended by hand rather than regenerated, because the baseline is included from phpstan.neon.dist and --generate-baseline would drop every existing entry. workbench/resources/dist is rebuilt for the classes the new view introduces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbLDdHUbgfXQ12eWn7zC68
The provider already loads through package discovery — composer.json declares it under extra.laravel.providers, and `testbench route:list` registers Mason's routes identically with or without this line. Listing it alongside AdminPanelProvider makes the workbench's provider set readable in one place instead of split between testbench.yaml and discovery, and keeps it working if the discovery cache is ever absent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BbLDdHUbgfXQ12eWn7zC68
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the gap raised in #56.
Brick::toHtml()has always taken a second?array $dataparameter, and nothing has ever passed anything to it —MasonRendererhardcodeddata: [], both iframe renderers omitted the argument. The hook was in the signature, the generator stub and the docs, but unreachable. This wires it up.What works now
Front end — a third argument on the helper, or
data()on the renderer (both accept a closure on the renderer side):The
@masondirective forwards its whole expression to the helper, so it picked the argument up for free, named arguments included.Infolists —
MasonEntrydefaults to['record' => $this->getRecord()], so a brick reading$data['record']works with no wiring.data()overrides it.The entry crosses a request boundary
MasonEntrydoes not render in the Livewire request — it renders in an iframe fed by a POST toMasonController, so its data has to survive the trip.DataPayloadreduces Eloquent models to a class-and-key marker, encrypts the JSON withCrypt, and re-resolves the model on arrival:null.Values that cannot survive the round trip are rejected at encode time rather than silently dropped —
json_encode()turns a closure into{}instead of failing, so anything that is an object but neitherJsonSerializablenorArrayablethrows, as does a resource.The editor preview is deliberately untouched
A record-dependent brick renders its
nullbranch in the editor for now, and the docs tell brick authors to write that branch on purpose.Plumbing is not the blocker — the preview route could reuse
DataPayloadin about five lines. What it should show is the open question:getRecord()is null until the first save, so it would only ever be truthful on Edit.Left for a follow-up, pending the answer in #56.
Workbench
PageMetarenders from the record rather than its own config, so the three call sites can be compared in a running app. It's seeded intoPageFactory, socomposer serveshows the contrast with no setup — open a page on the frontend, then edit the same page and watch that brick fall back to its placeholder.Notes for review
view()call in the repo is already baselined. Confirmed file-specific rather than a real defect by swapping view names betweenHeroandPageMeta— the error follows the file, not the string. Appended by hand, because the baseline is included fromphpstan.neon.distand--generate-baselinewould drop every existing entry.@masondirective, which it has never documented at all. Since that section now describes three ways to render the same content, the helper and renderer got headings alongside it, matchingdocs/bricks/rendering.md.resources/distandworkbench/resources/distare rebuilt; verified reproducible against a freshnpm run build.testbench.yamlnow listsMasonServiceProviderexplicitly. This is a no-op at runtime — the provider already loads through package discovery, andtestbench route:listis identical with or without it — but it keeps the workbench's provider set readable in one place.composer testis green — 201 passed, Rector/Pint/PHPStan clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01BbLDdHUbgfXQ12eWn7zC68