Часто используемый рабочий процесс разработки в системах контроля версий — это ветвление функций. Идея заключается в том, что мы разрабатываем новые функции в других отраслях, кроме основной. После того, как функция протестирована и готова к выпуску, она снова объединяется с основной веткой или веткой выпуска для развертывания. Этот подход помогает нам разрабатывать новые функции, не нарушая основную базу кода.
Однако разработка ветви функций может занять гораздо больше времени, чем обычный цикл выпуска. Это усложнит объединение ветви, поскольку нам придется иметь дело с возможными конфликтами слияния, логики или зависимости.
Переключение функций
Одним из методов, широко используемых в качестве альтернативы ветвлению объектов, является переключение функций . Переключатели функций (или переключатели функций) действуют как переключатели вкл / выкл. Они позволяют нам продолжать разработку в основной ветке, не раскрывая частично разработанные и рискованные функции для широкой публики. Мы можем убедиться, что наши функции остаются полностью совместимыми с существующими функциями нашего приложения.
Мы можем временно скрыть частично встроенную или рискованную функцию (переключатели выпуска) или ограничить готовые стабильные функции определенной группой пользователей (переключатели бизнеса). Эти два типа переключателей реализованы одинаково, но для разных целей. С выпуском переключателей мы скрываем наши незаконченные функции от пользователей, за исключением команды разработчиков и QA. Эти переключатели отключаются, когда функция становится стабильной. Они обычно управляются на уровне кода.
Используя бизнес-переключатели, мы можем сделать функцию доступной для определенной группы пользователей, или мы можем полностью отключить ее из-за некоторых условий — например, продажа интернет-магазина, тема Xmas на вашем сайте и т. Д. Им часто требуется интерфейс, такой как панель инструментов для просматривать состояние существующих переключателей с возможностью их выключения и включения.
Переключение функций используется многими крупными сайтами, включая Flickr, Facebook, Disqus, Etsy, Reddit, Gmail и Netflix.
Мартин Фаулер хорошо описал переключение функций, рассказав о плюсах и минусах и о том, как правильно их использовать.
В этом уроке мы расскажем, как создавать переключатели функций с помощью Toggle , библиотеки PHP, разработанной лабораториями Quandidate .
Кандидат Тоггл
Основная идея состоит в том, чтобы активировать и деактивировать функцию на основе некоторых условий во время выполнения. Например, мы могли бы активировать функцию для пользователей, которые зарегистрировали свои учетные записи в нашей системе неделю назад.
Переключатель состоит из нескольких компонентов.
Toggle Manager
Менеджер переключения, как следует из названия, отвечает за управление переключателями функций, включая сохранение переключателей, получение переключателей, проверку состояний переключателей и т. Д.
Диспетчер переключателей может сохранять переключатели функции двумя различными способами. Одним из методов является коллекция в памяти, которая представляет собой класс, заключающий в себе массив переключателей. Этот класс предоставляет некоторые методы для хранения и извлечения переключателей внутри коллекции. Второй подход хранения использует Redis для хранения и получения переключателей.
Класс хранилища внедряется в качестве зависимости в диспетчер переключения в момент его создания. В этом уроке мы будем использовать коллекцию в памяти для хранения переключателей.
<?php // ... $manager = new ToggleManager ( new InMemoryCollection ( ) ) ;
Переключает
В Toggle каждый переключатель функции — это объект с именем и группой условий. Мы оцениваем значения времени выполнения с учетом этих условий, чтобы решить, следует ли включить или отключить функцию.
<?php // ... $conditions = [ $condition1 , $condition2 ] ; $toggle = new Toggle ( 'newFeature' , $conditions ) ;
операторы
Операторы являются строительными блоками условий переключения. Если мы сравним их с операторами в языках программирования, они будут похожи на логические выражения в операторах if
. Следующие операторы поддерживаются Toggle из коробки:
- Больше чем
- GreaterThanEqual
- Меньше, чем
- LessThanEqual
- вклейка
- процент
Чтобы создать оператора:
<?php // ... $operator = new LessThan ( 1000 )
В предыдущем коде оператор применяется к значениям меньше 1000
. Операторы ничего не делают самостоятельно. Они предназначены для использования с объектами условий.
Идея, возможно, стала немного запутанной, но все станет ясно, когда мы соберем все вместе в следующих разделах.
условия
Как отмечалось ранее, каждый тумблер должен соответствовать одному или нескольким условиям, чтобы быть активированным. Что такое состояние в Toggle?
Условие — это объект, который принимает произвольный ключ и объект оператора :
<?php // ... $operator = new LessThan ( 100 ) ; $conditions = [ ] ; // This condition is met when user_id is less or equal than 100 $conditions [ ] = new OperatorCondition ( 'user_id' , $operator ) ; $toggle = new Toggle ( 'newFeature' , $conditions ) ;
контекст
Операторы, условия и объекты-переключатели создают инструкции для активации функции переключения во время выполнения. Так что в основном ничего не происходит, пока мы не проверим некоторые значения на соответствие условиям.
Context
— это объект, который позволяет нам присваивать значения условиям переключения. Эти значения могут быть любыми: от идентификационных номеров до любых значений в зависимости от нашего варианта использования Мы добавляем значения в объект Context
используя метод set()
:
<?php // ... $operator = new LessThan ( 100 ) ; $conditions = [ ] ; $conditions [ ] = new OperatorCondition ( 'user_id' , $operator ) ; $toggle = new Toggle ( 'newFeature' , $conditions ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 67 ) ;
user_id
ссылается на ключ нашего условия в приведенном выше примере.
Теперь, когда у нас есть условия и значения времени выполнения, мы можем проверить состояние переключателя с помощью метода active()
менеджера:
<?php $manager = new ToggleManager ( new InMemoryCollection ( ) ) ; $operator = new LessThan ( 100 ) ; $conditions = [ ] ; $conditions [ ] = new OperatorCondition ( 'user_id' , $operator ) ; $toggle = new Toggle ( 'newFeature' , $conditions ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 100 ) ; $manager - > add ( $toggle ) ; if ( $manager - > active ( 'newFeature' , $context ) ) { echo 'The newFeature toggle is enabled' ; }
active
принимает два параметра. Первый параметр — это имя переключателя, для которого мы хотим проверить состояние. Второй параметр — это объект контекста. active()
проверяет, существует ли такой переключатель внутри коллекции. Следовательно, он сравнивает значения контекста с соответствующими условиями и возвращает логическое значение.
Обратите внимание, что нам нужно добавить тумблер к менеджеру с помощью метода add()
перед оценкой.
Вместо использования метода active()
менеджера мы можем напрямую использовать activeFor()
самого объекта-переключателя:
<?php // ... // Conditions here $toggle = new Toggle ( 'newFeature' , $conditions ) ; if ( $toggle - > activeFor ( $context ) ) { echo 'The toggle is active' ; // Do something special here. } // ...
activeFor()
принимает объект Context
в качестве аргумента.
Переключить в действии
Мы будем использовать composer для установки Toggle и его зависимостей. Чтобы сделать это, сначала мы создаем каталог в нашем корневом веб-каталоге и запускаем внутри него следующую команду:
composer require qandidate / toggle
Теперь давайте создадим файл и ToggleConfig.php
его ToggleConfig.php
. Мы оставим все определения переключателей внутри этого файла. Наконец, мы оценим состояние переключателя и вернем его в виде массива.
Для этого примера давайте создадим переключатель функций, который включается до 20:00:
<?php //ToggleConfig.php use Qandidate \ Toggle \ Context ; use Qandidate \ Toggle \ Operator \ LessThan ; use Qandidate \ Toggle \ OperatorCondition ; use Qandidate \ Toggle \ Toggle ; use Qandidate \ Toggle \ ToggleCollection \ InMemoryCollection ; use Qandidate \ Toggle \ ToggleManager ; $manager = new ToggleManager ( new InMemoryCollection ( ) ) ; //------------------------------------------------- // Toggle for featureOne //------------------------------------------------- $operator = new LessThan ( 20 ) ; // < 20 $conditions = [ ] ; $conditions [ ] = new OperatorCondition ( 'time' , $operator ) ; // time < 20 $toggle = new Toggle ( 'featureOne' , $conditions ) ; // Adding the toggle to the collection $manager - > add ( $toggle ) ; $context = new Context ( ) ; $context - > set ( 'time' , ( int ) date ( 'G' ) ) ; //---------------------------------------------------------- // Return the status array return array ( 'featureOne' = > $manager - > active ( 'featureOne' , $context ) , ) ;
В предыдущем коде сначала мы импортировали все необходимые классы в скрипт. Мы ToggleManager
экземпляр ToggleManager
и InMemoryCollection
в качестве носителя данных. Затем мы добавили переключатель с одним условием. Условие выполняется, когда текущий час меньше 20 (20:00).
Наконец, мы оценили состояние переключателя и вернули его в виде массива.
Чтобы использовать функцию переключения, давайте создадим еще один файл с именем index.php
:
<?php require_once 'vendor/autoload.php' ; $toggles = require 'ToggleConfig.php' ; if ( $toggles [ 'featureOne' ] ) { echo 'The toggle is active' ; // Do something special here. }
Мы присвоили вывод ToggleConfig.php
(который представляет собой массив состояний переключения) переменной $toggles
.
Использование Toggle с фреймворком
Когда мы внедряем частично разработанную функцию в дикую природу, мы должны скрыть доступ к ней в компонентах пользовательского интерфейса вместе со всеми точками входа, ведущими к функциональности указанной функции.
В качестве примера мы собираемся использовать функцию переключения в проекте Laravel для защиты некоторых компонентов пользовательского интерфейса и группы URL-адресов. Однако эта идея применима и к другим системам.
Запустите проект скелета Laravel , затем продолжайте.
Сначала давайте установим Toggle. В корневом каталоге нашего проекта Laravel мы запускаем:
composer require qandidate / toggle
Создание переключателей объектов
Функциональные переключатели могут быть определены где угодно, если они загружены, когда наше приложение загружается. Хороший подход — определить все это в классе промежуточного программного обеспечения.
Чтобы создать промежуточное программное обеспечение, находясь в терминале, мы меняем каталог на наш каталог установки Laravel и запускаем следующую команду:
php artisan make:middleware TogglesMiddleware
В результате в нашем каталоге app/Http/Middleware
будет создан класс промежуточного программного обеспечения с именем ToggleMiddleware.php
. Нам нужно определить все переключатели внутри метода handle()
этого класса.
Для этого примера давайте создадим функцию, которая переключает доступ к определенной группе пользователей. Например, пользователи с идентификационным номером до 100 — «ранние приемники».
Чтобы использовать статусы переключателей в нашем приложении, мы будем использовать сервис Config
Laravel.
<?php namespace App \ Http \ Middleware ; use Closure ; use Config ; use Qandidate \ Toggle \ Context ; use Qandidate \ Toggle \ Operator \ LessThan ; use Qandidate \ Toggle \ OperatorCondition ; use Qandidate \ Toggle \ Toggle ; use Qandidate \ Toggle \ ToggleCollection \ InMemoryCollection ; use Qandidate \ ToggleManager ; class TogglesMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle ( $request , Closure $next ) { /* * Toggle for NewFeature */ $manager = new ToggleManager ( new InMemoryCollection ( ) ) $operator = new LessThan ( 100 ) ; // < 100 $condition = new OperatorCondition ( 'uid' , $operator ) ; // uid < 100 $toggle = new Toggle ( 'newFeature' , [ $condition ] ) ; $context = new Context ( ) ; // Here we use the User model to get user information $context - > set ( 'uid' , User : : get ( ) - > getId ( ) ) ; // Storing the statuses in the Config service Config : : set ( 'toggles.newFeature' , $manager - > active ( $toggle , $context ) ) ; return $next ( $request ) ; } }
На следующем этапе нам нужно зарегистрировать промежуточное программное обеспечение. Поскольку мы хотим запускать промежуточное ПО во время каждого HTTP-запроса к нашему приложению, мы регистрируем его как глобальное промежуточное ПО. Для этого мы добавляем имя класса нашего промежуточного программного $middleware
свойство $middleware
нашего класса app/Http/Kernel.php
:
<?php /** * The application's global HTTP middleware stack. * * @var array */ protected $middleware = [ //... 'App\Http\Middleware\TogglesMiddleware' , ] ;
Скрытие компонентов пользовательского интерфейса
Чтобы скрыть компоненты пользовательского интерфейса, связанные с нашей функцией, у нас есть два варианта. Мы можем либо получить доступ к сервису Config
и проверить статус переключателя прямо из представлений, либо мы можем выполнить всю логику в нашем контроллере и затем передать флаг представлению. Последний подход представляется более подходящим в соответствии с лучшими практиками MVC.
Итак, внутри нашего контроллера:
<?php // ... // Our business logic here. $showFeature = \ Config : : get ( 'toggles.newFeature' ) ; View : : render ( 'users.dashboard' , array ( 'showNewFeature' = > $showFeature , ) ) ; // ...
И внутри вид:
< div class = " row " > < div class = " col-md-3 " > Feature one </ div > < div class = " col-md-3 " > Feature one </ div > @if ($showFeature) < div class = " col-md-3 " > Feature one </ div > @endif </ div >
В предыдущем коде мы использовали оператор blade @if
чтобы проверить, является ли флаг true
или false
. В результате последний элемент <div>
будет отображаться, только если для showFeature
установлено значение true
.
Защита URL-адресов
Если наша новая функция содержит API или какой-либо контроллер, который мы должны скрыть от пользователей, мы должны также защитить их. Для этого мы создаем промежуточное программное обеспечение до , но на этот раз вместо того, чтобы регистрировать его как глобальное промежуточное программное обеспечение, мы назначаем его определенной группе маршрутов. Внутри метода handle промежуточного программного обеспечения мы проверяем состояние переключателя, а если он отключен, мы выдаем ошибку 404.
Другой подход заключается в том, что мы устанавливаем логический барьер в начале классов контроллера и выкидываем ошибку 404
если переключатель выключен. Однако, используя этот подход, запрос отправляется нашему контроллеру, даже если переключатель выключен.
Для этого примера мы используем первый подход. Начнем с создания промежуточного программного обеспечения:
php artisan make : middleware APIToggleMiddleware
В результате файл APIToggleMiddleware.php
создается в каталоге app/Http/Middleware
. Поскольку глобальное промежуточное ПО запускается раньше, чем для конкретного маршрута, мы будем использовать переключатель, который мы создали в нашем глобальном промежуточном ПО. Причина, по которой мы не определили переключатель внутри промежуточного программного обеспечения для конкретного маршрута, заключается в том, что нам могут понадобиться эти переключатели во всех контроллерах.
<?php namespace App \ Http \ Middleware ; use Config ; use Closure ; class APIToggleMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle ( $request , Closure $next ) { if ( Config : : get ( 'toggles.newFeature' ) ) { abort ( 404 , 'Page Not Found!' ) ; } return $next ( $request ) ; } }
В предыдущем коде мы использовали сервис Config
для получения статуса переключателя newFeature
. Если это ложь, мы просто выбрасываем ошибку 404
.
Чтобы зарегистрировать это промежуточное ПО, мы добавляем имя класса промежуточного ПО в свойство $routeMiddleware
в app/Http/Kernel.php
. $routeMiddleware
— это ассоциативный массив, в котором $routeMiddleware
все промежуточные программы, относящиеся к конкретному маршруту, поэтому нам также необходимо указать ключ для нашего промежуточного ПО. В нашем случае: api-toggle
:
<?php /** * The application's route middleware. * * @var array */ protected $routeMiddleware = [ 'auth' = > \ App \ Http \ Middleware \ Authenticate : : class , 'auth.basic' = > \ Illuminate \ Auth \ Middleware \ AuthenticateWithBasicAuth : : class , 'guest' = > \ App \ Http \ Middleware \ RedirectIfAuthenticated : : class , 'api-toggle' = > '\App\Http\MiddleWare\APIToggleMiddleware' , ] ;
Теперь мы можем использовать промежуточное ПО внутри нашего файла app/http/routes.php
как промежуточное ПО:
<?php Route : : group ( 'new-feature/api' , [ 'middleware' = > 'api-toggle' ] , function ( ) { Route : : get ( 'new-feature/' , function ( ) { . . . } ) ; Route : : get ( 'new-feature/create' , function ( ) { . . . } ) ; Route : : put ( 'new-feature/{id}' , function ( ) { . . . } ) ; } ) ;
В результате все маршруты в new-feature/api
будут доступны только пользователям с идентификационными номерами ниже 100.
Стратегии переключения
По умолчанию переключатель функции активен, когда хотя бы одно из условий выполнено, но это можно отрегулировать, используя стратегии переключения, которые Toggle предоставляет «из коробки».
Для этого нам нужно указать желаемую стратегию при создании объекта-переключателя:
<?php // ... $toggle = new Toggle ( 'newFeature' , $conditions , Toggle : : STRATEGY_AFFIRMATIVE ) ; // ...
утвердительный
Это стратегия по умолчанию, которую Toggle использует для проверки условий переключения. В этом случае должно быть выполнено хотя бы одно условие, чтобы переключатель был активным.
<?php // ... $manager = new ToggleManager ( new InMemoryCollection ( ) ) ; $conditions = [ ] ; $conditions [ ] = new OperatorCondition ( 'user_id' , new LessThan ( 100 ) ) ; $conditions [ ] = new OperatorCondition ( 'age' , new GreaterThan ( 21 ) ) ; $toggle = new Toggle ( 'newFeature' , $conditions , Toggle : : STRATEGY_AFFIRMATIVE ) ; $manager - > add ( $toggle ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 100 ) - > set ( 'age' , 20 ) ; if ( $toggle - > activeFor ( $context ) ) { echo 'The toggle is active' ; // Do something here }
Вышеупомянутый переключатель будет включен, потому что одно из условий ( user_id
) выполнено.
большинство
В стратегии большинства необходимо выполнить большинство условий. Например, если у нас есть три условия для переключения, должно быть выполнено как минимум два из них.
<?php // ... $manager = new ToggleManager ( new InMemoryCollection ( ) ) ; $conditions = [ ] ; $conditions [ ] = new OperatorCondition ( 'user_id' , new LessThan ( 42 ) ) ; $conditions [ ] = new OperatorCondition ( 'age' , new GreaterThan ( 24 ) ) ; $conditions [ ] = new OperatorCondition ( 'height' , new GreaterThan ( 5.7 ) ) ; $toggle = new Toggle ( 'newFeature' , $conditions , Toggle : : STRATEGY_MAJORITY ) ; $manager - > add ( $toggle ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 41 ) ; - > set ( 'age' , 25 ) ; - > set ( 'height' , 5.6 ) ; if ( $toggle - > activeFor ( $context ) ) { echo 'The toggle is active.' // Do something special here }
В предыдущем коде переключение будет включено, потому что два ( user_id
и age
) из трех условий выполнены.
единодушное
При переключении на единодушную стратегию должны быть выполнены все условия:
<?php // ... $manager = new ToggleManager ( new InMemoryCollection ( ) ) ; $conditions = [ ] ; $conditions [ ] = new OperatorCondition ( 'user_id' , new LessThan ( 42 ) ) ; $conditions [ ] = new OperatorCondition ( 'age' , new GreaterThan ( 24 ) ) ; $conditions [ ] = new OperatorCondition ( 'height' , new GreaterThan ( 5.7 ) ) ; $toggle = new Toggle ( 'newFeature' , $conditions , Toggle : : STRATEGY_MAJORITY ) ; $manager - > add ( $toggle ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 41 ) ; - > set ( 'age' , 25 ) ; - > set ( 'height' , 5.6 ) ; if ( $toggle - > activeFor ( $context ) ) { echo 'The toggle is active.' // Do something special here }
В приведенном выше примере переключение будет отключено, поскольку третье условие ( height
) не выполняется в зависимости от значения контекста.
Переключить статусы
Каждый переключатель функций может иметь три состояния: активное, неактивное и условно активное. Когда тумблер вручную установлен на активный, он всегда будет активен независимо от условий. Если переключатель установлен как неактивный, он всегда будет неактивным. Третье состояние является условно активным, где состояние переключения будет зависеть от условий. Это состояние по умолчанию.
Чтобы изменить состояние переключателя вручную, мы используем метод activate()
объекта переключателя. Этот метод принимает параметр, который является состоянием переключателя и может быть одной из следующих констант:
-
Toggle::CONDITIONALLY_ACTIVE
(состояние по умолчанию) -
Toggle::ACTIVE
-
Toggle::INACTIVE
<?php // Conditions here $toggle - > activate ( Toggle : : CONDITIONALLY_ACTIVE ) ; $manager - > add ( $toggle ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 41 ) ; - > set ( 'age' , 25 ) ; - > set ( 'height' , 5.6 ) ; if ( $toggle - > activeFor ( $context ) ) { echo 'The toggle is active.' // Do something special here }
Использование массивов или YAML для создания переключателей
До сих пор мы создавали различные объекты (операторы, условия и переключатели) для создания переключателей объектов. Мы также можем создавать переключатели функций, используя массивы и файлы YAML, если мы предпочитаем конфигурацию, а не код. В этом подходе мы определяем все условия и операторы как многомерный ассоциативный массив или как объекты YAML. Следовательно, мы используем сервис InMemoryCollectionSerializer
(который является частью библиотеки Toggle) для автоматического создания всех необходимых операторов, условий и переключателей.
Чтобы определить переключатели с помощью массива:
<?php // ... // Array value $data = [ 'some-feature' = > [ 'name' = > 'newFeatureToggle' , 'conditions' = > [ [ 'name' = > 'operator-condition' , 'key' = > 'user_id' , 'operator' = > [ 'name' = > 'greater-than' , 'value' = > 41 ] , ] , ] , 'status' = > 'conditionally-active' , ] , ] ;
Как мы видим, у нас есть множество переключателей. Внутри каждого массива у нас есть два элемента: name
и condition
. name
— это имя переключателя, а условие — массив условий. В подмассиве conditions
нам нужно указать класс условия (OperatorCondition), но в формате kebab-case: operator-condition
. Это же правило применяется к другим именам классов, которые мы используем в массиве конфигурации. Нам также нужно определить ключ условия (в данном случае user_id
) и оператор.
Чтобы создать переключатели, мы десериализовали вышеупомянутый массив, используя InMemoryCollectionSerializer
следующим образом:
<?php use Qandidate \ Toggle \ Context ; use Qandidate \ Toggle \ ToggleManager ; use Qandidate \ Toggle \ Serializer \ InMemoryCollectionSerializer ; // $data array ( // ... // ); $serializer = new InMemoryCollectionSerializer ( ) ; $collection = $serializer - > deserialize ( $data ) ; $manager = new ToggleManager ( $collection ) ; $context = new Context ( ) ; $context - > set ( 'user_id' , 42 ) ; if ( $manager - > active ( 'some-feature' , $context ) ) { echo 'The toggle is Active' ; // Do something special here }
Режим Yaml практически идентичен, но требует наличия пакета Symfony / yaml. Для получения дополнительной информации обратитесь к примерам .
Завершение
Переключатели функций позволяют нам разрабатывать непосредственно в основной ветке, не имея дело с конфликтами слияния. Они также помогают нам скрыть незавершенные и рискованные функции от конечных пользователей, и мы всегда можем откатиться на случай, если что-то пойдет не так с кодом. Однако, если мы не будем использовать их осторожно, они могут стать кошмаром в долгосрочной перспективе.
Нам нужно убедиться, что нам действительно нужен переключатель функций. Если мы используем функциональные переключатели всякий раз, когда можем, даже если они не нужны, в конце дня у нас будет куча едва отслеживаемых переключателей, разбросанных по всей нашей кодовой базе. Это увеличит наши шансы на то, что мы будем раскрывать ошибочные функции, даже не замечая этого.
Хотя переключение функций является фантастическим для одноразовых легко удаляемых переключателей, таких как темы Xmas, специальные акции, функции временного профиля и т. Д., Их следует рассматривать как второй вариант при разработке сложных функций. Вместо этого мы должны посмотреть, сможем ли мы разрабатывать и развертывать наши обновления небольшими коммитами в каждом цикле выпуска.
Аналогично, мы всегда должны удалять переключатели функций, как только они больше не нужны. Это включает удаление классов промежуточного программного обеспечения и файлов конфигурации, в которых мы их определяем.
Тем не менее, Qandidate Toggle — это библиотека PHP, которая делает переключение функций управления намного проще, чем раньше. Мы надеемся, что вам понравился этот учебник, в котором мы начали с базового примера и расширили его до среды Laravel.
Как вы делаете функцию переключения? Дайте нам знать! И если у вас есть какие-либо вопросы или комментарии, пожалуйста, оставьте их ниже!