Włączanie / wyłączanie funkcji w aplikacji Laravel


10

Tworzę aplikację Laravel, która ma wiele różnych funkcji. Chcę móc je włączać i wyłączać w zależności od wymagań konkretnej domeny. Obecnie mam w swojej konfiguracji szereg flag, takich jak:

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

... i tak dalej.

Następnie w moich kontrolerach i widokach sprawdzam te wartości konfiguracyjne, aby zobaczyć, czy powinienem coś wyświetlać, zezwalając na określone działania itp. Moja aplikacja zaczyna być zanieczyszczana przez tego rodzaju kontrole wszędzie.

Czy istnieje metoda najlepszych praktyk zarządzania funkcjami w aplikacji Laravel?


wyłączyć trasy zamiast kontrolera? lub może używasz oprogramowania pośredniego
Berto99

Funkcja! == strona.
StackOverflowNewbie


To może być rozwiązanie. Chcesz zamienić swój komentarz w odpowiedź?
StackOverflowNewbie

mh, nie ma problemu, nie jest odpowiedzią, ważne jest to, że może ci pomóc
Berto99

Odpowiedzi:


4

Jest to technicznie nazywane flagami funkcji - https://martinfowler.com/articles/feature-toggles.html

zależy od twoich wymagań, flag w config / bazie danych, rollout itp.

Ale to w zasadzie jeśli jest w kodzie i nie może być czyste.

Pakiety Laravel:

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

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

Niektóre usługi:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

Zobacz także https://marketingplatform.google.com/about/optimize/ for frontend.


1
Przykładowy projekt laravel z funkcją oznaczania można znaleźć tutaj: github.com/configcat/php-sdk/tree/master/samples/laravel
Peter

7

Ten sam problem napotkałem, gdy próbowałem wdrożyć wielu dostawców hoteli.

Użyłem kontenera usług.

najpierw stworzysz klasę dla każdej domeny Dzięki jego funkcjom:

  • jak Doman1.php, Domain2.php
  • następnie do każdego z nich dodasz swoją logikę.

następnie użyjesz wiązania u dostawcy usług aplikacji, aby powiązać domenę z klasą do użycia.

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

Pamiętaj, że możesz użyć klasy ogólnej, która zawiera funkcje ze wszystkimi domenami, a następnie użyć tej klasy ogólnej w swoich klasach

Wreszcie w kontrolerze możesz sprawdzić swoją domenę, a następnie użyć klasy, z której będziesz korzystać

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

0

Wygląda na to, że kodujesz rzeczy na podstawie wartości konfiguracji, aby włączyć lub wyłączyć niektóre funkcje. Zalecam kontrolowanie rzeczy na podstawie nazwanych tras, a nie wartości konfiguracji.

  1. Zgrupuj całą trasę jako całość lub według funkcji.
  2. Zdefiniuj nazwę dla wszystkich tras
  3. Kontroluj aktywność włączania / wyłączania według nazwy trasy i zapisu w bazie danych
  4. Użyj oprogramowania pośredniego Laravel, aby sprawdzić, czy dana funkcja jest włączona, czy wyłączona, pobierając bieżącą nazwę trasy z obiektu żądania i dopasowując ją do bazy danych.

więc nie będziesz mieć takich samych warunków powtarzania się w każdym miejscu i wypychania kodu. Oto przykładowy kod pokazujący, jak odzyskać wszystkie trasy, i możesz dopasować nazwę grupy tras, aby dalej przetwarzać w celu dopasowania do twojej sytuacji.

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

a tutaj jest przykładowy moduł obsługi oprogramowania pośredniego, w którym możesz sprawdzić, czy dana funkcja jest aktywna, dopasowując ją do tego, co już zapisałeś w bazie danych.

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

1
Zakłada się, że funkcje odpowiadają stronom w witrynie, prawda? Nie o to chodzi. Cechą może być fragment kodu na stronie (np. Mapa Google wyświetli się na pasku bocznym) lub jakiś rodzaj funkcji (np. Użytkownicy mogą eksportować niektóre dane).
StackOverflowNewbie

