torann/json-ld
torann/json-ld is a Laravel package for generating JSON-LD structured data. It provides a fluent builder for schema.org markup, supports multiple schema types, and makes adding SEO-friendly rich results to your application simple.
Architecture fit. This is a renderer, not a platform. It sits at the very edge of the request lifecycle — model in, schema.org array out, <script type="application/ld+json"> in the layout head — and touches nothing else. That is the right shape for what a TPM should be buying here: a presentation-layer convenience that makes structured data consistent and reviewable across templates, with zero coupling to domain logic. The correct architectural posture is to treat it as an implementation detail behind an internal SchemaPresenter interface, one presenter per schema type, so the package never appears in a controller, a model, or an architecture diagram. If a proposal requires the package to do more than emit arrays — validation, schema governance, automated generation across heterogeneous templates, CMS-level policy — that is scope creep and should be declined.
Integration feasibility. Technically trivial, commercially uncertain. The codebase is small enough to read end to end in an afternoon, BSD-2-Clause makes vendoring legally frictionless, and Laravel auto-discovery means the install is one composer require. The only genuinely risky step is constraint resolution: the last release is 0.0.19 (March 2020, pre-1.0), which almost certainly declares Laravel 6–7-era illuminate/support bounds. Run the install spike on a branch against the actual PHP/Laravel versions in production before anyone writes a plan around it. A failed resolve is a legitimate, cheap reason to stop — the presenter wrapper you'd write is identical against spatie/schema-org, so the switching cost is one day, not one sprint. Do not accept --ignore-platform-reqs as a workaround; that converts a clean "no" into an unowned upgrade liability.
Technical risk. Rank the risks honestly, because they are not where people expect:
aggregateRating, invented availability, stale prices, and placeholder values are manual-action risks from Google and are entirely a policy problem, not a package problem. This is the failure mode that can actually damage the business.</script> inside a title breaks the document. JSON_HEX_TAG (or <\/ substitution) plus {!! !!} — never {{ }}, which entity-encodes and invalidates the block.src/; verify facade name and builder methods against the installed version rather than the README.Key questions to put to Engineering before approval.
composer require torann/json-ld:0.0.19 resolve cleanly against our current PHP/Laravel, or does it need a constraint override? (Binary go/no-go.)@graph per page, stable @ids, no empty values) before the second developer touches this?Stack fit. Strong, conditional on the install spike passing. Laravel server-rendered Blade pages are exactly the target: package auto-discovery, facade/helper ergonomics, Blade-friendly output. It is a poor fit for client-side or SPA/headless output, where the head is swapped by JS and a JS-side library is the natural choice. Confirm rendering strategy before anything else — this is a disqualifier, not a preference.
Migration path. There is no migration; this is greenfield on a per-template basis. The realistic baseline is hand-written json_encode() arrays scattered across Blade partials, and the honest value proposition versus that baseline is consistency and reviewability, not capability. Frame the decision as a two-to-three-day convenience purchase with a wrapper, not a platform dependency — and set the expectation that the wrapper is the deliverable, not the package. If the install fails, the wrapper survives; only the toArray() internals change.
Compatibility. Three dimensions to verify, in order:
config:cache deploy that skips config:clear silently emits the previous shape. Add it to the deploy script and verify one page post-deploy.Also treat the 0.0.19 Book/MediaObject/Recipe additions correctly: those are output types, not application entities. Do not let this get scoped as a content-modeling initiative.
Sequencing. Ship in template order of coverage-per-effort, and instrument each step:
@graph with stable @ids, replacing stacked script tags. Duplication and overlapping markup are the most common cause of rich results silently not appearing.Gate steps 2–5 on a written convention doc and the presenter/feature-test pattern from step 1. Rollout by template, with Search Console enhancements instrumented per template so movement is attributable.
Maintenance. The dominant cost, and it is small but permanent. Budget a few hours per Laravel major upgrade to confirm the package still installs. Treat it as internal code from day one: pinned exact version, named owner, fork ready. BSD-2-Clause and a small codebase mean a fork is an afternoon's work if it ever blocks an upgrade — that optionality is what makes the maintenance risk acceptable. The recurring failure is not the package rotting; it is nobody noticing it has.
Support. Effectively none. No vendor, no release cadence, no security policy, 148 stars, zero dependents. Escalation path is internal: the named owner, then the fork, then spatie/schema-org behind the same interface. Do not represent this package as a supported dependency in risk reviews — it is a vendored convenience.
Scaling. Schema assembly is cheap; the scaling pressure is organisational, not computational. Cache the generated array keyed on the model and its updated_at timestamp — crude, but it makes the invalidation contract explicit and means you never ship a stale price or rating without writing an observer. The real scale limit is template surface area: at dozens of heterogeneous templates the presenter-per-type pattern starts to need a factory and a shared base presenter for site-wide defaults (organization name, logo, sameAs). If you find the same logo path in five presenters, that is the signal to move it to config. Beyond that threshold, this stops being the right tool — it is a renderer, not a structured-data platform.
Failure modes, ordered by business severity.
InStock on out-of-stock items, expired prices) — a manual-action risk. Mitigation is governance, not code: no defaults, empty-value stripping before encode, and a review checklist per type.</script> in user content. Mitigation: JSON_HEX_TAG plus {!! !!}, never {{ }}.null, '', [] render as valid JSON but invalid schema, flagged inconsistently by validators. Mitigation: recursive array_filter at the end of every presenter.@graph per page with stable @ids.Ramp-up. Low, roughly half a day for a competent Laravel developer — but the ramp-up that matters is the convention, not the API. The package's docs have drifted from its code, so the first task is reading src/ rather than the README to confirm facade names and builder methods. Publish a one-page convention doc alongside the pilot (one presenter per type, one graph per page, stable @ids, no empty values, absolute canonical URLs, ISO 8601 dates) so the second developer doesn't re-derive it. Add a validation gate to the definition of done: Google Rich Results Test plus the Schema Markup Validator on every new template, and a recurring review of Search Console enhancement reports.
How a TPM should hold this. It is a measurable bet with a clear kill criterion — rich-result eligibility is observable in Search Console, and impression/CTR deltas on pilot templates are attributable. Set the stop signal before starting: if the pilot template doesn't move after a reasonable window, stop expanding to further templates rather than rolling schema out everywhere on faith. Keep the package out of architecture and vendor-risk discussions, wrap it so replacement is a one-file change per type, and treat any pressure to extend it into validation, governance, or bulk automated generation as a signal to reassess the tool, not to expand its scope.
How can I help you explore Laravel packages today?