使用 Nexmo 发送和接收 Laravel 通知短信

发布日期:作者:

Sending and receiving SMS Laravel Notifications with Nexmo image

这是 Michael Heap 的一系列教程的第一部分,介绍如何使用 Laravel 构建多渠道帮助台系统。在这篇文章中,我们将从一个 简单的基于 Web 的帮助台系统 开始,并扩展它以使用内置的 Laravel 通知系统(由 Nexmo 提供支持)发送和接收 SMS 消息。

先决条件

要完成这篇文章,您需要一个 Nexmo 帐户 和已安装并配置的 Nexmo 命令行工具,以及您通常的 Laravel 先决条件。

在文章的后半部分,当您收到 SMS 消息时,我们将从 Nexmo 接收 Webhook,因此您需要以某种方式公开您的本地 Web 服务器。我们建议为此使用 ngrok

入门

搭建应用程序并生成模型和迁移很繁琐,让您创建表单和视图就更糟糕了。为了让每个人都免受痛苦,我们创建了一个快速入门仓库,您可以克隆并运行它,它为您完成了所有枯燥的工作。

首先 fork Deskmo 仓库,这样您就可以在进行了一些更改后将它们提交回 Github。然后,将本地机器上的 initial-scaffolding 分支克隆到您的本地机器上。此分支包含预生成的模型、迁移和表单。

git clone -b initial-scaffolding https://github.com/<YOUR_USER>/deskmo
cd deskmo

将仓库放到您的机器上后,将 .env.example 复制到 .env,并编辑您可能需要为应用程序运行而更改的任何值(例如数据库凭据)。

接下来要做的就是运行 composer install 来获取所有依赖项。这是一个普通的 Laravel 安装,除了添加了 laravelcollective/html 用于表单生成之外。

Composer 完成后,就可以运行应用程序了。我们需要生成一个新的应用程序密钥,运行数据库迁移并启动内置的 Web 服务器

php artisan key:generate
php artisan migrate
php artisan serve

要使用该应用程序,您需要 注册一个帐户(别担心,这只是在您的本地数据库中!)。您可能会注意到注册表单有一个额外的字段 phone_number。这是我们稍后将向其发送 SMS 的号码,因此请确保您以国际格式输入您的号码,省略前导 0(例如 14155550100)。如果您有兴趣了解如何向注册表单添加更多字段,请查看 此提交

创建帐户会自动登录您并带您到一个页面,上面写着没有票证。单击右上角的新建票证并创建一个新票证。

您的“收件人用户 ID”将是“1”。这可能在某个时候成为一个很好的自动完成,但现在输入用户 ID 也可以。

单击提交后,它应该带您回到票证列表,但这次会显示您刚刚创建的票证。

我们终于可以开始编写代码了!虽然我们有一个允许您创建票证的系统,但它还有很多其他功能。在这篇文章的剩余部分,我们将购买一个 Nexmo 电话号码,在每次创建票证时发送 SMS 通知,并允许客户通过 SMS 回复票证。

购买 Nexmo 电话号码

我们之前提到过我们需要一个 Nexmo 电话号码来接收 SMS 消息,这些消息会作为 Webhook 发送给我们。为了方便人们回复,我们也使用与我们的出站号码相同的号码。

您可以通过 Nexmo 仪表板 购买和配置号码,但今天我们将使用 Nexmo CLI。

要购买号码,我们使用 number:search 命令查找具有 SMS 和语音支持的号码,然后使用 number:buy 命令购买该号码。我们在这里购买的是美国号码,但您可以将 US 替换为 Nexmo 有本地号码的任何 45 个以上国家/地区 中的任何一个。

nexmo number:search US --sms --voice
nexmo number:buy <NUMBER> --confirm

记下返回的号码,因为我们很快就会用到它。

配置 Nexmo

要通过 Laravel 的 通知系统 发送 SMS 通知,我们需要使用 composer 安装 nexmo/client 并使用我们的 Nexmo API 密钥和私钥 以及我们刚刚购买的号码来配置我们的应用程序。

