Laravel 13 包开发指南
包是 Laravel 扩展功能的重要方式。本文将介绍如何开发高质量的 Laravel 13 包。
创建包
包结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| my-package/ ├── src/ │ ├── MyPackage.php │ ├── MyPackageServiceProvider.php │ ├── Facades/ │ │ └── MyPackage.php │ ├── Commands/ │ │ └── MyCommand.php │ └── Contracts/ │ └── MyInterface.php ├── config/ │ └── my-package.php ├── database/ │ ├── migrations/ │ └── seeders/ ├── resources/ │ └── views/ ├── routes/ │ └── routes.php ├── tests/ │ └── MyPackageTest.php ├── composer.json └── README.md
|
composer.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| { "name": "vendor/my-package", "description": "A Laravel package", "keywords": ["laravel", "package"], "license": "MIT", "type": "library", "authors": [ { "name": "Your Name", "email": "your@email.com" } ], "require": { "php": "^8.2", "illuminate/support": "^11.0" }, "require-dev": { "phpunit/phpunit": "^11.0", "orchestra/testbench": "^9.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": "dev", "prefer-stable": true }
|
服务提供者
创建服务提供者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?php
namespace Vendor\MyPackage;
use Illuminate\Support\ServiceProvider; use Vendor\MyPackage\Contracts\MyInterface; use Vendor\MyPackage\Services\MyService;
class MyPackageServiceProvider extends ServiceProvider { public function register(): void { $this->mergeConfigFrom( __DIR__.'/../config/my-package.php', 'my-package' );
$this->app->singleton(MyInterface::class, MyService::class);
$this->app->singleton('my-package', function ($app) { return new MyPackage($app->make(MyInterface::class)); }); }
public function boot(): void { $this->publishes([ __DIR__.'/../config/my-package.php' => config_path('my-package.php'), ], 'config');
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
$this->loadViewsFrom(__DIR__.'/../resources/views', 'my-package');
$this->publishes([ __DIR__.'/../resources/views' => resource_path('views/vendor/my-package'), ], 'views');
$this->loadRoutesFrom(__DIR__.'/../routes/routes.php');
if ($this->app->runningInConsole()) { $this->commands([ Commands\MyCommand::class, ]); } } }
|
配置文件
创建配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
return [ 'default' => env('MY_PACKAGE_DEFAULT', 'value'),
'options' => [ 'enabled' => true, 'timeout' => 30, ],
'connections' => [ 'default' => [ 'driver' => 'mysql', 'host' => env('MY_PACKAGE_DB_HOST', 'localhost'), ], ], ];
|
门面
创建门面
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php
namespace Vendor\MyPackage\Facades;
use Illuminate\Support\Facades\Facade;
class MyPackage extends Facade { protected static function getFacadeAccessor(): string { return 'my-package'; } }
|
命令
创建 Artisan 命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
namespace Vendor\MyPackage\Commands;
use Illuminate\Console\Command;
class MyCommand extends Command { protected $signature = 'my-package:do-something {--force : Force the operation}';
protected $description = 'Do something with my package';
public function handle(): int { $force = $this->option('force');
$this->info('Doing something...');
if ($force) { $this->warn('Force mode enabled'); }
return self::SUCCESS; } }
|
迁移
创建迁移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
return new class extends Migration { public function up(): void { Schema::create('my_package_table', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); }
public function down(): void { Schema::dropIfExists('my_package_table'); } };
|
路由
定义路由
1 2 3 4 5 6 7 8 9 10 11 12
| <?php
use Illuminate\Support\Facades\Route; use Vendor\MyPackage\Http\Controllers\MyController;
Route::middleware(['web'])->group(function () { Route::get('/my-package', [MyController::class, 'index'])->name('my-package.index'); });
Route::middleware(['api'])->prefix('api')->group(function () { Route::get('/my-package', [MyController::class, 'apiIndex']); });
|
控制器
创建控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
namespace Vendor\MyPackage\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Routing\Controller;
class MyController extends Controller { public function index() { return view('my-package::index'); }
public function store(Request $request) { return response()->json(['success' => true]); } }
|
视图
创建视图
1 2 3 4 5 6 7 8 9
| <!-- resources/views/index.blade.php --> @extends('layouts.app')
@section('content') <div class="my-package"> <h1>My Package</h1> <p>Welcome to my package!</p> </div> @endsection
|
中间件
创建中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
namespace Vendor\MyPackage\Http\Middleware;
use Closure; use Illuminate\Http\Request;
class MyMiddleware { public function handle(Request $request, Closure $next) { if (!$request->user()) { return response()->json(['error' => 'Unauthorized'], 401); }
return $next($request); } }
|
测试
包测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php
namespace Vendor\MyPackage\Tests;
use PHPUnit\Framework\TestCase; use Vendor\MyPackage\MyPackage;
class MyPackageTest extends TestCase { protected function setUp(): void { parent::setUp(); }
public function test_it_can_do_something(): void { $package = new MyPackage();
$result = $package->doSomething();
$this->assertTrue($result); } }
|
集成测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?php
namespace Vendor\MyPackage\Tests;
use Orchestra\Testbench\TestCase; use Vendor\MyPackage\MyPackageServiceProvider;
class IntegrationTest extends TestCase { protected function getPackageProviders($app): array { return [ MyPackageServiceProvider::class, ]; }
protected function getEnvironmentSetUp($app): void { $app['config']->set('my-package.default', 'test'); }
public function test_config_is_loaded(): void { $this->assertEquals('test', config('my-package.default')); }
public function test_facade_works(): void { $result = \Vendor\MyPackage\Facades\MyPackage::doSomething();
$this->assertTrue($result); } }
|
发布资源
发布配置
1 2
| php artisan vendor:publish --tag=config php artisan vendor:publish --tag=my-package-config
|
发布视图
1 2
| php artisan vendor:publish --tag=views php artisan vendor:publish --tag=my-package-views
|
发布迁移
1 2
| php artisan vendor:publish --tag=migrations php artisan vendor:publish --tag=my-package-migrations
|
最佳实践
1. 使用接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| interface PaymentGatewayInterface { public function charge(float $amount): bool; }
class StripeGateway implements PaymentGatewayInterface { public function charge(float $amount): bool { return true; } }
$this->app->bind(PaymentGatewayInterface::class, StripeGateway::class);
|
2. 提供配置选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| return [ 'driver' => env('MY_PACKAGE_DRIVER', 'default'),
'drivers' => [ 'default' => [ 'class' => Drivers\DefaultDriver::class, ], 'custom' => [ 'class' => Drivers\CustomDriver::class, ], ], ];
$driver = config('my-package.driver'); $driverClass = config("my-package.drivers.{$driver}.class");
$this->app->bind(DriverInterface::class, $driverClass);
|
3. 提供文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # My Package
## Installation
composer require vendor/my-package
## Configuration
php artisan vendor:publish --tag=my-package-config
## Usage
use Vendor\MyPackage\Facades\MyPackage;
MyPackage::doSomething();
|
总结
Laravel 13 的包开发提供了丰富的扩展能力。通过合理使用服务提供者、门面、配置和命令,可以构建出功能完善、易于使用的 Laravel 包。记住使用接口实现依赖注入、提供灵活的配置选项、编写完善的测试,并提供清晰的文档。包开发是 Laravel 生态系统的重要组成部分,高质量的包可以帮助社区更好地发展。