Увімкнення / вимкнення функцій у додатку Laravel


10

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

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

... і так далі.

Потім у своїх контролерах та поданнях я перевіряю ці значення конфігурації, щоб побачити, чи слід щось показувати, дозволяючи певні дії тощо. Моя програма починає скрізь забруднюватися подібними видами перевірок.

Чи є найкращий метод управління функціями в додатку Laravel?


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

Особливість! == сторінка.
StackOverflowNewbie


Насправді це може бути рішення. Хочете зробити свій коментар відповіддю?
StackOverflowNewbie

mh немає проблем, це не відповідь, важливо те, що це може допомогти вам
Berto99

Відповіді:


4

Це технічно називають прапорами функцій - https://martinfowler.com/articles/feature-toggles.html

залежить від ваших вимог, прапорів у конфігурації / базі даних, розгортанні тощо ...

Але це в основному, якщо він в коді і не може бути чистим.

Пакети 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);
    }
}

Не забудьте приєднати це проміжне програмне забезпечення до своїх маршрутів: до групи або для кожного маршруту.

Після цього ви можете ввести контрольний клас у своїх контролерах:

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

0

Laravel чудово підходить до цього, ви навіть можете зберігати свої функції в db та створювати зв’язок між доменом.

Я рекомендую використовувати Gates і Policies, що дасть вам кращий контроль над вашими контролерами та шаблонами леза. Це означає, що ви реєструєте ворота зі свого db або жорсткий код.

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

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