Aktivieren / Deaktivieren von Funktionen in einer Laravel-App

10

Ich baue eine Laravel-App mit verschiedenen Funktionen. Ich möchte sie je nach Anforderung einer bestimmten Domain aktivieren oder deaktivieren können. Derzeit habe ich in meiner Konfiguration eine Reihe von Flags wie:

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

... und so weiter.

Dann überprüfe ich in meinen Controllern und Ansichten diese Konfigurationswerte, um festzustellen, ob ich etwas anzeigen soll, bestimmte Aktionen zulasse usw. Meine App wird überall mit solchen Überprüfungen verschmutzt.

Gibt es eine Best-Practice-Methode zum Verwalten von Funktionen in einer Laravel-App?

StackOverflowNewbie
quelle
Routen anstelle von Controller deaktivieren? oder mit Middleware vielleicht
Berto99
Feature! == Seite.
StackOverflowNewbie
Könnte tatsächlich eine Lösung sein. Möchten Sie aus Ihrem Kommentar eine Antwort machen?
StackOverflowNewbie
mh kein Problem, ist keine Antwort, das Wichtigste ist, dass es Ihnen
helfen

Antworten:

4

Dies wird technisch als Feature-Flags bezeichnet - https://martinfowler.com/articles/feature-toggles.html

hängt von Ihren Anforderungen, Flags in Konfiguration / Datenbank, Rollout usw. ab.

Aber es ist im Grunde genommen, wenn es im Code ist und nicht sauber sein kann.

Laravel-Pakete:

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

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

Einige Dienstleistungen:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

Das Frontend finden Sie auch unter https://marketingplatform.google.com/about/optimize/ .

Daniel Šádek
quelle
1
Ein Beispiel für ein Laravel-Projekt mit Feature-Flaggen finden Sie hier: github.com/configcat/php-sdk/tree/master/samples/laravel
Peter
7

Ich habe das gleiche Problem festgestellt, als ich versucht habe, mehrere Hotelanbieter zu implementieren.

Ich habe einen Servicecontainer verwendet.

Zuerst erstellen Sie eine Klasse für jede Domain. Mit seinen Funktionen:

  • wie Doman1.php, Domain2.php
  • dann werden Sie in jedem von denen Ihre Logik hinzufügen.

Dann verwenden Sie die Bindung in Ihrem App-Dienstanbieter, um die Domain mit der zu verwendenden Klasse zu binden.

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

Beachten Sie, dass Sie eine allgemeine Klasse verwenden können, die die Funktionen für alle Domänen enthält. Verwenden Sie dann diese allgemeine Klasse in Ihren Klassen

Schließlich können Sie in Ihrem Controller Ihre Domain überprüfen, um dann die Klasse zu verwenden, die Sie verwenden werden

    app(url('/'))->methodName();
M.abdelrhman
quelle
0

Es sieht so aus, als würden Sie Dinge basierend auf Konfigurationswerten hart codieren, um bestimmte Funktionen zu aktivieren oder zu deaktivieren. Ich empfehle Ihnen, die Dinge anhand der benannten Routen und nicht anhand des Konfigurationswerts zu steuern.

  1. Gruppieren Sie die gesamte Route als Ganzes oder nach Funktionen.
  2. Definieren Sie den Namen für alle Routen
  3. Steuern Sie die Aktivierungs- / Deaktivierungsaktivität anhand des Routennamens und des Datensatzes in der Datenbank
  4. Verwenden Sie die Laravel-Middleware, um zu überprüfen, ob eine bestimmte Funktion aktiviert oder deaktiviert ist, indem Sie den aktuellen Routennamen vom Anforderungsobjekt abrufen und mit der Datenbank abgleichen.

Sie haben also nicht die gleichen Bedingungen, die sich überall wiederholen und Ihren Code aufblähen. Hier ist ein Beispielcode, der Ihnen zeigt, wie Sie alle Routen abrufen können. Sie können den Namen der Routengruppe abgleichen, um ihn entsprechend Ihrer Situation weiter zu verarbeiten.

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

und hier ist ein Beispiel für einen Middleware-Handler, mit dem Sie überprüfen können, ob eine bestimmte Funktion aktiv ist, indem Sie sie mit dem übereinstimmen, was Sie bereits in Ihrer Datenbank gespeichert haben.

public function handle($request, Closure $next)
    {
        if(Helper::isDisabled($request->route()->getName())){
             abort(403,'This feature is disabled.');
        }
        return $next($request);
    }