通过运行 composer require nexmo/client 来安装 nexmo/client。接下来,编辑 config/services.php 并添加以下配置选项

'nexmo' => [
'key' => env('NEXMO_KEY'),
'secret' => env('NEXMO_SECRET'),
'sms_from' => env('NEXMO_NUMBER'),
],

最后,编辑 .env 并提供您的 API 密钥、私钥和 Nexmo 号码。它应该如下所示

NEXMO_KEY=<YOUR_KEY>
NEXMO_SECRET=<YOUR_SECRET>
NEXMO_NUMBER=<YOUR_NUMBER>

这就是将 Nexmo 集成到 Laravel 的通知系统所需的全部操作 - 一个 composer install 和三个配置值。

发送通知

我们现在可以发送通知了,但我们还没有要发送的通知!通过运行 php artisan make:notification TicketCreated 生成一个新的通知,然后打开您新创建的文件(app/Notifications/TicketCreated.php)。

首先要更改的是第 32 行,via 函数返回 mail。我们不想通过电子邮件发送它,因此我们将值更改为 nexmo 以将其作为 SMS 发送。

通知类知道如何为电子邮件格式化消息,但不知道如何为 SMS 格式化消息。为了解决这个问题,添加一个具有以下内容的新方法

public function toNexmo($notifiable)
{
return (new NexmoMessage)
->content($this->entry->content);
}

此方法使用两个新对象 - NexmoMessage$this->entry。现在让我们将它们添加到类中,方法是将以下内容添加到您的导入中

use App\TicketEntry;
use Illuminate\Notifications\Messages\NexmoMessage;

您还需要更新您的 __construct() 方法以接受 TicketEntry 并将 $entry 添加为类变量

protected $entry;
 
public function __construct(TicketEntry $entry)
{
$this->entry = $entry;
}

我们现在有一个可以发送给用户的通知,但我们还没有完全完成,因为我们不知道需要将其发送给谁。

一个票证可以有多个订阅用户,一个用户可以订阅多个票证。要公开这种关系,我们需要在我们的 Ticket 类中添加一个新方法。我们将 ticket_subscriptions 指定为第二个参数,因为这是一个非标准的中间表名称(这些用户不拥有票证,他们只是订阅了该票证的通知)

public function subscribedUsers()
{
return $this->belongsToMany(User::class, 'ticket_subscriptions');
}

完成此操作后,打开 TicketController 并将以下内容添加到您的导入列表中,以便我们可以发送我们刚刚创建的通知

use App\Notifications\TicketCreated;
use Notification;

最后,我们需要实际发送通知。在 store 方法的底部,在我们重定向回索引页面之前,添加以下代码

Notification::send($ticket->subscribedUsers()->get(), new TicketCreated($entry));

这将获取所有订阅了该工单的用户,并为新创建的条目分发一个新的TicketCreated通知。保存您的更改,然后尝试创建一个新的工单,接收者 ID 为 1(应该是您的帐户)。您应该在几秒钟内收到包含条目内容的短信。

恭喜!我们刚刚在应用程序中添加了短信通知。配置好所有内容后,它非常简单,只需四个步骤。

  • 使用php artisan make:notification生成一个通知。
  • 更改via方法以使用nexmo
  • 添加一个toNexmo()方法,以便 Laravel 知道如何渲染消息。
  • 在我们的应用程序中添加Notification::send()来发送通知。

此部分的代码在 Github 上的 send-sms-notification 分支 上。

接收短信回复

所以,我们的客户已经收到了工单内容的短信,接下来呢?他们必须登录到电脑才能回复工单吗?

实际上,我们还没有让他们回复的方法,如果我们不得不花时间构建一些东西,让我们尽可能地简化他们的操作——让我们添加一种让他们通过短信回复的方法。

这篇文章假设您的 Webhook HTTP 方法已在您的 Nexmo 设置 中设置为 POST。默认情况下它设置为 GET,所以去检查一下吧!

