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

首先什么是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());
本作品采用《CC 协议》,转载必须注明作者和本文链接
打酱油
本帖由系统于 5年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 13

@sureyee 嗯,这样解偶一些

5年前 评论

这个吊,学习了

5年前 评论

666,写得不错!

5年前 评论

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

5年前 评论

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

5年前 评论

一直在用的 牛逼

4年前 评论
gyp719 4年前

@gyp719 你这个是调接口时,阿里云返回的错误,原因应该是你传了模板,没有传替换的值

4年前 评论
JaydenHong 4年前

配置文件怎么生成呢 兄弟

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!