Akram Wahid
quelle
1
Dies setzt voraus, dass die Funktionen Seiten auf der Website entsprechen, oder? Das ist nicht der Fall. Eine Funktion kann ein Ausschnitt innerhalb einer Seite sein (z. B. wird eine Google Map in der Seitenleiste angezeigt) oder eine Funktion (z. B. können Benutzer einige Daten exportieren).
StackOverflowNewbie
Sie haben Recht, aber meinen Sie einige Blöcke, die auf verschiedenen Seiten angezeigt werden? Was sind Ihre Einschränkungen, um es anzuzeigen? spezifisch seitenweise oder auf allen Seiten, die Sie anzeigen
Akram Wahid
Die Funktionen können eine ganze Seite oder nur ein Teil einer Seite oder nur einige Funktionen sein.
StackOverflowNewbie
0

Angenommen, diese Funktionen werden nur für HTTP-Anforderungen benötigt.

Ich würde eine Standardbasisklasse Featuresmit allen Standardflags erstellen :

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

Dann würde ich diese Klasse für jede Domäne erweitern und die für diese Domäne erforderlichen Überschreibungen festlegen:

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

Erstellen Sie dann eine Middleware, um die Feature-Class an den Container zu binden:

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

Vergessen Sie nicht, diese Middleware an Ihre Routen anzuhängen: an eine Gruppe oder für jede Route.

Danach können Sie Ihre Features-Klasse in Ihren Controllern eingeben:

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

Laravel ist damit großartig. Sie können Ihre Features sogar in db speichern und eine Beziehung zwischen der Domain herstellen.

Ich würde empfehlen, Gates und Richtlinien zu verwenden, um eine bessere Kontrolle über Ihre Controller und Blade-Vorlagen zu erhalten. Dies bedeutet, dass Sie die Gates aus Ihrer Datenbank registrieren oder fest codieren.

Wenn Sie beispielsweise die Funktion zum Exportieren von Produkten mit einer Schaltfläche in Ihrem System haben und diese Funktion einigen Benutzern zur Verfügung stellen möchten, können Sie Gates bei der Geschäftslogik registrieren.

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

Dann können Sie in den Controllern Folgendes tun

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

Hier ist ein Beispiel für Ihre Blade-Vorlagen:

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

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

Weitere Informationen finden Sie unter https://laravel.com/docs/5.8/authorization

Ersin Demirtas
quelle
0

Interessanter Fall haben Sie hier. Es kann interessant sein, sich eine FeatureSchnittstelle oder eine abstrakte Klasse anzusehen , die einige Methoden enthält, die Sie im Allgemeinen benötigen.

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

    ...
}

Sie könnten diese sogar in ExecutableFeatureund aufteilen RenderableFeature.

Weiterhin könnte eine Art Fabrikklasse geschaffen werden, um das Leben leichter zu machen.

// 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')
Thomas Van der Veen
quelle
0

In meinem Fall habe ich eine neue Tabelle in der Datenbank erstellt, die Sie beispielsweise aufrufen können Domains.

Fügen Sie alle spezifischen Funktionen, die in einigen Domänen angezeigt werden könnten, aber nicht in den übrigen, als Spalten für diese Tabelle als Bit für boolesche Werte hinzu. Wie, in meinem Fall allow_multiple_bookings, use_company_card... was auch immer.

Ziehen Sie dann in Betracht, eine Klasse Domainund ihr jeweiliges Repository zu erstellen , und fragen Sie einfach nach diesen Werten in Ihrem Code. Versuchen Sie dabei, diese Logik so weit wie möglich in Ihre Domäne (Ihr Modell, Ihre Anwendungsdienste usw.) zu übertragen.

Zum Beispiel würde ich die Controller-Methode nicht überprüfen, RequestBookingob die Domain, die eine Buchung anfordert, nur eine oder mehrere anfordern kann.

Stattdessen mache ich es auf einem, RequestBookingValidatorServiceder überprüfen kann, ob das Buchungsdatum abgelaufen ist, der Benutzer eine aktivierte Kreditkarte hat, ... oder die Domain, von der diese Aktion stammt, mehr als eine Buchung anfordern darf (und wenn dies bereits der Fall ist) irgendein).

Dies erhöht die Lesbarkeit, da Sie diese Entscheidung auf Ihre Anwendungsdienste übertragen haben. Außerdem stelle ich fest, dass ich, wenn ich eine neue Funktion benötige, Laravel- (oder Symfony-) Migrationen verwenden kann, um diese Funktion zur Tabelle hinzuzufügen, und sogar ihre Zeilen (Ihre Domänen) mit den Werten aktualisieren kann, die ich für dasselbe Commit möchte, das ich codiert habe.

vivoconunxino
quelle