Builder methods
Every method returns the builder, so declarations chain. Call name() first — it drives config keys, view and translation namespaces, and every publish tag:
| Method | What it does |
|---|---|
name(string $name) | The package handle. Drives config keys, view/translation namespaces and publish tags. |
hasConfigFile(?string $file = null) | Merge + publish a config file. Defaults to <name>.php (key <name>, tag <name>-config). |
hasMigrations() | Publish every database/migrations/*.php file under <name>-migrations, timestamp-injected and kept in the directory’s order. Nothing is auto-loaded. |
hasMigration(string $name) | Publish a single database/migrations/<name>.php.stub under <name>-migrations, timestamp-injected. Only for .php.stub sources. |
hasTranslations() | Load + publish translations (published to lang/vendor/<name>, tag <name>-translations). |
hasViews(?string $namespace = null) | Register + publish Blade views (namespace defaults to <name>, tag <name>-views). |
hasRoutes(string $file, ?string $enabledVia = null) | Load a route file (optionally gated behind a boolean config key, read like Config::boolean()) and publish it under <name>-routes. An unrecognised switch value throws at boot. |
hasCommands(array $commands) | Register console commands (console only). Repeated calls accumulate. |
hasFacadeAlias(string $class, ?string $configKey = null) | Register a class alias; the config value decides its name or skips it, and an unrecognised value throws. |
contributesToAbout(?Closure $data = null) | Add a section to php artisan about. |
publishesStubs(string $from, string $to, string $tag) | Publish an arbitrary set of files under a custom tag. |
A full declaration
A package that uses every builder method:
use Illuminate\Support\Str;
use RoundlyConsulting\PackageToolkit\Package;
public function configurePackage(Package $package): void
{
$package
->name('comments')
->hasConfigFile() // config/comments.php
->hasMigrations() // database/migrations/*.php
->hasMigration('create_comment_reactions_table') // database/migrations/<name>.php.stub
->hasTranslations() // resources/lang → comments::
->hasViews() // resources/views → comments::
->hasRoutes('comments.php', 'comments.routes.enabled') // routes/comments.php, gated by config
->hasCommands([PruneCommentsCommand::class]) // console only
->hasFacadeAlias(Comments::class, 'comments.alias') // alias decided by config
->contributesToAbout(static fn (): array => [
'Package' => 'comments',
'Key type' => (string) config('comments.key_type'),
'API key' => Str::mask((string) config('comments.api_key'), '*', 7),
])
->publishesStubs(
$package->basePath.'/stubs',
base_path('stubs/comments'),
'comments-stubs',
);
}Publish tags
Each declaration registers a publish group under a tag derived from the package name, so a host publishes exactly what it needs:
| Tag | Declared by | Published to |
|---|---|---|
<name>-config | hasConfigFile() | config/<file>.php |
<name>-migrations | hasMigrations(), hasMigration() | database/migrations/<Y_m_d_His>_<migration>.php |
<name>-translations | hasTranslations() | lang/vendor/<name> |
<name>-views | hasViews() | resources/views/vendor/<namespace> |
<name>-routes | hasRoutes() | routes/<file> |
<your tag> | publishesStubs() | The destination you pass |
php artisan vendor:publish --tag=comments-config
php artisan vendor:publish --tag=comments-migrations
php artisan vendor:publish --tag=comments-translations
php artisan vendor:publish --tag=comments-views
php artisan vendor:publish --tag=comments-routes
php artisan vendor:publish --tag=comments-stubsConfig files
hasConfigFile() merges config/<name>.php under the <name> key and publishes it under <name>-config. Pass a bare filename to ship a differently named file — the key follows the filename, the tag stays <name>-config. The .php extension is optional:
$package
->name('comments')
->hasConfigFile() // config/comments.php → config('comments.*')
->hasConfigFile('comments-webhooks'); // config/comments-webhooks.php → config('comments-webhooks.*')
// both files publish under the comments-config tagViews and translations
hasTranslations() loads resources/lang under the <name> namespace and publishes it to lang/vendor/<name>. hasViews() loads resources/views under the <name> namespace — or the one you pass, as in hasViews('talk') — and publishes it to resources/views/vendor/<namespace>.
Routes behind a config switch
hasRoutes() loads routes/<file> and publishes it under <name>-routes. The optional second argument names a boolean config key, read strictly like Config::boolean() — so a host can switch routes off from .env: false, 0, '0', 'false', 'off' and 'no' skip the file (case-insensitive); true, 1, '1', 'true', 'on', 'yes', or a switch that is not set — an absent key, null, or a blank '' (a host’s KEY=) — load it. Anything else — 'disabled', 'ture', 2 — throws InvalidConfigurationException at boot, so a typo never loads the routes:
// configurePackage()
$package
->name('comments')
->hasConfigFile()
->hasRoutes('comments.php', 'comments.routes.enabled');
// config/comments.php
return [
'routes' => [
'enabled' => true, // false — or 'off', '0', 'no' from env — drops the package routes entirely;
// a blank value (KEY=) counts as not set and loads them
],
];Class aliases
hasFacadeAlias() registers a class alias through Laravel’s AliasLoader. Without a config key the alias is always the class’s base name; with one, the config value decides when the provider registers — env-style booleans are parsed, so 'off' from .env opts out instead of becoming an alias named “off”, a blank value counts as not set and keeps the base name, and a value that is neither a boolean spelling nor a non-empty string throws:
| Config value | Result |
|---|---|
| No config key passed | Alias = the class’s base name. |
true / 1 / '1' / 'true' / 'on' / 'yes' | Alias = the class’s base name. |
| Key absent, or blank ('' / whitespace — not set) | Alias = the class’s base name. |
null (explicit) / false / 0 / '0' / 'false' / 'off' / 'no' | No alias is registered. |
| Any other non-empty string | That string is the alias name. |
| Anything else (2, 1.5, an array) | Throws InvalidConfigurationException at register time. |
// configurePackage()
$package
->name('comments')
->hasConfigFile()
->hasFacadeAlias(Comments::class, 'comments.alias');
// config/comments.php
return [
'alias' => true, // registers the alias "Comments"
// 'alias' => 'Talk', // registers the alias "Talk" instead
// 'alias' => false, // registers no alias — as do null, 'off', '0' and 'no'
// 'alias' => '', // blank counts as not set — registers "Comments"
// 'alias' => 2, // throws InvalidConfigurationException at register time
];The about command
contributesToAbout() adds a section named after the package (name() with its first letter upper-cased) to php artisan about. Without a callback it reports the package name; pass a closure returning label/value pairs for more. The closure runs when the command renders — redact anything secret:
use Illuminate\Support\Str;
$package
->name('comments')
->contributesToAbout(); // section "Comments" → Package: comments
$package
->name('comments')
->contributesToAbout(static fn (): array => [
'Package' => 'comments',
'Key type' => (string) config('comments.key_type'),
'API key' => Str::mask((string) config('comments.api_key'), '*', 7), // never render a raw secret
]);php artisan aboutArbitrary stubs
publishesStubs() is the escape hatch for files that fit no other helper — it publishes a source path to a destination under a tag you choose:
$package
->name('comments')
->publishesStubs(
$package->basePath.'/stubs', // source (the base path is already resolved here)
base_path('stubs/comments'), // destination in the host
'comments-stubs', // your tag
);Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.