Abilitazione / disabilitazione delle funzionalità in un'app Laravel


10

Sto costruendo un'app Laravel, che ha una serie di varie funzionalità. Voglio essere in grado di abilitarli o disabilitarli a seconda delle esigenze di un determinato dominio. Attualmente, nella mia configurazione ho una serie di flag come:

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

... e così via.

Quindi nei controller e nelle visualizzazioni, controllo quei valori di configurazione per vedere se dovrei visualizzare qualcosa, consentire determinate azioni, ecc. La mia app sta iniziando a essere inquinata da questo tipo di controlli ovunque.

Esiste un metodo di best practice per la gestione delle funzionalità in un'app Laravel?


disabilitare percorsi invece del controller? o usando il middleware forse
Berto99

Funzione! == pagina.
StackOverflowNewbie


Potrebbe effettivamente essere una soluzione. Vuoi trasformare il tuo commento in una risposta?
StackOverflowNewbie

mh nessun problema, non è una risposta, l'importante è che possa aiutarti
Berto99

Risposte:


4

Questo è tecnicamente chiamato flag delle caratteristiche - https://martinfowler.com/articles/feature-toggles.html

dipende dalle vostre esigenze, flag in config / database, implementazione, ecc ...

Ma è fondamentalmente se è nel codice e non può essere pulito.

Pacchetti Laravel:

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

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

Alcuni servizi:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

Guarda anche https://marketingplatform.google.com/about/optimize/ per frontend.


1
Puoi trovare un esempio di progetto laravel con funzionalità di segnalazione qui: github.com/configcat/php-sdk/tree/master/samples/laravel
Peter,

7

Ho riscontrato lo stesso problema quando ho cercato di implementare più fornitori di hotel.

Quello che ho fatto è stato l'utilizzo del contenitore di servizi.

per prima cosa creerai classe per ogni dominio Con le sue caratteristiche:

  • come Doman1.php, Domain2.php
  • poi all'interno di ognuno di questi aggiungerai la tua logica.

quindi utilizzerai l'associazione nel tuo fornitore di servizi app per associare il dominio con la classe da utilizzare.

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

Nota che puoi utilizzare la classe generale che contiene le funzionalità di tutti i domini, quindi utilizzare quella classe generale nelle tue classi

Finalmente nel tuo controller puoi controllare il tuo dominio e poi usare la classe che userai

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

0

Sembra che tu stia codificando le cose in base ai valori di configurazione per abilitare o disabilitare determinate funzionalità. Ti consiglio di controllare le cose in base alle rotte denominate anziché al valore di configurazione.

  1. Raggruppa tutto il percorso nel suo insieme o per funzionalità.
  2. Definire il nome per tutti i percorsi
  3. Controlla l'attività di abilitazione / disabilitazione per nome percorso e record nel database
  4. Utilizzare il middleware Laravel per verificare se una particolare funzione è abilitata o disabilitata ottenendo il nome della rotta corrente dall'oggetto richiesta e abbinandolo al database.

quindi non avrai le stesse condizioni ripetendo ogni dove e gonfiando il tuo codice .. ecco un codice di esempio che ti mostra come recuperare tutti i percorsi e puoi abbinare il nome del gruppo di percorsi per elaborare ulteriormente in base alla tua situazione.

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

e qui è un gestore di middleware di esempio in cui è possibile verificare se una particolare funzionalità è attiva abbinandola a ciò che è già stato archiviato nel database.

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

1
Ciò presuppone che le funzionalità equivalgano alle pagine del sito, giusto? Non è così. Una funzione potrebbe essere un frammento all'interno di una pagina (ad es. Una Google Map verrà visualizzata nella barra laterale) o una sorta di funzionalità (ad es. Gli utenti possono esportare alcuni dati).
StackOverflowNewbie

hai ragione, ma intendi alcuni blocchi che vengono visualizzati su pagine diverse? quali sono i tuoi vincoli per visualizzarlo? pagina specifica saggia o su tutte le pagine che la visualizzi
Akram Wahid

Le funzionalità possono essere un'intera pagina, o solo una porzione di una pagina, o solo alcune funzionalità.
StackOverflowNewbie

0

Supponendo che tali funzionalità siano necessarie solo per le richieste HTTP.

Vorrei creare una Featuresclasse base predefinita con tutti i flag predefiniti:

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

Quindi estenderei quella classe per ciascun dominio e imposterò le sostituzioni necessarie per quel dominio:

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

Quindi creare un middleware per associare la classe Features al contenitore:

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

Non dimenticare di collegare questo middleware ai percorsi: a un gruppo o per ciascun percorso.

Dopodiché puoi digitare Suggerimento la tua classe Features nei tuoi controller:

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

0

Laravel è fantastico in questo, puoi persino archiviare le tue funzionalità in db e creare una relazione tra il dominio.

Consiglierei di usare Gates e Politiche, che ti daranno un miglior controllo nei tuoi controller e template blade. Questo significa che registri i gate dal tuo db o li codifichi.

Ad esempio, se si dispone della funzione di esportazione dei prodotti con un pulsante nel proprio sistema e si desidera rendere tale funzione disponibile per alcuni utenti, è possibile registrare gate con la logica aziendale.

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

Quindi è possibile effettuare le seguenti operazioni nei controller

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

Ecco un esempio per i modelli di blade:

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

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

maggiori informazioni disponibili su https://laravel.com/docs/5.8/authorization


0

Caso interessante che hai qui. Potrebbe essere interessante esaminare Featureun'interfaccia o una classe astratta che contiene alcuni metodi generalmente necessari.

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

    ...
}

Potresti persino dividerli in ExecutableFeaturee RenderableFeature.

Più avanti potrebbe essere fatto un qualche tipo di classe di fabbrica per rendere la vita più facile.

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

Quello che ho fatto nel mio caso è stata la creazione di una nuova tabella nel database, Domainsad esempio è possibile chiamarla .

Aggiungi tutte le funzionalità specifiche, quelle che potrebbero essere mostrate su alcuni domini ma non negli altri, come colonne per quella tabella come bit per valori booleani. Come, nel mio caso allow_multiple_bookings, use_company_card... qualunque cosa.

Quindi, considera la possibilità di creare una classe Domaine il relativo repository e chiedi semplicemente questi valori sul tuo codice, cercando di spingere il più possibile quella logica nel tuo dominio (modello, servizi applicativi, ecc.).

Ad esempio, non farei il controllo sul metodo del controller RequestBookingse il dominio che richiede una prenotazione può richiederne solo uno o più.

Invece lo faccio su un dispositivo RequestBookingValidatorServiceche può verificare se il datetime di prenotazione è passato, l'utente ha una carta di credito abilitata, ... o il dominio da cui proviene questa azione è autorizzato a richiedere più di una prenotazione (e quindi se ha già qualunque).

Questo aggiunge la praticità della leggibilità, poiché hai spinto questa decisione ai tuoi servizi applicativi. Inoltre, trovo che ogni volta che ho bisogno di una nuova funzionalità posso usare le migrazioni Laravel (o Symfony) per aggiungere quella funzione sulla tabella e potrei anche aggiornare le sue righe (i tuoi domini) con i valori che desidero sullo stesso commit che ho codificato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.