1581836640
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P10): Users Get Unread Messages After Logging In
Next, we formally started to develop chat room chat functions.
After the user logs in, the unread messages need to be sent to the client to remind the user how many unread messages there are. Today we will write the front-end and back-end code around this function.
First, open at the front resources/js/pages/Load.vue
, after the user logs will jump to the page, the Vue component mounted
functions to achieve the following:
async mounted() {
this.$store.commit("setTab", true);
// Listen only once globally
if (!this.isLogin) {
// We are logged in and send entry information.
if (this.userid) {
// Handling unread messages
socket.on("count", userCount => {
this.$store.commit("setUnread", userCount);
console.log(userCount);
});
this.$store.commit("setLoginState", true);
}
}
},
After the user first logs in successfully, the server will listen Websocket the count
event, and returns the data to initialize the various chat rooms in the callback function depending on the service end unread messages.
The front-end code is relatively simple without any adjustments. Next, we go to the back-end to implement the corresponding code to obtain the number of unread messages.
We create a new counts
table to store user unread messages in each chat room.
Quick commands generated by Artisan Count
model classes and the corresponding database migration categories:
php artisan make:model Count -m
Then modify the model class app/Count.php
code as follows:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Count extends Model
{
public static $ROOMLIST = [1, 2];
public $timestamps = false;
}
Note: In order to simplify the logic here, we write the chat room data hard.
In the database/migrations
directory just edit the migration class generated by CreateCountsTable
the up
method is as follows:
class CreateCountsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('counts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id');
$table->smallInteger('room_id');
$table->integer('count')->default(0);
});
}
...
We counts
define the four fields of the table, user_id
indicates a user ID, room_id
represents the room ID, count
indicates the number of messages that the user does not read in this room.
Finally, run the database migration command to generate countsthe table:
php artisan migrate
Open file Websocket route routes/websocket.php
in login
the route acquired unread messages written code is as follows:
WebsocketProxy::on('login', function (WebSocket $websocket, $data) {
if (!empty($data['token']) && ($user = \App\User::where('api_token', $data['token'])->first())) {
$websocket->loginUsing($user);
// Get unread messages
$rooms = [];
foreach (\App\Count::$ROOMLIST as $roomid) {
// Loop all rooms
$result = \App\Count::where('user_id', $user->id)->where('room_id', $roomid)->first();
$roomid = 'room' . $roomid;
if ($result) {
$rooms[$roomid] = $result->count;
} else {
$rooms[$roomid] = 0;
}
}
$websocket->toUser($user)->emit('count', $rooms);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
We users, traversing all rooms unread message, and then count
send it to the client conduit, if a client is listening this pipe, can read the message data and processed.
Below we log in the user again in the browser, and we can see the Websocket communication information as follows:
You can see the corresponding number of unread messages in Vue-> Vuex:
At this point the data table is empty, so the data is 0, we can counts
insert some test data in the table:
Then log in and test again, you can see the number of unread messages:
#laravel #swoole #vue
1582067700
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P13): Send Text/Emotional Messages
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:
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:
Next, let’s first look at the front-end component implementation.
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 submess
method:
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.
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).
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.
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.
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:
Restart the Swoole HTTP server, and you can see the rendered historical chat records in the front-end chat room room 1:
You can view all historical messages by scrolling up and down.
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.
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:
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
1582164420
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.
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:
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.
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
Finally, we in routes/websocket.php
the message
channel 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.
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:
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
1581836640
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P10): Users Get Unread Messages After Logging In
Next, we formally started to develop chat room chat functions.
After the user logs in, the unread messages need to be sent to the client to remind the user how many unread messages there are. Today we will write the front-end and back-end code around this function.
First, open at the front resources/js/pages/Load.vue
, after the user logs will jump to the page, the Vue component mounted
functions to achieve the following:
async mounted() {
this.$store.commit("setTab", true);
// Listen only once globally
if (!this.isLogin) {
// We are logged in and send entry information.
if (this.userid) {
// Handling unread messages
socket.on("count", userCount => {
this.$store.commit("setUnread", userCount);
console.log(userCount);
});
this.$store.commit("setLoginState", true);
}
}
},
After the user first logs in successfully, the server will listen Websocket the count
event, and returns the data to initialize the various chat rooms in the callback function depending on the service end unread messages.
The front-end code is relatively simple without any adjustments. Next, we go to the back-end to implement the corresponding code to obtain the number of unread messages.
We create a new counts
table to store user unread messages in each chat room.
Quick commands generated by Artisan Count
model classes and the corresponding database migration categories:
php artisan make:model Count -m
Then modify the model class app/Count.php
code as follows:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Count extends Model
{
public static $ROOMLIST = [1, 2];
public $timestamps = false;
}
Note: In order to simplify the logic here, we write the chat room data hard.
In the database/migrations
directory just edit the migration class generated by CreateCountsTable
the up
method is as follows:
class CreateCountsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('counts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id');
$table->smallInteger('room_id');
$table->integer('count')->default(0);
});
}
...
We counts
define the four fields of the table, user_id
indicates a user ID, room_id
represents the room ID, count
indicates the number of messages that the user does not read in this room.
Finally, run the database migration command to generate countsthe table:
php artisan migrate
Open file Websocket route routes/websocket.php
in login
the route acquired unread messages written code is as follows:
WebsocketProxy::on('login', function (WebSocket $websocket, $data) {
if (!empty($data['token']) && ($user = \App\User::where('api_token', $data['token'])->first())) {
$websocket->loginUsing($user);
// Get unread messages
$rooms = [];
foreach (\App\Count::$ROOMLIST as $roomid) {
// Loop all rooms
$result = \App\Count::where('user_id', $user->id)->where('room_id', $roomid)->first();
$roomid = 'room' . $roomid;
if ($result) {
$rooms[$roomid] = $result->count;
} else {
$rooms[$roomid] = 0;
}
}
$websocket->toUser($user)->emit('count', $rooms);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
We users, traversing all rooms unread message, and then count
send it to the client conduit, if a client is listening this pipe, can read the message data and processed.
Below we log in the user again in the browser, and we can see the Websocket communication information as follows:
You can see the corresponding number of unread messages in Vue-> Vuex:
At this point the data table is empty, so the data is 0, we can counts
insert some test data in the table:
Then log in and test again, you can see the number of unread messages:
#laravel #swoole #vue
1600583123
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
1581568500
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P7): Implementation of Front-end User Authentication Function Based on Muse UI 3.0
Since our chat room project front-end interface is based on the Muse UI, while Muse UI based on Material Design realization, so before you start, you need to import documents in view resources/views/index.blade.php
of <head>
the </head>
following new font and icon files introduced between the label:
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="//at.alicdn.com/t/font_500440_9oye91czwt8.css">
This is the font and icon library recommended by Muse UI(the last one is the Alibaba icon icon style file, which will be used in the chat room window). After the introduction, refresh the home page and you can see the following icons are rendered:
Note: Muse UI is an elegant Material Design UI component library based on Vue 2.0. It can be likened to Bootstrap, iView, Element and other UI frameworks.
Click on the “我的” link, prompting you to log in:
Click the “去登录” link to enter the login page to complete user authentication:
At first glance, it may seem that there is no username and password input box, but it is actually the reason for the color overlay of the background image:
But this style and Vue-Webchat renderings slightly different, because we are now installing the 3.0 version of the Muse UI, while the Vue-Webchat using Muse UI version 2.0, it is necessary to resources/js/pages/Login.vue
templates do for Muse UI component code Some adjustments:
<div class="content">
<form action="" name="form2">
<mu-text-field v-model="username" label="User" label-float icon="account_circle" full-width></mu-text-field>
<br/>
<mu-text-field v-model="password" type="password" label="password" label-float icon="locked" full-width></mu-text-field>
<br/>
<div class="btn-radius" @click="submit">Sign in</div>
</form>
<div @click="register" class="tip-user">Register Account</div>
</div>
...
export default {
data() {
return {
loading: "",
username: "",
password: ""
};
},
methods: {
async submit() {
const name = this.username.trim();
const password = this.password.trim();
...
Rerun the npm run dev
compiler front-end resources, and then force a refresh the login page, you can see the following renderings, and still OK:
Incidentally, the registration component resources/js/pages/Register.vue
template code to be modified, the logic and Login.vue
the same:
<div class="content">
<form action="" name="form1">
<mu-text-field v-model="username" label="Username" label-float icon="account_circle" full-width></mu-text-field>
<br/>
<mu-text-field v-model="password" type="password" label="Password" label-float icon="locked" full-width></mu-text-field>
<br/>
<div class="btn-radius" @click="submit">Signup</div>
</form>
<div @click="login" class="tip-user">
I already have a account
</div>
</div>
...
export default {
data() {
return {
username: "",
password: ""
};
},
methods: {
async submit() {
const name = this.username.trim();
const password = this.password.trim();
...
Then run npm run dev
recompiled front-end resources.
Next, we start with user registration, and jointly adjust the front-end and back-end user authentication related interfaces to open the user registration interface http://webchats.test/#/register
:
Commit logic source code by reading the front-end registration form, you can see the logic and back-end is ultimately responsible for registration interface interaction is located resources/js/api/server.js
in:
// Registration interface
RegisterUser: data => Axios.post('/register', data),
Referring to our previous backend API functions to achieve certification tutorials route definition, where the backend interface will need to adjust to /api/register
, as we are resources/js/api/axios.js
already defined baseURL
, so here it does not make any adjustments:
const baseURL = '/api';
...
// Request unified processing
instance.interceptors.request.use(async config => {
if (config.url && config.url.charAt(0) === '/') {
config.url = `${baseURL}${config.url}`;
}
return config;
}, error => Promise.reject(error));
In addition, the front end to the rear end of the data transfer register form further comprises a default avatar URL (corresponding to logically located Register.vue
in):
const data = {
name: name,
password: password,
src: src
};
const res = await this.$store.dispatch("registerSubmit", data);
This field can be mapped to the rear end of users
the new table avatar
fields, in order to simplify the front-end logic, we can assume that the user input is a mailbox account and mailbox rear prefix taken as the user name.
Corresponding to the app/Http/Controllers/AuthController.php
modified rear end user registration process register
to achieve the following:
public function register(Request $request)
{
// Verify registration fields
$validator = Validator::make($request->all(), [
'name' => 'bail|required|email|max:100|unique:users',
'password' => 'bail|required|string|min:6',
'src' => 'bail|active_url|max:255'
]);
if ($validator->fails()) {
return [
'errno' => 1,
'data' => $validator->errors()->first()
];
}
// Create user in database and return
$email = $request->input('name');
try {
$user = User::create([
'name' => substr($email, 0, strpos($email, '@')),
'email' => $email,
'avatar' => $request->input('src'),
'password' => Hash::make($request->input('password')),
'api_token' => Str::random(60)
]);
if ($user) {
return [
'errno' => 0,
'data' => $user
];
} else {
return [
'errno' => 1,
'data' => 'Failed to save user to database'
];
}
} catch (QueryException $exception) {
return [
'errno' => 1,
'data' => 'Save user to database exception:' . $exception->getMessage()
];
}
}
We modified the form validation rules and return data format to fit the front-end code. Also, do not forget to modify the App\User
model class of $fillable
property, the new avatar
field:
protected $fillable = [
'name', 'email', 'password', 'api_token', 'avatar'
];
Restart the Swoole HTTP server for the back-end interface adjustment to take effect:
bin/laravels reload
Vue-Webchat this demonstration project is relatively simple piece of processing in the user authentication, user authentication is successful will save user information localStorage
, as long as localStorage
contained in userid
it that the user has been authenticated, userid
is the user account, the corresponding logic is saved after user registration or login Finished:
const res = await this.$store.dispatch("registerSubmit", data);
if (res.status === "success") {
Toast({
content: res.data.data,
timeout: 1000,
background: "#2196f3"
});
this.$store.commit("setUserInfo", {
type: "userid",
value: data.name
});
this.$store.commit("setUserInfo", {
type: "src",
value: data.src
});
this.getSvgModal.$root.$options.clear();
this.$store.commit("setSvgModal", null);
this.$router.push({ path: "/" });
socket.emit("login", { name });
} else {
await Alert({
content: res.data.data
});
}
But now in this “我的” page error, suggesting that <mu-raised-button>
this component registration fails, this is Muse UI 3.0 version of the reason, the new version has been incorporated into this component <mu-button>
. So we need to open a user interface where the file resources/js/pages/Home.vue
, modify the corresponding component templates code show as below:
<div class="logout">
<mu-button @click="logout" class="demo-raised-button" full-width>Sign out</mu-button>
</div>
Further, Muse UI 3.0 pair of <mu-list>
use has been adjusted, it is necessary to adjust the reference code corresponding to the following components:
<mu-list>
<mu-list-item button @click="changeAvatar">
<mu-list-item-action>
<mu-icon slot="left" value="send"/>
</mu-list-item-action>
<mu-list-item-title>Modify avatar</mu-list-item-title>
</mu-list-item>
<mu-list-item button @click="handleTips">
<mu-list-item-action>
<mu-icon slot="left" value="inbox"/>
</mu-list-item-action>
<mu-list-item-title>Sponsor</mu-list-item-title>
</mu-list-item>
<mu-list-item button @click="handleGithub">
<mu-list-item-action>
<mu-icon slot="left" value="grade"/>
</mu-list-item-action>
<mu-list-item-title>Github address</mu-list-item-title>
</mu-list-item>
<mu-list-item button @click="rmLocalData">
<mu-list-item-action>
<mu-icon slot="left" value="drafts"/>
</mu-list-item-action>
<mu-list-item-title>Clear cache</mu-list-item-title>
</mu-list-item>
</mu-list>
Then run npm run dev
recompiled front-end resources, then refresh “我的” page:
You can see the user center interface that is normally rendered, and the list items also support clicking:
We click the “Logout” button on this page to log out, and then click the “我的” link at the bottom of the page again to enter the login page, and test the call to the login interface.
And registration function as front-end login screen and function need not be adjusted, and registered the corresponding logic is the same, ultimately through /api/login
the login logic interface call back-end user. Backend login interface needs to be adjusted to fit the front end of the login form submission, in app/Http/Controllers/AuthController.php
, modify login
as follows:
public function login(Request $request)
{
// Verify login fields
$validator = Validator::make($request->all(), [
'name' => 'required|email|string',
'password' => 'required|string',
]);
if ($validator->fails()) {
return [
'errno' => 1,
'data' => $validator->errors()->first()
];
}
$email = $request->input('name');
$password = $request->input('password');
$user = User::where('email', $email)->first();
// Token information is returned if the user successfully checks
if ($user && Hash::check($password, $user->password)) {
$user->api_token = Str::random(60);
$user->save();
return [
'errno' => 0,
'data' => $user
];
}
return [
'errno' => 1,
'data' => 'Username and password do not match, please re-enter'
];
}
Restart the Swoole HTTP server for the backend modification to take effect:
bin/laravels reload
Then fill in the user information on the front-end login page and click the “Login” button:
After login successfully, it also jumps to the homepage. This time, we clicked the “我的” link at the bottom of the page, but it showed that the authentication was not successful. This is because the account information, the backend interface field and the frontend default interface field are obtained from the returned data after the backend login interface is successfully called. res.data.data.email
Does not match, the back-end user account field is , and the front-end access is a non-existent field res.data.name
. In addition, the avatar field does not match:
const res = await this.$store.dispatch("loginSubmit", data);
if (res.status === "success") {
Toast({
content: res.data.data,
timeout: 1000,
background: "#2196f3"
});
this.$store.commit("setUserInfo", {
type: "userid",
value: res.data.name
});
this.$store.commit("setUserInfo", {
type: "src",
value: res.data.src
});
this.getSvgModal.$root.$options.clear();
this.$store.commit("setSvgModal", null);
this.$router.push({ path: "/" });
socket.emit("login", { name });
} else {
Alert({
content: res.data.data
});
}
Through this opportunity, we register and login to the back-end interface calls successfully unified gets saved data from back-end to front-end users localStorage
, while preserving api_token
the field to the front end for future use.
In the first resources/js/pages/Login.vue
, modify the back-end interface call log on user information stored after the success localStorage
of the relevant code:
const res = await this.$store.dispatch("loginSubmit", data);
if (res.status === "success") {
Toast({
content: res.data.message,
timeout: 1000,
background: "#2196f3"
});
this.$store.commit("setUserInfo", {
type: "userid",
value: res.data.user.email
});
this.$store.commit("setUserInfo", {
type: "token",
value: res.data.user.api_token
});
this.$store.commit("setUserInfo", {
type: "src",
value: res.data.user.avatar
});
this.getSvgModal.$root.$options.clear();
this.$store.commit("setSvgModal", null);
this.$router.push({ path: "/" });
socket.emit("login", { name });
} else {
Alert({
content: res.data.message
});
}
At the same time, the back-end interface also separates user data from message data:
public function register(Request $request)
{
// Verify registration fields
$validator = Validator::make($request->all(), [
'name' => 'bail|required|email|max:100|unique:users',
'password' => 'bail|required|string|min:6',
'src' => 'bail|active_url|max:255'
]);
if ($validator->fails()) {
return [
'errno' => 1,
'message' => $validator->errors()->first()
];
}
// Create user in database and return
$email = $request->input('name');
try {
$user = User::create([
'name' => substr($email, 0, strpos($email, '@')),
'email' => $email,
'avatar' => $request->input('src'),
'password' => Hash::make($request->input('password')),
'api_token' => Str::random(60)
]);
if ($user) {
return [
'errno' => 0,
'user' => $user,
'message' => 'User registered successfully'
];
} else {
return [
'errno' => 1,
'message' => 'Failed to save user to database'
];
}
} catch (QueryException $exception) {
return [
'errno' => 1,
'message' => 'Save user to database exception:' . $exception->getMessage()
];
}
}
public function login(Request $request)
{
// Verify login fields
$validator = Validator::make($request->all(), [
'name' => 'required|email|string',
'password' => 'required|string',
]);
if ($validator->fails()) {
return [
'errno' => 1,
'message' => $validator->errors()->first()
];
}
$email = $request->input('name');
$password = $request->input('password');
$user = User::where('email', $email)->first();
// Token information is returned if the user successfully checks
if ($user && Hash::check($password, $user->password)) {
$user->api_token = Str::random(60);
$user->save();
return [
'errno' => 0,
'user' => $user,
'message' => 'User login successful'
];
}
return [
'errno' => 1,
'message' => 'Username and password do not match, please re-enter'
];
}
public function logout(Request $request)
{
$user = Auth::guard('auth:api')->user();
if (!$user) {
return [
'errno' => 1,
'message' => 'User has logged out'
];
}
$userModel = User::find($user->id);
$userModel->api_token = null;
$userModel->save();
return [
'errno' => 0,
'message' => 'User exited successfully'
];
}
Review resources/js/pages/Register.vue
Related localStorage
code is as follows:
const res = await this.$store.dispatch("registerSubmit", data);
if (res.status === "success") {
Toast({
content: res.data.message,
timeout: 1000,
background: "#2196f3"
});
this.$store.commit("setUserInfo", {
type: "userid",
value: res.data.user.email
});
this.$store.commit("setUserInfo", {
type: "token",
value: res.data.user.api_token
});
this.$store.commit("setUserInfo", {
type: "src",
value: res.data.user.avatar
});
this.getSvgModal.$root.$options.clear();
this.$store.commit("setSvgModal", null);
this.$router.push({ path: "/" });
socket.emit("login", { name });
} else {
await Alert({
content: res.data.message
});
}
The user authentication logic related to WebSocket will be discussed in the next tutorial. Finally resources/js/pages/Home.vue
cleared when the user exits the token
data:
async logout() {
const data = await Confirm({
title: "prompt",
content: "Do you have the heart to leave?"
});
if (data === "submit") {
clear();
this.$store.commit("setUserInfo", {
type: "userid",
value: ""
});
this.$store.commit("setUserInfo", {
type: "token",
value: ""
});
this.$store.commit("setUserInfo", {
type: "src",
value: ""
});
this.$store.commit("setUnread", {
room1: 0,
room2: 0
});
this.$router.push("/");
this.$store.commit("setTab", false);
}
},
Recompile the front-end resources:
npm run dev
Restart the Swoole HTTP server:
bin/laravels reload
Access the login page again, after a successful login, the top of the page will be prompted to log in successfully, go to “我的” page, you can also see the login has been successful, and localStorage
you can see in the new token
field:
At this point, the front-end and back-end linkage user authentication function has been completed. Next, we will formally begin to enter the code development of real-time chat rooms based on WebSocket.
#laravel #swoole #vue