Ethan Hughes

Ethan Hughes

1581307620

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4): Front-end Resource Initialization

As said at the beginning , the front-end interface of this practical tutorial will be based on the chat room project implemented by the front-end technology stack https://github.com/hua1995116/webchat in order to focus on the development of chat room functions based on Swoole for the convenience of introduction, it will be called Vue-Webchat.

However, Vue-Webchat’s front and back ends are implemented based on JavaScript. The front end uses the Vue framework, the back end uses the Express framework, the database uses MongoDB , and the front and back ends of WebSocket communication are based on Socket.io. In the project, we want to use the Laravel framework as the backend to store data in the MySQL database, and the WebSocket server is implemented based on Swoole. In addition, we also want to use Laravel Mix provided by Laravel as a front-end resource compilation tool to replace the native Webpack, so the original front-end code must be appropriately modified.

Front-end code for this project has been submitted to the Github repository: https://github.com/nonfu/webchat.

Front-end directory structure migration

In the Vue-Webchat this project, located in the front-end source code src directory:

src

Let’s delete project Laravel webchat of resources/js all files in a directory, then the Vue-Webchat of src directory migrated:

src

Here I make the adjustment on the part of the directory structure, such as the original project static/css/reset.css moved to resources/css/reset.css, the original project src/styles style under the code integrated into resources/sass/app.scss in order to better fit Laravel project directory structure. In addition, I will be the original project src directory App.vue and BaseTransition.vue files to the resources/js/layout directory, the original project src directory under the view subdirectory rename pages, this is just personal preference, of course, the corresponding need to modify all of the introduction of the file path.

Further, a front end JavaScript file entry main.js for renaming app.js.

Front view entry file

The front-end view entry file of the original project is located in the root directory, index.html and the view entry file of this project is located at resources/views/index.blade.php:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <link href="{{ asset('css/app.css') }}" rel="stylesheet" type="text/css" />

    <link rel="icon" type="image/x-icon" href="/favicon.ico">

    <title>Laravel学院在线聊天室</title>

    <script type='text/javascript'>
        window.Laravel = <?php echo json_encode(['csrfToken' => csrf_token(),]); ?>
    </script>
</head>
<body>
    <div id="app"></div>
    <script type="text/javascript" src="{{ asset('js/app.js') }}"></script>
</body>
</html>

Accordingly, the need to routes/web.php route the file to adjust the default home page view template:

Route::get('/', function () {
    return view('index');
});

Front-end code adjustment

Next, let’s focus on the modifications made to the front-end code to fit the Laravel back-end framework and the Swoole WebSocket server.

Interaction with back-end interface

Interaction with back-end interfaces are packaged in a unified resources/js/api directory, in axios.js the base package provides a library of Axios, and all back-end interface calls are located in server.js, where we need to axios.js be adjusted, adding CSRF Token request header:

import axios from 'axios';
const baseURL = '/api/';

const instance = axios.create();

instance.defaults.timeout = 30000; // 30s timeout on all interfaces
instance.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

// CSRF Token for all request headers
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
    instance.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

...

The routing and processing logic that specifically calls the back-end interface is ignored first, and then debug one by one when developing specific functions later.

Interaction with WebSocket server

Vue-Webchat project-based socket.io-client libraries to communicate as a client and WebSocket server, where we follow the client program, but the need to modify the connection configuration, back-end configuration changes to Swoole WebSocket server, modify the resources/js/socket.js code as follows:

// WebSocket request via Socket.io client
import io from 'socket.io-client'
const socket = io.connect(process.env.APP_URL + ':' + process.env.LARAVELS_LISTEN_PORT + '/ws/')
export default socket

And axios as specific call this socket module where the first matter, the development of specific functions to back one by one when debugging.

Adjustment of alias paths

Vue-Webchat item view upon introduction assembly and the module configuration @ path alias, such that:

import {queryString} from '@utils/queryString';

This project canceled this configuration and was introduced directly through relative paths:

import {queryString} from './utils/queryString';

All involve @ local path references should be adjusted, mainly in the pages Vue components under a subdirectory.

Front-end style compilation adjustments

As mentioned earlier, JavaScript files of this project is the entrance app.js, CSS resource file in my original project introduced here out of all, integrated into the webpack.mix.js lower compiled by Laravel Mix:

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css')
    .styles([
        'resources/css/reset.css',
        'public/css/app.css',
        'node_modules/muse-ui/dist/muse-ui.css'
    ], 'public/css/app.css');

