Créer un package Laravel réutilisable

Vous avez du code que vous réutilisez entre projets ? C'est le moment de le transformer en package. Ce guide couvre tout : structure, Service Provider, config, tests et publication.

Pourquoi créer un package ?

Un package permet de réutiliser du code entre projets, de le versionner indépendamment, et de le partager avec la communauté (ou juste votre équipe).

Cas d'usage typiques

Intégrations API : wrapper pour une API externe (Stripe, Mailchimp...)
Fonctionnalités communes : système d'audit, gestion de media, permissions
Composants UI : Blade components, Livewire components
Outils dev : debugging, profiling, helpers

Structure du package

                
Structure recommandée
my-package/ ├── src/ │ ├── MyPackageServiceProvider.php │ ├── Facades/ │ │ └── MyPackage.php │ ├── Services/ │ │ └── MyPackageService.php │ └── Http/ │ ├── Controllers/ │ └── Middleware/ ├── config/ │ └── my-package.php ├── database/ │ └── migrations/ ├── resources/ │ └── views/ ├── routes/ │ └── web.php ├── tests/ │ ├── TestCase.php │ └── Feature/ ├── composer.json ├── README.md └── LICENSE

Initialiser le package

1. Créer le composer.json

                
composer.json
{ "name": "vendor/my-package", "description": "Description de mon package", "type": "library", "license": "MIT", "authors": [ { "name": "Votre Nom", "email": "email@example.com" } ], "require": { "php": "^8.2", "illuminate/support": "^11.0|^12.0" }, "require-dev": { "orchestra/testbench": "^9.0", "phpunit/phpunit": "^11.0" }, "autoload": { "psr-4": { "Vendor\\MyPackage\\": "src/" } }, "autoload-dev": { "psr-4": { "Vendor\\MyPackage\\Tests\\": "tests/" } }, "extra": { "laravel": { "providers": [ "Vendor\\MyPackage\\MyPackageServiceProvider" ], "aliases": { "MyPackage": "Vendor\\MyPackage\\Facades\\MyPackage" } } }, "minimum-stability": "stable" }

Package Discovery

La section extra.laravel permet l'auto-discovery : Laravel enregistre automatiquement votre Service Provider à l'installation, sans modification de config/app.php.

Le Service Provider

