[laravel] 라 라벨 앱에서 기능 활성화 / 비활성화

다양한 기능을 갖춘 Laravel 앱을 만들고 있습니다. 특정 도메인 요구 사항에 따라 활성화 또는 비활성화 할 수 있기를 원합니다. 현재 구성에 다음과 같은 일련의 플래그가 있습니다.

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

… 등등.

그런 다음 컨트롤러 및보기에서 해당 구성 값을 확인하여 무언가를 표시 해야하는지 여부, 특정 작업 등을 허용 해야하는지 여부를 확인합니다. 앱이 모든 곳에서 이러한 종류의 검사로 오염되기 시작합니다.

Laravel 앱에서 기능을 관리하는 가장 좋은 방법이 있습니까?



답변

이것은 기술적으로 기능 플래그라고합니다-https: //martinfowler.com/articles/feature-toggles.html

요구 사항, 구성 / 데이터베이스의 플래그, 롤아웃 등에 따라 다릅니다.

그러나 기본적으로 코드에 있으면 깨끗하지 않습니다.

라 라벨 패키지 :

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/참조하십시오 .


답변

여러 호텔 제공 업체를 구현하려고 할 때도 같은 문제가 발생했습니다.

내가 한 일은 서비스 컨테이너를 사용하는 것이 었습니다.

먼저 자신의 기능으로 각 도메인에 대한 클래스를 만듭니다.

  • Doman1.php와 같은, Domain2.php
  • 그런 다음 각각의 내부에 논리를 추가합니다.

그런 다음 앱 서비스 공급자에서 바인딩을 사용하여 도메인을 사용할 클래스에 바인딩합니다.

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

참고 당신이 다음 수업이 일반적으로 클래스를 사용하는 모든 도메인을 일반 기능을 보유하고 수업 진행을 사용할 수 있습니다

마지막으로 컨트롤러에서 도메인을 확인한 다음 사용할 클래스를 사용할 수 있습니다

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


답변

특정 기능을 활성화 또는 비활성화하기 위해 구성 값을 기반으로 하드 코딩하는 것처럼 보입니다. 구성 값이 아닌 명명 된 경로를 기반으로 항목을 제어하는 ​​것이 좋습니다.

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


답변

이러한 기능이 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()) {
        //
    }
}


답변

Laravel은 이것으로 훌륭합니다. 심지어 기능을 db에 저장하고 도메인 간의 관계를 만들 수도 있습니다.

컨트롤러 및 블레이드 템플릿을보다 효율적으로 제어 할 수있는 게이트 및 정책을 사용하는 것이 좋습니다. 즉, 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 에서 확인할 수 있습니다.


답변

흥미로운 사건이 있습니다. 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')


답변

필자의 경우 데이터베이스에서 새 테이블을 만드는 것이 었습니다 Domains. 예를 들어 호출 할 수 있습니다.

부울 값의 비트로 해당 테이블의 열로 일부 도메인에는 표시 될 수 있지만 나머지에는 표시되지 않는 모든 특정 기능을 추가하십시오. 마찬가지로, 내 경우에는, allow_multiple_bookings, use_company_card… 뭐든간에.

그런 다음 클래스 Domain와 해당 저장소를 작성하고 코드에서 이러한 값을 요청하여 가능한 한 해당 논리를 도메인 (모델, 애플리케이션 서비스 등)으로 푸시하십시오.

예를 들어, RequestBooking예약을 요청하는 도메인이 하나 이상을 요청할 수 있는지 컨트롤러 방법을 확인하지 않습니다 .

대신 RequestBookingValidatorService예약 날짜 시간이 지 났는지, 사용자가 신용 카드를 사용할 수 있는지 확인하거나 …이 작업이 시작된 도메인에서 둘 이상의 예약을 요청할 수 있습니다 (이미 이미 예약 된 경우) 어떤).

이 결정을 응용 프로그램 서비스에 적용하면 가독성이 향상됩니다. 또한 새로운 기능이 필요할 때마다 Laravel (또는 Symfony) 마이그레이션을 사용하여 테이블에 해당 기능을 추가 할 수 있으며 코딩 한 동일한 커밋에서 원하는 값으로 행 (도메인)을 업데이트 할 수도 있습니다.