Чат на Laravel 5.6 + Ratchet. Часть 3. Отправка и получение сообщений

Настало время для самого интересного. В этой части мы будем отправлять сообщения в чат и динамически получать их. Итак. Отправка сообщений:

body.on('click', '.message-submit', function () {
    let conversation_id = $(this).attr('data-conversation_id');
    let message_text = $('#new-message-' + conversation_id).val();
    if (message_text) {
      sendMessageAjax(message_text, conversation_id);
    } else {
      alert('Введите текст сообщения');
    }
  });
function sendMessageAjax(message_text, conversation_id) {
    ajaxSetup();
    $.ajax({
      url     : "/sendMessageAjax/",
      dataType: "json",
      data    : {
        message_text   : message_text,
        conversation_id: conversation_id
      },
      success : function () {
        $('#new-message-' + conversation_id).val('');
      },
      error: function(jqxhr, status, errorMsg) {
        ajaxErrorsHandling(jqxhr, status, errorMsg);
      }
    });
  }

Прописываем роут

Route::get('/sendMessageAjax/', 'HomeController@sendMessageAjax')->name('sendMessageAjax');

В контроллере создаем функцию добавления сообщения

public function sendMessageAjax(Request $request)
    {
        $message                  = new Messages;
        $message->text            = $request->message_text;
        $message->author_id       = Auth::user()->id;
        $message->conversation_id = $request->conversation_id;
        $message->save();
        $message->author       = Auth::user()->name;
        $message->conversation = Conversations::find($message->conversation_id);
        //зададим флаг, по которому будем отличать сообщение в чате от других Push-уведомлений
        $message->message_flag = 'new_message';

        //формируем данные для отправки через Веб-сокет
        $data = [
            'topic_id' => 'onNewData',
            'data'     => $message
        ];
        //Отправляем данные через наш пушер, который мы написали в первой части (ссылка в конце страницы)
        Pusher::sendDataToServer($data);

        //Здесь мы будем искать слова, начинающиеся с символа "@". И если в нашей БД есть пользователь с таким ником, отправим ему уведомление, что его упомянули в чате
        $text = explode(" ", $message->text);

        foreach ($text as $word) {
            if (substr($word, 0, 1) == "@") {
                if ($refer = User::where('name', substr($word, 1))->first()) {
                    //Если у пользователя включены уведомления на email, отошлем ему письмо
                    if ($refer->conversations_subscribe == 1) {
                        MailController::conversationMentionMessage($refer->email, $message->conversation, $refer->id);
                    }
                }
            }
        }

        $followers = Followers::getFollowers($message->conversation_id, 'conversation');
        $unique_id = time();
        foreach ($followers as $follower) {
            //Разошлем всем подписчикам, у которых включены email-уведомления, письма
            if ($follower->conversations_subscribe == 1) {
                MailController::messageCreatedMessage($follower->email, $message->conversation, $follower->id);
            }
        }
        if ($message) {
            echo json_encode(['success' => $message]);
        } else {
            echo json_encode(['fail' => 'Error occurred']);
        }
    }

Теперь перейдем к клиентской части. Инициализируем наше подключение к Пуш-серверу:

  let ip = '192.168.10.10'; //Указываем IP вашего сервера
  let conn = new ab.connect(
    'ws://' + ip + ':8080',
    function (session) {
      session.subscribe('onNewData', function (topic, data) {
        notification(data.data);
      });
    },

    function (code, reason, detail) {
      console.warn('Websocket connection closed! Code = ' + code + '; Reason = ' + reason + '; Detail = ' + detail + ';')
    },
    {
      'maxRetries'          : 60,
      'retryDelay'          : 4000,
      'skipSubprotocolCheck': true
    }
  );
  function notification(data) {
    if (data.message_flag == 'new_message') {//Если данные, присланные пуш-сервером - это сообщение чата
      let message_class = '';
      if (data.author_id == $('#current-user').val()) {
        message_class = 'message-item-my';
      } else {
        message_class = 'message-item-other';
      }
      let text_string = '';
      if (data.text) {
        text_string = data.text;
      }
      $('#message-block-' + data.conversation_id).append('' +
        '<div class="message-item ' + message_class + '" id="message-item-' + data.id + '">\n' +
        '  <div class="message-heading">\n' +
        '    <b>' + data.author + '</b> <small>' + data.created_at + '</small>\n' +
        '  </div>\n' +
        '<div class="message-body">\n' +
        text_string +
        '</div>\n' +
        '</div>' +
        '').scrollTop(999999999);
    }
  }

Если у вас еще не запущен пуш-сервер, запускаем его командой

php artisan push_server:serve

и пробуем отправить сообщение