masz rację, ale czy masz na myśli niektóre bloki wyświetlane na różnych stronach? jakie są ograniczenia, aby to wyświetlić? konkretna strona mądra lub na wszystkich stronach, które ją wyświetlasz
Akram Wahid

Funkcje mogą obejmować całą stronę lub tylko część strony lub tylko niektóre funkcje.
StackOverflowNewbie

0

Zakładając, że te funkcje są potrzebne tylko w przypadku żądań HTTP.

Utworzyłbym domyślną Featuresklasę bazową ze wszystkimi domyślnymi flagami:

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

Następnie rozszerzałbym tę klasę dla każdej domeny i ustawiłem przesłonięcia potrzebne dla tej domeny:

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

Następnie utwórz oprogramowanie pośrednie, aby powiązać klasę funkcji z kontenerem:

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

Nie zapomnij dołączyć tego oprogramowania pośredniego do swoich tras: do grupy lub dla każdej trasy.

Następnie możesz wpisać Wskazówka do swojej klasy Funkcje w kontrolerach:

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

0

Laravel świetnie sobie z tym radzi, możesz nawet przechowywać swoje funkcje w db i tworzyć relacje między domeną.

Polecam używać bramek i zasad, które zapewnią lepszą kontrolę nad kontrolerami i szablonami bloków. Oznacza to, że rejestrujesz bramy z bazy danych lub kodujesz je na stałe.

Na przykład, jeśli masz funkcję eksportu produktów z przyciskiem w systemie i chcesz udostępnić tę funkcję niektórym użytkownikom, możesz zarejestrować bramki za pomocą logiki biznesowej.

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

Następnie możesz wykonać następujące czynności w kontrolerach

<?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
    }
}

Oto przykład szablonów ostrzy:

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

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

więcej informacji dostępnych na https://laravel.com/docs/5.8/authorization


0

Ciekawa sprawa, którą tu masz. Interesujące może być spojrzenie na Featureinterfejs lub klasę abstrakcyjną, która zawiera kilka metod, których zwykle potrzebujesz.

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

    ...
}

Możesz nawet podzielić je na ExecutableFeaturei RenderableFeature.

Co więcej, można by stworzyć jakąś klasę fabryczną, aby ułatwić życie.

// 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

W moim przypadku utworzyłem nową tabelę w bazie danych, którą można nazwać Domainsna przykład.

Dodaj wszystkie specyficzne funkcje, które mogą być wyświetlane w niektórych domenach, ale nie w pozostałych, jako kolumny tej tabeli jako bit dla wartości boolowskich. Podobnie jak w moim przypadku allow_multiple_bookings, use_company_card... cokolwiek.

Następnie zastanów się nad utworzeniem klasy Domaini odpowiedniego repozytorium i po prostu zapytaj o te wartości w kodzie, próbując wcisnąć jak najwięcej logiki do swojej domeny (model, usługi aplikacji itp.).

Na przykład nie sprawdziłbym metody kontrolera, RequestBookingczy domena, która żąda rezerwacji, może zażądać tylko jednego lub więcej.

Zamiast tego robię to na urządzeniu, RequestBookingValidatorServicektóre może sprawdzić, czy data rezerwacji minęła, użytkownik ma włączoną kartę kredytową, ... lub Domena, z której pochodzi ta akcja, może poprosić o więcej niż jedną rezerwację (a następnie, jeśli już ma każdy).

Dodaje to wygodę czytelności, ponieważ przekazałeś tę decyzję swoim usługom aplikacyjnym. Ponadto stwierdzam, że ilekroć potrzebuję nowej funkcji, mogę użyć migracji Laravel (lub Symfony), aby dodać tę funkcję do tabeli, a nawet mogę zaktualizować jej wiersze (twoje domeny) o wartości, które chcę dla tego samego zatwierdzenia, które kodowałem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.