reedware/laravel-sms

增加了通过各种驱动发送短信的功能。

v2.0.0 2023-04-01 18:00 UTC

This package is auto-updated.

Last update: 2024-08-30 01:21:14 UTC


README

Latest Stable Version Laravel Version Build Status

简介

本包提供了一种干净、简单的服务,可以与SMS驱动程序集成,让您能够快速通过您选择的本地或基于云的服务开始发送邮件。

已将一些驱动程序的实现移至单独的包中,以便您只需包含所需驱动程序的集成。默认情况下仅包含不需要第三方包的驱动程序。

支持的驱动程序

本包还设计得任何人都可以添加或覆盖现有驱动程序,这样您就不受默认提供的限制。

配置

可以通过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方法中完成。在这个方法中,您可以调用各种方法,如fromtoview,以配置消息的呈现和投递。

配置发送者

使用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方法手动将数据传递到视图中。通常,您仍然会通过可配置类的构造函数传递数据;然而,您应将此数据设置为protectedprivate属性,以便数据不会自动提供给模板。然后,在调用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方法接受电话号码、用户实例或用户集合。如果您传递对象或对象集合,短信提供商将自动使用它们的numbercarrier属性来设置短信接收者,因此请确保这些属性在您的对象上可用。一旦您指定了接收者,您可以将您的可配置类实例传递给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类实例上调用onQueueonConnection方法,允许您指定消息的连接和队列名称。

$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 服务或定义您的新短信提供者。这种成本通常以毫秒为单位衡量,但对于使用多个第三方包的大型应用程序来说,任何节省的时间都是宝贵的。