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

Statamic Builder Laravel Package

tdwesten/statamic-builder

A Statamic addon by tdwesten that streamlines building and scaffolding custom Statamic addons, giving developers a practical starting point for creating, organizing, and managing addon code more efficiently in Laravel and Statamic projects.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and publish config

    composer require tdwesten/statamic-builder
    php artisan vendor:publish --tag="statamic-builder-config"
    

    The package is a thin code-generation layer: you write PHP classes, it emits the YAML that Statamic actually reads. Nothing changes at runtime unless you run the build step.

  2. Where to look first

    • The package README for the current API surface (it's a 37-star package — the source in vendor/tdwesten/statamic-builder/src is the real documentation).
    • The published config (class registration / discovery rules).
    • Your project's existing resources/blueprints, content/*/*.yaml, and resources/fieldsets — those are what you'll be replacing.
  3. First use case: port one collection, not all of them Pick a small, stable collection (e.g. blog). Generate the builder class with the package's make:* artisan command (e.g. php artisan make:collection Blog, php artisan make:blueprint, make:fieldset, make:navigation, make:taxonomy), fill in the fields, run the package's build/sync command, then diff the generated YAML against what was there before. If the diff is empty or trivial, you're ready to scale.

    // app/Statamic/Collections/Blog.php
    class Blog extends Collection
    {
        public function handle(): string { return 'blog'; }
        public function title(): string  { return 'Blog'; }
    
        public function routes(): array
        {
            return ['/blog/{slug}'];
        }
    
        public function fields(): array
        {
            return [
                Field::make('title')->type('text')->required(),
                Field::make('slug')->type('slug'),
                Field::make('content')->type('markdown'),
            ];
        }
    }
    

Implementation Patterns

One class per artifact, grouped by type. Keep app/Statamic/Collections, Blueprints, Fieldsets, Navigations, Taxonomies. Handles and titles are returned from methods; everything else is fluent config. This keeps the "what" (structure) separate from the "where" (registration).

Compose with fieldsets, not copy-paste. Any field group used by more than one blueprint (SEO, hero, CTA) becomes a Fieldset class and is referenced from blueprints. This is the single biggest maintenance win over YAML — in YAML you duplicate, here you use another class.

class Hero extends Fieldset
{
    public function handle(): string { return 'hero'; }

    public function fields(): array
    {
        return [
            Field::make('heading')->type('text'),
            Field::make('image')->type('assets')->container('assets'),
        ];
    }
}

// inside a Blueprint's fields():
Field::make('hero')->type('group')->fields(Hero::make()->fields()),

Treat PHP as source of truth, YAML as build output. The workflow that scales is:

edit PHP → run build command → commit generated YAML → deploy

Add the build step to your local setup (a composer script or a make build-content target) and to CI as a drift check: run the build and fail if git diff is non-empty. That catches "someone edited YAML in prod" immediately.

Reuse beyond fields. Collection routes(), taxonomies(), dated(), orderable() etc. are just methods — so you can build a small abstract BaseCollection with common behavior and subclass it per collection. Custom field types are the same: wrap repetitive config in a factory method (Field::make('price')->type('money')->config([...])) and call it everywhere.

Environment split. Keep builder classes in app/ (autoloaded in every env), but only run generation in local/CI. Production should ship the committed YAML. If you rely on the package's auto-discovery at boot, verify it doesn't regenerate or rewrite files on deploy.

Testing. Because the definitions are plain PHP objects, you can instantiate a collection in a test and assert on fields(), handles, and routes without touching Statamic's Stache. That's a real advantage for large multi-site projects.

Gotchas and Tips

  • CP edits write YAML, not PHP. If an editor changes a blueprint field in the Statamic Control Panel, it lands in YAML and diverges from your builder class. Either lock blueprint editing down in production or accept that the next build will clobber those changes. Decide this before onboarding content editors.
  • Clear the Stache after every build. Statamic caches blueprints/collections. php artisan statamic:stache:clear (or cache:clear in dev) after generating, or you'll debug phantom "field doesn't exist" errors for an hour.
  • Typo'd field types fail silently. ->type('markdownn') won't error at build time — it produces YAML Statamic can't resolve, and the field just vanishes in the CP. Double-check handles/types against php artisan statamic:...-listed addons or the fieldtype source.
  • Not every YAML key is first-class in the fluent API. When a field needs something exotic (visibility, validate, width, conditional logic, replicator sets with nested configs), expect to fall back to a ->config([...]) / raw array escape hatch. Grep the vendor source for the method before assuming it exists.
  • Field order is meaningful. Keep fields() returning a stable array order; associative arrays or loops that depend on iteration order will produce noisy YAML diffs on every rebuild.
  • Pin the version. At 37 stars, the package is maintained but niche; breaking changes in method names between Statamic majors (or between the package's own minor releases) are plausible. Use a composer.lock and read the changelog before upgrading. Check the composer constraint against your Statamic version first.
  • Generated YAML conflicts in Git. With multiple devs rebuilding, expect merge conflicts in resources/blueprints. Best mitigation: only regenerate when definitions actually change, and review the diff as part of the PR rather than committing build noise.
  • Debugging recipe. When a build produces the wrong YAML: (1) dd() the class's fields() return value, (2) inspect the emitted YAML file directly, (3) php artisan statamic:stache:clear, (4) re-check the CP. If the YAML is right but the CP is wrong, it's a caching problem, not a builder problem.
  • Don't reach for it on a one-off site. If you have two collections and a handful of fields, YAML is fine and has zero build step. The payoff is on multi-site installs, large field libraries, or teams that want PHP-side code review and IDE autocompletion on field config.
  • Extension points: subclass the base builder classes, add trait-based field factories, and register custom classes in the package config. If you need a custom fieldtype, implement it as a normal Statamic addon and reference it by handle from the builder — the builder doesn't need to know about it.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource