Introduction
Laravel is renowned for its robust ecosystem and the ease with which developers can extend its functionality through packages. Whether you’re creating a package for personal use, internal projects, or sharing with the broader community, understanding how to develop Laravel packages can significantly enhance your development skills. This comprehensive guide will walk you through the entire process of developing a Laravel package, from setup to distribution, covering best practices and advanced techniques.
1. Introduction to Laravel Package Development
What is a Laravel Package?
A Laravel package is a self-contained piece of functionality that can be easily integrated into Laravel applications. Packages can include routes, controllers, views, migrations, configuration files, and more. They help to modularize code, promote reuse, and can significantly reduce development time.
Why Develop Packages?
- Code Reusability: Share common functionality across multiple projects.
- Modularity: Encapsulate specific features or components.
- Community Contribution: Share your solutions with the Laravel community.
- Maintainability: Isolate and manage complex functionality separately from the main application.
2. Setting Up the Development Environment
Prerequisites
Ensure you have the following installed:
- PHP (>=7.3)
- Composer
- Laravel (>=8.x)
Initial Setup
Create a new Laravel project for testing your package:
composer create-project --prefer-dist laravel/laravel package-dev
cd package-dev
3. Creating the Package
Directory Structure
Laravel packages typically follow a standardized directory structure. Create a directory for your package inside the packages
directory of your Laravel project:
mkdir -p packages/vendor/package-name
cd packages/vendor/package-name
Package Skeleton
Initialize the package with Composer:
composer init
Follow the prompts to set up your package’s composer.json
. Here’s an example structure:
{
"name": "vendor/package-name",
"description": "A sample Laravel package",
"type": "library",
"require": {
"php": ">=7.3",
"illuminate/support": "^8.0"
},
"autoload": {
"psr-4": {
"Vendor\\PackageName\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Vendor\\PackageName\\PackageNameServiceProvider"
]
}
}
}
Directory Structure
Your package should now look like this:
packages/
vendor/
package-name/
src/
composer.json
Service Provider
Create a service provider for your package. This will be the main entry point for integrating your package with Laravel.
Create PackageNameServiceProvider.php
in the src
directory:
namespace Vendor\PackageName;
use Illuminate\Support\ServiceProvider;
class PackageNameServiceProvider extends ServiceProvider
{
public function boot()
{
// Publish config files
$this->publishes([
__DIR__.'/../config/package-name.php' => config_path('package-name.php'),
]);
// Load routes
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
// Load views
$this->loadViewsFrom(__DIR__.'/../resources/views', 'package-name');
// Load migrations
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
// Load translations
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'package-name');
}
public function register()
{
// Merge package config with application config
$this->mergeConfigFrom(
__DIR__.'/../config/package-name.php', 'package-name'
);
}
}
Autoloading
Update your composer.json
autoload section to autoload your package’s classes:
"autoload": {
"psr-4": {
"Vendor\\PackageName\\": "src/"
}
}
Run composer dump-autoload
to regenerate the autoloader files.
4. Service Providers
Registering Service Provider
Laravel automatically discovers package service providers listed in the extra
section of your composer.json
. If needed, you can manually register the service provider in the config/app.php
file:
'providers' => [
// Other service providers...
Vendor\PackageName\PackageNameServiceProvider::class,
],
5. Package Configuration
Configuration File
Create a configuration file for your package. This file will be published to the application’s config
directory.
Create config/package-name.php
:
return [
'option' => 'value',
];
Merging Configuration
In your service provider, merge the package configuration with the application’s configuration:
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/../config/package-name.php', 'package-name'
);
}
Publishing Configuration
Allow users to publish your package’s configuration file:
public function boot()
{
$this->publishes([
__DIR__.'/../config/package-name.php' => config_path('package-name.php'),
]);
}
Users can publish the configuration file using the following Artisan command:
php artisan vendor:publish --provider="Vendor\PackageName\PackageNameServiceProvider" --tag=config
6. Package Routes and Controllers
Defining Routes
Create a routes file for your package. This file will be loaded by the service provider.
Create routes/web.php
:
use Vendor\PackageName\Http\Controllers\PackageNameController;
Route::get('package-name', [PackageNameController::class, 'index']);
Creating Controllers
Create a controller for your package.
Create src/Http/Controllers/PackageNameController.php
:
namespace Vendor\PackageName\Http\Controllers;
use App\Http\Controllers\Controller;
class PackageNameController extends Controller
{
public function index()
{
return view('package-name::index');
}
}
Loading Routes
In your service provider, load the routes file:
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}
7. Views and Assets
Creating Views
Create a directory for your package’s views.
Create resources/views/index.blade.php
:
<!DOCTYPE html>
<html>
<head>
<title>Package Name</title>
</head>
<body>
<h1>Hello from Package Name!</h1>
</body>
</html>
Loading Views
In your service provider, load the views:
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'package-name');
}
Publishing Views
Allow users to publish your package’s views:
public function boot()
{
$this->publishes([
__DIR__.'/../resources/views' => resource_path('views/vendor/package-name'),
]);
}
Users can publish the views using the following Artisan command:
php artisan vendor:publish --provider="Vendor\PackageName\PackageNameServiceProvider" --tag=views
Publishing Assets
Create a directory for your package’s assets.
Create resources/assets/js/app.js
and resources/assets/css/app.css
.
Allow users to publish your package’s assets:
public function boot()
{
$this->publishes([
__DIR__.'/../resources/assets' => public_path('vendor/package-name'),
], 'public');
}
Users can publish the assets using the following Artisan command:
php artisan vendor:publish --provider="Vendor\PackageName\PackageNameServiceProvider" --tag=public
8. Migrations and Commands
Creating Migrations
Create a directory for your package’s migrations.
Create database/migrations/create_package_name_table.php
:
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePackageNameTable extends Migration
{
public function up()
{
Schema::create('package_name', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('package_name');
}
}
Loading Migrations
In your service provider, load the migrations:
public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
Creating Commands
Create a command for your package.
Create src/Console/Commands/PackageNameCommand.php
:
namespace Vendor\PackageName\Console\Commands;
use Illuminate\Console\Command;
class PackageNameCommand extends Command
{
protected $signature = 'package-name:command';
protected $description = 'Package Name Command';
public function handle()
{
$this->info('Package Name Command executed successfully!');
}
}
Registering Commands
In your service provider, register the command:
public function register()
{
$this->commands([
\Vendor\PackageName\Console\Commands\PackageNameCommand::class,
]);
}
Users can run the
command using the following Artisan command:
php artisan package-name:command
9. Testing Packages
Setting Up Testing Environment
Create a testing environment for your package using PHPUnit and Orchestra Testbench.
Install Orchestra Testbench:
composer require --dev orchestra/testbench
Creating Tests
Create a test class for your package.
Create tests/Feature/PackageNameTest.php
:
namespace Vendor\PackageName\Tests\Feature;
use Orchestra\Testbench\TestCase;
use Vendor\PackageName\PackageNameServiceProvider;
class PackageNameTest extends TestCase
{
protected function getPackageProviders($app)
{
return [PackageNameServiceProvider::class];
}
public function testExample()
{
$response = $this->get('/package-name');
$response->assertStatus(200);
$response->assertSee('Hello from Package Name!');
}
}
Running Tests
Run your tests using PHPUnit:
vendor/bin/phpunit
10. Distributing Packages
Publishing on Packagist
To share your package with the community, publish it on Packagist.
- Create a GitHub repository for your package.
- Push your package to GitHub.
- Submit your package to Packagist at https://packagist.org/packages/submit.
Using GitHub
You can also distribute your package directly from GitHub by including the repository in the composer.json
file of your Laravel application:
"repositories": [
{
"type": "vcs",
"url": "https://github.com/vendor/package-name"
}
],
"require": {
"vendor/package-name": "dev-main"
}
11. Advanced Techniques
Using Facades
Create a facade for your package to provide a clean, expressive syntax for accessing your package’s functionality.
Create src/Facades/PackageName.php
:
namespace Vendor\PackageName\Facades;
use Illuminate\Support\Facades\Facade;
class PackageName extends Facade
{
protected static function getFacadeAccessor()
{
return 'package-name';
}
}
Binding Services to the Container
In your service provider, bind your package’s services to the Laravel service container:
public function register()
{
$this->app->singleton('package-name', function ($app) {
return new PackageName;
});
}
Using Blade Directives
Create custom Blade directives for your package.
In your service provider:
use Illuminate\Support\Facades\Blade;
public function boot()
{
Blade::directive('packageName', function ($expression) {
return "<?php echo 'Package Name: ' . $expression; ?>";
});
}
Using the directive in a view:
@packageName('Example')
Middleware and Policies
Create middleware and policies to extend your package’s functionality.
Middleware
Create src/Http/Middleware/PackageNameMiddleware.php
:
namespace Vendor\PackageName\Http\Middleware;
use Closure;
class PackageNameMiddleware
{
public function handle($request, Closure $next)
{
// Middleware logic
return $next($request);
}
}
Register the middleware in your service provider:
protected $routeMiddleware = [
'package-name' => \Vendor\PackageName\Http\Middleware\PackageNameMiddleware::class,
];
Policies
Create a policy for your package.
Create src/Policies/PackageNamePolicy.php
:
namespace Vendor\PackageName\Policies;
use App\Models\User;
class PackageNamePolicy
{
public function view(User $user)
{
// Policy logic
return true;
}
}
Register the policy in your service provider:
use Illuminate\Support\Facades\Gate;
public function boot()
{
Gate::policy(\Vendor\PackageName\Models\PackageName::class, \Vendor\PackageName\Policies\PackageNamePolicy::class);
}
12. Best Practices
Follow PSR Standards
Adhere to PSR-1, PSR-2, and PSR-4 standards for code formatting, naming conventions, and autoloading.
Write Documentation
Provide clear and comprehensive documentation for your package, including installation instructions, usage examples, and configuration options.
Use Semantic Versioning
Follow semantic versioning (MAJOR.MINOR.PATCH) to indicate breaking changes, new features, and bug fixes.
Test Thoroughly
Write unit tests and integration tests to ensure your package works as expected and is free of bugs.
Maintain Security
Regularly update your package to address security vulnerabilities and keep dependencies up-to-date.
13. Conclusion
Developing Laravel packages is a powerful way to extend the functionality of your applications and share your solutions with the community. By following this comprehensive guide, you can create robust, maintainable, and well-documented packages. From setting up the development environment to advanced techniques and best practices, you now have the knowledge to build high-quality Laravel packages. Embrace the power of Laravel’s ecosystem, and contribute to the vibrant community of Laravel developers.