laravel + vue.js + pusher - テーマを作ってチャット

(2025-03-16)

リアルタイムのチャットにこだわり続けていますが、本当の意味でまだ理解できておらず、徒に無駄に時間を浪費しています。

chatGPT に相談しているのですが、どうもあまり進捗しません。

かなりの時間を浪費して亀のようにしか進めません。

chatGPT を過大評価していたかもしれません。 chatGPT は自分ができないことになると、適当なコードをエラーの記憶の蓄積もなしに膨大な量を回答してきます。

まあ、フリーのものを使用させてもらっていますし、そんなに期待するのもおかしいのですが、これまでの chatGPT は余りにも優秀と感じていたので、 付き合い方を変えた方がいいかもしれません。

chatGPT に頼りすぎると自分で必死で考えるということが少しおろそかになるかもしれません。

pusher によるリアルタイムのチャット

これは、最初は livewire で実装しようと思ったのですがうまくいかず、ララジャパンというサイトにいいサンプルがあったのでコピーさせてもらいました。

割と簡単に動くので、テーマを作ってそのテーマごとにリアルタイムのチャットを導入しようと思ったのですが、簡単ではありません。

サンプロコードの意味を完全に理解していないと、当然ながら応用できるはずもありません。

chat.blade.php から vue ファイルに変数を渡す

昔の私のホームページに php から JavaScript に変数を渡すという記事があって、それなりにアクセスがありました。

それは当時それなりに難しいテーマでしたが、今回もここで引っかかりました。

chatGPT はなかなかいいコードを作ってくれません。結局はネットで調べました。

blade から vue ファイルに変数を渡すのは prop というものを使うようです。

<div id="app">
<chat :message="{{ $message }}"></chat>
</div>

から送って、

<template>
<div>
<p>{{ message }}</p> <!-- 親から渡されたメッセージを表示 -->
</div>
</template>
<script>
export default {
props: {
message: String, // 'message'プロップを定義
},
};
</script>

で受けます。

chat.blade.php から chat.js に themeId を渡す

vue ファイルを親ファイルである js ファイルでインポートしてもその変数は受け取れないようです。

chat.js
import { createApp } from 'vue';
import ChatForm from './components/ChatForm.vue';
createApp({})
.component('chat-form', ChatForm)
.mount('#app');

ここがとてもわかりにくいところで、chatGPT もしっかりと理解していなかったようで、なかなか答えを見つけられませんでした。

動かない chatGPT に訊くのではなく、もっと早くにネット検索をすればよかったのです。

blade で

@push('scripts')
<script>
const themeId = {{ $themeId }};
</script>
@endpush

とすれば、chat.js で

function addMessage(message) {
axios.post(`/chat/${themeId}/messages`, {
content: message.content,
user_id: message.user.id
})
}

としてすぐ使えます。

addMessage が表示されない

これも chatGPT がなかなか答えを見つけられませんでした。延々と同じコードを繰り返して、最後にやっと教えてくれました。

blade で

<chat-form :user="{{ auth()->user() }}" :theme-id="{{ $themeId }}" @messagesent="addMessage"></chat-form>

とすると、messagesent というイベントをリッスンしてイベントが発生したら addMessage というメソッドを実行します。

イベントは vue ファイル内で定義されています。

const emit = defineEmits(['messagesent']);

addMessage というメソッドは親コンポーネントで定義されています。

function addMessage(message) {
axios.post(`/chat/${themeId}/messages`, {
content: message.content,
user_id: message.user.id
});
}

なんとわかりにくい。

流れ

親子関係は、chat.blade.php が親で vue ファイルが子です。 chat.js は vue ファイルとサーバー間で通信を行うためのロジックを提供する JavaScript のファイルです。

ChatForm.vue
<script setup>
import { ref } from 'vue';
const props = defineProps({
user: Object,
});
const newMessage = ref('');
const emit = defineEmits(['messagesent']);
function sendMessage(e) {
if (newMessage.value === '') return;
emit('messagesent', {
user: props.user,
content: newMessage.value,
});
newMessage.value = '';
}
</script>
<template>
<div class="input-group">
<input
type="text"
class="form-control"
placeholder="メッセージをタイプしてください..."
v-model="newMessage"
/>
<span class="input-group-btn">
<button class="btn btn-primary" @click="sendMessage">
送信
</button>
</span>
</div>
</template>

最初はボタンの押下から始まります。これにより sendMessage が実行されます。 emitは関数の上に定義されています。ここでは messagesent というイベントをリッスンします。

blade では

<chat-form @messagesent="addMessage"></chat-form>

chat-form は単なるタグではなくて Vue コンポーネントを指しています。ハイフンでつながっているのでケバブケースと呼ばれます。 つまりChatForm.vueというコンポーネントを指しているのです。

そこで定義されている messagesent というイベントが発火して、chat.js の addMessage を実行します。

chat.blade.php
@push('scripts')
<script>
const themeId = {{ $themeId }};
</script>
@vite(['resources/js/chat.js'])
@endpush

としているので、chat.js を読み込んでいます。その中に addMessage メソッドが定義されているのです。

とてもむずかしいので、やはり vue を最初から勉強したほうが速いかもしれません。