reedware / laravel-sms
增加了通过各种驱动发送短信的功能。
Requires
- php: >=7.3
- illuminate/contracts: ^8.0|^9.0|^10.0
- illuminate/support: ^8.0|^9.0|^10.0
Requires (Dev)
- laravel/framework: ^8.0|^9.0|^10.0
- mockery/mockery: ^1.4.2
- orchestra/testbench-core: ^6.0|^7.0|^8.0
- phpunit/phpunit: ^8.5|^9.5
README
简介
本包提供了一种干净、简单的服务,可以与SMS驱动程序集成,让您能够快速通过您选择的本地或基于云的服务开始发送邮件。
已将一些驱动程序的实现移至单独的包中,以便您只需包含所需驱动程序的集成。默认情况下仅包含不需要第三方包的驱动程序。
支持的驱动程序
- 数组
- 电子邮件
- 日志
- Twilio(需要reedware/laravel-sms-twilio)
本包还设计得任何人都可以添加或覆盖现有驱动程序,这样您就不受默认提供的限制。
配置
可以通过sms
配置文件配置短信服务。此文件中配置的每个短信提供者都可以有自己的选项,甚至自己的唯一“传输”方式,允许您的应用程序使用不同的短信服务发送特定的短信。例如,您的应用程序可能使用Twilo发送交易性短信,同时使用Nexmo/Vonage发送大量短信。
驱动程序先决条件
基于API的驱动程序,如Nexmo和Zenvia,需要Guzzle HTTP库,您可以通过Composer包管理器安装。
composer require guzzlehttp/guzzle
默认情况下不提供需要第三方包的附加驱动程序。您需要安装特定于驱动程序的包(它将包括您所需的第三方依赖项)。您可以通过查看支持的驱动程序列表来确定您需要安装哪个包。
电子邮件驱动程序
要使用电子邮件驱动程序,首先确保您的邮件器设置正确。您可以使用与Laravel集成的任何邮件驱动程序。
Twilio驱动程序
要使用Twilio驱动程序,首先安装特定于驱动程序的包
composer require reedware/laravel-sms-twilio
然后,将配置文件config/sms.php
中的default
选项设置为twilio
。接下来,验证您的Twilio提供者配置文件是否包含以下选项
'your-driver-name' => [
'transport' => 'twilio',
'account_sid' => 'your-twilio-account-sid',
'auth_token' => 'your-twilio-auth-token'
],
如果您不使用“US”Twilio区域,您可以在提供者配置中定义您的区域ID
'your-driver-name' => [
'transport' => 'twilio',
'account_sid' => 'your-twilio-account-sid',
'auth_token' => 'your-twilio-auth-token',
'region' => 'sg1' // singapore
],
此外,默认情况下禁用了ssl主机和同伴验证。要启用此功能,您可以在提供者配置中包含verify标志
'your-driver-name' => [
'transport' => 'twilio',
'account_sid' => 'your-twilio-account-sid',
'auth_token' => 'your-twilio-auth-token',
'verify' => true
],
有关更多信息,请参阅Laravel SMS Twilio包的文档。
生成Textables
您的应用程序发送的每种类型的短信都表示为“Textable”类。这些类默认存储在app/SMS
目录中。您可以使用make:sms
命令生成新的Textable。
php artisan make:sms OrderShipped
编写Textables
所有可配置类的配置都在build
方法中完成。在这个方法中,您可以调用各种方法,如from
、to
和view
,以配置消息的呈现和投递。
配置发送者
使用from
方法
首先,让我们探索配置短信的发送者。换句话说,即消息将“来自”谁。有两种配置发送者的方式。首先,您可以在您可配置类的build
方法中使用from
方法
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->from('9995551234')
->view('sms.orders.shipped');
}
使用全局from
地址
然而,如果您的应用程序使用相同的“from”号码发送所有短信,则在每个生成的可配置类中调用from
方法可能会变得繁琐。相反,您可以在配置文件config/sms.php
中指定全局“from”号码。如果没有在可配置类中指定其他“from”地址,则将使用此地址
'from' => '9995551234',
或者,您也可以为短信提供商特别定义一个“from”号码
'your-driver-name' => [
'transport' => 'email',
'from' => '9995551234'
],
配置视图
在可配置类的build
方法中,您可以使用view
方法指定在渲染短信内容时应使用哪个模板
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('sms.orders.shipped');
}
您可能希望创建一个
resources/views/sms
目录来存放所有短信模板;然而,您可以将它们放在resources/views
目录中的任何位置。
纯文本消息
如果您想将消息定义为预渲染的文本,您可以使用text
方法。与view
方法不同,text
方法已经包含了消息的已渲染内容。
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->text('Your order has been shipped!');
}
视图数据
通过公共属性
通常,您会想传递一些数据到视图中,以便在渲染短信时使用。您有两种方法可以将数据提供给视图。首先,您在可配置类上定义的任何公共属性都将自动提供给视图。例如,您可以将数据传递到可配置类的构造函数中,并将其设置为类上定义的公共属性
<?php
namespace App\SMS;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Reedware\LaravelSMS\Textable;
class OrderShipped extends Textable
{
use Queueable, SerializesModels;
/**
* The order instance.
*
* @var \App\Models\Order
*/
public $order;
/**
* Creates a new message instance.
*
* @param \App\Models\Order $order
*
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Builds the message.
*
* @return $this
*/
public function build()
{
return $this->view('sms.orders.shipped');
}
}
一旦数据被设置为公共属性,它将自动在视图中可用,因此您可以像访问Blade模板中的任何其他数据一样访问它
<div>
Price: {{ $order->price }}
</div>
注意:始终将
$message
变量传递给短信视图,因此您应避免在视图有效负载中传递message
变量。
通过with
方法
如果您想在不发送到模板之前自定义短信数据的格式,您可以通过with
方法手动将数据传递到视图中。通常,您仍然会通过可配置类的构造函数传递数据;然而,您应将此数据设置为protected
或private
属性,以便数据不会自动提供给模板。然后,在调用with
方法时,传递一个包含您希望提供给模板的数据的数组
<?php
namespace App\SMS;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Reedware\LaravelSMS\Textable;
class OrderShipped extends Textable
{
use Queueable, SerializesModels;
/**
* The order instance.
*
* @var \App\Models\Order
*/
protected $order;
/**
* Creates a new message instance.
*
* @param \App\Models\Order $order
*
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Builds the message.
*
* @return $this
*/
public function build()
{
return $this->view('sms.orders.shipped')
->with([
'orderName' => $this->order->name,
'orderPrice' => $this->order->price,
]);
}
}
一旦数据被传递给with
方法,它将自动在视图中可用,因此您可以像访问Blade模板中的任何其他数据一样访问它
<div>
Price: {{ $orderPrice }}
</div>
发送短信
要发送消息,请使用SMS
外观的to
方法。to
方法接受电话号码、用户实例或用户集合。如果您传递对象或对象集合,短信提供商将自动使用它们的number
和carrier
属性来设置短信接收者,因此请确保这些属性在您的对象上可用。一旦您指定了接收者,您可以将您的可配置类实例传递给send
方法
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Order;
use App\SMS\OrderShipped;
use Illuminate\Http\Request;
use Reedware\LaravelSMS\SMS;
class OrderController extends Controller
{
/**
* Ships the given order.
*
* @param \Illuminate\Http\Request $request
* @param integer $orderId
*
* @return \Illuminate\Http\Response
*/
public function ship(Request $request, $orderId)
{
$order = Order::findOrFail($orderId);
// Ship order...
SMS::to($request->user())->send(new OrderShipped($order));
}
}
遍历接收者
有时,您可能需要通过遍历接收者/电话号码的数组来将可配置短信发送给接收者列表。由于to
方法将电话号码追加到可配置短信的接收者列表中,因此您应始终为每个接收者重新创建可配置短信实例
foreach (['9995551234', '9995556789'] as $recipient) {
SMS::to($recipient)->send(new OrderShipped($order));
}
通过特定提供商发送短信
默认情况下,您在sms
配置文件中配置为default
提供程序的短信服务提供商将被使用。然而,您可以使用provider
方法通过特定的提供程序配置发送消息。
SMS::provider('twilio')
->to($request->user())
->send(new OrderShipped($order));
闭包消息
如果您不希望使用textables,您也可以通过使用闭包实现来发送短信。要发送基于闭包的消息,请在SMS
外观上使用send
方法,并为其提供三个参数。首先,包含短信文本的view
的名称。其次,您希望传递给视图的数据数组。最后,一个接收消息实例的Closure
回调,允许您自定义接收者、正文和其他短信消息方面。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Order;
use Illuminate\Http\Request;
use Reedware\LaravelSMS\SMS;
class OrderController extends Controller
{
/**
* Ships the given order.
*
* @param \Illuminate\Http\Request $request
* @param integer $orderId
*
* @return \Illuminate\Http\Response
*/
public function ship(Request $request, $orderId)
{
$order = Order::findOrFail($orderId);
// Ship order...
SMS::send('sms.orders.shipped', ['order' => $order], function($m) use ($request) {
$m->to($request()->user->number);
});
}
}
如果您想发送纯文本消息,您可以使用SMS
外观上的raw
方法。
SMS::raw('Your order has been shipped!', function($m) use ($request) {
$m->to($request()->user->number);
});
队列消息
排队短信消息
由于发送短信可能会大大增加应用程序的响应时间,许多开发者选择将短信消息排队以进行后台发送。使用Laravel内置的队列可以轻松实现这一点。要排队短信消息,请在指定消息的接收者后,在SMS
外观上使用queue
方法。
SMS::to($request->user())
->queue(new OrderShipped($order));
此方法将自动将作业推送到队列,以便在后台发送消息。在使用此功能之前,您需要配置您的队列。
延迟消息队列
如果您希望延迟队列短信消息的发送,您可以使用later
方法。作为其第一个参数,later
方法接受一个表示消息应发送时间的DateTime
实例。
$when = now()->addMinutes(10);
SMS::to($request->user())
->later($when, new OrderShipped($order));
推送到特定队列
由于使用make:sms
命令生成的所有textable类都使用了Illuminate\Bus\Queueable
特质,您可以在任何textable类实例上调用onQueue
和onConnection
方法,允许您指定消息的连接和队列名称。
$message = (new OrderShipped($order))
->onConnection('sqs')
->onQueue('sms');
SMS::to($request->user())
->queue($message);
默认排队
如果您有希望始终排队的textable类,您可以在该类上实现ShouldQueue
契约。现在,即使您在发送短信时调用send
方法,textable也会排队,因为它实现了契约。
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Textable implements ShouldQueue
{
//
}
渲染Textables
捕获消息内容
$invoice = App\Models\Invoice::find(1);
return (new App\SMS\InvoicePaid($invoice))->render();
在浏览器中预览Textables
有时您可能希望捕获textable的消息内容而无需发送它。为此,您可以调用textable的render
方法。此方法将返回textable的评估内容,作为一个字符串。
Route::get('textable', function () {
$invoice = App\Models\Invoice::find(1);
return new App\SMS\InvoicePaid($invoice);
});
本地化Textables
您还可以用除当前语言之外的任何语言发送textable,并且如果短信消息被排队,语言也会被记住。
为此,SMS
外观提供了一个locale
方法来设置所需的语言。当textable正在格式化时,应用程序将切换到该区域设置,然后在格式化完成后返回到之前的区域设置。
SMS::to($request->user())->locale('es')->send(
new OrderShipped($order)
);
用户首选区域设置
有时,应用程序会存储每个用户的偏好区域设置。通过在一个或多个模型上实现HasLocalePreference
契约,您可以指示应用程序在发送短信时使用此存储的区域设置。
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Returns the user's preferred locale.
*
* @return string
*/
public function preferredLocale()
{
return $this->locale;
}
}
实现接口后,首选区域设置将在发送textable和通知到模型时自动使用。因此,使用此接口时无需调用locale
方法。
SMS::to($request->user())->send(new OrderShipped($order));
SMS & 本地开发
当开发一个发送短信的应用程序时,你可能不希望真正发送到实际的移动设备。在本地开发过程中,有几种方法可以“禁用”短信的实际发送。
日志驱动
而不是发送您的短信,日志短信驱动会将所有短信写入您的日志文件以供检查。
通用收件人
另一种解决方案是设置所有由应用程序发送的短信的通用收件人。这样,您应用程序生成的所有短信都将发送到特定的电话号码,而不是发送短信时实际指定的电话号码。这可以通过在 config/sms.php
配置文件中的 to
选项来完成
'to' => [
'number' => '9995551234',
'carrier' => 'Example'
],
事件
在发送短信的过程中,会触发两个事件。在发送消息之前会触发 MessageSending
事件,在消息发送后会触发 MessageSent
事件。此外,在短信传输失败时,还会触发 MessageFailed
事件。记住,这些事件是在短信正在 发送 时触发的,而不是在它排队时。
您可以在您的 EventServiceProvider
中为该事件注册事件监听器
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Reedware\LaravelSMS\Events\MessageSending' => [
'App\Listeners\LogSendingMessage',
],
'Reedware\LaravelSMS\Events\MessageSent' => [
'App\Listeners\LogSentMessage',
],
'Reedware\LaravelSMS\Events\MessageFailed' => [
'App\Listeners\LogFailedMessage',
]
];
添加自定义提供者
您可以使用 SMS
门面上的 extend
方法来定义自己的短信提供者。您应该在服务提供者中放置对 extend
的调用。作为一个基本示例,我们将使用 Laravel 应用程序附带的 AppServiceProvider
,但理想情况下,您会想要一个专用的服务提供者。以下是我们可以放置在该提供者中的代码
<?php
namespace App\Providers;
use App\Services\SMS\CallfireTransport;
use GuzzleHttp\Client;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstraps the application services.
*
* @return void
*/
public function boot()
{
SMS::extend('callfire', function($app, $name, array $config) {
// Return an instance of Reedware\LaravelSMS\Contracts\Transport...
return new CallfireTransport(new Client, $config['username'], $config['password']);
});
}
}
如上例所示,传递给 extend
方法的回调应该返回一个实现 Reedware\LaravelSMS\Contracts\Transport
接口的实现。此接口包含您需要实现以定义自定义短信提供者的方法。一旦您的自定义短信提供者已经定义,您就可以在 sms.php
配置文件的 providers
配置中使用此提供者
'providers' => [
'callfire' => [
'transport' => 'callfire',
'username' => 'my-callfire-username',
'password' => 'my-callfire-password'
]
]
如果您需要启动的示例,您可以查看任何 reedware/laravel-sms-*
包,因为这些定义了自定义短信提供者。
延迟注册
当注册自定义短信提供者时,上述简化的示例使用了 SMS
门面。然而,SMS
服务是延迟加载的,这意味着它不会在第一次被调用之前初始化。通过在服务提供者中调用它,您实际上是在强制启动 SMS
服务。这种开销的运行时成本很低,因为这个包相当轻量级,但对于更大的应用程序来说可能不是最佳选择。对于较小的应用程序,这种性能差异可能微不足道。
更性能友好的版本是在 SMS
服务启动后扩展它,而不是仅仅为了扩展它而启动 SMS
服务,并可能永远不会在当前请求中使用它。这里有一个类似上面的修改后的示例,但使用延迟注册
<?php
namespace App\Providers;
use App\Services\SMS\CallfireTransport;
use GuzzleHttp\Client;
use Illuminate\Support\ServiceProvider;
use Reedware\LaravelSMS\Events\ManagerBooted;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstraps the application services.
*
* @return void
*/
public function boot()
{
$this->app['events']->listen(ManagerBooted::class, function($event) {
$event->manager->extend('callfire', function($app, $name, array $config) {
return new CallfireTransport(new Client, $config['username'], $config['password']);
});
});
}
}
此实现监听一个在 SMS
服务启动后触发的事件,该事件本身不会启动服务。同样,这里唯一真正节省的运行时开销是针对那些 不 使用 SMS
服务的请求,因此不需要启动 SMS
服务或定义您的新短信提供者。这种成本通常以毫秒为单位衡量,但对于使用多个第三方包的大型应用程序来说,任何节省的时间都是宝贵的。