laravel - 为什么我的 websocket 实时聊天不起作用
问题描述
我正在使用 laravel websocket 构建实时聊天,我逐步按照 YouTube 上的教程进行操作,但是完成后我尝试发送消息,但它不会出现在其他聊天中,直到我刷新页面并且我解决了中的所有错误控制台不再出现错误但仍然无法正常工作我搜索了两天的解决方案但没有找到任何可以帮助我的东西,我几乎放弃了帮助我
这是我的包版本
"php": "^7.3|^8.0",
"beyondcode/laravel-websockets": "1.x-dev",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.40",
"laravel/sanctum": "^2.11",
"laravel/tinker": "^2.5",
"laravel/ui": "^3.3",
"league/flysystem-aws-s3-v3": "^1.0",
"league/flysystem-cached-adapter": "~1.0",
"livewire/livewire": "^2.5",
"pusher/pusher-php-server": "^5.0",
"unicodeveloper/laravel-password": "^1.0"
我的代码
聊天控制器
namespace App\Http\Controllers;
use App\Events\MessageSent;
use App\Models\Message;
use Illuminate\Http\Request;
class ChatsController extends Controller
{
public function index()
{
return view('chat.chat');
}
public function fetchMessages()
{
return Message::with('user')->get();
}
public function sendMessages(Request $request)
{
$message = session('LoggedAccount')->message()->create([
'message'=>$request->message
]);
broadcast(new MessageSent($message->load('user')))->toOthers();
return ['status'=>'success'];
}
}
网页.php
use App\Events\websocketdemoevent;
use App\Http\Controllers\UserController;
use App\Http\Controllers\AccountTypeController;
use App\Http\Controllers\AdCatogaryController;
use App\Http\Controllers\AdController;
use App\Http\Controllers\AdDescriptionController;
use App\Http\Controllers\AdInfoController;
use App\Http\Controllers\AdTypeController;
use App\Http\Controllers\AdTypeNameController;
use App\Http\Controllers\CatogaryDetailsController;
use App\Http\Controllers\ChatsController;
use App\Http\Controllers\ControleAddNewAdPageAPI;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
broadcast(new websocketdemoevent('x'));
return view('auth.login');
});
Route::post('/cheack', [UserController::class, 'cheack'])->name('auth.cheack');
Route::group(['middleware' => ['AdminAuth']], function () {
// Post Route
Route::post('/create', [UserController::class, 'create'])->name('auth.create');
Route::post('/createAccountType', [AccountTypeController::class, 'create']);
Route::post('/createAdCatogary', [AdCatogaryController::class, 'create']);
Route::post('/createAdType', [AdTypeController::class, 'create']);
Route::post('/createAd', [AdController::class, 'create']);
Route::post('/createAdDescription', [AdDescriptionController::class, 'create']);
Route::post('/createAdTypeName', [AdTypeNameController::class, 'create']);
Route::post('/createAdCatogaryDetails', [CatogaryDetailsController::class, 'create']);
Route::post('/createAdinfo', [AdInfoController::class, 'create']);
Route::post('/messages', [ChatsController::class,'sendMessages']);
// View Route
//Create Pages
Route::view('/createAccountType', 'CreatePages.CreateAccountType');
Route::view('/createAdCatogary', 'CreatePages.CreateAdCatogary');
Route::view('/createAdType', 'CreatePages.CreateAdType');
Route::view('/createAd', 'CreatePages.CreateAd');
Route::view('/createAdDescription', 'CreatePages.CreateAdDescription');
Route::view('/createAdTypeName', 'CreatePages.CreateAdTypeName');
Route::view('/createAdCatogaryDetails', 'CreatePages.CreateAdCatogaryDetails');
Route::view('/createAdinfo', 'CreatePages.CreateAdInfo');
//Controle Pages
Route::view('/controleAccount', 'ControlePages.ControleAllUsers');
Route::view('/controleAdCatogary', 'ControlePages.ControleAllAdCatogary');
Route::view('/controleCatogaryDetails', 'ControlePages.ControleAllCatogaryDetails');
Route::view('/controleAccountType', 'ControlePages.ControleAllAccountType');
Route::view('/controleAdDescription', 'ControlePages.ControleAllAdDescription');
Route::view('/controleAdTypeName', 'ControlePages.ControleAllAdTypeName');
Route::view('/controleAdInfo', 'ControlePages.ControleAllAdInfo');
Route::view('/controleAd', 'ControlePages.ControleAllAd');
Route::view('/controleAdType', 'ControlePages.ControleAllAdType');
// Get Route
Route::get('login', [UserController::class, 'login']);
Route::get('/register', [UserController::class, 'MakeAccount']);
Route::get('/register', [AccountTypeController::class, 'WebAddAccount']);
Route::get('/dashboard', [UserController::class, 'profile']);
Route::get('/logout', [UserController::class, 'logout']);
Route::get('/chats', [ChatsController::class,'index']);
Route::get('/messages', [ChatsController::class,'fetchMessages']);
Broadcast::channel('chat', function ($user) {
return $user;
});
});
引导程序.js
window._ = require('lodash');
/**
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
* for JavaScript based Bootstrap features such as modals and tabs. This
* code may be modified to fit the specific needs of your application.
*/
try {
window.Popper = require('popper.js').default;
window.$ = window.jQuery = require('jquery');
require('bootstrap');
} catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
wsHost: window.location.hostname,
wsPort: 6001,
forceTLS: false,
disableStats: true,
});
window.Echo.channel('DemoChannal')
.listen('websocketdemoevent',(e)=>{
console.log(e);
});
应用程序.js
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue').default;
Vue.config.productionTip = false
/**
* The following block of code may be used to automatically register your
* Vue components. It will recursively scan this directory for the Vue
* components and automatically register them with their "basename".
*
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
*/
// const files = require.context('./', true, /\.vue$/i)
// files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default))
Vue.component('chats', require('./components/ChatComponent.vue').default);
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
const app = new Vue({
el: '#app',
});
聊天组件.vue
<div class="row">
<div class="col-8">
<div class="card card-defualt">
<div class="card-header">Messages</div>
<div class="card-body p-0">
<ul class="list-unstyled" style="height: 300px; overflow-y: scroll">
<li class="p-2" v-for="( message, index ) in messages" :key="index">
<strong>{{ message.user.account_phone_number }}</strong>
{{ message.message }}
</li>
</ul>
</div>
<input
@keyup.enter="sendMessage"
v-model="newMessage"
type="text"
name="message"
placeholder="Enter Your Message..."
class="form-control"
/>
</div>
<span class="text-muted">User Is Typingg...</span>
</div>
<div class="col-4">
<div class="card card-defualt">
<div class="card-header">Active Users</div>
<div class="card-body">
<ul>
<li class="py-2">Mahmoud</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
messages: [],
newMessage: ''
}
},
created() {
this.fetchMessages();
Echo.join('chat')
.listen('MessageSent',(event)=>{
this.messages.push(event.message);
});
},
methods: {
fetchMessages() {
axios.get("messages").then(response => {
this.messages = response.data;
});
},
sendMessage() {
this.messages.push({
user: this.user,
message: this.newMessage
});
axios.post("messages", { message: this.newMessage });
this.newMessage = '';
},
},
};
</script>
// console.log(response.data);
聊天刀片.php
@extends('BootstrapLayout')
@section('insideHead')
<title>Chat</title>
<link href="{{asset('css/styles.css')}}" rel="stylesheet" />
<link rel="stylesheet" href="{{ mix('css/app.css') }}" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/js/all.min.js" crossorigin="anonymous"></script>
@endsection
@section('insideBody')
<body class="bg-dark">
<div id="layoutAuthentication">
<div id="layoutAuthentication_content">
<main id="app">
<div class="container" >
<chats :user="{{session('LoggedAccount')}}"></chats>
</div>
</main>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="{{asset('js/scripts.js')}}"></script>
<script src="js/app.js"></script>
</body>
@endsection
.env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:giz1gRYj511Abo5ExkQJj9oIzNitsws4EF5qOuokHFc=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mostaql1
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=pusher
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=anyID
PUSHER_APP_KEY=anyKey
PUSHER_APP_SECRET=anySecret
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
JWT_SECRET=L1JwpcKMqpe2I9VOu7FX3iC282m8h4hYkexuFAYIqEfH777eZiIu0dP5yXDx11OW
websockets.php
<?php
use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize;
return [
/*
* Set a custom dashboard configuration
*/
'dashboard' => [
'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001),
],
/*
* This package comes with multi tenancy out of the box. Here you can
* configure the different apps that can use the webSockets server.
*
* Optionally you specify capacity so you can limit the maximum
* concurrent connections for a specific app.
*
* Optionally you can disable client events so clients cannot send
* messages to each other via the webSockets.
*/
'apps' => [
[
'id' => env('PUSHER_APP_ID'),
'name' => env('APP_NAME'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
// 'path' => env('PUSHER_APP_PATH'),
// 'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
],
],
/*
* This class is responsible for finding the apps. The default provider
* will use the apps defined in this config file.
*
* You can create a custom provider by implementing the
* `AppProvider` interface.
*/
'app_provider' => BeyondCode\LaravelWebSockets\Apps\ConfigAppProvider::class,
/*
* This array contains the hosts of which you want to allow incoming requests.
* Leave this empty if you want to accept requests from all hosts.
*/
'allowed_origins' => [
//
],
/*
* The maximum request size in kilobytes that is allowed for an incoming WebSocket request.
*/
'max_request_size_in_kb' => 250,
/*
* This path will be used to register the necessary routes for the package.
*/
'path' => 'test/websockets',
/*
* Dashboard Routes Middleware
*
* These middleware will be assigned to every dashboard route, giving you
* the chance to add your own middleware to this list or change any of
* the existing middleware. Or, you can simply stick with this list.
*/
'middleware' => [
'web',
Authorize::class,
],
'statistics' => [
/*
* This model will be used to store the statistics of the WebSocketsServer.
* The only requirement is that the model should extend
* `WebSocketsStatisticsEntry` provided by this package.
*/
'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,
/**
* The Statistics Logger will, by default, handle the incoming statistics, store them
* and then release them into the database on each interval defined below.
*/
'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class,
/*
* Here you can specify the interval in seconds at which statistics should be logged.
*/
'interval_in_seconds' => 60,
/*
* When the clean-command is executed, all recorded statistics older than
* the number of days specified here will be deleted.
*/
'delete_statistics_older_than_days' => 60,
/*
* Use an DNS resolver to make the requests to the statistics logger
* default is to resolve everything to 127.0.0.1.
*/
'perform_dns_lookup' => false,
],
/*
* Define the optional SSL context for your WebSocket connections.
* You can see all available options at: http://php.net/manual/en/context.ssl.php
*/
'ssl' => [
/*
* Path to local certificate file on filesystem. It must be a PEM encoded file which
* contains your certificate and private key. It can optionally contain the
* certificate chain of issuers. The private key also may be contained
* in a separate file specified by local_pk.
*/
'local_cert' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT', null),
/*
* Path to local private key file on filesystem in case of separate files for
* certificate (local_cert) and private key.
*/
'local_pk' => env('LARAVEL_WEBSOCKETS_SSL_LOCAL_PK', null),
/*
* Passphrase for your local_cert file.
*/
'passphrase' => env('LARAVEL_WEBSOCKETS_SSL_PASSPHRASE', null),
],
/*
* Channel Manager
* This class handles how channel persistence is handled.
* By default, persistence is stored in an array by the running webserver.
* The only requirement is that the class should implement
* `ChannelManager` interface provided by this package.
*/
'channel_manager' => \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager::class,
];
频道.php
<?php
use Illuminate\Support\Facades\Broadcast;
/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/
Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::channel('chat', function ($user) {
return $user;
});
我没有注释 App\Providers\BroadcastServiceProvider::class,
广播服务提供者.php
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes(['middleware' => 'AdminAuth']);
require base_path('routes/channels.php');
}
}
事件/MessageSent.php
<?php
namespace App\Events;
use App\Models\Message;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Message $message)
{
$this->message=$message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PresenceChannel('chat');
}
}
模型/Message.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Message extends Model
{
use HasFactory;
protected $table = 'messages';
protected $primaryKey = 'message_id';
protected $fillable = [
//'account_id',
'message'
];
public function user()
{
return $this->belongsTo(User::class,'account_id','account_id');
}
}
模型/用户.php
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
protected $table = 'users';
protected $primaryKey = 'account_id';
protected $fillable = [
'account_phone_number',
'serial_number',
'e_mail',
'password',
'account_type_id'
];
public function message()
{
return $this->hasMany(Message::class,'account_id','account_id');
}
}
解决方案
推荐阅读
- javascript - 将参数传递给函数反应失败
- dataset - 获取新月,集群数据集中的集群
- redis - 为什么我的 redis 服务器每分钟关闭一次?
- optimization - 我们可以使用强化学习在成本函数中获得预定义常量的“好”值吗?
- r - 在 R 中调用函数内部的变量
- cmake - 使用 cmake 构建 mxnet 无法找到 mkl_blas.h
- apache-spark - Spark 3.1 - Java 失败并出现 IllegalArgumentException:无法增长 BufferHolder 错误
- c - 如何在 scanf("%d%c"); 中只输入字符
- javascript - Vue如何将嵌套数组合并为一个数组
- qt - 如何在 ui 窗口中创建草图区域