在Laravel应用中启用/禁用功能


10

我正在构建一个Laravel应用程序,它具有许多不同的功能。我希望能够根据特定域的要求启用或禁用它们。当前,我在配置中有一系列标志,例如:

'is_feature_1_enabled' => true,
'is_feature_2_enabled' => false,

... 等等。

然后,在我的控制器和视图中,检查那些配置值,以查看是否应该显示某些内容,允许某些操作等。我的应用到处都开始受到这类检查的污染。

有没有在Laravel应用中管理功能的最佳实践方法?


禁用路由而不是控制器?或使用中间件
Berto99

功能!==页面。
StackOverflowNewbie


可能实际上是一种解决方案。想要将您的评论变成答案吗?
StackOverflowNewbie

嗯,没问题,不是答案,重要的是它可能会对您
有所

Answers:


4

这在技术上称为功能标记-https: //martinfowler.com/articles/feature-toggles.html

取决于您的要求,配置/数据库中的标志,部署等...

但这基本上是如果在代码中并且不能清除。

Laravel软件包:

https://github.com/alfred-nutile-inc/laravel-feature-flag

https://github.com/francescomalatesta/laravel-feature

一些服务:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

参阅https://marketingplatform.google.com/about/optimize/了解前端。


1
您可以在此处找到带有功能标记的laravel示例项目:github.com/configcat/php-sdk/tree/master/samples/laravel
Peter

7

当我尝试实施多个酒店提供商时遇到了相同的问题。

我所做的是使用服务容器。

首先,您将为每个域创建具有其功能的类:

  • 像Doman1.php,Domain2.php
  • 然后在其中每一个中添加逻辑。

那么您将在应用服务提供商中使用绑定将要使用的类绑定到域。

$this->app->bind('Domain1',function (){
       return new Domain1();
    });
    $this->app->bind('Domain2',function (){
        return new Domain2();
    });

请注意,您可以使用包含所有领域附带的功能的通用类,然后在您的类中使用该通用类

最后,您可以在控制器中检查您的域,然后使用要使用的类

    app(url('/'))->methodName();

0

看起来您正在基于配置值对事物进行硬编码,以启用或禁用某些功能。我建议您根据命名的路由而不是配置值来控制事物。

  1. 将所有路线整体或按功能分组。
  2. 定义所有路线的名称
  3. 通过路由名称控制启用/禁用活动并记录在数据库中
  4. 使用Laravel中间件,通过从请求对象获取当前路由名称并将其与数据库匹配,来检查是否启用或禁用了特定功能。

因此,您不会在相同的条件下到处重复代码,并使代码膨胀。.以下示例代码向您展示了如何检索所有路由,并且可以匹配路由组名称以进行进一步处理以匹配您的情况。

Route::get('routes', function() {
$routeCollection = Route::getRoutes();

echo "<table >";
    echo "<tr>";
        echo "<td width='10%'><h4>HTTP Method</h4></td>";
        echo "<td width='10%'><h4>Route</h4></td>";
        echo "<td width='80%'><h4>Corresponding Action</h4></td>";
    echo "</tr>";
    foreach ($routeCollection as $value) {
        echo "<tr>";
            echo "<td>" . $value->getMethods()[0] . "</td>";
            echo "<td>" . $value->getPath() . "</td>";
            echo "<td>" . $value->getName() . "</td>";
        echo "</tr>";
    }
echo "</table>";
});

这是一个示例中间件处理程序,您可以在其中通过与已存储在数据库中的内容进行匹配来检查特定功能是否处于活动状态。

public function handle($request, Closure $next)
    {
        if(Helper::isDisabled($request->route()->getName())){
             abort(403,'This feature is disabled.');
        }
        return $next($request);
    }

1
假设这些功能等同于网站上的页面,对吗?事实并非如此。功能可以是页面内的某些代码段(例如,侧边栏中将显示Google Map),也可以是某种功能(例如,用户可以导出一些数据)。
StackOverflowNewbie

您是正确的,但是您是说某些块显示在不同的页面上吗?您要显示什么约束?明智的特定页面或在您显示的所有页面上
Akram Wahid

