آموزش نوشتن یک برنامه چت با لاراول و Vuejs

laravel-pusher-vuejs

کم و بیش همه شما دوستان و طرفداران دنیای وب با چت روم ها و سیستم های چت آنلاین کار کرده اید. گاهی این سوال پیش می آید که آیا این سیستم خیلی پیشرفته و پیچیده است و من توانایی اجرای آن را دارم یا خیر؟ در پاسخ به این سوال باید بگویم که با مطالعه این آموزش قطعا به ساده ترین شکل ممکن برنامه چت با لاراول و Vuejs را پیاده سازی خواهید کرد.

در این آموزش یک برنامه چت با لاراول و Vue.Js ایجاد می کنیم. برای بهبود عملکرد سیستم از سرویس پیام رسانی فوری pusher و برای سمت کلاینت هم از Laravel Echo و کتابخانه pusher.js برای بروزرسانی بلادرنگ رابط کاربری مان استفاده خواهیم کرد.

در این مقاله یاد می گیریم که لاراول چطور یک رویداد را منتشر کرده و Vue.js این رویداد را گرفته و کامپوننت vue را بصورت بلادرنگ بروزرسانی می کند.

سرفصل های آموزش ساخت برنامه چت با لاراول و Vuejs

  • نصب لاراول
  • ایجاد مدل و Migration
  • تعریف رابطه بین مدل ها
  • ایجاد کنترلر و روت
  • ایجاد کامپوننت های vue
  • ایجاد یک فرم داخل chatFormComponnent
  • استایل دهی کامپوننت MessageComponent
  • استایل دهی به usersSection
  • ایجاد کنترلر MessageController
  • استفاده از Axios برای ارسال یک درخواست شبکه
  • نمایش پیام ها
  • ارسال یک پیام
  • ذخیره پیام ها در پایگاه داده
  • تنظیم pusher: سرویس پیام رسانی بلادرنگ (آنلاین)
  • نمایش کاربران آنلاین
  • ارسال و دریافت پیام ها بصورت بلادرنگ

1- نصب لاراول

برای نصب آخرین نسخه لاراول لطفا ابتدا دستورات زیر را در ترمینال وارد کنید.

laravel new chat

# or

composer create-project laravel/laravel chat --prefer-dist

به داخل فولدر پروژه بروید:

cd chat

دستور زیر را برای نصب وابستگی های Frontend اجرا کنید. npm یک سیستم مدیریت پکیج بسیار قدرتمند است که در آن تمام وابستگی های کار با فریم ورک های سمت فرانتد مانند Vuejs وجود دارد.

npm install

پروژه را در ویرایشگر مورد علاقه خود باز کنید، من از Visual Studio Code استفاده می کنم.

npm install

یک پایگاه داده ایجاد و مشخصات آن را در فایل .env قرار دهید.

حال یک چهارچوب احراز هویت که توسط لاراول ارائه می شود را ایجاد کنید.

php artisan make:auth

حال به ترمینال رفته و دستور زیر را برای Migrate کردن جداول در پایگاه داده اجرا کنید.

php artisan migrate

سپس به آدرس http://localhost:8000/register رفته و یک کاربر را ثبت نام کنید.

2- ایجاد مدل و migration مربوط به آن

در این مرحله باید یک مدل Message به همراه migration مربوط به آن بوجود بیاورید. از این مدل برای ذخیره پیام ها استفاده می کنیم.

بنابراین به ترمینال رفته و دستور زیر را برای ایجاد مدل و migration اجرا کنید.

php artisan make:model Message -m

حال به فایل migration رفته و کدهای زیر را به آن اضافه کنید.

// create_messages_table

public function up()
{
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->text('body');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
}

با دستور زیر جدول را به پایگاه داده تان migrate کنید.

php artisan migrate

3-تعریف روابط بین مدل ها

برای نوشتن برنامه چت با لاراول و Vuejs دو مدل داریم:

  • User
  • Message

روابط این مدل به شرح زیر است:

مدل user رابطه hasMany با مدل Message دارد. بدین معنی است که هر کاربر می تواند تعداد بی شماری (رابطه یک به چند) پیام ارسال کند.

Message هم رابطه belongsTo با مدل user دارد. بدین معنی است که هر پیام قطعا مختص یک کاربر است.

// User.php

public function messages()
{
    return $this->hasMany(Message::class);
}

سپس فایل Message را باز کرده و رابطه معکوس این مدل را با مدل user تعریف می کنیم.

// Message.php

public function user()
{
   return $this->belongsTo(User::class);
}

همچنین برای جلوگیری از بروز خطای Mass Assignment فیلدهای fillable مان را در فایل Message تعریف می کنیم.

