Cómo crear un portal de SMS con Laravel y Twilio

March 22, 2019
Redactado por
Brian Iyoha
Colaborador
Las opiniones expresadas por los colaboradores de Twilio son propias

Cómo crear un portal de SMS con Laravel y Twilio

En algún momento del flujo de trabajo de la aplicación, es posible que deba transmitir información importante a sus usuarios. Gracias al avance de las tecnologías web, esto se puede hacer fácilmente con las notificaciones push. Sin embargo, la mayoría de estos servicios requieren que sus usuarios tengan conexión a Internet y, por desgracia, esto no sucede siempre. Afortunadamente, podemos superar esta dificultad mediante un sistema de notificación que no depende de Internet.

En este tutorial, le enseñaremos cómo utilizar El SMS programable de Twilio para crear un portal de notificación por SMS con Laravel. Una vez que hayamos terminado, habrá desarrollado un portal personalizado de notificación por SMS que le permite notificar a sus usuarios a través de un panel de control con SMS.

Requisito previo 

Para realizar este tutorial, necesitará lo siguiente:

Configurar el proyecto

En primer lugar, debemos crear un nuevo proyecto Laravel, ya sea con el instalador de Laravel o con Composer. En este tutorial, utilizaremos el instalador de Laravel. Si no lo tiene instalado, puede consultar cómo hacerlo desde la Documentación de Laravel. Para generar un proyecto nuevo de Laravel, ejecute este comando en su terminal:

$ laravel new sms-portal

Ahora, cambie su directorio de trabajo a sms-portal e instale el SDK de Twilio a través de Composer:

$ cd sms-portal
$ composer require twilio/sdk

Si no tiene instalado Composer en su computadora, puede hacerlo siguiendo las instrucciones aquí.

Después de instalar el SDK de PHP de Twilio, necesitamos obtener nuestras credenciales de Twilio desde el panel de control de Twilio. Diríjase a su panel de control, tome su account_sid y auth_token.

Pantalla de la consola Twilio con información de SID de cuenta

Ahora, vaya a la sección Phone Number (Número de teléfono) para obtener su número de teléfono con SMS activado.

Pantalla de la consola Twilio con lista de números activos

El siguiente paso es actualizar el archivo .env con nuestras credenciales de Twilio. Por lo tanto, abra el archivo .env que está ubicado en la raíz del directorio del proyecto y agregue estos valores:

TWILIO_SID="INSERT YOUR TWILIO SID HERE"
TWILIO_AUTH_TOKEN="INSERT YOUR TWILIO TOKEN HERE"
TWILIO_NUMBER="INSERT YOUR TWILIO NUMBER IN [E.164] FORMAT"

Configurar la base de datos

Ahora que tenemos un proyecto Laravel básico con el SDK de Twilio instalado, vamos a crear nuestra base de datos. Si utiliza una aplicación de GUI como phpMyAdmin para administrar su base de datos, continúe y cree una base de datos llamada sms_portal y omita esta sección. Si no cuenta con esa aplicación, utilice una herramienta equivalente; y, si no tiene MySQL instalada, instálela desde el sitio oficial para su plataforma.

Ponga en marcha el terminal y ejecute este comando para iniciar sesión en MySQL:

$ mysql -u {your_user_name}

NOTA: Agregue el -p si tiene una contraseña para su instancia de MySQL.

Una vez que haya iniciado sesión, ejecute el siguiente comando para crear una nueva base de datos:

mysql> create database sms_portal;
mysql> exit;

Cambiemos la configuración de nuestra base de datos según corresponda en el archivo .env en la raíz de nuestra carpeta del proyecto.

DB_DATABASE=sms_portal
DB_USERNAME=root
DB_PASSWORD=

Crear migración

Ya que hemos creado nuestra base de datos, vamos a crear nuestra migración a la base de datos. Podemos hacer esto simplemente ejecutando este comando en nuestro terminal:

$ php artisan make:migration create_users_phone_number_table

Esto generará un archivo de migración {current_time_stamp}_create_users_phone_number_table en el directorio /database/migrations.

