Habilitar / deshabilitar características en una aplicación Laravel

10

Estoy creando una aplicación Laravel, que tiene varias características diferentes. Quiero poder habilitarlos o deshabilitarlos según los requisitos de un dominio en particular. Actualmente, tengo en mi configuración una serie de banderas como:

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

... y así.

Luego, en mis controladores y vistas, verifico esos valores de configuración para ver si debo mostrar algo, permitiendo ciertas acciones, etc. Mi aplicación está comenzando a contaminarse con este tipo de comprobaciones en todas partes.

¿Existe un método de mejores prácticas para administrar funciones en una aplicación Laravel?

StackOverflowNewbie
fuente
desactivar rutas en lugar de controlador? o usando middleware tal vez
Berto99
Característica! == página.
StackOverflowNewbie
En realidad podría ser una solución. ¿Quieres hacer tu comentario en una respuesta?
StackOverflowNewbie
mh no hay problema, no es una respuesta, lo importante es que podría ayudarte
Berto99

Respuestas:

4

Esto se denomina técnicamente indicadores de características: https://martinfowler.com/articles/feature-toggles.html

depende de sus requisitos, banderas en la configuración / base de datos, despliegue, etc.

Pero básicamente está en el código y no puede estar limpio.

Paquetes Laravel:

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

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

Algunos servicios:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

También mire https://marketingplatform.google.com/about/optimize/ para la interfaz.

Daniel Šádek
fuente
1
Puede encontrar un proyecto de muestra de laravel con marcado de características aquí: github.com/configcat/php-sdk/tree/master/samples/laravel
Peter
7

Me encontré con el mismo problema cuando intenté implementar varios proveedores de hoteles.

Lo que hice fue usar el contenedor de servicio.

primero crearás una clase para cada dominio con sus características:

  • como Doman1.php, Domain2.php
  • entonces dentro de cada uno de ellos agregará su lógica.

entonces usará el enlace en su proveedor de servicios de aplicaciones para vincular el dominio con la clase que usará.

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

Tenga en cuenta que puede usar la clase general que contiene las características para todos los dominios y luego usar esa clase general en sus clases

Finalmente en su controlador puede verificar su dominio y luego usar la clase que va a usar

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

Parece que está codificando cosas basadas en valores de configuración para habilitar o deshabilitar ciertas funciones. Le recomiendo que controle las cosas en función de las rutas con nombre en lugar del valor de configuración.

  1. Agrupe toda la ruta como un todo o por función.
  2. Definir nombre para todas las rutas
  3. Controle la actividad de activación / desactivación por nombre de ruta y registro en la base de datos
  4. Use el middleware Laravel para verificar si una característica particular está habilitada o deshabilitada obteniendo el nombre de ruta actual del objeto de solicitud y haciéndolo coincidir con la base de datos.

por lo que no tendrá las mismas condiciones repitiendo cada lugar e inflando su código ... aquí hay un código de muestra que le muestra cómo recuperar todas las rutas, y puede hacer coincidir el nombre del grupo de ruta para seguir el proceso para que coincida con su situación.

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

y aquí hay un ejemplo de controlador de middleware donde puede verificar si una característica particular está activa haciendo coincidir con lo que ya ha almacenado en su base de datos.

public function handle($request, Closure $next)
    {
        if(Helper::isDisabled($request->route()->getName())){
             abort(403,'This feature is disabled.');
        }
        return $next($request);
    }
Akram Wahid
fuente
1
Esto supone que las características equivalen a páginas en el sitio, ¿verdad? Ese no es el caso. Una característica podría ser algún fragmento dentro de una página (por ejemplo, se mostrará un mapa de Google en la barra lateral) o algún tipo de funcionalidad (por ejemplo, los usuarios pueden exportar algunos datos).
StackOverflowNewbie
tienes razón, sin embargo, ¿te refieres a algunos bloques que se muestran en diferentes páginas? ¿Cuáles son sus limitaciones para mostrarlo? página específica sabia o en todas las páginas que muestra
Akram Wahid
Las características pueden ser una página completa, o solo una parte de una página, o solo alguna funcionalidad.
StackOverflowNewbie
0

Suponiendo que esas características solo son necesarias para las solicitudes HTTP.

Crearía una Featuresclase base predeterminada con todas las banderas predeterminadas:

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

Luego, ampliaría esa clase para cada dominio y establecería las anulaciones necesarias para ese dominio:

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

Luego cree un Middleware para vincular la Clase de entidades al contenedor:

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

No olvide adjuntar este middleware a sus rutas: a un grupo o para cada ruta.

Después de esto, puede TypeHint su clase de Características en sus controladores:

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

Laravel es genial con esto, incluso puede almacenar sus características en db y crear una relación entre el dominio.

Recomendaría usar Gates y Políticas, que le darán un mejor control en sus controladores y plantillas de Blade. Esto significa que registra las puertas desde su base de datos o codifíquelas.

Por ejemplo, si tiene una función de exportación de productos con un botón en su sistema y desea que esa función esté disponible para algunos usuarios, puede registrar las puertas con lógica empresarial.

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

Luego puede hacer lo siguiente en los controladores

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

Aquí hay un ejemplo para sus plantillas de cuchillas:

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

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

Más información disponible en https://laravel.com/docs/5.8/authorization

Ersin Demirtas
fuente
0

Interesante caso que tienes aquí. Puede ser interesante buscar una Featureinterfaz o clase abstracta que contenga algunos métodos que generalmente necesita.

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

    ...
}

Incluso podría dividir estos en ExecutableFeaturey RenderableFeature.

Además, se podría hacer algún tipo de clase de fábrica para hacer la vida más fácil.

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

Lo que hice en mi caso fue crear una nueva tabla en la base de datos, podría llamarla, Domainspor ejemplo.

Agregue todas las características específicas, aquellas que podrían mostrarse en algunos dominios pero no en el resto, como columnas para esa tabla como bit para valores booleanos. Como, en mi caso allow_multiple_bookings, use_company_card... lo que sea.

Luego, considere crear una clase Domainy su repositorio respectivo, y solo pregunte estos valores en su código, tratando de insertar esa lógica lo más posible en su dominio (su modelo, servicios de aplicación, etc.).

Por ejemplo, no haría la verificación del método del controlador RequestBookingsi el dominio que solicita una reserva solo puede solicitar uno o más.

En cambio, lo hago para RequestBookingValidatorServiceverificar si la fecha y hora de reserva ha pasado, el usuario tiene una tarjeta de crédito habilitada, ... o el dominio del que proviene esta acción puede solicitar más de una reserva (y luego si ya tiene ninguna).

Esto agrega la conveniencia de la legibilidad, ya que ha llevado esta decisión a los servicios de su aplicación. Además, encuentro que cada vez que necesito una nueva característica puedo usar migraciones de Laravel (o Symfony) para agregar esa característica en la tabla e incluso podría actualizar sus filas (sus dominios) con los valores que quiero en el mismo compromiso que codifiqué.

vivoconunxino
fuente