Le Service Provider est le cœur de votre package. Il enregistre tout : bindings, config, routes, vues, migrations.

                
src/MyPackageServiceProvider.php
namespace Vendor\MyPackage; use Illuminate\Support\ServiceProvider; class MyPackageServiceProvider extends ServiceProvider { public function register(): void { // Merge config avec celle de l'app $this->mergeConfigFrom( __DIR__.'/../config/my-package.php', 'my-package' ); // Bind le service principal $this->app->singleton(MyPackageService::class, function ($app) { return new MyPackageService( config('my-package.api_key') ); }); } public function boot(): void { // Publier la config $this->publishes([ __DIR__.'/../config/my-package.php' => config_path('my-package.php'), ], 'my-package-config'); // Charger les routes $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); // Charger les vues $this->loadViewsFrom(__DIR__.'/../resources/views', 'my-package'); // Publier les vues $this->publishes([ __DIR__.'/../resources/views' => resource_path('views/vendor/my-package'), ], 'my-package-views'); // Charger les migrations $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); // Ou publier les migrations $this->publishesMigrations([ __DIR__.'/../database/migrations' => database_path('migrations'), ], 'my-package-migrations'); } }

mergeConfigFrom vs publishes

Différence importante

mergeConfigFrom() dans register() : charge les valeurs par défaut.
publishes() dans boot() : permet à l'utilisateur de personnaliser.
Utilisez les deux pour que les configs non publiées fonctionnent quand même.

Fichier de configuration

                
config/my-package.php
return [ /* |-------------------------------------------------------------------------- | API Key |-------------------------------------------------------------------------- | | Your API key for the external service. | */ 'api_key' => env('MY_PACKAGE_API_KEY'), /* |-------------------------------------------------------------------------- | Cache Duration |-------------------------------------------------------------------------- | | How long to cache API responses (in minutes). | */ 'cache_duration' => env('MY_PACKAGE_CACHE_DURATION', 60), /* |-------------------------------------------------------------------------- | Route Prefix |-------------------------------------------------------------------------- | | The prefix for all package routes. | */ 'route_prefix' => 'my-package', ];

Accès depuis l'application :

                
Utilisation
$apiKey = config('my-package.api_key'); $duration = config('my-package.cache_duration');

Créer une Facade

Les facades offrent une syntaxe pratique pour accéder à votre service.

                
src/Facades/MyPackage.php
namespace Vendor\MyPackage\Facades; use Illuminate\Support\Facades\Facade; /** * @method static array getData() * @method static void sendNotification(string $message) * * @see \Vendor\MyPackage\Services\MyPackageService */ class MyPackage extends Facade { protected static function getFacadeAccessor(): string { return \Vendor\MyPackage\Services\MyPackageService::class; } }
                
Utilisation dans l'app
use Vendor\MyPackage\Facades\MyPackage; // Via la facade $data = MyPackage::getData(); // Ou via injection public function __construct( private MyPackageService $service, ) {}

Tests du package

Utilisez orchestra/testbench pour tester votre package dans un environnement Laravel.

                
tests/TestCase.php
namespace Vendor\MyPackage\Tests; use Orchestra\Testbench\TestCase as BaseTestCase; use Vendor\MyPackage\MyPackageServiceProvider; abstract class TestCase extends BaseTestCase { protected function getPackageProviders($app): array { return [ MyPackageServiceProvider::class, ]; } protected function getPackageAliases($app): array { return [ 'MyPackage' => \Vendor\MyPackage\Facades\MyPackage::class, ]; } protected function defineEnvironment($app): void { $app['config']->set('my-package.api_key', 'test-key'); } }
                
tests/Feature/MyPackageTest.php
namespace Vendor\MyPackage\Tests\Feature; use Vendor\MyPackage\Tests\TestCase; use Vendor\MyPackage\Facades\MyPackage; class MyPackageTest extends TestCase { public function test_can_get_data(): void { $result = MyPackage::getData(); $this->assertIsArray($result); } public function test_config_is_loaded(): void { $this->assertEquals( 'test-key', config('my-package.api_key') ); } }
                
phpunit.xml
<?xml version="1.0" encoding="UTF-8"?> <phpunit colors="true" bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Package Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>

Développement local

Pendant le développement, liez votre package à un projet Laravel pour le tester en conditions réelles.

                
composer.json de l'app Laravel
{ "repositories": [ { "type": "path", "url": "../my-package", "options": { "symlink": true } } ], "require": { "vendor/my-package": "*" } }
                
Terminal
composer update vendor/my-package

Symlink

Avec symlink: true, les modifications dans le package sont immédiatement visibles dans l'app. Pas besoin de re-composer à chaque changement.

Publication sur Packagist

1. Créer le repo GitHub

                
Terminal
git init git add . git commit -m "Initial commit" git remote add origin git@github.com:vendor/my-package.git git push -u origin main

2. Créer un tag de version

                
Terminal
git tag v1.0.0 git push --tags

3. Soumettre sur Packagist

Ensuite, votre package est installable via :

                
Terminal
composer require vendor/my-package

Bonnes pratiques

Checklist package Laravel

GitHub Actions pour les tests

                
.github/workflows/tests.yml
name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: php: [8.2, 8.3] laravel: [11.*, 12.*] steps: - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - name: Install dependencies run: | composer require "illuminate/support:${{ matrix.laravel }}" --no-update composer update --prefer-dist --no-progress - name: Run tests run: vendor/bin/phpunit

Besoin d'aide sur votre projet ?

Je peux vous accompagner dans le développement ou l'optimisation de votre application.

Me contacter