通过 Laravel 消息通知使用 EasySms 短信服务,让你的代码更简洁
66

首先什么是EasySms
请走传送门: 又一个轮子:一款满足你的多种发送需求的短信发送组件: overtrue/easy-sms
什么是消息通知
请看文档 消息通知

下面来说说我是如何集成EasySms的。
阅读源码发现,包中提供了许多接口供我们进行扩展,也有很多方法可以进行集成,下面来看看我集成Easysms的一种方法。

通过ServiceProvider集成EasySms服务

  1. 新增一个ServiceProvider,名称自定义

    php artisan make:provider MessageServiceProvider

  2. 在服务提供者中注册服务。

    namespace App\Providers;
    
    use App\Channels\EasySmsChannel;
    use App\Gateways\ChanZorGateway;
    use Illuminate\Notifications\ChannelManager;
    use Illuminate\Support\ServiceProvider;
    use Overtrue\EasySms\EasySms;
    
    class MessageServiceProvider extends ServiceProvider
    {
        /**
         * Bootstrap services.
         *
         * @return void
         */
        public function boot()
        {
    
        }
    
        /**
         * Register services.
         *
         * @return void
         */
        public function register()
        {
            // 这里将easySms注册为一个单例,并且添加门面访问
            $this->app->singleton('easySms', function () {
                $config = config('easysms');
                return new EasySms($config);
            });
        }
    }
  3. 添加门面,方便访问。(这里并不是必要的,如果不使用门面,在channel中使用app()->make('easySms')->send()也可以实现)

    namespace App\Facades;
    use Illuminate\Support\Facades\Facade;
    
    class EasySms extends Facade
    {
        protected static function getFacadeAccessor()
        {
            return 'easySms';
        }
    }
  4. app.php中注册门面和服务

    'providers' => [
         App\Providers\MessageServiceProvider::class,
    ],
    'aliases' => [
        'EasySms' => App\Facades\EasySms::class,
    ]

    这样服务就集成完毕了,下面我们来添加自定义网关。

添加自定义网关

添加自定义网关需要实现 GatewayInterface,这里我直接集成抽象类 Overtrue\EasySms\Gateways\Gateway

  1. 添加自定义网关

    namespace App\Gateways;
    
    use Overtrue\EasySms\Contracts\MessageInterface;
    use Overtrue\EasySms\Contracts\PhoneNumberInterface;
    use Overtrue\EasySms\Gateways\Gateway;
    use Overtrue\EasySms\Support\Config;
    use Overtrue\EasySms\Traits\HasHttpRequest;
    
    class ChanZorGateway extends Gateway
    {
        use HasHttpRequest;
    
        const ENDPOINT_HOST = 'http://api.chanzor.com';
    
        const ENDPOINT_URI = '/send';
    
        protected $account;
    
        protected $password;
    
        protected $sign = null;
    
        protected $client;
    
        /**
         * Send a short message.
         *
         * @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to
         * @param \Overtrue\EasySms\Contracts\MessageInterface $message
         * @param \Overtrue\EasySms\Support\Config $config
         *
         * @return array
         */
        public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
        {
            $params = [
                'account' => $config->get('account'),
                'password' => $config->get('password'),
                'content' => $message->getContent() . '【' . $config->get('sign') . '】',
                'mobile' => $to->getNumber(),
            ];
    
            return $this->post(self::ENDPOINT_URI, $params);
        }
    
        protected function getBaseUri()
        {
            return self::ENDPOINT_HOST;
        }
    }
  2. 定义laravel通知频道

    namespace App\Channels;
    
    use App\Facades\EasySms;
    use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
    use Overtrue\EasySms\PhoneNumber;
    
    class EasySmsChannel
    {
    
        /**
         * 发送给定通知
         * @param Model $notifiable
         * @param Notification $notification
         * @return mixed
         */
        public function send($notifiable, $notification)
        {
            return EasySms::send($notifiable->routeNotificationFor('easySms'), $notification->toSms($notifiable));
        }
    }
  3. 现在需要在刚刚定义的服务提供者中导入网关否则网关是无法使用的,由于在laravel中开箱即用的只有database, mail, nexmo,slack这些,所以,也需要将easySms注册到服务提供者中
    public function boot()
    {
        // 导入网关
        $this->app->make('easySms')->extend('chanzor', function () {
            return new ChanZorGateway(config('easysms.gateways.chanzor'));
        });
        // 设置频道
        $this->app->make(ChannelManager::class)
            ->extend('easySms', function ($app) {
                return $app->make(EasySmsChannel::class);
            });
    }

最后就是定义短信通知类了

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Overtrue\EasySms\Message;

class OrderPaidNotification extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['easySms'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return Message
     */
    public function toSms($notifiable)
    {
        return (new Message())->setContent( $notifiable->username . ',您好! 这是短信内容。');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

注意
需要注意的是,定义好之后需要在User中添加一个获取手机号码的方法。

public function routeNotificationForEasySms()
{
    return $this->mobile;
}

为什么需要定义这个方法,为了扩展,如果在Channel中写死了之后,UserAdmin使用不同的字段名作为手机号码就会出错了。

使用

首先配置easysms的配置项

  'gateways' => [
        'errorlog' => [
            'file' => '/tmp/easy-sms.log',
        ],
        'yunpian' => [
            'api_key' => '824f0ff2f71cab52936axxxxxxxxxx',
        ],
        'aliyun' => [
            'access_key_id' => '',
            'access_key_secret' => '',
            'sign_name' => '',
        ],
        'chanzor' => [
            'account' => env('SMS_CHANZOR_ACCOUNT'),
            'password' => env('SMS_CHANZOR_PASSWORD'),
            'sign' => env('SMS_CHANZOR_SIGN')
        ]
        //...
    ],

然后使用laravel开箱的方式进行调用。

$user = App\User::first();
$user->notify(new  App\Notifications\OrderPaidNotification());

如果有不优雅的地方,请指正!谢谢~~~

本帖由系统于 1个月前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 6
doobi

这个吊,学习了

1个月前
overtrue

666,写得不错!

1个月前
sureyee

@overtrue 想了想,似乎在Notification中通过toSms()方法返回Message对象更好一些,这样也和laravel 提供的文档保持一致。

1个月前
overtrue

@sureyee 嗯,这样解偶一些

1个月前
sureyee

@overtrue 哈哈哈,多谢超哥的点评,已经做了些修改。

1个月前

很棒

1个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!