// Message.php

protected $fillable = ['body'];

همچنین یک فیلد دیگر به نام selfMessage به آن اضافه می کنیم. این فیلد مشخص می کند یک پیام مربوط به ما و یا به کاربری که با آن چت می کنیم است.

// Message.php

protected $appends = ['selfMessage'];

در زیر یک متد تعریف کردیم که به کاربر می گوید که پیامی که دریافت میکند مربوط به کاربری است که به سیستم وارد (login) شده است یا نه (یا مربوط به کاربر مهمان است).

ما باید این موارد را از هم متمایز کنیم چون می خواهیم کلاس های css مختلفی به پیام های هر گروه از کاربران بدهیم.

// Message.php

public function getSelfMessageAttribute()
{
    return $this->user_id === auth()->user()->id;
}

در نهایت مدل Message.php باید شبیه زیر باشد.

// Message.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{

    protected $fillable = ['body'];

    protected $appends = ['selfMessage'];

    public function getSelfMessageAttribute()
    {
        return $this->user_id === auth()->user()->id;
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

4- ایجاد کنترلر و روت

یک کنترلر به نام chatController.php ایجاد کنید

php artisan make:controller ChatController

یک متد index() داخل آن تعریف نمایید.

// ChatController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ChatController extends Controller
{
    public function __construct()
    {
        return $this->middleware('auth');
    }

    public function index()
    {
        return view('chat');
    }
}

سپس یک فایل ویو به نام chat.blade.php داخل فولدر views ایجاد کنید. کدهای زیر را در فایل chat.blade.php قرار دهید.

<!-- chat.blade.php -->

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Chats</div>
                <div class="card-body">
                   Chats
                </div>
            </div>
        </div>
        <div class="col-md-4">
        <div class="card">
                <div class="card-header">Users</div>
                <div class="card-body">
                   Users
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

فایل web.php را باز کرده و روت های زیر را در آن وارد کنید.

// web.php

Route::get('/chat', 'ChatController@index')->name('chat');

به آدرس http://localhost:8000/chat بروید و ظاهر  برنامه چت با لاراول و Vuejs ببینید.

نوشتن برنامه چت در لاراول و vue

5- ایجاد کامپوننت Vuejs

ترمینال را باز کرده و کدهای زیر را در آن تایپ کنید.

npm run watch

حال اگر ما در فایل های vue کدی بنویسیم، بطور خودکار کامپایل شده و داخل فولدر public > js قرار میگیرد.

به مسیر assets > js > component رفته و فایل ExampleComponnent.vue را به chatComponent.vue تغییر نام دهید. همچنین در فولدر asset > js فایل app.js را مطابق زیر تغییر دهید.

// app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('chat-component', require('./components/ChatComponent.vue'));

const app = new Vue({
    el: '#app'
});

فایل را ذخیره کنید. می بینید که یک پیام نوتیفیکیشن به عنوان Laravel Mix Compilation Successfully ظاهر می شود.

سپس کدهای زیر را در فایل chatComponent.vue بنویسید.

<template>
    <div class="col-md-8">
        <div class="card">
            <div class="card-header">Chats</div>
            <div class="card-body">
                Chats
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

همچنین یک فایل با نام UserComponent.vue داخل فولدر components ایجاد کنید.

<template>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">Users</div>
            <div class="card-body">
                Users
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

حال می خواهیم این کامپوننت ها را تفکیک کنیم.

کدهای داخل فایل chatComponent.vue را به دو کامپوننت تفکیک می کنیم:

  • chatMessageComponent
  • chatFormComponent

سپس داخل فولدر components یک فایل کامپوننت vue به نام chatMessagesComponent.vue ایجاد می کنیم.

 // ChatMessagesComponent.vue

<template>
    <div class="chat__messages">
        Messages
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

همچنین به یک فرم نیاز داریم تا کاربران پیام های خود را در آن نوشته و آن را برای کاربران دیگر ارسال کنند.

سپس داخل فولدر componnets یک فایل کامپوننت vue به نام chatFormComponent.vue ایجاد می کنیم.

 // ChatFormComponent.vue

<template>
    <div>
        Form
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

سپس هر دو کامپوننت ها را داخل فایل assets > js > app.js ثبت می کنیم.

// app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('chat-component', require('./components/ChatComponent.vue'));
Vue.component('user-component', require('./components/UserComponent.vue'));
Vue.component('chat-messages-component', require('./components/ChatMessageComponent.vue'));
Vue.component('chat-form-component', require('./components/ChatFormComponent.vue'));

const app = new Vue({
    el: '#app'
});

همچنین کدهای chatMessageComponnent را به یک کامپوننت دیگر به نام MessageComponnent تفکیک می کنیم.

این کامپوننت فقط کار نمایش پیام ها را بر عهده دارد و هر پیام باید این کامپوننت را رندر کند.

سپس کامپوننت MessageComponent.vue  را در داخل فولدر Componnents ایجاد می کنیم.

 / MessageComponent.vue

<template>
    <div class="chat__message">
       Message
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

این کامپوننت را داخل فایل app.js ثبت می کنیم.

// app.js

Vue.component('message-component', require('./components/MessageComponent.vue'));

حال فایل chatMessageComponnent.vue را با کدهای زیر بروزرسانی کنید.

// ChatMessagesComponent.vue

<template>
    <div class="message-area">
        <message-component></message-component>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

فایل را ذخیره کنید. خروجی باید مانند زیر باشد.

ایجاد یک فرم برای چت در برنامه لاراول

6- ایجاد یک فرم داخل chatFormComponnent

حال کدهای زیر را در فایل chatFormComponnent.vue اضافه کنید.

// ChatFormComponent.vue

<template>
    <div>
        <form class="form">
            <textarea 
                cols="25"
                rows="5"
                class="form-input">
            </textarea>
            <span class="notice">
                Hit Return to send a message
            </span>
        </form>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

<style>
    .form {
        padding: 8px;
    }
    .form-input {
        width: 100%;
        border: 1px solid #d3e0e9;
        padding: 5px 10px;
        outline: none;
    }
    .notice {
        color: #aaa
    }
    
</style>

همان طور که می بینید ما به کامپوننت مان کمی استایل هم داده ایم. ظاهر برنامه چت با لاراول و Vuejs باید مطابق تصویر زیر باشد.

ایجاد فرم برای برنامه چت در لاراول

7- استایل دهی به MessageComponnent

 حال داخل فایل MessageComponnent استایل های زیر را اضافه کنید.

// MessageComponent.vue

<template>
    <div class="message self">
        <strong class="user">Krunal</strong>
        <p class="body">Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam cumque quaerat rem quia veniam exercitationem, commodi numquam omnis! Non placeat perspiciatis nulla illum cumque ad natus asperiores fuga. Facere, dignissimos.</p>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

<style>
    .user {
        font-weight: 800;
    }
    .body {
        margin-bottom: 0;
        white-space: pre-wrap;
    }
    .message {
        border-bottom: 1px solid #000000
    }
    .self {
        background-color: #f0f0f0;
        padding: 10px;
    }
</style>

من یک پیام آزمایشی را در برنامه چت با لاراول و Vuejs نوشتم. ظاهر برنامه باید مطابق زیر باشد.

ارسال چت در لاراول

همان طور که می بینید یک کلاس به نام self که یک کلاس شرطی است، داریم. از این کلاس برای تمایز پیام های کاربرانی که به سیستم لاگین کرده اند با پیام های سایر کاربران استفاده میکنیم. این کار باید بصورت خودکار انجام بگیرد.

8- استایل دهی بخش کاربران

داخل فایل UserComponnent.vue کلاس ها و استایل های زیر را اضافه کنید تا برنامه چت با لاراول و Vuejs‌ از نظر ظاهری حداقل استاندارد را داشته باشد.

من این کامپوننت را برای یک کاربر آزمایشی ایجاد میکنم.

// UserComponent.vue

<template>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">Users</div>
            <div class="card-body">
                <div class="users">
                    <a href="#">Krunal</a>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

<style>
    .users {
        background-color: #fff;
        border-radius: 3px;
    }
</style>

9- ایجاد یک کنترلر MessageController در برنامه چت با لاراول و Vuejs

یک کنترلر با نام MessageController با استفاده از دستور زیر ایجاد کنید.

php artisan make:controller MessageController

حال فایل web.php را باز کرده و روت های (Routes) زیر را در آن تعریف نمایید.

// web.php

Route::get('/message', 'MessageController@index')->name('message');

داخل MessageController یک متد index تعریف میکنیم. این متد یک آبجکت json بر می گرداند. به عبارت دیگر خروجی این متد برای کار با api و در نهایت داده ی json‌ است.

تمام پیام های ما داخل این آبجکت json ذخیره شده است.

// MessageController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Message;

class MessageController extends Controller
{
    public function index()
    {
        $messages = Message::with(['user'])->get();

        return response()->json($messages);
    }
}

ما از روتی که در بالا تعریف کردیم برای ارسال یک درخواست شبکه از یک کامپوننت vue توسط Axios Api استفاده میکنیم.

در روت بالا ما توسط Api Axios یک درخواست شبکه را از یک کامپوننت vue ارسال کردیم و در پاسخ، تمام پیام ها را دریافت و آنها را نمایش می دهیم.

10- استفاده از Axiso برای ارسال یک درخواست Ajax یا شبکه در برنامه چت با لاراول و Vuejs

حال ما باید Axios را برای ارسال یک درخواست Ajax یا درخواست شبکه به سرور لاراول و بازیابی پیام ها و نمایش آنها به کاربران، پیکربندی کنیم.

ما درخواست get را داخل فایل chatMessageComponnent.vue می نویسیم.

// ChatMessageComponent.vue

<template>
    <div class="message-area">
        <message-component></message-component>      
    </div>
</template>

<script>
    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                this.messages = response.data;
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

موقعی که کامپوننت آماده شد، یک درخواست از نوع Ajax به سرور ارسال می کنیم و یک پاسخ دریافت می کنیم. سپس این پاسخ را به پروپرتی message نسبت می دهیم.

حال باید پیام ها را به MessageComponent پاس بدهیم. این کامپوننت مسئول نمایش همه پیام ها است.

سپس کدهای زیر را داخل template در فایل chatMessageComponnent.vue می نویسیم.

// ChatMessageComponent.vue

<template>
    <div class="message-area">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

11- نمایش پیام ها در برنامه چت با لاراول و Vuejs

حال باید پروپرتی message را به فایل messageComponnent.vue پاس بدهیم.

// MessageComponent.vue

<template>
    <div class="message self">
        <strong class="user">{{ message.user.name }}</strong>
        <p class="body">{{ message.body }}</p>
    </div>
</template>

<script>
    export default {
        props: ['message']
    }
</script>

من در این فایل از هیچ استایلی استفاده نکردم. اما شما میتوانید در صورت تمایل به آن استایل هم اضافه کنید.

هم اکنون هیچ پیامی در دیتابیس نداریم، اگر پیامی در دیتابیس ذخیره شود میتوانیم همه پیام ها را در اینجا ببینیم.

12- ارسال یک پیام در برنامه چت با لاراول و Vuejs

ما در فرم هیچ دکمه ارسال یا submit قرار ندادیم و هر زمان که کاربر دکمه Enter را فشار داد، پیام به سرور ارسال می شود.

ابتدا، هنگام ارسال یک پیام باید رابط کاربری بصورت بلادرنگ خودش را بروزرسانی کند.

برای اینکار ما به تعدادی رویداد نیاز داریم که به برنامه Vue.js مان بگوید که پیام جدید دریافت شده است، لطفا رابط کاربری را بروزرسانی کن.

یک فایل به نام event.js را داخل فولدر js ایجاد کنید و کدهای زیر را در آن بنویسید.

// event.js

import Vue from 'vue';

export default new Vue();

حال باید پیام ها را در برنامه چت با لاراول و Vuejs به آرایه message اضافه کنیم. برای اینکار به یک پیام موقت که شامل پروپرتی های زیر است، نیاز داریم:

  1. ID
  2. متن پیام
  3. نام کاربری

هنگامی که کاربر روی دکمه Enter کلیک کرد، باید یک آبجکت موقت ایجاد و آن را به یک رویداد پاس بدهیم و سپس از طریق کامپوننت chatMessageComponnent آن رویداد را تعریف کنیم.

در قدم بعد آبجکتی که به یک رویداد پاس داده شده بود را گرفته و آن را به آرایه message اضافه می کنیم.

سپس vue.js به طور خودکار  تغییرات کامپوننت messageComponnent را شناسایی کرده و سپس رابط کاربری را بروزرسانی می کند.

دلیل استفاده از یک رویداد به این خاطر است که ما یک داده در کامپوننت vue.js داریم و می خواهیم این داده را به یک کامپوننت vue.js دیگر پاس دهیم.

حالا من کدنویسی فایل chatFormComponnent را تکمیل می کنم. این فایل یک رویداد که شامل یک آبجکت موقت است را بر می گرداند.

// ChatFormComponent.vue

<template>
    <form class="form">
        <textarea
            id="body"
            cols="28"
            rows="5"
            class="form-input"
            @keydown="typing"
            v-model="body">
        </textarea>
        <span class="notice">
            Hit Return to send a message
        </span>
    </form>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                body: null
            }
        },
        methods: {
            typing(e) {
                if(e.keyCode === 13 && !e.shiftKey) {
                    e.preventDefault();
                    this.sendMessage();
                }        
            },
            sendMessage() {
                if(!this.body || this.body.trim() === '') {
                    return
                }
                let messageObj = this.buildMessage();
                Event.$emit('added_message', messageObj);
                this.body = null;
            },
            buildMessage() {
                return {
                    id: Date.now(),
                    body: this.body,
                    selfMessage: true,
                    user: {
                        name: 'Krunal'
                    }
                }
            }
        }
    }
</script>

<style>
    .form {
        padding: 8px;
    }
    .form-input {
        width: 100%;
        border: 1px solid #d3e0e9;
        padding: 5px 10px;
        outline: none;
    }
    .notice {
        color: #aaa
    }
    
</style>

در آبجکت موقت من نام کاربری را بصورت دستی نوشتم، اما در برنامه های واقعی باید نام کاربری که به سیستم لاگین کرده است را بنویسیم.

حال باید این رویداد را داخل کامپوننت chatMessage.vue بگیریم.

// ChatMessageComponent.vue

<template>
    <div class="message-area">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                console.log(response.data);
                this.messages = response.data;
            });
            Event.$on('added_message', (message) => {
                this.messages.unshift(message);
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

داخل فایل view > layout > app.blade.php کدهای زیر را در بین تگ <head> بنویسید.

<script>
        window.Laravel = {!! json_encode([
            'csrfToken'=> csrf_token(),
            'user'=> [
                'authenticated' => auth()->check(),
                'id' => auth()->check() ? auth()->user()->id : null,
                'name' => auth()->check() ? auth()->user()->name : null, 
                ]
            ])
        !!};
</script>

در کد بالا در صورتی که کاربر به سیستم لاگین کرده باشد، نام کاربری آن را گرفته و به متغیر سراسری Laravel نسبت می دهیم. به این وسیله می توانیم در داخل کامپوننت vue.js به نام کاربری دسترسی داشته باشیم. هنگامی که یک آبجکت پیام موقت ایجاد کنیم، می توانیم نام پروپرتی را به پروپرتی نام کاربری Laravel نسبت بدهیم و در انتها نام کاربری که لاگین کرده است را خواهیم دید.

حال کدهای زیر را داخل فایل chatMessageComponnent می نویسیم.

// ChatMessageComponent.vue

<template>
    <form class="form">
        <textarea
            id="body"
            cols="28"
            rows="5"
            class="form-input"
            @keydown="typing"
            v-model="body">
        </textarea>
        <span class="notice">
            Hit Return to send a message
        </span>
    </form>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                body: null
            }
        },
        methods: {
            typing(e) {
                if(e.keyCode === 13 && !e.shiftKey) {
                    e.preventDefault();
                    this.sendMessage();
                }        
            },
            sendMessage() {
                if(!this.body || this.body.trim() === '') {
                    return
                }
                let messageObj = this.buildMessage();
                Event.$emit('added_message', messageObj);
                this.body = null;
            },
            buildMessage() {
                return {
                    id: Date.now(),
                    body: this.body,
                    selfMessage: true,
                    user: {
                        name: Laravel.user.name
                    }
                }
            }
        }
    }
</script>

<style>
    .form {
        padding: 8px;
    }
    .form-input {
        width: 100%;
        border: 1px solid #d3e0e9;
        padding: 5px 10px;
        outline: none;
    }
    .notice {
        color: #aaa
    }
    
</style>

در نهایت کدهای ChatMessageComponent.vue باید مانند زیر باشد

// ChatMessageComponent.vue

<template>
    <div class="message-area" ref="message">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                console.log(response.data);
                this.messages = response.data;
            });
            Event.$on('added_message', (message) => {
                this.messages.unshift(message);
                if(message.selfMessage) {
                    this.$refs.message.scrollTop = 0;
                }
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

حال اگر کاربری که لاگین کرده، یک پیامی را ارسال کند، اسکرولر به سمت بالا می رود.

13- ذخیره پیام ها در پایگاه داده برنامه چت با لاراول و Vuejs

مسیر زیر را برای ذخیره پیام ها تعریف کنید.

// web.php

Route::post('/message', 'MessageController@store')->name('message.store');

حال متد store() را برای ذخیره داده ها در پایگاه داده MySQL، تعریف می کنیم.

// MessageController.php

public function store(Request $request)
{
     $message = $request->user()->messages()->create([
         'body' => $request->body
     ]);

     return response()->json($message);
}

سپس داخل فایل chatFormComponnent کد زیر را برای ارسال یک درخواست Axios به روتی که تعریف کردیم، می نویسم و بدنه پیام ها را به عنوان یک پارامتر به آن پاس می دهیم.

// ChatFormComponent.vue

sendMessage() {
        if(!this.body || this.body.trim() === '') {
               return
        }
           let messageObj = this.buildMessage();
           Event.$emit('added_message', messageObj);
            axios.post('/message', {
                body: this.body.trim()
            }).catch(() => {
                 console.log('failed');
            });
            this.body = null;
        },

فایل را ذخیره و به آدرس http://localhost:8000/chat بروید.

یک متن را در textbox وارد کرده و دکمه Enter را بزنید. می بینید که یک پیام بصورت بلادرنگ ظاهر می شود، که این همان پیام موقت است.

حال صفحه را رفرش کنید. و ببینید که آیا این پیام هنوز هم وجود دارد یا نه؟ اگر وجود داشته باشد یعنی با موفقیت پیام را ذخیره کردیم.

14- تنظیم Pusher: سرویس پیام بلادرنگ (Pusher‌ در لاراول)

ما از یک سرویس Pusher برای پیام های بلادرنگ در برنامه چت با لاراول و Vuejs استفاده می کنیم. ابتدا باید Broadcast_driver که داخل فایل .env وجود دارد را تغییر دهیم.

// .env
BROADCAST_DRIVER=pusher

سپس در فایل config > app.js خط زیر را از حالت کامنت خارج کنید.

App\Providers\BroadcastServiceProvider::class,

در قدم بعد باید یک برنامه pusher ایجاد کنیم. برای اینکار به آدرس http://pusher.com رفته و یک اکانت ایجاد کنید. بعد از اینکه به اکانت تان لاگین کردید به آدرس http://dashboard.pusher.com هدایت می شوید.

در این صفحه یک برنامه ایجاد کنید و سپس به بخش App Keys بروید. در این بخش تمام اطلاعات را گرفته و داخل فایل .env در برنامه لاراول تان قرار دهید.

// .env

PUSHER_APP_ID=your app id
PUSHER_APP_KEY=your app key
PUSHER_APP_SECRET=your app secret
PUSHER_APP_CLUSTER=your cluster

حال باید پکیج pusher php server را توسط کامپوزر نصب کنیم.

composer require pusher/pusher-php-server

همچنین به وابستگی های زیر هم برای بخش front-end سایت نیاز داریم:

  1. pusher.js
  2. Laravel Echo

هر دو اینها را با دستور زیر نصب کنید

npm install pusher-js laravel-echo  --save

حال فایل assets > js > bootstrap.js را باز کرده و خط زیر را از حالت کامنت خارج کنید. بصورت پیش فرض این خطوط کامنت شده اند.

// bootstrap.js

import Echo from 'laravel-echo'

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true
});

حال داخل فایل .env متغیرهایی شبیه زیر داریم.

// .env

MIX_PUSHER_APP_KEY=your pusher key
MIX_PUSHER_APP_CLUSTER=your cluster

در کد بالا، در قسمت MIX_PUSHER_KEY، public Key ایی که از سایت pusher گرفتید را وارد کنید، همانند قسمت PUSHER_APP_KEY.

همچنین در قسمت MIX_PUSHER_APP_CLUSTER نام clusterتان را که از سایت pusher گرفتید را وارد نمایید. در نهایت فایل را ذخیره کرده و ترمینال را ببینید اگر پیام خطایی نداشتید یعنی کار را به درستی انجام داده اید.

15- نمایش کاربران آنلاین

ابتدا داخل فایل routes > channel.php کدهای زیر را قرار دهید.

// channels.php

<?php

Broadcast::channel('chat', function ($user) {
    return [
        'id' => $user->id,
        'name' => $user->name
    ];
});

در کد بالا ما یک کانال (channel) سراسری ایجاد کردیم که یک آبجکت user را بر میگرداند. حال اگر یک کاربر به برنامه چت با لاراول و Vuejs ملحق شود، ما باید رابط کاربری vue.js مان را بروزرسانی کنیم. بهتر است که با استفاده از یک رویداد اینکار را انجام دهیم. سپس هنگامی که یک کاربر جدید به چت ملحق شد، ما یک رویداد را ارسال می کنیم و بخش front-end سایت این رویداد را گرفته و رابط کاربری را بروزرسانی می کند.

داخل فولدر resource > assets > js یک فایل به نام echo.js را ایجاد می کنیم.

این فایل تمام رویدادها را گرفته و به vue.js اطلاع می دهد که رابط کاربری را بروزرسانی کند.

// echo.js

Echo.join('chat')
    .here(users => {
        console.log(users);
    })
    .joining(user => {
        console.log(user);
    })
    .leaving(user => {
        console.log(user);
    })

سپس این فایل را داخل فایل resource > assets > js > bootstrap.js، با استفاده از دستور require مورد استفاده قرار دهید. من در انتهای فایل اینکار را انجام دادم.

// bootstrap.js

window._ = require('lodash');
window.Popper = require('popper.js').default;

try {
    window.$ = window.jQuery = require('jquery');

    require('bootstrap');
} catch (e) {}

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

import Echo from 'laravel-echo'

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    encrypted: true
});

require('./echo');

حال، هنگامی که کاربری به چت ملحق شود یا چت را ترک کند ما باید یک رویداد را منتشر کنیم.

داخل فایل echo.js کدهای زیر را بنویسید.

// echo.js

import Event from './event';

Echo.join('chat')
    .here(users => {
        Event.$emit('users.here', users);
    })
    .joining(user => {
        Event.$emit('users.joined', user);
    })
    .leaving(user => {
        Event.$emit('users.left', user);
    })