Add front-end dependencies to package.json

Next, we add the front-end code involved in all new dependencies to the project under the root directory package.json in which:

"devDependencies": {
    "axios": "^0.18",
    "cross-env": "^5.1",
    "file-loader": "^4.2.0",
    "jquery": "^3.2",
    "laravel-mix": "^4.0.7",
    "lodash": "^4.17.5",
    "muse-ui": "^3.0.2",
    "popper.js": "^1.12",
    "resolve-url-loader": "^2.3.1",
    "sass": "^1.23.1",
    "sass-loader": "7.*",
    "socket.io-client": "^2.3.0",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2",
    "timers": "^0.1.1",
    "url-loader": "^2.2.0",
    "vivus": "^0.4.5",
    "vue": "^2.6.10",
    "vue-cropper": "^0.4.9",
    "vue-picture-preview": "^1.3.0",
    "vue-router": "^3.1.2",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.10",
    "vuex": "^3.1.1",
    "xss-filters-es6": "^1.0.0"
}

And then run the npm install install all front-end dependent libraries.

Of course, all the above front-end code migrations and adjustments have been submitted to the Github repository, you can download the corresponding resources directly to the local here: https://github.com/nonfu/webchat.

Compile front-end resources

Finally, in the root directory of the project npm run dev to compile all of these resources through the front Laravel Mix:

front-end resources

After the compilation is successful, next, we will start debugging the interaction between the front-end interface and the back-end interface, which will be explained in subsequent tutorials.

#laravel #vue #swoole

What is GEEK

Buddha Community

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4)
Ethan Hughes

Ethan Hughes

1582067700

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P13)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P13): Send Text/Emotional Messages

Functional Overview

Sending messages supports a variety of formats, including ordinary text, emoticons, pictures, etc. Today we will introduce the most basic text and emoticons (Emoji is essentially a text message). Sending text messages requires entering text in the bottom text input box And click the Send button to send the message:

This is image title

To send an emoticon (only Emoji is supported here) message, you need to click the emoticon icon to pop up a selection box, and then click to select an emoticon. The emoticon will be automatically rendered to the message text box and then sent with the text message:

This is image title

Front-end components

Next, let’s first look at the front-end component implementation.

Message sending logic

The front end interface components in the chat room resources/js/pages/Chat.vue, the bottom of the corresponding transmitted message is a JavaScript code, click the send button, will call this submessmethod:

submess() {
    // Determine if the send message is empty
    if (this.chatValue !== '') {
      if (this.chatValue.length > 200) {
        Alert({
          content: 'Please enter less than 100 words'
        });
        return;
      }
      const msg = inHTMLData(this.chatValue); // Prevent xss

      const obj = {
        username: this.userid,
        src: this.src,
        img: '',
        msg,
        roomid: this.roomid,
        time: new Date(),
        api_token: this.auth_token
      };
      // Pass message
      socket.emit('message', obj);
      this.chatValue = '';
    } else {
      Alert({
        content: 'the content can not be blank'
      });
    }
}

There will be a basic check in it. For example, the message content cannot be empty or exceed 100 characters. In addition, the input information will be processed to avoid XSS attacks. After all the above processes are completed, the message object will be initialized, and then Call the following code to send a message object through WebSocket communication:

socket.emit('message', obj);

After sending, clear the contents of the text box.

Message rendering logic

Rendering logic embedded in the page message from the sub-assembly Message is achieved by two-way data binding:

<Message
    v-for="obj in getInfos"
    :key="obj._id"
    :is-self="obj.userid === userid"
    :name="obj.username"
    :head="obj.src"
    :msg="obj.msg"
    :img="obj.img"
    :mytime="obj.time"
    :container="container"
    ></Message>

Here we noticed obj.username === userid replaced with obj.userid === userid, because the original VueChat implementations userid and username are equivalent, and we are here userid and email equivalence, is-self attributes used to distinguish when rendering the message issued its own or someone else’s hair (his own hair in the right Side, others posted on the left).

Emoji components

The corresponding implementation of the Emoji selection box is as follows:

<div class="fun-li emoji">
    <i class="icon iconfont icon-emoji"></i>
    <div class="emoji-content" v-show="getEmoji">
      <div class="emoji-tabs">
        <div class="emoji-container" ref="emoji">
          <div class="emoji-block" :style="{width: Math.ceil(emoji.people.length / 5) * 48 + 'px'}">
            <span v-for="(item, index) in emoji.people" :key="index">{{item}}</span>
          </div>
          <div class="emoji-block" :style="{width: Math.ceil(emoji.nature.length / 5) * 48 + 'px'}">
            <span v-for="(item, index) in emoji.nature" :key="index">{{item}}</span>
          </div>
          <div class="emoji-block" :style="{width: Math.ceil(emoji.items.length / 5) * 48 + 'px'}">
            <span v-for="(item, index) in emoji.items" :key="index">{{item}}</span>
          </div>
          <div class="emoji-block" :style="{width: Math.ceil(emoji.place.length / 5) * 48 + 'px'}">
            <span v-for="(item, index) in emoji.place" :key="index">{{item}}</span>
          </div>
          <div class="emoji-block" :style="{width: Math.ceil(emoji.single.length / 5) * 48 + 'px'}">
            <span v-for="(item, index) in emoji.single" :key="index">{{item}}</span>
          </div>
        </div>
        <div class="tab">
          <!-- <a href="#hot"><span>Hot</span></a>
          <a href="#people"><span>People</span></a> -->
        </div>
      </div>
    </div>
  </div>

The specific rendering logic is not the focus of this project. Interested students can go through the source code by themselves.

Run npm run dev recompiled front-end resources for the changes to take effect.

Back-end implementation

Writing API resource classes

Since the message rendering components Message need to pass the message data will be rendered, and the front-end and back-end message object attribute messages table can not be one to one, so we can write an API resource to do automatic conversion data structures between the two.

Prior to this, we first Message define its relationship with the user model class:

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    public $timestamps = false;

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

Then create by Artisan command Message API resource corresponding model class:

php artisan make:resource MessageResource

The path corresponding to the generated command app/Http/Resources/MessageResource.php to write conversion process toArray is as follows:

<?php
namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class MessageResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'userid' => $this->user->email,
            'username' => $this->user->name,
            'src' => $this->user->avatar,
            'msg' => $this->msg,
            'img' => $this->img,
            'roomid' => $this->room_id,
            'time' => $this->created_at
        ];
    }
}

The target structure we transform must be consistent with the front-end message object attribute field names, so that the message data returned by the back-end can be rendered normally on the front-end.

Modify history chat history interface

Next, we can write before the interface chat history applications above MessageResource do return JSON data structures automatically converted, open app/Http/Controllers/MessageController.php, amend history as follows:

use App\Http\Resources\MessageResource;

/**
 * Get historical chat history
 * @param Request $request
 * @return \Illuminate\Http\JsonResponse
 */
public function history(Request $request)
{
    ...
    // Paging query messages
    $messages = Message::where('room_id', $roomId)->skip($skip)->take($limit)->orderBy('created_at', 'asc')->get();
    $messagesData = [];
    if ($messages) {
        // Automatic transformation of JSON data structures based on API resource classes
        $messagesData = MessageResource::collection($messages);
    }
    // Return response
    return response()->json([
        'data' => [
            'errno' => 0,
            'data' => $messagesData,
            'total' => $messageTotal,
            'current' => $current
        ]
    ]);
}

Note: For the implementation principle of the API resource class, you can refer to the corresponding documents . We only use it here and do not introduce it in depth.

At this point, we are messages filling some test data in the table:

This is image title

Restart the Swoole HTTP server, and you can see the rendered historical chat records in the front-end chat room room 1:

This is image title

You can view all historical messages by scrolling up and down.

Implement message sending and broadcasting functions

Finally, we implement message sending and broadcasting functions based on Websocket.

Open the back-end Websocket routing file routes/websocket.php, write the implementation code that receives the message and broadcasts it to all online users in the chat room:

use App\Message;
use Carbon\Carbon;
    
