在 Laravel 中简单使用 Upyun SMS 短信服务

在 Laravel 中使用 又拍云 的短信服务很简单,但这里主要讨论设计模式: 服务层。

简单讲,服务层可以抽象你的程序逻辑,正如其名,提取通用的服务逻辑,这样只需要在一处,服务层(Service Layer)维护代码即可。

回到这个案例,Upyun 短信发送跟各个服务商大同小异:

  1. 前端收集手机号码
  2. 生成短信参数
  3. 调用短信发送 API
  4. 处理返回,成功或失败

第三步调用短信发送 API 可以被抽象成服务层,这样,你就可以在不同的控制器方便调用,借助 Laravel 的服务容器,这一过程非常轻松愉悦。

安装依赖: composer require guzzlehttp/guzzle

然后在 app 目录下创建 Services 目录及 UpyunMessenger.php 文件,如果你愿意,可以用自己喜欢的别的名字,但为了使用 PSR-4 自动加载的便利,请记得更改以下代码中的命名空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
namespace App\Services;

use Illuminate\Contracts\Config\Repository;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Psr7\Request as GuzzleRequest;

class UpyunMessenger
{
// 以下为又拍云短信服务配置
protected $uri; // API 接口,默认 https://sms-api.upyun.com/api/messages
protected $method; // 请求方式,默认 POST
protected $token; // 又拍云授权 token
protected $templateId; // 短信模板 ID,发送短信时必选

// Guzzle client
protected $client;

// 自动注入 Laravel 配置文件
// 及 Guzzle client 实例
// 也可以手动 `new GuzzleClient()` 创建
function __construct(Repository $config, GuzzleClient $client)
{
$this->uri = $config['services']['upyun_messenger']['uri'];
$this->method = $config['services']['upyun_messenger']['method'];
$this->token = $config['services']['upyun_messenger']['token'];
$this->templateId = $config['services']['upyun_messenger']['template_id'];

$this->client = $client;
}

public function send(string $phoneNumber, string $code)
{
// 新建一个请求,并配置请求方法和 uri
$request = new GuzzleRequest($this->method, $this->uri);
// 发送请求
$response = $this->client->send($request, [
// 请求头设置授权 token
'headers' => [
'Authorization' => $this->token,
],
// 参数为手机号,模板 ID,及短信参数
'form_params' => [
'mobile' => $phoneNumber,
'template_id' => $this->templateId,
'vars' => $code,
],
]);
// Guzzle 返回结果为 string,手动转 json,方便调用
return json_decode($response->getBody());
}
}

之后,就可以在需要的控制器,在构造时自动注入该服务类并调用方法即可,如 PhoneNumberController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#...
use App\Services\UpyunMessenger;

class PhoneNumberController extends Controller
{
public function __construct(UpyunMessenger $messenger)
{
$this->messenger = $messenger;
}

public function sendVerification(Request $request, $user)
{
#...
$code = 'abcdef';
$this->messenger->send($request->phone_number, $code);
}
}

可以看到,只需要定义一个服务类,在控制器中的注入是自动完成的,正如在服务类中注入 app 配置,这完全归功于 Laravel 实现的强大的服务容器(即控制反转IoC或依赖注入DI)。