داخل فایل UserComponent.vue کدهای زیر را بنویسید. کدهای زیر را برای دریافت رویدادهای منتشر شده و تغییر دادن رابط کاربری می نویسیم.

// UserComponent.vue

<template>
    <div class="col-md-4">
        <div class="card">
            <div class="card-header">Users</div>
            <div class="card-body">
                <div class="users" v-for="user in users" :key="user.id">
                    <a href="#">{{ user.name }}</a>
                </div>
            </div>
        </div>
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {

        data() {
            return {
                users: []
            }
        },

        mounted() {
            Event.$on('users.here', (users) => {
                this.users = users;
            })
            .$on('users.joined', (user) => {
                this.users.unshift(user);
            })
            .$on('users.left', (user) => {
                this.users = this.users.filter(u => {
                    return u.id != user.id
                });
            });
        }
    }
</script>

<style>
    .users {
        background-color: #fff;
        border-radius: 3px;
    }
</style>

حال اگر یک کاربر جدید به برنامه لاراول مان وارد شود و به آدرس http://localhost:8000/chat برود، می توانیم نام آن را در قسمت کاربران ببینیم.

هنگامی که کاربری چت را ترک کند، آرایه users دوباره بروزرسانی می شود و ما می توانیم کاربران باقی مانده را ببینیم.

16- ارسال و دریافت پیام های بلادرنگ

حال تنها نکته ای که باقی مانده، ارسال و دریافت پیام های بلادرنگ توسط pusher در برنامه چت با لاراول و Vuejs است.

در قسمت back-end، باید یک کلاس event ایجاد کنیم.

هنگامی که یک پیام جدیدی ایجاد شود، این کلاس یک رویداد را ارسال و دریافت می کند.

به ترمینال رفته و کدهای زیر را برای ایجاد یک رویداد وارد کنید.

php artisan make:event MessageCreated

حال هنگامی که یک پیامی را ذخیره کردیم، باید این پیام را به channel ارسال کنیم.

برای اینکار کدهای زیر را در متد store() کنترلر MessageController.php بنویسید.

// MessageController.php

<?php

namespace App\Http\Controllers;

use App\Message;
use Illuminate\Http\Request;
use App\Events\MessageCreated;

class MessageController extends Controller
{
    public function index()
    {
        $messages = Message::with(['user'])->get();

        return response()->json($messages);
    }

    public function store(Request $request)
    {
        $message = $request->user()->messages()->create([
            'body' => $request->body
        ]);

        broadcast(new MessageCreated($message))
                ->toOthers();

        return response()->json($message);
    }
}

داخل فایل MessageController.php باید آرگومان ها را گرفته و به channel پاس دهیم.

// MessageCreated.php

<?php

namespace App\Events;

use App\Message;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class MessageCreated implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    public function broadcastWith()
    {
        $this->message->load(['user']);

        return [
            'message' => array_merge($this->message->toArray(), [
                'selfMessage' => false
            ])
        ];
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PresenceChannel('chat');
    }
}

برای اینکار، ابتدا یک اینترفیس برای برنامه چت با لاراول و Vuejs به نام shouldBroadCast را پیاده سازی می کنیم. سپس، نام آن را از privateChannel به presenceMessage تغییر داده و نام “chat” را به کانال مان ارسال می کنیم.

در قدم بعد، پیام ها را از یک کنترلر گرفته و به متد broadcastWith() می فرستیم.

حال داخل فایل echo.js ما یک کد برای دریافت رویدادها می نویسیم.

// echo.js

import Event from './event';

Echo.join('chat')
    .here(users => {
        Event.$emit('users.here', users);
    })
    .joining(user => {
        Event.$emit('users.joined', user);
    })
    .leaving(user => {
        Event.$emit('users.left', user);
    })
    .listen('MessageCreated', (data) => {
        Event.$emit('added_message', data.message);
    });

