Как воспользоваться сервис-провайдером или фасадом с передачей параметра?

Рейтинг: 0Ответов: 1Опубликовано: 27.04.2023

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

Приведу пример текущей реализации:

<?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 если тип такой не найден.

Тут есть несколько путей.

  1. Сделать просто try-catch и самому решать что делать с ошибкой.

  2. Если ключ летит с фронта. Проверять есть ли такая константа прям в валидаторе или реквесте (не знаю что там у вас) и всё в контроллере уже не придётся обрабатывать.

  3. Вариант сделать таблицу в бд с вашими сервисами и их привязками. Где колонки будут 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);
}