WebsocketProxy::on('message', function (WebSocket $websocket, $data) {
    if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
        // Get message content
        $msg = $data['msg'];
        $roomId = intval($data['roomid']);
        $time = $data['time'];
        // Message content or room number cannot be empty
        if(empty($msg) || empty($roomId)) {
            return;
        }
        // Record log
        Log::info($user->name . 'in the room' . $roomId . 'Post message: ' . $msg);
        // Save message to database
        $message = new Message();
        $message->user_id = $user->id;
        $message->room_id = $roomId;
        $message->msg = $msg;
        $message->img = ''; // The picture field is temporarily blank
        $message->created_at = Carbon::now();
        $message->save();
        // Broadcast messages to all users in the room
        $room = Count::$ROOMLIST[$roomId];
        $messageData = [
            'userid' => $user->email,
            'username' => $user->name,
            'src' => $user->avatar,
            'msg' => $msg,
            'img' => '',
            'roomid' => $roomId,
            'time' => $time
        ];
        $websocket->to($room)->emit('message', $messageData);
        // Update the number of unread messages in this room for all users
        $userIds = Redis::hgetall('socket_id');
        foreach ($userIds as $userId => $socketId) {
            // Update the number of unread messages per user and send them to the corresponding online users
            $result = Count::where('user_id', $userId)->where('room_id', $roomId)->first();
            if ($result) {
                $result->count += 1;
                $result->save();
                $rooms[$room] = $result->count;
            } else {
                // If a record of the number of unread messages for a user does not exist, initialize it
                $count = new Count();
                $count->user_id = $user->id;
                $count->room_id = $roomId;
                $count->count = 1;
                $count->save();
                $rooms[$room] = 1;
            }
            $websocket->to($socketId)->emit('count', $rooms);
        }
    } else {
        $websocket->emit('login', 'Login to enter chat room');
    }
});

Implementation logic is very simple, to ensure that the user is authenticated, room number and content of the message is not empty premise, after obtaining the text message sent by the client (including Emoji expression), save it to the messages table, and then broadcast to All users can be in the room, where we did not use MessageResource to do automatically translate data structure, because the server can not get WebSocket Illuminate\Http\Request instance, which can lead to JSON serialization error.

Note: The picture sending is also based on this message channel. We will implement the corresponding processing code in the next article.

Finally, we also updated the number of unread messages for users, stored them in a database, and sent them to all online users.

Test live chat

At this point, we have completed all the coding work, restart the Swoole server:

bin/laravels restart

Log in to different users in Chrome and Firefox browsers and enter the same chat room to start a live chat online:

This is image title

Because it is based on Websocket communication, the page does not need to be refreshed to instantly get messages sent by the other party.

In the next tutorial, we will introduce the implementation of picture message sending. The source code for this project has been submitted to Github: https://github.com/nonfu/webchat.

#laravel #swoole #vue #online-chat

Ethan Hughes

Ethan Hughes

1582164420

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P14)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P14): Send a Picture Message

In the last tutorial we demonstrated the release of text/emotional messages in chat rooms. Today we will look at how to post picture messages.

Front-end interaction code

We started from the front end assembly, assembly in a chat room Chat.Vue, the client core logic to send pictures located fileup approach, we need to be adjusted to fit the rear end of the original code based interface Laravel + Swoole of:

fileup() {
    const that = this;
    const file1 = document.getElementById('inputFile').files[0];
    if (file1) {
      const formdata = new window.FormData();
      formdata.append('file', file1);
      formdata.append('api_token', this.auth_token);
      formdata.append('roomid', that.roomid);
      this.$store.dispatch('uploadImg', formdata);
      const fr = new window.FileReader();
      fr.onload = function () {
        const obj = {
          username: that.userid,
          src: that.src,
          img: fr.result,
          msg: '',
          roomid: that.roomid,
          time: new Date(),
          api_token: that.auth_token
        };
        socket.emit('message', obj);
      };
      fr.readAsDataURL(file1);
      this.$nextTick(() => {
        this.container.scrollTop = 10000;
      });
    } else {
      console.log('Must have file');
    }
},

When we click on the camera icon in the chat room, the image upload window will pop up:

This is image title

After selecting the picture, it will call the above fileup method to upload pictures.

It involves two logic: first calls the back-end interface to upload pictures based on the HTTP protocol and save the message to the messages table, it will send a message to Websocket server after a successful upload, and then by Websocket server broadcasts a message to all online users.

The upload image corresponds to this line of code:

this.$store.dispatch('uploadImg', formdata);

The final call to the back-end interface code is located resources/js/api/server.js in:

// upload image
postUploadFile: data => Axios.post('/file/uploadimg', data, {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
}),

We will write this interface in the backend shortly.

Sending a picture message based on Websocket corresponds to this line of code:

socket.emit('message', obj);

This is no different than sending a text message before the code is simply obj there to add a imgfield only.

Image upload interface

Next, we write an image upload interface on the Laravel backend.

In the routes/api.php new route file/uploadimg:

Route::middleware('auth:api')->group(function () {
    ...
    Route::post('/file/uploadimg', 'FileController@uploadImage');
}

Then create the controller with Artisan commands FileController:

php artisan make:controller FileController

In the newly generated file controller app/Http/Controllers/FileController.php in preparation uploadImage codes are as follows:

<?php
namespace App\Http\Controllers;

use App\Message;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class FileController extends Controller
{
    public function uploadImage(Request $request)
    {
        if (!$request->hasFile('file') || !$request->file('file')->isValid() || !$request->has('roomid')) {
            return response()->json([
                'data' => [
                    'errno' => 500,
                    'msg'   => 'Invalid parameter (room number/picture file is empty or invalid)'
                ]
            ]);
        }
        $image = $request->file('file');
        $time = time();
        $filename = md5($time . mt_rand(0, 10000)) . '.' . $image->extension();
        $path = $image->storeAs('images/' . date('Y/m/d', $time), $filename, ['disk' => 'public']);
        if ($path) {
            // If the picture is uploaded successfully, the corresponding picture message is saved to the messages table
            $message = new Message();
            $message->user_id = auth('api')->id();
            $message->room_id = $request->post('roomid');
            $message->msg = '';  // Text message left blank
            $message->img = Storage::disk('public')->url($path);
            $message->created_at = Carbon::now();
            $message->save();
            return response()->json([
                'data' => [
                    'errno' => 200,
                    'msg'   => 'Saved successfully'
                ]
            ]);
        } else {
            return response()->json([
                'data' => [
                    'errno' => 500,
                    'msg'   => 'File upload failed, please try again'
                ]
            ]);
        }
    }
}

This mainly involves image upload and message saving logic. Because we will save the picture to the storage/public next directory, in order to let the picture can be requested through the Web URL, you need to storage create a soft catalog:

php artisan storage:link

Websocket server broadcast

Finally, we in routes/websocket.php the messagechannel complementary picture message processing logic:

WebsocketProxy::on('message', function (WebSocket $websocket, $data) {
    ...
    // Get message content
    $msg = $data['msg'];
    $img = $data['img'];
    $roomId = intval($data['roomid']);
    $time = $data['time'];
    // Message content (including pictures) or room number cannot be empty
    if((empty($msg)  && empty($img))|| empty($roomId)) {
        return;
    }
    // Record log
    Log::info($user->name . 'in the room' . $roomId . 'Post message: ' . $msg);
    // Save messages to the database (except for picture messages, because they were saved during the upload)
    if (empty($img)) {
        $message = new Message();
        $message->user_id = $user->id;
        $message->room_id = $roomId;
        $message->msg = $msg;  // Text message
        $message->img = '';  // Picture message left blank
        $message->created_at = Carbon::now();
        $message->save();
    }
    // Broadcast messages to all users in the room
    $room = Count::$ROOMLIST[$roomId];
    $messageData = [
        'userid' => $user->email,
        'username' => $user->name,
        'src' => $user->avatar,
        'msg' => $msg,
        'img' => $img,
        'roomid' => $roomId,
        'time' => $time
    ];
    $websocket->to($room)->emit('message', $messageData);
    ...

Very simple, just add the picture message field uploaded by the client to the field of the previous broadcast message, without any other logic.

At this point, we can complete the front-end and back-end code for image message sending. Next, we test the sending of image messages on the chat room interface.

Test image message release

Before you start, recompile the front-end resources:

npm run dev

Make front-end code changes take effect. And restart Swoole HTTP and WebSocket server:

bin/laravels restart

Let the backend code changes take effect.

Then, open the chat room in Chrome and Firefox browsers, log in and enter the same room, you can send picture messages to each other in real time:

This is image title

At this point, we have completed the main function of the chat room. Next, we will optimize the project code, especially the performance and elegance of the back-end WebSocket communication.

#laravel #swoole #vue #chat-room

Ethan Hughes

Ethan Hughes

1581307620

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4): Front-end Resource Initialization

As said at the beginning , the front-end interface of this practical tutorial will be based on the chat room project implemented by the front-end technology stack https://github.com/hua1995116/webchat in order to focus on the development of chat room functions based on Swoole for the convenience of introduction, it will be called Vue-Webchat.

However, Vue-Webchat’s front and back ends are implemented based on JavaScript. The front end uses the Vue framework, the back end uses the Express framework, the database uses MongoDB , and the front and back ends of WebSocket communication are based on Socket.io. In the project, we want to use the Laravel framework as the backend to store data in the MySQL database, and the WebSocket server is implemented based on Swoole. In addition, we also want to use Laravel Mix provided by Laravel as a front-end resource compilation tool to replace the native Webpack, so the original front-end code must be appropriately modified.

Front-end code for this project has been submitted to the Github repository: https://github.com/nonfu/webchat.

Front-end directory structure migration

In the Vue-Webchat this project, located in the front-end source code src directory:

src

Let’s delete project Laravel webchat of resources/js all files in a directory, then the Vue-Webchat of src directory migrated:

src

Here I make the adjustment on the part of the directory structure, such as the original project static/css/reset.css moved to resources/css/reset.css, the original project src/styles style under the code integrated into resources/sass/app.scss in order to better fit Laravel project directory structure. In addition, I will be the original project src directory App.vue and BaseTransition.vue files to the resources/js/layout directory, the original project src directory under the view subdirectory rename pages, this is just personal preference, of course, the corresponding need to modify all of the introduction of the file path.

Further, a front end JavaScript file entry main.js for renaming app.js.

Front view entry file

The front-end view entry file of the original project is located in the root directory, index.html and the view entry file of this project is located at resources/views/index.blade.php:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <link href="{{ asset('css/app.css') }}" rel="stylesheet" type="text/css" />

    <link rel="icon" type="image/x-icon" href="/favicon.ico">

    <title>Laravel学院在线聊天室</title>

    <script type='text/javascript'>
        window.Laravel = <?php echo json_encode(['csrfToken' => csrf_token(),]); ?>
    </script>
</head>
<body>
    <div id="app"></div>
    <script type="text/javascript" src="{{ asset('js/app.js') }}"></script>
</body>
</html>

Accordingly, the need to routes/web.php route the file to adjust the default home page view template:

Route::get('/', function () {
    return view('index');
});

Front-end code adjustment

Next, let’s focus on the modifications made to the front-end code to fit the Laravel back-end framework and the Swoole WebSocket server.

Interaction with back-end interface

Interaction with back-end interfaces are packaged in a unified resources/js/api directory, in axios.js the base package provides a library of Axios, and all back-end interface calls are located in server.js, where we need to axios.js be adjusted, adding CSRF Token request header:

import axios from 'axios';
const baseURL = '/api/';

const instance = axios.create();

instance.defaults.timeout = 30000; // 30s timeout on all interfaces
instance.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

// CSRF Token for all request headers
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
    instance.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

...

The routing and processing logic that specifically calls the back-end interface is ignored first, and then debug one by one when developing specific functions later.

Interaction with WebSocket server

Vue-Webchat project-based socket.io-client libraries to communicate as a client and WebSocket server, where we follow the client program, but the need to modify the connection configuration, back-end configuration changes to Swoole WebSocket server, modify the resources/js/socket.js code as follows:

// WebSocket request via Socket.io client
import io from 'socket.io-client'
const socket = io.connect(process.env.APP_URL + ':' + process.env.LARAVELS_LISTEN_PORT + '/ws/')
export default socket

And axios as specific call this socket module where the first matter, the development of specific functions to back one by one when debugging.

Adjustment of alias paths

Vue-Webchat item view upon introduction assembly and the module configuration @ path alias, such that:

import {queryString} from '@utils/queryString';

This project canceled this configuration and was introduced directly through relative paths:

import {queryString} from './utils/queryString';

All involve @ local path references should be adjusted, mainly in the pages Vue components under a subdirectory.

Front-end style compilation adjustments

As mentioned earlier, JavaScript files of this project is the entrance app.js, CSS resource file in my original project introduced here out of all, integrated into the webpack.mix.js lower compiled by Laravel Mix:

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css')
    .styles([
        'resources/css/reset.css',
        'public/css/app.css',
        'node_modules/muse-ui/dist/muse-ui.css'
    ], 'public/css/app.css');

Add front-end dependencies to package.json

Next, we add the front-end code involved in all new dependencies to the project under the root directory package.json in which:

"devDependencies": {
    "axios": "^0.18",
    "cross-env": "^5.1",
    "file-loader": "^4.2.0",
    "jquery": "^3.2",
    "laravel-mix": "^4.0.7",
    "lodash": "^4.17.5",
    "muse-ui": "^3.0.2",
    "popper.js": "^1.12",
    "resolve-url-loader": "^2.3.1",
    "sass": "^1.23.1",
    "sass-loader": "7.*",
    "socket.io-client": "^2.3.0",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2",
    "timers": "^0.1.1",
    "url-loader": "^2.2.0",
    "vivus": "^0.4.5",
    "vue": "^2.6.10",
    "vue-cropper": "^0.4.9",
    "vue-picture-preview": "^1.3.0",
    "vue-router": "^3.1.2",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.10",
    "vuex": "^3.1.1",
    "xss-filters-es6": "^1.0.0"
}

And then run the npm install install all front-end dependent libraries.

Of course, all the above front-end code migrations and adjustments have been submitted to the Github repository, you can download the corresponding resources directly to the local here: https://github.com/nonfu/webchat.

Compile front-end resources

Finally, in the root directory of the project npm run dev to compile all of these resources through the front Laravel Mix:

front-end resources

After the compilation is successful, next, we will start debugging the interaction between the front-end interface and the back-end interface, which will be explained in subsequent tutorials.

#laravel #vue #swoole

Luna  Mosciski

Luna Mosciski

1600583123

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework.

Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

This framework was created by Evan You and still it is maintained by his private team members. Vue is of course an open-source framework which is based on MVVM concept (Model-view view-Model) and used extensively in building sublime user-interfaces and also considered a prime choice for developing single-page heavy applications.

Released in February 2014, Vue JS has gained 64,828 stars on Github, making it very popular in recent times.

Evan used Angular JS on many operations while working for Google and integrated many features in Vue to cover the flaws of Angular.

“I figured, what if I could just extract the part that I really liked about Angular and build something really lightweight." - Evan You

#vuejs #vue #vue-with-laravel #vue-top-story #vue-3 #build-vue-frontend #vue-in-laravel #vue.js

Ethan Hughes

Ethan Hughes

1581062820

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P2)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (Part 2): Background Database Preparation and API Authentication Function Implementation

In the last tutorial, we prepared the development environment for the chat room project and initialized the project. Today we will set up the database and complete the simple API authentication function.

Database preparation

Regarding the database, we need to add an avatar field to the user table that comes with Laravel, and then create a message table to store the messages sent by users in order to query historical messages.

Update user table

First, we run the following Artisan command to generate the database migration file for the user table users new avatar fields:

php artisan make:migration alter_users_add_avatar_column --table=users

Located just edit the automatically generated database\migrations directory of AlterUsersAddAvatarColumn categories:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AlterUsersAddAvatarColumn extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('avatar')->after('password')->nullable()->comment('用户头像');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('avatar');
        });
    }
}

