websemantics/builder-extension
A lightweight extension for Laravel's builder and scaffolding tooling that adds extra make commands, customizable stubs, and conventions, helping you generate boilerplate faster and keep generated code consistent across projects.
What it is: a Builder-module extension for PyroCMS 3.x. The core Builder module generates addon skeletons (modules, plugins, themes) from the admin UI; this package adds its own scaffold/extension into that generator so generated code follows a more opinionated structure. It is a tooling package, not a runtime dependency — nothing it installs should end up on a request path in production.
Baseline assumptions before you start (verify these first, they determine whether the rest of this document applies to you):
pyrocms/builder-module (or equivalent Builder addon) is installed and working. This package extends it; it does not replace it.Install:
composer require websemantics/builder-extension
composer dump-autoload
Builder discovers scaffold extensions through a registry (typically the extensions array in the Builder config, publishable with php artisan vendor:publish). Register the package's extension class there, or via Admin → Addons → Builder if the UI exposes the toggle. If the package ships its own service provider, add it to config/app.php only if auto-discovery is disabled in your project.
Where to look first, in this order:
src/ — the extension class itself. Its public methods tell you exactly what the scaffold does and which base class you'd extend to customize it.resources/ (or stubs/) — the templates that actually get written to disk. This is 90% of the package's value; read every stub before you generate anything.config/ — the publishable config, if any. Look for paths, namespaces, and file-name conventions.composer.json — require block confirms the supported PyroCMS/Laravel range.First use case: scaffold one throwaway module end to end — repository, model, migration, admin controller, lang files — then git diff the result against a hand-written module in the same codebase. That diff is the real evaluation. If the generated structure is closer to your conventions than your manual boilerplate, keep it; if it isn't, you've spent twenty minutes instead of a sprint.
Generate once, then own the code. Treat the output as a starting point you commit, not a source of truth you regenerate. The productive loop is: scaffold → review the diff → commit → hand-edit from there. Regenerating over a module you've already customized is how people lose an afternoon.
Use it as a convention enforcer, not a code generator. The durable win is that every new module in the codebase starts with identical structure: same repository pattern, same namespace layout, same lang-file keys, same migration naming. That matters more on a team of three than any individual keystroke saved.
Stub-first customization. When the generated output is almost right, don't patch the generator's PHP — copy the stubs into your app (publish them, or copy into a project-local directory referenced by config) and edit the stubs. Stub edits survive package updates and are readable by junior developers in a way that a subclass with three overridden protected methods is not.
Subclass the extension for project-wide rules. If you need behavior the stubs can't express — deriving the addon slug from a vendor prefix, injecting a license header, wiring a base controller — extend the package's extension class, override the relevant hook, and register your class in the Builder registry instead of the package's. Keep that subclass in your own src/, never in vendor/.
Pair with artisan for CI. Scaffolding is interactive/admin-driven; that's fine for humans but useless in CI. If you want consistency enforced automatically, wrap the generator in a small artisan command of your own that calls it non-interactively with fixed inputs, so php artisan make:acme-module Blog produces the canonical skeleton in one shot. This is the single highest-leverage investment if your team uses this regularly.
Smoke-test generated addons immediately. Generated code that doesn't boot is worse than no code. After each scaffold:
php artisan migrate
php artisan route:list | grep <slug>
php artisan cache:clear
Then hit the admin route and the front-end route once each. A scaffold that produces a broken service provider registration is the most common failure mode with Builder extensions of this vintage.
Keep it out of production images. Add the package to require-dev in your root composer.json if your deployment strips dev dependencies. Generated addons are self-contained; the generator has no business on a live server.
It's a 2018 package with 12 stars — assume it is abandoned. Pin it exactly ("websemantics/builder-extension": "1.0.x" or whatever the tag is) and commit composer.lock. Do not run composer update on this dependency casually. If your PyroCMS install has since moved to a newer Laravel, expect the service provider to fail on container or config API changes; the fix is usually two lines, but you'll be making it yourself.
Fork-or-vendor is the realistic long-term plan. Given the age, plan for the package to become unmaintained mid-project. Because the valuable part is the stubs, the migration path is straightforward: fork it, or lift the stubs into your own stubs/ directory and point a thin wrapper command at them. Budget half a day for this rather than being surprised by it.
Namespace and path assumptions are hard-coded in stubs. Generated files assume a specific addon location (addons/{vendor}/{slug} vs vendor/{vendor}/{slug}) and a specific PSR-4 root. If your project deviates, the generator will happily write files Composer can't autoload. Check composer.json autoload.psr-4 in the generated output and run composer dump-autoload after every scaffold until you trust it.
Slug casing and hyphens. Vendors and slugs with dashes (my-vendor) frequently break StudlyCase conversion in generators of this era, producing My-vendor namespaces or class names that don't match filenames. On Linux the filesystem is case-sensitive and the failure appears as a confusing "class not found" at runtime. Test with a hyphenated name before rolling this out to the team.
The Builder caches its extension list. If your new extension doesn't appear in the UI, it's almost always cache, not code:
php artisan cache:clear
php artisan config:clear
php artisan view:clear
Check the Builder config file is actually being read — php artisan config:show builder (or tinker into config('builder')) is faster than guessing.
Regeneration overwrites silently. There is generally no "file exists, skip?" prompt. Commit before scaffolding, or scaffold into a clean branch. If you must regenerate over existing code, copy the directory aside first — there is no undo.
Generated migrations are a trap in shared databases. A scaffolded migration often picks a timestamp based on generation time, which can land it before existing migrations that its foreign keys depend on. Rename the migration file after generation if it references tables created later in the sequence.
Permissions on generated directories. If generation runs via the web UI (admin panel), the files are owned by the web server user, not you. You'll get "permission denied" when editing them from the CLI and "cannot write" when regenerating from CLI. Decide which user owns addon directories and stick to it; generate from CLI in local development.
Debugging hooks. When output is wrong, work backwards: tinker into the extension instance, dump the resolved stub path and the variable map passed to the template renderer. Nine times out of ten the bug is a variable the stub expects that the generator didn't supply — a silent null interpolated into a namespace.
Extension points worth knowing. The extension base class is your seam. Override the stub-resolution method to point at project-local templates, override the data-preparation method to fix naming, and leave rendering alone. If the class offers no such seams, that's a strong signal to vendor the stubs and write your own thin generator instead — the package's remaining value is the templates, not the plumbing.
How can I help you explore Laravel packages today?