Ativando / desativando recursos em um aplicativo Laravel

10

Estou criando um aplicativo Laravel, que possui vários recursos. Quero poder habilitá-los ou desabilitá-los, dependendo dos requisitos de um domínio específico. Atualmente, tenho na minha configuração uma série de sinalizadores, como:

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

... e assim por diante.

Em meus controladores e visualizações, verifico esses valores de configuração para ver se devo exibir alguma coisa, permitindo determinadas ações etc. Meu aplicativo está começando a ficar poluído com esses tipos de verificações em todos os lugares.

Existe um método de práticas recomendadas para gerenciar recursos em um aplicativo Laravel?

StackOverflowNewbie
fonte
desativar rotas em vez de controlador? ou usando middleware talvez
Berto99 05/01
Recurso! == página.
StackOverflowNewbie
Pode realmente ser uma solução. Deseja transformar seu comentário em resposta?
StackOverflowNewbie
mh não tem problema, não é uma resposta, o importante é que ele pode ajudá-lo
Berto99 08/01

Respostas:

4

Isso é tecnicamente chamado de sinalizadores de recursos - https://martinfowler.com/articles/feature-toggles.html

depende de seus requisitos, sinalizadores em config / database, rollout, etc ...

Mas é basicamente se está no código e não pode ser limpo.

Pacotes Laravel:

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

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

Alguns serviços:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

Veja também https://marketingplatform.google.com/about/optimize/ para obter front-end.

Daniel Šádek
fonte
11
Você pode encontrar um exemplo de projeto laravel com sinalização de recurso aqui: github.com/configcat/php-sdk/tree/master/samples/laravel
Peter
7

Encontrei o mesmo problema ao tentar implementar vários provedores de hotéis.

O que eu fiz foi usar o contêiner de serviço.

primeiro você criará uma classe para cada domínio Com seus recursos:

  • como Doman1.php, Domain2.php
  • então dentro de cada um desses você adicionará sua lógica.

em seguida, você vai usar obrigatório em seu provedor de serviços aplicativo para vincular o domínio com classe para uso.

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

Observe que você pode usar a classe geral que contém os recursos de todos os domínios e, em seguida, usar essa classe geral em suas classes

Finalmente, no seu controlador, você pode verificar seu domínio e usar a classe que você usará

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

Parece que você está codificando as coisas com base nos valores de configuração para ativar ou desativar determinados recursos. Eu recomendo que você controle as coisas com base nas rotas nomeadas, em vez do valor da configuração.

  1. Agrupe toda a rota como um todo ou por características.
  2. Definir nome para todas as rotas
  3. Controlar a atividade de ativar / desativar pelo nome da rota e registrar no banco de dados
  4. Use o middleware Laravel para verificar se um recurso específico está ativado ou desativado, obtendo o nome da rota atual do objeto de solicitação e combinando-o com o banco de dados.

portanto, você não terá as mesmas condições repetindo todos os locais e inchará o seu código. Aqui está um código de exemplo que mostra como recuperar todas as rotas e você pode combinar o nome do grupo de rotas para continuar o processo para atender à sua situação.

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 aqui está um exemplo de manipulador de middleware, no qual você pode verificar se um recurso específico está ativo, correspondendo ao que você já armazenou em seu banco de dados.

public function handle($request, Closure $next)
    {
        if(Helper::isDisabled($request->route()->getName())){
             abort(403,'This feature is disabled.');
        }
        return $next($request);
    }
Akram Wahid
fonte
11
Isso pressupõe que os recursos equivalem às páginas do site, certo? Esse não é o caso. Um recurso pode ser algum trecho de uma página (por exemplo, um Google Map será exibido na barra lateral) ou algum tipo de funcionalidade (por exemplo, os usuários podem exportar alguns dados).
StackOverflowNewbie
você está correto, no entanto, quer dizer alguns blocos que são exibidos em páginas diferentes? quais são as restrições para exibi-lo? página específica ou em todas as páginas que você a exibe
Akram Wahid
Os recursos podem ser uma página inteira, ou apenas uma parte da página, ou apenas algumas funcionalidades.
precisa saber antes
0

Supondo que esses recursos sejam necessários apenas para solicitações HTTP.

Eu criaria uma Featuresclasse base padrão com todos os sinalizadores padrão:

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

Então eu estenderia essa classe para cada domínio e definiria as substituições necessárias para esse domínio:

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

Em seguida, crie um Middleware para vincular a classe Features ao contêiner:

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

Não se esqueça de anexar este middleware às suas rotas: a um grupo ou para cada rota.

Depois disso, você pode TypeHint sua classe Features em seus controladores:

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

O Laravel é ótimo com isso, você pode até armazenar seus recursos em db e criar uma relação entre o domínio.

Eu recomendaria usar Gates and Policies, o que lhe dará um melhor controle em seus controladores e modelos de blade. Isso significa que você registra as portas do seu banco de dados ou codifica-as.

Por exemplo, se você possui um recurso de produtos de exportação com um botão em seu sistema e deseja disponibilizar esse recurso para alguns usuários, pode registrar portões com lógica de negócios.

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

Então você pode fazer o seguinte nos 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
    }
}

Aqui está um exemplo para seus modelos de blade:

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

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

mais informações disponíveis em https://laravel.com/docs/5.8/authorization

Ersin Demirtas
fonte
0

Caso interessante que você tem aqui. Pode ser interessante procurar uma Featureinterface ou classe abstrata que contenha alguns métodos que você geralmente precisa.

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

    ...
}

Você pode até dividi-los em ExecutableFeaturee RenderableFeature.

Mais adiante, algum tipo de classe fabril poderia ser feita para facilitar a vida.

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

O que fiz no meu caso foi criar uma nova tabela no banco de dados, você poderia chamá-lo, Domainspor exemplo.

Adicione todos os recursos específicos, aqueles que poderiam ser mostrados em alguns domínios, mas não nos demais, como colunas para essa tabela e como bit para valores booleanos. Como, no meu caso allow_multiple_bookings, use_company_card... tanto faz.

Em seguida, considere criar uma classe Domaine seu respectivo repositório e pergunte esses valores em seu código, tentando inserir o máximo possível essa lógica em seu domínio (seu modelo, serviços de aplicativos etc.).

Por exemplo, eu não faria a verificação do método do controlador para saber RequestBookingse o domínio que está solicitando uma reserva pode solicitar apenas um ou mais.

Em vez disso, faço isso em um RequestBookingValidatorServiceque pode verificar se a data e hora da reserva já passou, o usuário tem um cartão de crédito ativado ... ou o domínio do qual essa ação vem tem permissão para solicitar mais de uma reserva (e, se já tiver qualquer).

Isso adiciona a conveniência da legibilidade, pois você levou essa decisão aos seus serviços de aplicativo. Além disso, acho que sempre que precisar de um novo recurso, posso usar as migrações do Laravel (ou Symfony) para adicionar esse recurso à tabela e até atualizar suas linhas (seus domínios) com os valores que quero no mesmo commit que codifiquei.

vivoconunxino
fonte