codinglabsau / laravel-feature-flags
为 Laravel 提供动态功能开关。
Requires
- php: ^8.1
- illuminate/contracts: ^10.0|^11.0
- spatie/laravel-package-tools: ^1.14.1
Requires (Dev)
- codinglabsau/php-styles: dev-main
- orchestra/testbench: ^8.0.1
- pestphp/pest: ^2.0
- pestphp/pest-plugin-laravel: ^2.0
README
Laravel Feature Flags 允许立即、零部署切换应用程序功能。
每个功能开关的状态可以在应用程序代码的任何位置进行检查(包括通过 @feature('name')
blade 指令),以确定您设置的条件是否已满足以启用该功能。
每个功能可以处于以下三种状态之一
- 开启:对所有人启用
- 关闭:对所有人禁用
- 动态:根据特定于功能的闭包进行评估(带有回退选项)
安装
使用 Composer 安装
composer require codinglabsau/laravel-feature-flags
数据库迁移
php artisan vendor:publish --tag="feature-flags-migrations"
php artisan migrate
发布配置
php artisan vendor:publish --tag="feature-flags-config"
设置您的缓存存储
此包缓存功能状态以减少冗余数据库查询。每当功能状态更改时,缓存将过期。
默认情况下,此包将使用您应用程序中配置的默认缓存。
如果您想切换到不同的缓存驱动程序,请更新您的 .env
FEATURES_CACHE_STORE=file
使用方法
在数据库中创建一个新的功能并设置初始状态
use Codinglabs\FeatureFlags\Models\Feature; use Codinglabs\FeatureFlags\Enums\FeatureState; Feature::create([ 'name' => 'search-v2', 'state' => FeatureState::on() ]);
建议在新的部署之前或在部署后尽快对功能进行播种。
检查功能是否已启用
Blade 视图
在任何视图文件中使用 @feature
blade 指令。
@feature('search-v2') // new search goes here @else // legacy search here @endfeature
在您的代码中
使用 FeatureFlag
门面在您的应用程序代码中方便地检查功能的状态。
use Codinglabs\FeatureFlags\Facades\FeatureFlag; if (FeatureFlag::isOn('search-v2')) { // new feature code } else { // old code }
中间件
将 feature
注册为 HTTP Kernel 中的路由中间件以保护路由。如果功能不解析为开启状态,将返回 404 响应。
// app/Http/Kernel.php protected $routeMiddleware = [ // ... 'feature' => \Codinglabs\FeatureFlags\Middleware\VerifyFeatureIsOn::class, ]; // routes/web.php Route::get('search-v2', \App\Http\Controllers\SearchV2Controller::class)->middleware('feature:search-v2');
检查功能是否已禁用
Blade 视图
@unlessfeature('search-v2') // no new features for you @endfeature
在您的代码中
use Codinglabs\FeatureFlags\Facades\FeatureFlag; if (FeatureFlag::isOff('search-v2')) { // no new features for you }
获取底层当前状态
如果您想了解底层的 FeatureState
值
use Codinglabs\FeatureFlags\Facades\FeatureFlag; // value from Codinglabs\FeatureFlags\Enums\FeatureState $featureState = FeatureFlag::getState('search-v2');
更新功能状态
要更改功能的状态,您可以调用以下方法
use Codinglabs\FeatureFlags\Facades\FeatureFlag; FeatureFlag::turnOn('search-v2'); FeatureFlag::turnOff('search-v2'); FeatureFlag::makeDynamic('search-v2');
或者您可以直接通过传递功能状态枚举来设置状态
FeatureFlag::updateFeatureState('search-v2', FeatureState::on())
建议您只使用上述方法更新功能的状态,因为它将负责刷新缓存和分发功能更新事件
\Codinglabs\FeatureFlags\Events\FeatureUpdatedEvent::class
如果您在功能状态更新时有任何下游影响,例如在动态处理器中引用任何缓存的项,则应监听 FeatureUpdatedEvent
事件。
高级使用
动态功能
可以在 AppServiceProvider
的 boot()
方法中定义动态处理器
use Codinglabs\FeatureFlags\Facades\FeatureFlag; FeatureFlag::registerDynamicHandler('search-v2', function ($feature, $request) { return $request->user() && $request->user()->hasRole('Tester'); });
动态处理器仅在功能处于 dynamic
状态时才会被调用。这将允许您定义围绕是否启用该功能的自定义规则,如上面的示例所示,其中用户只有拥有测试者角色才能访问该功能。
每个处理器都提供了功能名称和当前请求作为参数,并必须返回一个布尔值。
动态功能的默认处理器
您还可以定义一个默认处理器,它将是未为它们定义显式处理器的功能的全局处理器
FeatureFlag::registerDefaultDynamicHandler(function ($feature, $request) { return $request->user() && $request->user()->hasRole('Tester'); });
使用 registerDynamicHandler()
定义的显式处理程序将优先于默认处理程序。如果没有定义默认处理程序或显式处理程序,则该功能默认将解析为 off
。
处理缺失功能
功能必须在数据库中存在,否则将抛出 MissingFeatureException
异常。可以通过显式处理功能不存在的情况来关闭此行为。
FeatureFlag::handleMissingFeaturesWith(function ($feature) { // log or report this somewhere... })
如果已定义缺失功能的处理程序,则不会抛出异常,并且功能将解析为 off
。
使用您自己的模型
要使用您自己的模型,更新配置并替换现有的引用为您的模型。
// app/config/feature-flags.php 'feature_model' => \App\Models\Feature::class,
请确保还使用 FeatureStateCast
将状态列强制转换为功能状态枚举。
// app/Models/Feature.php use Codinglabs\FeatureFlags\Casts\FeatureStateCast; protected $casts = [ 'state' => FeatureStateCast::class ];
与 UI 共享功能(Inertiajs 示例)
// app/Middleware/HandleInertiaRequest.php use Codinglabs\FeatureFlags\FeatureFlags; use Codinglabs\FeatureFlags\Models\Feature; Inertia::share([ 'features' => function () { return Feature::all() ->filter(fn ($feature) => FeatureFlags::isOn($feature['name'])) ->pluck('name'); } ]);
// app.js Vue.mixin({ methods: { hasFeature: function(feature) { return this.$page.features.includes(feature) } } })
<!-- SomeComponent.vue --> <div v-if="hasFeature('search-v2')">Some cool new feature</div>
测试
composer test
安全漏洞
请查看我们如何报告安全漏洞的安全策略:我们的安全策略。
鸣谢
许可协议
MIT 许可协议(MIT)。有关更多信息,请参阅 许可文件。