Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Twill Metadata Laravel Package

cwsdigital/twill-metadata

A Twill CMS package that adds configurable SEO metadata fields to your modules, letting editors manage page titles, descriptions, and Open Graph tags from the admin UI, plus Blade helpers to render the resulting meta tags in your front end.

View on GitHub
Deep Wiki
Context7

Getting Started

cwsdigital/twill-metadata adds a metadata layer (SEO tags, Open Graph, social cards, etc.) to Twill CMS modules without you hand-rolling JSON columns and form fieldsets on every model.

Minimal setup:

  1. composer require cwsdigital/twill-metadata
  2. Publish and run the migrations: php artisan vendor:publish --tag=twill-metadata-migrations && php artisan migrate
  3. Add the package trait to the model(s) you want metadata on (e.g. App\Models\Page).
  4. Add the metadata fieldset/partial to the matching Twill form (typically an include inside resources/views/admin/pages/form.blade.php).
  5. Render the metadata in your front-end Blade layout via the trait's accessors/relations.

Where to look first: the package's README, the published migration (it tells you the table shape and whether metadata is morph-related per model), and the service provider (look for a repository binding — see the v1.4.1 note on depending on abstractions). Those three files answer 90% of "how do I wire this up" questions.

First use case: a Page module that needs per-page <title>, description, OG image, and canonical URL, editable in the Twill admin and rendered in layouts/app.blade.php. Start there before rolling it across every module — the trait/migration pattern is uniform, so validating it on one model de-risks the rest.

Implementation Patterns

Trait on the model, fieldset in the form. The consistent pattern is: trait on the Eloquent model, Twill form partial in the admin form, accessors in the view. Keep the admin include in a shared partial (admin/partials/metadata) and @include it from each module's form so field naming and translations stay in sync.

Repository-backed persistence. v1.4.1 changed the repository behaviour to depend on abstractions rather than concrete classes. If you're extending or overriding how metadata is saved (custom validation, computed defaults, audit logging), bind against the package's interface in a service provider rather than subclassing a concrete repository — the concrete class is explicitly the unstable surface. Check your AppServiceProvider for any instance()/bind() calls against concrete metadata repository classes and switch them to the interface.

Fill metadata from the model, not the form, for derived values. For things like og:image pulled from a Twill media relation or a title falling back to the model's title, compute in the model/accessor layer and let the admin field be an override. This keeps CLI/imported content from shipping empty tags.

Decorate the output, don't inline it. Wrap rendering in a Blade component or a small presenter (<x-metadata :model="$page" />) so you can swap defaults, add noindex for non-published states, and unit-test the tag output without booting Twill.

Multi-module rollout. Add the trait per model, but consider one shared metadata table (morph) vs per-model columns depending on what the migration ships. If it's morph-based, watch index size and add a whereMorphRelation scope early for admin listings.

Seeding/importing. Set metadata in seeders and imports through the same repository/interface your app uses, not raw DB inserts — the repository layer is where defaults and normalization live.

Gotchas and Tips

v1.4.1 forces metadata columns to be nullable — for a reason. This was required for Laravel 11. If you published the migrations on an earlier version, re-publish and diff them; existing installs upgrading to Laravel 11 can hit insert failures on models that don't supply every metadata field. Running php artisan vendor:publish --tag=twill-metadata-migrations --force on an old install will not alter an existing table — write your own ->change() migration to drop the NOT NULL constraints.

Abstractions over concretes is a forward-compatibility signal. Treat any concrete repository class as internal. Overrides should target the interface and be resolved via the container, or the next minor bump can break your app in a way that only shows up at runtime.

Migrations are the upgrade risk, not the PHP code. Both changes in v1.4.1 touch schema and persistence. After upgrading, run the full flow on a fresh DB and on a copy of production data; the nullable change behaves differently in each case.

Twill compatibility matrix. Verify the package against your Twill major version before upgrading either. Metadata fieldsets rely on Twill's form/block APIs, and Twill majors have historically moved those around.

Debugging missing tags. Order of checks: is the trait on the model → does the admin form actually include the fieldset (and is it inside the right locale/translatable block) → is the record's metadata relation/casts populated ($model->metadata in tinker) → is the Blade component reading the right locale. Most "metadata doesn't save" reports are a form include placed outside the translatable wrapper, not a package bug.

Null vs empty string. Post-nullable-change, distinguish "no metadata row" from "row with null fields" in your accessors, and give the presenter explicit fallbacks (?? $model->title) so you never render <meta name="description" content="">.

Caching. If you cache rendered pages, metadata changes made in Twill won't invalidate your cache automatically — hook model saved events into your cache-busting, including on the metadata record itself if it's a separate model.

Extension points that are safe: trait methods on your own model, the Blade component, published migrations, and interface bindings. Extension points that are risky: concrete repository subclasses, published view overrides (they drift across releases — re-diff on upgrade), and relying on the exact column names in your own queries rather than through the model.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle