Включение / отключение функций в приложении 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.