当 Nexmo 收到发送到您手机号码的短信时,他们会向您的应用程序发送一个 HTTP 请求。不幸的是,您的应用程序目前正在您的本地机器上运行,Nexmo 无法访问它。这就是ngrok发挥作用的地方。如果您还没有安装它,阅读这篇介绍,然后在安装完成后回来。

要将您的本地服务器公开到互联网,请运行ngrok http 8000。这将启动一个ngrok会话,其中包含大量信息。您唯一需要的是您的ngrok主机名,它将在单词Forwarding旁边,看起来像http://abc123.ngrok.io

Session Status online
Account Michael (Plan: Free)
Version 2.2.8
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://abc123.ngrok.io -> localhost:8000
Forwarding https://abc123.ngrok.io -> localhost:8000
 
Connections ttl opn rt1 rt5 p50 p90
6 0 0.00 0.00 0.11 0.78

获得ngrok URL 后,您需要使用 Nexmo CLI 工具告诉 Nexmo 您的公共 URL 是什么。运行以下命令,将占位符替换为您的 Nexmo 号码和ngrok URL。

nexmo link:sms <NEXMO_NUMBER> http://<NGROK_URL>/ticket-entry

这会将 Nexmo 配置为将该号码的所有 Webhook 发送到http://<NGROK_URL>/ticket-entry。让我们快速测试一下,以确保一切配置正确。打开TicketEntryController,并将store方法替换为以下内容。

public function store(Request $request)
{
error_log(print_r($request->all(), true));
}

这将在我们运行php artisan serve的终端控制台中记录所有传入的请求,但它现在还不能工作。在我们可以测试我们的短信接收器之前,还需要进行一项更改——我们需要为ticket-entry路由禁用 CSRF 保护。

默认情况下,Laravel 通过在每个表单提交时验证 CSRF 令牌来保护我们免受跨站点攻击(这是件好事!),但 Nexmo 不知道要使用的令牌。要解决这个问题,请编辑Http/Middleware/VerifyCsrfToken.php并将ticket-entry添加到$except数组中,如下所示。

protected $except = [
'/ticket-entry'
];

如果您现在回复之前收到的其中一条短信,您应该在屏幕上看到一个信息数组(可能需要一分钟才会出现)。

(
[msisdn] => 14155550201
[to] => 14155550100
[messageId] => 0C0103008660C470
[text] => This is a test
[type] => text
[keyword] => THIS
[message-timestamp] => 2018-01-09 02:11:40
[timestamp] => 1515464263
[nonce] => e143f53f-dev2-42f5-9103-f81e56a406c3
[sig] => ef52cbcabcc2e631eff3853ec1bfe38b
)

这意味着我们的集成配置正确,并且该号码每收到一条短信,Nexmo 就会向我们的应用程序发送一个 HTTP 请求。

最后要做的是用一些代码替换print_r语句,这些代码会将条目添加到工单中。由于 SMS 消息没有回复的概念,我们所能做的就是查找用户正在关注的具有最新活动的工单,并假设他们的回复是针对该工单的。

由于这种逻辑相当复杂,让我们将其添加到我们的User模型中以供将来重用。打开app/User.php并添加以下方法。

public function latestTicketWithActivity() {
// Get all tickets this user is watching
$watchedTickets = TicketSubscription::select('ticket_id')->where('user_id', $this->id)->get()->pluck('ticket_id');
 
// Grab the latest ticket with activity that's in this list
$latestTicketEntry = TicketEntry::select("ticket_id")->whereIn('ticket_id', $watchedTickets)->orderBy('created_at', 'desc')->limit(1)->first();
 
// Fetch the actual ticket
return $latestTicketEntry->ticket()->first();
}

这将返回具有给定用户的最新TicketEntryTicket对象。这就是我们将传入消息附加到工单所需的一切。

回到TicketEntryController,将print_r行替换为以下代码,这将根据用户的电话号码查找用户,获取最新的工单,并将一个包含传入消息内容的新条目附加到该工单。