We users added a table avatar field is used to store the user avatar path.

Create message table

Next, we create the message model and the corresponding data table migration file with the following Artisan commands:

php artisan make:model Message -m

This is image title

This command will app create a directory Message model class, while database/migrations generating the corresponding database migration files directory. Just edit the generated CreateMessagesTable class code as follows:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('msg')->comment('文本消息');
            $table->string('img')->comment('图片消息');
            $table->bigInteger('user_id');
            $table->smallInteger('room_id');
            $table->timestamp('created_at');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('messages');
    }
}

In the store the user to send a message messages table, we adopted msg storage field text messages, img field stores the picture message (a message containing only a picture), user_id for users and associated record, room_id for the association and the room because the message can not be changed, So we only set the creation time field created_at.

Run the migration command

To Laradock as a development environment, for example, then we laradock run the following command to start the container (ensure Docker has been launched, has started ignored) under the project directory:

docker-compose up -d nginx mysql redis

This is image title

And then connect to local MySQL database, create a chat room project webchat database:

This is image title

Please select the data encoding format utf8mb4 to support emoji.

Then through the docker exec -it laradock_workspace_1 bash command to enter the laradock_workspace_1 Bash interface, and enter the var /www/webchat directory run the following database migration command allows the above data table changes to take effect:

php artisan migrate