Великолепно! Осталось добавить функционал на кнопки подписки, упоминания пользователя и написать обработку для почтовых событий. Действия на кнопки подписки/отписки:

  body.on('click', '.follow-button-block', function () {
    let post_id = $(this).attr('data-post_id');
    let type = $(this).attr('data-type');
    let user_id = $('#current-user').val();
    toggleFollowerAjax(post_id, user_id, type);
  });

  body.on('click', '.add-follower-button', function () {
    $('.followers-dialog').toggle(300);
  });
  body.on('click', '.followers-dialog li', function () {
    let post_id = $(this).attr('data-post_id');
    let user_id = $(this).attr('data-user_id');
    let type = $(this).attr('data-type');
    toggleFollowerAjax(post_id, user_id, type);
  });
  function toggleFollowerAjax(post_id, user_id, type) {
    $('.loading').show();
    ajaxSetup();
    $.ajax({
      url     : "/toggleFollowerAjax/",
      dataType: "json",
      data    : {
        post_id: post_id,
        user_id: user_id,
        type   : type
      },
      success : function (data) {
        $('.loading').hide();
        if (type == 'conversation') {
          if (data.success.followed == 'followed') {
            if ($('#current-user').val() == user_id) {
              $('#conversations-item-' + post_id + ' .follow-button-block').addClass('follow-button-block-active');
              $('#conversations-item-' + post_id + ' .follow-button-text').text(' Вы подписаны');
            }
            $('#conversations-item-' + post_id + ' .follower-list-item-' + data.success.user.id + ' .toggle-follower')
              .removeClass('fa-plus-square-o').addClass('fa-minus-square-o');
            $('#conversations-item-' + post_id + ' .followers-list').append('' +
              '<a href="#" class="users-item followers-item-' + data.success.user.id + '">' +
              data.success.user.name+
              '</a>');
          } else {
            if ($('#current-user').val() == user_id) {
              $('#conversations-item-' + post_id + ' .follow-button-block').removeClass('follow-button-block-active');
              $('#conversations-item-' + post_id + ' .follow-button-text').text(' Подписаться');
            }
            $('#conversations-item-' + post_id + ' .follower-list-item-' + data.success.user.id + ' .toggle-follower')
              .addClass('fa-plus-square-o').removeClass('fa-minus-square-o');
            $('#conversations-item-' + post_id + ' .followers-item-' + data.success.user.id).detach();
          }
        }
      },
      error   : function (jqxhr, status, errorMsg) {
        ajaxErrorsHandling(jqxhr, status, errorMsg);
      }
    });
  }

Прописываем роут

Route::get('/toggleFollowerAjax/', 'HomeController@toggleFollowerAjax')->name('toggleFollowerAjax');

В контроллере:

    public function toggleFollowerAjax(Request $request)
    {
        if (Followers::isFollower($request->user_id, $request->type, $request->post_id)) {
            $follower = Followers::getFollower($request->user_id, $request->type, $request->post_id);
            $follower->delete();
            $result['followed'] = 'unfollowed';
        } else {
            $follower              = new Followers();
            $follower->type        = $request->type;
            $follower->follower_id = $request->user_id;
            $follower->post_id     = $request->post_id;
            $follower->save();
            $result['followed'] = 'followed';
        }
        $user           = User::find($request->user_id);
        $user           = User::getInfo($user);
        $result['user'] = $user;
        if ($result) {
            echo json_encode(['success' => $result]);
        } else {
            echo json_encode(['fail' => 'Error occurred']);
        }
    }

Добавляем функционал на кнопку упоминания пользователя:

  body.on('click', '.mention-dialog li', function () {
    let conversation_id = $(this).attr('data-conversation_id');
    let user_name = $(this).attr('data-name');
    let conversation_text = $('#new-message-' + conversation_id).val() + ' @' + user_name;
    $('#new-message-' + conversation_id).val(conversation_text);
    $('#message-editor-' + conversation_id + ' .emojionearea-editor').text(conversation_text);
    $('.mention-dialog').hide(300);
  });
  body.on('click', '.message-mention-button', function () {
    let conversation_id = $(this).attr('data-conversation_id');
    $('#mention-dialog-' + conversation_id).toggle();
  });

Для всплывающих менюшек:

  body.on('mouseenter', '.dialog-hover-block', function () {
    $(this).find("ul").css('left', $(this).position().left - 25).slideDown("fast");
  });

  body.on('mouseleave', '.dialog-hover-block ul', function () {
    $(this).slideUp("fast");
  });
  body.mouseup(function (e) {
    let container = $(".dialog");
    if (container.has(e.target).length == 0) {
      container.hide();
    }
  });

Ну и осталось создать почтовые события. Создадим контроллер /app/Http/Controllers/MailController.php

<?php

namespace App\Http\Controllers;

use App\Mail\ConversationMention;
use App\Mail\MessageCreated;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\User;

class MailController extends Controller
{
    /**
     * Отправить заданный заказ.
     *
     * @param  Request $request
     * @param  int     $orderId
     *
     * @return Response
     */

    public static function conversationMentionMessage($email, $conversation, $recipient_id)
    {
        $params = [
            'conversation' => $conversation,
            'recipient_id' => $recipient_id
        ];
        Mail::to($email)->send(new ConversationMention($params));
    }
    public static function messageCreatedMessage($email, $conversation, $recipient_id)
    {
        $params = [
            'conversation' => $conversation,
            'recipient_id' => $recipient_id
        ];
        Mail::to($email)->send(new MessageCreated($params));
    }
}

/app/Mail/ConversationMention.php

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class ConversationMention extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($params)
    {
        $this->params = $params;
    }
    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('emails.conversation_mention')->with($this->params);
    }
}

/app/Mail/MessageCreated.php

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class MessageCreated extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($params)
    {
        $this->params = $params;
    }
    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('emails.message_created')->with($this->params);
    }
}

И шаблоны к ним:

/resources/views/emails/conversation_mention.blade.php

@component('mail::message')
# Вас упомянули в беседе

Вас упомянули в беседе "{{$conversation->title}}"

<a href="{{url('/').'/conversations/'.$conversation->id}}">Посмотреть беседу</a>
С уважением,<br>
{{ config('app.name') }}
@endcomponent

/resources/views/emails/message_created.blade.php

@component('mail::message')
# Новое сообщение в беседе

В беседе "{{$conversation->title}}", за которой вы следите, было добавлено новое сообщение

<a href="{{url('/').'/conversations/'.$conversation->id}}">Посмотреть беседу</a>
С уважением,<br>
{{ config('app.name') }}
@endcomponent

Ну в общем-то и всё. Мы создали полноценный чат с возможностью push и email уведомлений. Если я что-то упустил или опечатался, пишите ваши замечания в комментарии. Всегда ваш, Pyro338))

Первая часть
Вторая часть
Четвертая часть

Добавить комментарий