// Make sure it's in the correct format
$data = $this->validate($request, [
'msisdn' => 'required',
'text' => 'required'
]);
 
// Find the user based on their phone number
$user = User::where('phone_number', $data['msisdn'])->firstOrFail();
 
// And then find their latest ticket
$ticket = $user->latestTicketWithActivity();
 
// Create a new entry with the incoming SMS content
$entry = new TicketEntry([
'content' => $data['text'],
'channel' => 'sms',
]);
 
// Attach this entry to the user and ticket, then save
$entry->user()->associate($user);
$entry->ticket()->associate($ticket);
$entry->save();
 
return response('', 204);

最后,我们需要为我们在上面代码中使用的类添加两个额外的导入。

use App\User;
use App\TicketEntry;

保存并发送短信回复,然后刷新最新的工单,您将看到您的消息出现了。

接收短信比发送短信还要容易!我们从零能力到通过 Webhook 回复短信,只用了五个小步骤。

  • 为一个号码配置 Nexmo Webhook。
  • 运行ngrok公开您的本地系统。
  • 为 Webhook 终结点禁用 CSRF 保护。
  • 获取最新的活动工单。
  • 将传入消息保存为一个新条目。

此部分的代码在 Github 上的 receive-sms-reply 分支 上。

结论

在这篇文章中,我们介绍了通过 Laravel 通知系统和 Nexmo 发送通知,以及接收传入的短信并将它们存储在数据库中。

在本系列的下一部分中,我们将研究如何将语音通话支持添加到我们的帮助台应用程序。响铃的电话比短信更难忽视,对于时间敏感的问题,它可能是正确选择。

如果您想要一些 Nexmo 积分来完成这篇文章并测试平台,请通过 [email protected] 联系我们,并引用 LaravelNews,我们会帮您解决这个问题。

如果您有任何想法或问题,请随时通过 Twitter 上的 @mheap[email protected] 联系我们。


非常感谢 Nexmo 在 Laravel News 上赞助本教程系列。

Michael Heap photo

Michael 是 Nexmo 的 PHP 开发人员倡导者。他使用各种语言和工具,在用户组和会议上与来自世界各地的观众分享他的技术专长。当他抽出时间编写代码时,他喜欢减少系统中的复杂性,使其更加可预测。

Cube

Laravel 新闻

加入 40,000 多名其他开发者,绝不错过新的提示、教程等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动补全和本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

不妥协

Joel 和 Aaron,来自 No Compromises 播客的两位经验丰富的开发者,现在可以为您的 Laravel 项目聘用。⬧ 固定费率 7500 美元/月。⬧ 没有冗长的销售流程。⬧ 没有合同。⬧ 100% 退款保证。

不妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序取得成功。

Kirschbaum
Shift logo

Shift

运行旧版本的 Laravel?即时、自动化的 Laravel 升级和代码现代化,使您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

使用经验丰富的 Laravel 开发人员为您的项目增值,他们拥有 4-6 年的经验,每月只需 2500 美元。获得 160 小时的专用专业知识和 15 天的无风险试用。立即安排电话会议!

Bacancy
Lucky Media logo

Lucky Media

立即获得幸运——Laravel 开发的理想选择,拥有超过 10 年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar:Laravel 电子商务

Laravel 的电子商务。一个开源软件包,将现代无头电子商务功能的强大功能带到 Laravel。

Lunar:Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 工作板

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,它包含运行现代 SaaS 所需的所有功能。支付、漂亮的结账、管理面板、用户仪表板、身份验证、预置组件、统计信息、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您实现 Laravel 无缝升级、降低成本和加速创新的合作伙伴,帮助企业取得成功

Rector
MongoDB logo

MongoDB

通过强大的 MongoDB 和 Laravel 集成来增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。了解 MongoDB 的灵活、现代数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的无依赖项输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

将 Swagger UI 添加到您的 Laravel 应用程序

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel Prompts 构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包在您的 Laravel 应用程序中添加评论

阅读文章