Ahora, abra la carpeta del proyecto en su IDE (entorno de desarrollo integrado) o editor de texto favorito para que podamos comenzar a hacer cambios, según sea necesario. Abra el archivo de migración que acabamos de crear. Deberíamos tener el mismo contenido que este:

Pantalla de Visual Studio con ejemplo de código

Necesitamos agregar la columna phone_number a nuestra tabla. Podemos hacerlo mediante la edición del método up(), como se indica a continuación:

public function up() {
    Schema::create('users_phone_number', function (Blueprint $table) {
        $table->increments('id');
        $table->string('phone_number'); 
        $table->timestamps();
    });
}

Ahora, tenemos nuestro archivo de migración listo para migrar. Podemos hacerlo simplemente ejecutando lo siguiente en nuestro terminal:

$ php artisan migrate

Debemos tener un resultado de salida similar a este:

Pantalla de terminal con información de migración

Crear la interfaz de usuario

En este punto, tenemos nuestra configuración del proyecto. Es hora de crear una interfaz de usuario sencilla para agregar datos a nuestra base de datos y también para enviar notificaciones de SMS.

Abra /resources/views/welcome.blade.php y realice los siguientes cambios en el bloque <head>.

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SMS Portal With Twilio</title>
    <!-- Bootstrap styles CDN -->
     <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
</head>

Simplemente eliminamos el diseño inicial en la página y agregamos Bootstrap mediante BootstrapCDN  para darle un estilo más sencillo. A continuación, vamos a crear dos formularios en la página. Uno para registrar el número de teléfono de los usuarios y otro para enviar mensajes de notificación personalizados a los usuarios seleccionados. Realice los siguientes cambios en el bloque body.

<body>
    <div class="container">
        <div class="jumbotron">
            <div class="row">
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Add Phone Number
                        </div>
                        <div class="card-body">
                            <form>
                                <div class="form-group">
                                    <label>Enter Phone Number</label>
             <input type="tel" class="form-control" placeholder="Enter Phone Number">
                                </div>
                  <button type="submit" class="btn btn-primary">Register User</button>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Send SMS message
                        </div>
                        <div class="card-body">
                            <form>
                                <div class="form-group">
                                    <label>Select users to notify</label>
                                    <select multiple class="form-control">
                                        @foreach ($users as $user)
                                        <option>{{$user->phone_number}}</option>
                                        @endforeach
                                    </select>
                                </div>
                                <div class="form-group">
                                    <label>Notification Message</label>
                                 <textarea class="form-control" rows="3"></textarea>
                                </div>
             <button type="submit" class="btn btn-primary">Send Notification</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

Si observa con más detalle el código anterior, notará que tenemos lo siguiente:

      <select multiple class="form-control">
            @foreach ($users as $user)
              <option>{{$user->phone_number}}</option>
           @endforeach
      </select>

Este fragmento ayuda a generar un campo de opción para cada número de teléfono de usuario disponible devuelto con esta vista. Implementaremos esta característica en breve.  

Guardar el número de teléfono de los usuarios

En primer lugar, vamos a crear un modelo que usaremos para consultar y también insertar registros en la base de datos. Ponga en marcha su terminal en el directorio del proyecto y ejecute lo siguiente:

 $ php artisan make:model UsersPhoneNumber

Abra el archivo creado en app/UsersPhoneNumber.php y agregue el siguiente código:

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class UsersPhoneNumber extends Model
{
   protected $table= "users_phone_number";
   protected $fillable = [
        'phone_number'
    ];
}

NOTA: Estos son algunos aspectos que debe considerar.

  • Agregar protected $table= "users_phone_number"; le indica a Eloquent del nombre de la tabla que se utilizará. Si esto no se proporciona, el nombre plural de la clase se utilizará como nombre de tabla. 
  • Agregar protected $fillable le indica a Eloquent que realice la asignación masiva de campos (leer más en el enlace anterior).

Lo siguiente es crear el controlador donde implementaremos la lógica necesaria para cada ruta de solicitud. Una vez más, ponga en marcha el terminal y ejecute lo siguiente:

$ php artisan make:controller HomeController

Esto generará un archivo del controlador en app/Http/Controllers/HomeController.php con el siguiente contenido:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    //
}

Crear el método para guardar el número de teléfono

Creemos una función que genere un ejemplo de un nuevo modelo UsersPhoneNumber con los datos transferidos desde el cuerpo de la solicitud:

<?php

/**
 * Store a new user phone number.
 *
 * @param  Request  $request
 * @return Response
 */
public function storePhoneNumber(Request $request)
{
    //run validation on data sent in
    $validatedData = $request->validate([
        'phone_number' => 'required|unique:users_phone_number|numeric'
    ]);
    $user_phone_number_model = new UsersPhoneNumber($request->all());
    $user_phone_number_model->save();
    return back()->with(['success'=>"{$request->phone_number} registered"]);
}

A partir del código anterior, ejecutamos la validación de los datos que se transfieren desde el cuerpo de $request antes de proceder con la creación de una nueva instancia de UsersPhoneNumber. Después de guardar el número de teléfono del usuario, nos dirigirá nuevamente a la página de bienvenida con un mensaje de éxito que se muestra en la sesión.

Devolver la vista con los números de teléfono de los usuarios

Ahora tenemos que devolver la vista con los datos de los números de teléfono registrados. Escribimos una función simple en el HomeController (Controlador de inicio) que consulta la tabla users_phone_number y devuelve los resultados de la consulta a la vista:

<?php

/**
 * Show the forms with users phone number details.
 *
 * @return Response
 */
public function show()
{
    $users = UsersPhoneNumber::all(); //query db with model
    return view('welcome', compact("users")); //return view with data
}

NOTA: compact() es una función PHP que le permite crear una matriz con nombres variables y sus valores.

Enviar mensajes con SMS programable de Twilio

En el siguiente paso, implementamos el envío de SMS mediante la biblioteca de SMS programable de Twilio. Cree una función privada en el HomeController (Controlador de inicio) para que actúe como una función de ayuda para enviar mensajes:

<?php

/**
 * Sends sms to user using Twilio's programmable sms client
 * @param String $message Body of sms
 * @param Number $recipients string or array of phone number of recepient
 */
private function sendMessage($message, $recipients)
{
    $account_sid = getenv("TWILIO_SID");
    $auth_token = getenv("TWILIO_AUTH_TOKEN");
    $twilio_number = getenv("TWILIO_NUMBER");
    $client = new Client($account_sid, $auth_token);
    $client->messages->create($recipients, 
            ['from' => $twilio_number, 'body' => $message] );
}

La función recibe dos parámetros de $message y $recipients. Luego, obtenemos nuestras credenciales Twilio almacenadas en las variables del entorno mediante la función getent() de PHP integrada, después de la cual creamos un ejemplo de un nuevo Twilio Client con las credenciales. Ahora, podemos proceder a enviar el SMS mediante una llamada a:

$client->messages->create($recipients, [
    'from' => $twilio_number, 
    'body' => $message
]);

La función messages->create() acepta dos parámetros del receptor del mensaje y una matriz con las propiedades de from y body donde from es su número de teléfono activo de Twilio.

Enviar una notificación al usuario cuando se registra

Hasta el momento, hemos completado nuestra función sendMessage(), que usaremos para enviar mensajes a los usuarios. Ahora, actualicemos nuestra función storePhoneNumber() para notificar a los usuarios cuando se registran correctamente. Para ello, realice los siguientes cambios a la función storePhoneNumber():

<?php

/**
 * Store a new user phone number.
 *
 * @param  Request  $request
 * @return Response
 */
public function storePhoneNumber(Request $request)
{
    //run validation on data sent in
    $validatedData = $request->validate([
        'phone_number' => 'required|unique:users_phone_number|numeric',
    ]);
    $user_phone_number_model = new UsersPhoneNumber($request->all());
    $user_phone_number_model->save();
    $this->sendMessage('User registration successful!!', $request->phone_number);
    return back()->with(['success' => "{$request->phone_number} registered"]);
}