功能可以是整个页面,也可以是页面的一部分,也可以是某些功能。
StackOverflowNewbie

0

假定仅HTTP请求才需要这些功能。

我将Features使用所有默认标志创建一个默认基类:

Class Features {
    // Defaults
    protected $feature1_enabled = true;
    protected $feature2_enabled = true;

    public function isFeature1Enabled(): bool
    {
        return $this->feature1_enabled;
    }

    public function isFeature2Enabled(): bool
    {
        return $this->feature2_enabled;
    }
}

然后,我将为每个域扩展该类,并设置该域所需的替代:

Class Domain1 extends Features {
    // override
    protected $feature1_enabled = false;
}

然后创建一个中间件以将Features Class绑定到容器:

class AssignFeatureByDomain
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        switch ($_SERVER['HTTP_HOST']) {
            case 'domain1':
                app()->bind(Features::class, Domain1::class);
                break;
            default:
                abort(401, 'Domain rejected');
        }

        return $next($request);
    }
}

别忘了将此中间件附加到您的路由:一个组或每个路由。

之后,您可以在控制器中TypeHint您的Feature类:

public function index(Request $request, Features $features)
{
    if ($features->isFeature1Enabled()) {
        //
    }
}

0

Laravel很好用,您甚至可以将功能存储在db中,并在域之间创建关系。

我建议您使用Gates and Policies,这将使您更好地控制控制器和刀片服务器模板。这意味着您可以从数据库中注册门或对其进行硬编码。

例如,如果您在系统中具有带有按钮的导出产品功能,并且想要将该功能提供给某些用户,则可以向业务逻辑注册门。

//Only admins can export products
Gate::define('export-products', function ($user) {
    return $user->isAdmin;
});

然后,您可以在控制器中执行以下操作

<?php

namespace App\Http\Controllers;

use App\Product;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class ProductsController extends Controller
{
    /**
     * Export products
     *
     * @param  Request  $request
     * @param  Post  $post
     * @return Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function export(Request $request)
    {
        $this->authorize('export-products');

        // The current user can export products
    }
}

这是您的刀片模板的示例:

@can('export-products', $post)
    <!-- The Current User Can export products -->
@endcan

@cannot('export-products')
    <!-- The Current User Can't export products -->
@endcannot

有关更多信息,请访问https://laravel.com/docs/5.8/authorization


0

有趣的情况,你在这里。查看Feature包含通常需要的一些方法的接口或抽象类可能会很有趣。

interface Feature
{
    public function isEnabled(): bool;

    public function render(): string;

    // Not entirely sure if this would be a best practice but the idea is to be
    // able to call $feature->execute(...) on any feature.
    public function execute(...);

    ...
}

您甚至可以将它们定义为ExecutableFeatureRenderableFeature

进一步讲,某种工厂课程可以使生活更轻松。

// Call class factory.
Feature::make('some_feature')->render();
...->isEnabled();

// Make helper method.
feature('some_feature')->render();

// Make a blade directives.
@feature('some_feature')
@featureEnabled('some_feature')

0

在我的案例中,我要做的是在数据库上创建一个新表Domains,例如可以将其称为。

添加所有特定功能,这些功能可能会在某些域中显示,但不会在其余域中显示,作为该表的列作为布尔值的位。就我而言allow_multiple_bookingsuse_company_card...等等。

然后,考虑创建一个类Domain及其各自的存储库,并在您的代码中询问这些值,尝试将尽可能多的逻辑推入您的域(您的模型,应用程序服务等)中。

例如,对于要RequestBooking预订的域是否只能请求一个或多个,我不会对控制器方法进行检查。

相反,我在RequestBookingValidatorService可以检查预订日期时间是否已过去,用户已启用信用卡的...上执行此操作,或者允许此操作来自的域请求多个预订(然后,如果已经有任何)。

由于已将此决定推到了应用程序服务中,因此增加了可读性。另外,我发现,每当我需要一个新功能时,都可以使用Laravel(或Symfony)迁移在表上添加该功能,甚至可以使用我在编码时提交的值来更新其行(您的域)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.