Включение / отключение функций в приложении Laravel


10

Я создаю приложение Laravel, которое имеет ряд различных функций. Я хочу иметь возможность включать или отключать их в зависимости от требований конкретного домена. В настоящее время в моей конфигурации есть ряд флагов, таких как:

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

... и так далее.

Затем в своих контроллерах и представлениях я проверяю эти значения конфигурации, чтобы увидеть, следует ли мне отображать что-либо, разрешать определенные действия и т. Д. Мое приложение начинает загрязняться такими проверками повсюду.

Есть ли лучший метод управления функциями в приложении Laravel?


отключить маршруты вместо контроллера? или используя промежуточное ПО, может быть
— Berto99

Особенность! == страница.
— StackOverflowNewbie


На самом деле может быть решением. Хотите сделать свой комментарий в ответ?
— StackOverflowNewbie

нет проблем, это не ответ, важно то, что это может помочь вам
— Berto99

Ответы:


4

Технически это называется флагами функций - https://martinfowler.com/articles/feature-toggles.html.

зависит от ваших требований, флагов в config / database, rollout и т.д ...

Но это в основном, если в коде и не может быть чистым.

Пакеты Laravel:

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

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

Некоторые услуги:

https://launchdarkly.com/

https://bullet-train.io/

https://configcat.com/

Также посмотрите https://marketingplatform.google.com/about/optimize/ для веб-интерфейса.


1
Вы можете найти пример проекта Laravel с пометкой здесь: github.com/configcat/php-sdk/tree/master/samples/laravel
— Питер

7

Я столкнулся с той же проблемой, когда пытался внедрить несколько поставщиков отелей.

Я использовал сервисный контейнер.

Сначала вы создадите класс для каждого домена со своими особенностями:

  • как Doman1.php, Domain2.php
  • тогда внутри каждого из них вы добавите свою логику.

тогда вы будете использовать привязку в вашем сервис-провайдере приложений, чтобы связать домен с классом для использования.

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

Обратите внимание, что вы можете использовать общий класс, который содержит все функции, и использовать этот общий класс в ваших классах.

Наконец, в вашем контроллере вы можете проверить свой домен, чтобы использовать класс, который вы собираетесь использовать

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

0

Похоже, вы жестко программируете вещи, основываясь на значениях конфигурации, чтобы включить или отключить определенные функции. Я рекомендую вам управлять вещами на основе именованных маршрутов, а не значений конфигурации.

  1. Сгруппируйте весь маршрут целиком или по функциям.
  2. Определите название для всех маршрутов
  3. Управляйте активностью включения / выключения по имени маршрута и записи в базе данных
  4. Используйте промежуточное ПО Laravel, чтобы проверить, включена или отключена конкретная функция, получив текущее имя маршрута из объекта запроса и сопоставив его с базой данных.

поэтому у вас не будет одинаковых условий, повторяющих каждый раз и раздувающих ваш код. Вот пример кода, показывающий, как извлечь все маршруты, и вы можете сопоставить имя группы маршрутов для дальнейшей обработки в соответствии с вашей ситуацией.

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

и вот пример обработчика промежуточного программного обеспечения, где вы можете проверить, активна ли конкретная функция, сопоставив ее с тем, что вы уже сохранили в своей базе данных.

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

1
Это предполагает, что функции равны страницам на сайте, верно? Это не тот случай. Функцией может быть какой-то фрагмент на странице (например, карта Google будет отображаться на боковой панели), или какая-то функциональность (например, пользователи могут экспортировать некоторые данные).
— StackOverflowNewbie

Вы правы, но имеете в виду некоторые блоки, которые отображаются на разных страницах? какие у вас ограничения для его отображения? конкретная страница, мудрая или на всех страницах, которые вы отображаете
— Akram Wahid

Функциями могут быть целая страница, или просто часть страницы, или просто некоторая функциональность.
— StackOverflowNewbie

0

Предполагая, что эти функции нужны только для HTTP-запросов.

Я бы создал Featuresбазовый класс по умолчанию со всеми флагами по умолчанию:

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

Затем я бы расширил этот класс для каждого домена и установил переопределения, необходимые для этого домена:

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

Затем создайте промежуточное программное обеспечение, чтобы связать класс пространственных объектов с контейнером:

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

Не забудьте прикрепить это промежуточное ПО к своим маршрутам: к группе или для каждого маршрута.

После этого вы можете напечатать свой класс Features в ваших контроллерах:

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

0

Laravel отлично справляется с этим, вы даже можете хранить свои функции в БД и создавать отношения между доменом.

Я бы порекомендовал использовать Gates и Policies, которые дадут вам лучший контроль над вашими контроллерами и шаблонами блейдов. Это означает, что вы регистрируете ворота из своей базы данных или жестко их кодируете.

Например, если у вас есть функция экспорта товаров с помощью кнопки в вашей системе, и вы хотите сделать эту функцию доступной для некоторых пользователей, вы можете зарегистрировать ворота с помощью бизнес-логики.

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

Тогда вы можете сделать следующее в контроллерах

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

Вот пример для ваших шаблонов лезвий:

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

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

дополнительная информация доступна на https://laravel.com/docs/5.8/authorization


0

Интересный случай у вас здесь. Может быть интересно взглянуть на Featureинтерфейс или абстрактный класс, который содержит несколько методов, которые вам обычно нужны.

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

    ...
}

Вы могли бы даже разделить их на ExecutableFeatureи RenderableFeature.

Далее можно было бы создать какой-то заводской класс, чтобы облегчить жизнь.

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

В моем случае я создал новую таблицу в базе данных, например, ее можно назвать Domains.

Добавьте все конкретные функции, которые могут отображаться в некоторых доменах, но не в остальных, в качестве столбцов для этой таблицы в качестве бита для логических значений. Как в моем случае allow_multiple_bookings, use_company_card... что угодно.

Затем рассмотрите возможность создания класса Domainи его соответствующего репозитория и просто задайте эти значения в своем коде, стараясь как можно больше внедрить эту логику в ваш домен (модель, службы приложений и т. Д.).

Например, я бы не стал проверять метод контроллера, RequestBookingесли домен, запрашивающий бронирование, может запросить только один или несколько.

Вместо этого я делаю это на элементе, RequestBookingValidatorServiceкоторый может проверить, прошло ли время бронирования, у пользователя есть активированная кредитная карта, ... или домен, с которого происходит это действие, может запросить более одного бронирования (и затем, если он уже имеет Любые).

Это добавляет удобство чтения, поскольку вы передали это решение своим службам приложений. Кроме того, я обнаружил, что всякий раз, когда мне нужна новая функция, я могу использовать миграции Laravel (или Symfony), чтобы добавить эту функцию в таблицу, и я даже могу обновить ее строки (ваши домены) значениями, которые я хочу для того же кода, который я кодировал.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.