Задача
Добавить в приложение систему оповещения пользователей через СМС-сообщения с возможностью выбора провайдера.
Решение
Оптимальным решением, на мой взгляд, является добавление собственного компонента.
Компонент — это блок программы с четко определенным набором действий (контрактом), способный решать возложенные на него задачи посредством различных драйверов. Так, например, встроенный компонент
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 компонент для аутентификации через различные соц.сети