This is image title

At this time, refresh webchat the database, you can see the users table and the messages table of:

This is image title

API authentication implementation

After the database is ready, let’s implement the user authentication function, because the user can only send messages after logging in.

Since we ultimately want to achieve is separated from the front and rear end applications, it is necessary to provide the background is API-based user authentication, in order to simplify the process, we are here Laravel own, based on the most simple token realization API certification (ie drive config/auth.php in the default apiguard, If you want to use it in a production environment, we recommend API authentication based on Passport , which is more secure). For specific implementation, you can refer to the official API certification document.

Added token field in user table

According to the official document describes, we also need to users add a field to store a table tokens, so that users log on for the first time after the subsequent authentication token, which will be registered in this simple API implementation when the user authentication Generated, and no longer change. We generate the database migration file with the following Artisan command:

php artisan make:migration alter_users_add_api_token --table=users

Then edit the AlterUsersAddApiToken class codes are as follows:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AlterUsersAddApiToken extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('api_token')
                ->after('remember_token')
                ->unique();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('api_token');
        });
    }
}

The tokens that field is called api_token, we have to set up a unique index, and then laradock_workspace_1 run the migration command vessel to make this change to take effect:

php artisan migrate

User registration and login function implementation

Next, let’s create a controller for API authentication AuthController:

php artisan make:controller AuthController

Then you just created app/Http/Controllers/AuthController.php to write the following code in the registration method:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class AuthController extends Controller
{
    public function register(Request $request) 
    {
        // 验证注册字段
        Validator::make($request->all(), [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:6']
        ])->validate();

        // 在数据库中创建用户并返回包含 api_token 字段的用户数据
        return User::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => Hash::make($request->input('password')),
            'api_token' => Str::random(60)
        ]);
    }
}

In the registration method register, first verify user input registration field meets the specified rule, if verification is to create a new user record to complete the registration process, when adding a new user records, also insert a api_token field, where we set it to random characters string.

In order for the above batch (field) assignment to take effect, but also in the User class of $fillable new properties in the api_token field:

protected $fillable = [
    'name', 'email', 'password', 'api_token'
];

Next, we AuthController write the login method codes are as follows:

public function login(Request $request) 
{
    // 验证登录字段
    $request->validate([
        'email' => 'required|string',
        'password' => 'required|string',
    ]);

    $email = $request->input('email');
    $password = $request->input('password');
    $user = User::where('email', $email)->first();
    // 用户校验成功则返回 Token 信息
    if ($user && Hash::check($password, $user->password)) {
        return response()->json(['user' => $user, 'success' => true]);
    }

    return  response()->json(['success' => false]);
}

Is also very simple, to verify the user logon fields meets validation rules entered, if authenticated, through the email field from a database query corresponding to the user record, if it exists and by password verification, comprising returns api_token the user data field in the subsequent user You can authenticate by bringing this token field in the request. Laravel supports three ways to set the request token by default: query string, form request parameters, and Bearer Token. If the user record does not exist or the password verification fails, a login failure message is returned.

Finally, routes/api.php define registration and login route:

Route::post('/register', 'AuthController@register');
Route::post('/login', 'AuthController@login');

Postman Test API Certification

Next, we come under a simple test based API Token authentication token, open Postman, first access the API-based registration function to a POST request http://webchat.test/api/register routing, and entities added in the fields required for registration request, the registration is successful, it returns The newly created user data contains the token field. You can then use this token to perform API authentication directly:

This is image title

Then, let’s test the login route, and also request http://webchat.test/api/login it in POST mode . Add the login field in the requesting entity. The login successfully returns the user data and the success ID containing the token field. The API can be performed by returning the token value in the data. Certification:

This is image title

With the token value, we can bring this token in the request data to access the authentication route. For example, Laravel comes with http://webchat.test/api/user, we request it by GET, and then set the authentication method to Bearer Token, and on the right Token input box side of the input register or log in to return after a successful api_token field values, so that you can get the resources to certification, API certification represents a success:

This is image title

Previous: Laravel + Swoole + Vue to build a live online chat room (1)

#laravel #swoole #vue