Fantástico. Ahora, cada vez que se agrega el número de teléfono de un usuario a nuestra base de datos, podemos enviarle al usuario un mensaje de notificación para avisarle sobre la acción que se llevó a cabo.

Enviar notificaciones personalizadas

A continuación, escribamos una función para enviar mensajes personalizados a los usuarios seleccionados. Agregue el código siguiente a HomeController:

<?php 

/**
 * Send message to a selected users
 */
public function sendCustomMessage(Request $request)
{
    $validatedData = $request->validate([
        'users' => 'required|array',
        'body' => 'required',
    ]);
    $recipients = $validatedData["users"];
    // iterate over the array of recipients and send a twilio request for each
    foreach ($recipients as $recipient) {
        $this->sendMessage($validatedData["body"], $recipient);
    }
    return back()->with(['success' => "Messages on their way!"]);
}

Esta función pasa los datos validados del cuerpo de cuerpo de $request a la variable $validatedData, lo que nos permite iterar sobre la matriz de $validatedData[users] y enviar a cada usuario el mensaje recibido de $validatedData["body"]. A continuación, redirigiremos a la página de bienvenida con un mensaje que se muestra en la sesión.

Crear rutas

Hemos creado correctamente las funciones del controlador. Ahora agreguemos nuestras rutas a la aplicación. Abra routes/web.php y realice los siguientes cambios:

<?php
Route::get('/', 'HomeController@show');
Route::post('/', 'HomeController@storePhoneNumber');
Route::post('/custom', 'HomeController@sendCustomMessage');

Actualizar el campo del formulario con rutas

Ahora diríjase a resources/views/welcome.blade.php y realice los siguientes cambios en el campo del formulario:

//add the method attribute to the Register User form
// also add the name attributes to the input field 
<form method="POST">
    @csrf
    <div class="form-group">
        <label>Enter Phone Number</label>
        <input type="tel" class="form-control" name="phone_number" placeholder="Enter Phone Number">
    </div>
    <button type="submit" class="btn btn-primary">Register User</button>  
</form>

y

//add the method and action attributes to the Send custom message form
// also add the name attributes to the input fields 
<form method="POST" action="/custom">
    @csrf
    <div class="form-group">
        <label>Select users to notify</label>
        <select name="users[]" multiple class="form-control">
            @foreach ($users as $user)
            <option>{{$user->phone_number}}</option>
            @endforeach
        </select>
    </div>
    <div class="form-group">
        <label>Notification Message</label>
        <textarea name="body" class="form-control" rows="3"></textarea>
    </div>
    <button type="submit" class="btn btn-primary">Send Notification</button>
</form>

Prueba de nuestro código

Unamos todo el código que hemos hecho hasta ahora. En este punto, su HomeController.php debe verse de la siguiente manera:

<?php
namespace App\Http\Controllers;

use App\UsersPhoneNumber;
use Illuminate\Http\Request;
use Twilio\Rest\Client;

class HomeController extends Controller
{
    /**
     * Show the forms with users phone number details.
     *
     * @return Response
     */
    public function show()
    {
        $users = UsersPhoneNumber::all();
        return view('welcome', compact("users"));
    }
    /**
     * Store a new user phone number.
     *
     * @param  Request  $request
     * @return Response
     */
    public function storePhoneNumber(Request $request)
    {
        //run validation on data sent in
        $validatedData = $request->validate([
            'phone_number' => 'required|unique:users_phone_number|numeric',
        ]);
        $user_phone_number_model = new UsersPhoneNumber($request->all());
        $user_phone_number_model->save();
        $this->sendMessage('User registration successful!!', $request->phone_number);
        return back()->with(['success' => "{$request->phone_number} registered"]);
    }
    /**
     * Send message to a selected users
     */
    public function sendCustomMessage(Request $request)
    {
        $validatedData = $request->validate([
            'users' => 'required|array',
            'body' => 'required',
        ]);
        $recipients = $validatedData["users"];
        // iterate over the array of recipients and send a twilio request for each
        foreach ($recipients as $recipient) {
            $this->sendMessage($validatedData["body"], $recipient);
        }
        return back()->with(['success' => "Messages on their way!"]);
    }
    /**
     * Sends sms to user using Twilio's programmable sms client
     * @param String $message Body of sms
     * @param Number $recipients Number of recipient
     */
    private function sendMessage($message, $recipients)
    {
        $account_sid = getenv("TWILIO_SID");
        $auth_token = getenv("TWILIO_AUTH_TOKEN");
        $twilio_number = getenv("TWILIO_NUMBER");
        $client = new Client($account_sid, $auth_token);
        $client->messages->create($recipients, ['from' => $twilio_number, 'body' => $message]);
    }
}

