Задача
Добавить в приложение систему оповещения пользователей через СМС-сообщения с возможностью выбора провайдера.
Решение
Оптимальным решением, на мой взгляд, является добавление собственного компонента.
Компонент — это блок программы с четко определенным набором действий (контрактом), способный решать возложенные на него задачи посредством различных драйверов. Так, например, встроенный компонент
Cache
может использовать драйвера: file
, memcached
или redis
.При построении нашего компонента, мы применим принцип проектирования Мост, тот же принцип, на котором построены компоненты Laravel.
Итак, приступим.
Драйвера
Как гласит один из постулатов: программируйте в соответствии с интерфейсом, а не с реализацией.
С него и начнем, но в контексте компонента точнее использовать термин контракт. Контракт, по сути, набор интерфейсов для описания функционала компонента.
interface SmsContract
{
public function send();
}
Далее добавим драйвера, исполняющие контракт. Общую логику вынесем в абстрактный класс.
abstract class Driver implements SmsContract
{
protected $message;
protected $recipient;
public function to($phoneNumber)
{
$this->recipient = $phoneNumber;
}
public function content($message)
{
$this->message = $message;
}
abstract public function send();
}
А непосредственно логику отправки опишем в каждом классе драйвера.
class SmsRuDriver extends Driver
{
protected $client;
protected $from;
public function __construct(SmsRuClient $smsRuClient, $from)
{
$this->client = $smsRuClient;
$this->from = $from;
}
public function send()
{
return $this->client->sendSms([
'type' => 'text',
'from' => $this->from,
'to' => $this->recipient,
'text' => trim($this->message)
]);
}
}
class SmartSenderDriver extends Driver
{
protected $client;
protected $from;
public function __construct(SmartSenderClient $smartSenderClient, $from)
{
$this->client = $smartSenderClient;
$this->from = $from;
}
public function send()
{
return $this->client->message()->send([
'type' => 'text',
'from' => $this->from,
'to' => $this->recipient,
'text' => trim($this->message)
]);
}
}
Управление Компонентом
Для выбора и настройки драйверов добавим класс
SmsManager
, унаследованный от класса Manager
.use Illuminate\Support\Manager;
class SmsManager extends Manager
{
public function setDriver($name = null)
{
return $this->driver($name);
}
public function createSmsRuDriver(): SmsContract
{
return new SmsRuDriver(
$this->createSmsRuClient(),
$this->app['config']['sms.sms_ru.from']
);
}
public function createSmartSenderDriver(): SmsContract
{
return new SmartSenderDriver(
$this->createSmartSenderClient(),
$this->app['config']['sms.smart_sender.from']
);
}
public function getDefaultDriver()
{
return $this->app['config']['sms.default'];
}
protected function createSmsRuClient()
{
return new SmsRuClient(
$this->app['config']['sms.sms_ru.key'],
$this->app['config']['sms.sms_ru.secret']
);
}
protected function createSmartSenderClient()
{
return new SmartSenderClient(
$this->app['config']['sms.smart_sender.key'],
$this->app['config']['sms.smart_sender.secret']
);
}
}
Теперь достаточно указать нужные параметры в файле конфигурации и класс
SmsManager
укажет нашему компоненту через какой драйвер отправлять СМС-сообщения. Фасад
Для доступа к функционалу компонента из любой точки приложения добавим фасад.
Сначала создаем сам класс фасада:
use Illuminate\Support\Facades\Facade;
class Sms extends Facade
{
protected static function getFacadeAccessor()
{
return 'sms';
}
}
Далее связываем класс
SmsManager
с фасадом с помощью сервис-провайдера и регистрируем псевдоним.class SmsServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->singleton('sms', function ($app) {
return new SmsManager($app);
});
}
public function provides()
{
return ['sms'];
}
}
И последнее, регистрируем наш сервис-провайдер вместе с остальными провайдерами в файле конфигурации (
app/config/app.php
).'providers' => [
...
App\Providers\SmsServiceProvider::class,
],
Применение
Когда для нашего компонента создан фасад и указаны параметры в файле конфигураций, для отправки СМС-сообщения достаточно добавить следующий код:
Sms::to($phone)->content('Beware! He`s not who he is')->send();
Или, если необходимо явно указать драйвер:
Sms::setDriver('sms_ru')->to($phone)->content('why don`t you answer me?')->send();
Вывод
Наш компонент построен по принципу проектирования Мост, то есть реализация и абстракция разделены таким образом, что могут изменяться независимо. Мы можем управлять драйверами через класс
SmsManager
, при этом сам код применения остается без изменений.Фасад обеспечивает простой доступ к функционалу нашего компонента.
Такой подход к проектированию компонентов обеспечивает простоту и гибкость их использования.
Ссылки
Laravel Socialite компонент для аутентификации через различные соц.сети