codinglabsau/laravel-feature-flags

为 Laravel 提供动态功能开关。

v1.4.1 2024-03-11 04:07 UTC

This package is auto-updated.

Last update: 2024-09-11 05:08:31 UTC


README

Latest Version on Packagist Test Total Downloads

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 事件。

高级使用

动态功能

可以在 AppServiceProviderboot() 方法中定义动态处理器

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)。有关更多信息,请参阅 许可文件