بعد از دریافت رویداد، یک رویداد دیگر منتشر و این رویداد توسط کامپوننت chatMessageComponent.vue دریافت می شود.

// ChatMessageComponent.vue

<template>
    <div class="message-area" ref="message">
        <message-component 
            v-for="message in messages" 
            :key="message.id" 
            :message="message">
        </message-component>      
    </div>
</template>

<script>

    import Event from '../event.js';

    export default {
        data() {
            return {
                messages: []
            }
        },
        mounted() {
            axios.get('/message').then((response) => {
                this.messages = response.data;
            });
            Event.$on('added_message', (message) => {
                this.messages.unshift(message);
                if(message.selfMessage) {
                    this.$refs.message.scrollTop = 0;
                }
            });
        }
    }
</script>
<style>
    .message-area {
        height: 400px;
        max-height: 400px;
        overflow-y: scroll;
        padding: 15px;
        border-bottom: 1px solid #eee;
    }
</style>

در انتها برای تست برنامه چت با لاراول و Vuejs مراحل زیر را اجرا کنید:

  1. آدرس http://localhost:8000/chat را باز کنید.
  2. در صورتی که به سایت لاگین نکرده باشید، به صفحه لاگین هدایت می شوید. بعد از آن به آدرس بالا بروید.
  3. یک پنجره جدید در مرورگر باز و در صفحه ثبت نام یک کاربر جدید را ثبت کنید و به آدرس فوق بروید.
  4. در قسمت کاربران می بینید که دو کاربر آنلاین هستند.
  5. حال یک پیام تایپ کرده و دکمه Enter را بزنید.
  6. در هر دو پنجره ی چت، پیام ها بدون رفرش صفحه، بروزرسانی می شوند.
  7. اگر در پنجره چت دومی، کاربری به پیام ما جواب دهد، ما می توانیم آن پیام را ببینیم.
  8. رنگ پس زمینه پیام های کاربران فرستنده و گیرنده با هم تفاوت دارند.
  9. به این ترتیب به پایان این مقاله آموزشی رسیدیم.

سورس این برنامه را می توانید از سایت گیت هاب دریافت کنید.

نویسنده شوید
دیدگاه‌های شما (9 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

علی
16 شهریور 1401
سلام. اگر از سرویس pusher در سایتهایی که روی سرور های داخل ایران قرار دارند استفاده کنیم مشکلی از نظر تحریم پیش نمیاد؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

سونیا
29 آبان 1399
سلام. اگر از جی کویری استفاده کنیم هم میشه از پوشر استفاده کرد به این شیوه که توی سایت گفتید یا فقط باید از ویو استفاده کرد؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

فرهاد
07 تیر 1399
داداش مسخرمون کرده . پوشه assets دقیقا کجاست ؟؟ اصلا مشخص نیست کجا آدرس دادی؟؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
08 تیر 1399
این پوشه در مسیر resources/assets/ لاراول قرار دارد.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

ایگل
18 خرداد 1399
سلام ممنون از سایت خوبتون ، من فایل assetرو پیدا نکردم آیا باید لاراول میکس رو نصب کنم ؟؟با لاراول ۷ خواستم بنویسم که مسیر assets > js > component فقط تو resource بود آیا باید سورس ها رو به همین فایل اضافه کنم ؟؟ممنون میشم پاسخ بدین

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مرتضی
15 شهریور 1398
ارسال one to one رو چطوری میتونیم پیاده کنیم؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
22 فروردین 1398
با سلام.. خیلی خوب بود لطفا در صورت امکان اموزش چت ویدیویی با استفاده از سرویس webrtc رو هم آموزش بدید.بنظرم خیلی بدرد بخور میتونه باشه

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مسعود ابراهیمی
20 اسفند 1397
سایت فقط روکسو بقیش مسخره بازیه واقعا بهترین منبع لاراوله دمتون گرم

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

behnam
28 آذر 1397
با سلام و خسته نباشید بابت سایت خوبتون. فایل ChatComponent.vue باید به شکل زیر اصلاح بشه. Chats import ChatMessagesComponent from './ChatMessagesComponent'; import ChatFormComponent from './chatFormComponent'; export default { mounted() { console.log('Component mounted.') }, component:{'chat-messages-component' : ChatMessagesComponent ,'chat-form-component' : ChatFormComponent } }

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

پژواک
07 آذر 1397
با سلام تمام مراحل اموزش بالا رو انجام دادم ولی بعد از اجرا در صفحه چت فقط امکان لاگین و خروج و ثبت نام دارم نمی توانم پیام تایپ کنم مشکل چیست؟ حتی جعبه ورود پیام هم موجود نیست

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.