Как воспользоваться сервис-провайдером или фасадом с передачей параметра?
Мне нужно получить нужный экземпляр класса по передаваемому параметру. Но текущая реализация мне кажется громоздкой и какой-то неправильной. Можно ли это сделать как-то короче через сервис-провайдер либо фасад?
Приведу пример текущей реализации:
<?php
namespace App\Core\Domain\Constants;
use App\Core\Domain\NotificationSending\Sms\Providers\GmsViberSms;
use App\Core\Domain\NotificationSending\Sms\Providers\SerwerSms\SerwerSmsProvider;
class OrderChatMessageType
{
public const VIBER = 1;
public const SERWERSMS = 2;
public const PROVIDERS = [
self::VIBER => GmsViberSms::class,
self::SERWERSMS => SerwerSmsProvider::class
];
// Достаем нужный класс
private static function get_class_provider(int $service_type): ?string
{
return self::PROVIDERS[$service_type] ?? null;
}
// Достаем экземпляр класса
public static function get_provider(int $service_type)
{
return resolve(self::get_class_provider($service_type));
}
}
И собственно получение экзепляра:
$service_provider = OrderChatMessageType::get_provider($service_type);
(Каждый из этих провайдеров имплеменрирует единый интерфейс)
Ответы (1 шт):
По сути Ваш код не плох и имеет место быть. Почему в данном случае уменьшить код достаточно сложно. Проблема заключается в том, что в передаёте цифровые ключи (предположу что с фронта) и в этот момент Вам придётся всё равно проверять типы и получать провайдер. То есть переделывая код на контейнеры вы всё равно не уменьшите код особо, а просто разнесёте по файлам. НО! есть мега крутой способ как это всё таки можно сделать =) Задаче связать число с классом.
Если что это всё мои фантазии, но они имеют место быть и будут работать). Далее решать уже Вам.
Мы удаляем весь код из OrderChatMessageType кроме первых двух констант.
Либо вообще удаляете класс и создаёте enum если у вас php 8
Далее в сервис провайдере в методе register или boot делаем следующее:
$this->app->bind(OrderChatMessageType::VIBER, GmsViberSms::class);
$this->app->bind(OrderChatMessageType::SERWERSMS, SerwerSmsProvider::class);
И по сути всё.
Далее resolve(1) или resolve(OrderChatMessageType::VIBER) будет возвращать ваш класс то есть метод get_provider по сути не нужен.
$service_provider = resolve($service_type);
В результате мы удалили лишний класс. Но к сожалению остаётся проблема, что будет вызван exception если тип такой не найден.
Тут есть несколько путей.
Сделать просто try-catch и самому решать что делать с ошибкой.
Если ключ летит с фронта. Проверять есть ли такая константа прям в валидаторе или реквесте (не знаю что там у вас) и всё в контроллере уже не придётся обрабатывать.
Вариант сделать таблицу в бд с вашими сервисами и их привязками. Где колонки будут
abstract,concrete, гдеabstractбудетprimary keyи при передаче вresolveделать так (метод find возвращает null если не найдет):resolve(Service::find($service_type)) // Service это название модели нашей таблицы
тогда в провайдере при регистрации можно делать так:
Service::all()->each(function($service) {
$this->app->bind($service->abstract, $service->concrete);
});
UPD
Если использовать бд, то есть способ связанный с model bindings чтобы не писать постоянно find
В маршруте делаем так:
Route::get('/test/{service}', [MyController::class, 'index'])...;
В результате в методе можно сразу получить наш класс.
public function index(Request $request, Service $service)
{
$service_provider = resolve($service->abstract);
}