Laravelアプリの機能の有効化/無効化


10

Laravelアプリを作成しています。このアプリには、さまざまな機能があります。特定のドメインの要件に応じて、それらを有効または無効にできるようにしたいと思います。現在、私の設定には次のような一連のフラグがあります:

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

... 等々。

次に、コントローラーとビューで、これらの構成値をチェックして、何かを表示したり、特定のアクションを許可したりする必要があるかどうかを確認します。私のアプリは、あらゆる種類のチェックで汚染され始めています。

Laravelアプリの機能を管理するベストプラクティスの方法はありますか?


コントローラの代わりにルートを無効にしますか?またはミドルウェアの使用
Berto99

機能!==ページ。
StackOverflowNewbie


実際に解決策になるかもしれません。コメントを回答にしたいですか?
StackOverflowNewbie

mh問題ない、答えではない、重要なのはそれがあなたを助けるかもしれないということです
Berto99

回答:


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
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マップがサイドバーに表示されるなど)である場合と、ある種の機能(ユーザーが一部のデータをエクスポートできる場合など)である場合があります。
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;
}

次に、ミドルウェアを作成して、機能クラスをコンテナにバインドします。

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);
    }
}

このミドルウェアをルート、つまりグループまたは各ルートにアタッチすることを忘れないでください。

この後、コントローラーのFeaturesクラスをTypeHintできます。

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

0

Laravelはこれに優れており、機能をdbに保存して、ドメイン間の関係を作成することもできます。

コントローラーとブレードテンプレートをより適切に制御できるゲートとポリシーを使用することをお勧めします。これは、データベースからゲートを登録するか、ゲートをハードコードすることを意味します。

たとえば、システムにボタンがある製品のエクスポート機能があり、その機能を一部のユーザーが利用できるようにしたい場合は、ゲートをビジネスロジックに登録できます。

//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(...);

    ...
}

これらをExecutableFeatureとに分割することもできRenderableFeatureます。

さらに、ある種のファクトリークラスを作成して、生活を楽にすることができます。

// 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、予約をリクエストしているドメインが1つ以上しかリクエストできない場合、コントローラーメソッドのチェックは行いません。

代わりにRequestBookingValidatorService、予約日時が経過したかどうか、ユーザーが有効なクレジットカードを持っているかどうかを確認できる、またはこのアクションの発生元のドメインが複数の予約をリクエストできるようにしています(そしてすでに予約されている場合)どれか)。

この決定をアプリケーションサービスにプッシュしたため、これにより読みやすさが向上します。また、新しい機能が必要なときはいつでも、Laravel(またはSymfony)の移行を使用してテーブルにその機能を追加でき、コード化した同じコミットで必要な値で行(ドメイン)を更新することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.