Создаем онлайн (real time) чат используя Laravel и Nexmo
Вот и настало время 3й части об интеграции Laravel и Nexmo. В первых двух частях вы научились отправлять SMS и совершать голосовые звонки используя Nexmo.
Сегодня мы расширим функционал и добавим онлайн чат используя Nexmo Stitch. Stitch - возьмет на себя заботу о всем том, что необходимо для создания real-time чата, предоставляя нам websocket к которому мы будем подключаться и получать события для нашего чата. Сегодня мы используем только Javascript api, однако Stitch может так же работать на iOS и Android, так что вы с легкостью сможете добавить поддержку этих платформ в свой проект.
Перед началом работы
Перед тем как вы начнете чтение данной статьи, вам необходимо иметь аккаунт Nexmo, а так же загрузить код Deskmo из github, если вы пропустили первые две статьи.
Nexmo Stitch на данный момент находится на стадии Developer Preview, так что вам необходимо установить бета версии Nexmo CLI и Nexmo PHP. Выполните следующие команды в той же директории где находится ваш composer.json :
npm install -g [email protected] composer require nexmo/client:1.3.0-beta5
А так же выполните команду php artisan serve чтобы запустить вебсервер.
Создание пользователя в Nexmo
Нам потребуется создать персональный профиль для каждого пользователя, для того чтобы использовать Nexmo Stitch. Чтобы на 100% быть уверенными что у каждого нашего пользователя есть профиль в Nexmo, мы будем создавать такой профиль во время регистрации.
На заметку: профиль в Nexmo - это не тоже самое что аккаунт в Nexmo. Профиль необходим чтобы вы могли связать пользователя в Nexmo с вашим локальным пользователем. Создание профиля НЕ создает аккаунт в Nexmo.
Сперва нам необходимо изменить таблицу users , и добавить в нее поле nexmo_id . а затем добавить в метод Auth\[email protected] API запрос, который будет создавать профиль в Nexmo и возвращать ID. Начнем с создания миграции. Выполните команду:
php artisan make:migration add_nexmo_id_to_users
И измените созданный файл миграции следующим образом:
public function up()
public function down()
Далее мы добавим поле nexmo_id в модель User . Откройте файл app/User.php и добавьте nexmo_id в массив $fillable . Это позволит передавать nexmo_id в метод create , во время регистрации в RegisterController .
И наконец мы можем изменить app/Http/Controllers/Auth/RegisterController.php и добавить в него API вызов, который будет создавать профиль в Nexmo. Добавьте в начале файла строки:
use Nexmo; use Nexmo\User\User as NexmoUser;
Теперь измените метод create , добавив в него создание нового объекта NexmoUser и отправку запроса с помощью Nexmo::User()->create() . Мы будем использовать email пользователя в качестве его имени в Nexmo:
$user = (new NexmoUser())->setName($data['email']); $nexmoUser = Nexmo::user()->create($user);
return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => bcrypt($data['password']), 'phone_number' => $data['phone_number'], 'nexmo_id' => $nexmoUser->getId(), ]);
Осторожно! Команда migrate:refresh сотрет все данные из вашей базы данных. Если у вас там есть что-то нужно, то советую сделать бекап, перед тем как выполнять эту команду.
С этого момента все новые пользователи которые будут регистрироваться в нашей системе, будут так же автоматически зарегистрированны в Nexmo. Выполните команду php artisan migrate:refresh чтобы пересоздать базу данных. После этого перейдите на страницу регистрации и создайте двух новых пользователей (менеджера - role и кастомера - role >
Добавление чата как метод нотификации
Настало время изменить процесс создания заявки, и добавить в него возможность выбрать веб-чат как способ для связи. Откройте файл resources/views/ticket/create.blade.php и добавьте в него код ниже:
<div > <label> <input type="radio" name="notification_method" value="in-app-messaging"> In-App Messaging </label> </div>
Затем откройте app/Http/Controllers/TicketController.php и добавьте еще один elseif блок (где-то в районе 100 строчки):
> elseif ($data['notification_method'] === 'in-app-messaging') else
Теперь менеджер может выбрать веб-чат как способ связи, но пока что этот способ не работает. Чтобы он заработал, нам нужно сказать Nexmo, что мы хотим создать диалог между менеджером и пользователем.
Все в том же файле TicketController.php добавьт в начало:
И замените добавленный ранее elseif на следующий код:
> elseif ($data['notification_method'] === 'in-app-messaging')
Тут мы создаем диалог в Nexmo, и добавляем в него наших пользователей. Этот диалог понадобится нам позднее, когда мы будем делать веб-часть, а пока просто сохраним его в моделе Ticket.
Создадим миграцию для таблицы tickets, и добавим в нее новое поле conversation_id.
php artisan make:migration add_conversation_id_to_ticket
public function up()
public function down()
Выполните команду php artisan migrate чтобы применить изменения, а затем вновь откройте app/Http/Controllers/TicketController.php и добавьте сохранение conversation_id (сразу после строчки $conversation->addMember($users[$cc->user->nexmo_id]); ):
$ticket->conversation_id = $conversation->getId(); $ticket->save();
И заключительная деталь, добавим индикацию для заявки доступен онлайн чат. Откройте файл resources/views/ticket/index.blade.php и добавьте ячейку в таблицу:
Не забудьте добавить еще один тег th , чтобы таблица не съезжала.
Установка Stitch SDK
Мы закончили подготовительную работу на бекенде, настало время для фронта. Nexmo предоставляет нам SDK, которое сделает всю работу за нас. Давайте же его установим:
npm install nexmo-conversation --save-dev
Изменим файл webpack.mix.js чтобы он копировал SDK из папки node_modules в папку public/js :
mix.js('resources/assets/js/app.js', 'public/js') .js('node_modules/nexmo-conversation/dist/conversationClient.js', 'public/js') .sass('resources/assets/sass/app.scss', 'public/css');
Далее мы будем работать с JS кодом, поэтому стоит запустить команду npm run watch , чтобы наш JS код автоматически пересобирался при изменениях.
Не забудем подключить conversationClient.js в наш HTML. Откройте файл resources/views/layouts/app.blade.php и добавьте код:
прямо перед подкючением js/app.js .
Интерфейс для Чата
Для создания интерфейса нам потребуется внести изменения в файлы resources/views/ticket/show.blade.php и app/Http/Controllers/TicketController.php . Начнем с того, что добавим в метод TicketController::show всю необходимую информацию для подключения нашего диалога. Ранее мы передавали только $ticket объект, но теперь нам так же потребуется передавать JSON Web Token (JWT) и conversation ID.
Если вы не знакомы с концепцией JWT - ничего страшного. Для начала я приведу весь код, а далее пройдемся по каждой сточке:
return view('ticket.show', [ 'ticket' => $ticket, 'user_jwt' => Nexmo::generateJwt([ 'exp' => time() + 3600, 'sub' => Auth::user()->email, 'acl' => ["paths" => ["/v1/sessions/**" => (object)[], "/v1/users/**" => (object)[], "/v1/conversations/**" => (object)[]]], ]), 'conversation_id' => $ticket->conversation_id, ]);
Что тут происходит:
- Передаем информацию по заявке ($ticket)
- Создаем JWT (user_jwt):
- Время жизни токена - 1 час (exp)
- Пользователь которого мы авторизовываем (sub)
- Пути для которых данный токен считается валидным. (acl)
Это все что нам понадобится чтобы подключиться к Stitch.
Кроме прочего, нам так же необходимо добавить html код для добавления ответов. Откройте файл resources/views/ticket/show.blade.php и добавьте следующий код сразу после закрывающего тега div с классом .panel-body :
@if ($conversation_id) <div > <form action="" method="POST" > <div > <label for="reply">Add a reply</label> <textarea rows="3"></textarea> </div> <button type="submit" style="display:none;" >Save</button> </form> </div> @endif
В дополнение к форме, нам необходимо чтобы Nexmo SDK мог получить доступ к переменным user_jwt , conversation_id и ticket_id . Добавьте код ниже, прямо перед строчкой с @endsection :
На этом мы закончили с HTML частью, и теперь нам осталась только написать Javascript код, который свяжет все воедино. Откройте файл resources/assets/js/app.js и замените все что там есть, кодом ниже:
if (typeof CONVERSATION_ID !== "undefined" && CONVERSATION_ID !== "")
Это весь Javacript код который нам необходим для начала работы. Сохраните файл и откройте новую заявку с выбранным методом "веб-чат" в качестве способа связи. Все что вы напишите в форме, будет отправленно через websocket в Nexmo, откуда потом будет разосланно всем подписчикам которые подключенены. Попробуйте открыть еще одно окно браузера в инкогнито, и зайдите в нем на ту же страницу. Любой контент который вы добавите в одном окне, должен будет появиться в другом.
Теперь, когда все работает, настало время добавить немного деталей, чтобы сделать интерфейс более удобным для пользователей, например, индикацию что кто-то набирает текст. И даже об этом Nexmo уже позаботился для нас в своем SDK. Все что нам необходимо сделать, это добавить реакцию на соответствующие события. Для простоты предположим, что событие "набирает текст. " будет происходить когда поле textarea получает фокус, и соответсвенно когда это поле теряет фокус, пользователь перестал набирать текст. Добавьте этот код сразу после строчки $("#reply-submit").show(); :
// We assume they're typing when the input is focused replyInput.focus(() => conversation.startTyping().then(console.log).catch(console.log)); replyInput.blur(() => conversation.stopTyping().then(console.log).catch(console.log));
// Create an element to hold our typing message let typingIndicator = $("<div>");
// Someone's typing, show the typingIndicator conversation.on("text:typing:on", data => );
// They stopped typing, remove the typingIndicator conversation.on("text:typing:off", data => );
Это лишь одно из событий которое Nexmo SDK может обрабатывать. Так же вам доступны событие: пользователь присоединился к чату, пользователь покинул чат, и даже событие когда пользователь увидел сообщение.
Сохранение переписки
Должно быть вы заметили, что если вы обновите страницу, то вся переписка исчезнет. Если вы хотите сохранить переписку, у вас есть два варианта:
- Делать POST запрос к нашему серверу, каждый раз когда приходит новое сообщение и сохранять его в базе данных.
- Использовать метод conversation.getEvents() из Nexmo SDK, чтобы получить всю историю для конкретного диалога, и вывести ее на экран.
Так как мы уже сораняем SMS и голосовые ответы в базе данных, нам не составит труда добавить сохранение переписки для чата. Откройте файл app.js и сразу после строчки conversation.on('text') добавьте следующий код:
conversation.on('text', (sender, message) =>