laranail/package-tools
laranail/package-tools is a toolkit for Laravel package developers, offering helpers and scaffolding for service providers, configuration, migrations, and common package concerns so you can build cleaner, more consistent add-ons with less boilerplate.
Installation
composer require laranail/package-tools
Add to composer.json under require-dev if only for package development:
"require-dev": {
"laranail/package-tools": "^1.0"
}
First Use Case: Package Scaffolding
Run the package:new Artisan command to generate a boilerplate package structure:
php artisan package:new MyPackage
This creates:
src/ (core classes)config/ (package config)resources/ (views, assets)tests/ (package tests)package.json (if applicable)Key Files to Review
PackageServiceProvider.php (entry point)config/package.php (default config)src/Package.php (fluent builder base)Use the Package class to define package metadata and features declaratively:
use Laranail\PackageTools\Package;
$package = new Package('vendor/package', '1.0.0')
->name('My Awesome Package')
->description('Does amazing things.')
->author('Jane Developer', '[email protected]')
->requires('laravel/framework', '^10.0')
->conflicts('other/package', '^1.0')
->provides('package-feature');
Common Methods:
| Method | Purpose | Example |
|---|---|---|
addConfig() |
Register package config | ->addConfig('package.php') |
addView() |
Register views | ->addView('path/to/views') |
addAsset() |
Register assets (JS/CSS) | ->addAsset('path/to/assets') |
addCommand() |
Register Artisan commands | ->addCommand(\MyCommand::class) |
addMacro() |
Extend Laravel classes | ->addMacro(Str::class, ...) |
Automatically discover and register package features using attributes:
use Laranail\PackageTools\Attributes\PackageFeature;
#[PackageFeature]
class MyFeature {
public function register(): void {
// Feature logic here
}
}
The package scans for classes with [PackageFeature] and registers them automatically.
Publish package config with:
php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider" --tag="config"
Override defaults in config/package.php.
Publish views/assets:
php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider" --tag="views"
php artisan vendor:publish --provider="Vendor\Package\PackageServiceProvider" --tag="assets"
Load features conditionally:
if (config('package.feature.enabled')) {
$this->app->make(\Vendor\Package\Features\MyFeature::class)->register();
}
Use the PackageTestCase base class for package tests:
use Laranail\PackageTools\Testing\PackageTestCase;
class MyPackageTest extends PackageTestCase {
protected function getPackageProviders(): array {
return [\Vendor\Package\PackageServiceProvider::class];
}
public function test_feature() {
$this->assertTrue(config('package.feature.enabled'));
}
}
Attribute Discovery Scope
src/ directory by default.#[PackageFeature(scan: true)] to force scan outside src/.Config Caching
php artisan config:clear
Command/Provider Registration Order
PackageServiceProvider is last in config/app.php to avoid conflicts.Asset Versioning
styles.css?v=1.0.0) to bypass cache:
$this->package->addAsset('path/to/assets', 'public/path', 'v=' . $this->version);
Enable Debug Mode
Set PACKAGE_TOOLS_DEBUG=true in .env to log discovery and registration steps.
Check Discovery Logs Run with:
PACKAGE_TOOLS_DEBUG=1 php artisan package:discover
Validate Metadata
Use the package:validate command to check for issues:
php artisan package:validate
Custom Attributes Extend discovery with your own attributes:
use Laranail\PackageTools\Contracts\PackageAttribute;
#[Attribute]
class MyCustomAttribute implements PackageAttribute {
public function handle(object $subject, Package $package): void {
// Custom logic
}
}
Hooks System
Register hooks in PackageServiceProvider:
$this->package->hook('booting', function () {
// Runs after package boots
});
Dynamic Config Override config dynamically:
$this->package->config->set('feature.enabled', true);
#[LazyLoad] are loaded only when accessed.$this->package->memoize() to cache expensive operations:
$data = $this->package->memoize('data', function () {
return Cache::remember('package-data', 60, fn() => DB::table(...));
});
How can I help you explore Laravel packages today?