y para nuestra vista, su welcome.blade.php debe verse así:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SMS Portal With Twilio</title>
    <!-- Styles -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
        crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <div class="jumbotron">
            @if (session('success'))
            <div class="alert alert-success">
                {{ session('success') }}
            </div>
            @endif
            @if ($errors->any())
            <div class="alert alert-danger">
                <ul>
                    @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                    @endforeach
                </ul>
            </div>
            @endif
            <div class="row">
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Add Phone Number
                        </div>
                        <div class="card-body">
                            <form method="POST">
                                @csrf
                                <div class="form-group">
                                    <label>Enter Phone Number</label>
                                    <input type="tel" class="form-control" name="phone_number" placeholder="Enter Phone Number">
                                </div>
                                <button type="submit" class="btn btn-primary">Register User</button>
                            </form>
                        </div>
                    </div>
                </div>
                <div class="col">
                    <div class="card">
                        <div class="card-header">
                            Send SMS message
                        </div>
                        <div class="card-body">
                            <form method="POST" action="/custom">
                                @csrf
                                <div class="form-group">
                                    <label>Select users to notify</label>
                                    <select name="users[]" multiple class="form-control">
                                        @foreach ($users as $user)
                                        <option>{{$user->phone_number}}</option>
                                        @endforeach
                                    </select>
                                </div>
                                <div class="form-group">
                                    <label>Notification Message</label>
                                    <textarea name="body" class="form-control" rows="3"></textarea>
                                </div>
                                <button type="submit" class="btn btn-primary">Send Notification</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Si su código es el mismo, está listo para continuar. Si no es así, vuelva a revisar su código para averiguar lo que falta.

Ejecutar nuestra aplicación

Abra el terminal, vaya al directorio del proyecto y ejecute el siguiente comando:

$ php artisan serve

Esto servirá a su aplicación Laravel en un puerto de host local, normalmente 8000. Abra el enlace del host local impreso después de ejecutar el comando en su navegador y debería recibir una página como esta:

Pantalla de ejecución del proyecto

Continúe y registre un nuevo número de teléfono. Si todo sale bien, debería recibir un mensaje de texto en breve en el que se lo notifica acerca del registro.

Pantalla de la aplicación SMS con mensaje recibido

 También puede probar el envío de notificaciones personalizadas mediante la selección de un usuario del campo de selección y la inserción del texto que se enviará a los usuarios seleccionados en el área de texto. Cuando haya terminado, haga clic en el botón Send Notification (Enviar notificación) y recibirá un SMS con el mensaje de notificación personalizado.

Animación con página de ejemplo funcional

Conclusión

Ahora que completó este tutorial, debe poder integrar el SMS programable de Twilio en su aplicación Laravel y enviar mensajes de notificación a través de SMS. Si desea ver el código fuente completo de este tutorial, puede encontrarlo en GitHub

Puede avanzar en esto aún más si permite que los usuarios realicen acciones a partir de las notificaciones de SMS que se les envían.

Este artículo fue traducido del original "How to Create an SMS Portal With Laravel and Twilio". Mientras estamos en nuestros procesos de traducción, nos encantaría recibir sus comentarios en help@twilio.com - las contribuciones valiosas pueden generar regalos de Twilio.