【メモ】LaravelでDM機能つくった

はじめに

Laravelの練習としてDM機能をつくったので、自分用のメモとしての記録です。

モデル

Userモデルはすでにあるので、やりとりをまとめるBoardモデル、メッセージ単体のMessageモデルを作成。

$php artisan make:model Board -m
$php artisan make:model Message -m

-m をつけることで、マイグレーションファイルも同時作成。

マイグレーション

create_users_table.php

usersテーブルにプロフィール写真用のiconカラムを追加。

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->string('icon')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

create_boards.table.php

boardsテーブルには、送り元のユーザーID(s_user_id)、送り先のユーザーID(r_user_id)を追加。

    public function up()
    {
        Schema::create('boards', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('s_user_id');
            $table->bigInteger('r_user_id');
            $table->timestamps();
        });
    }

create_messages_table.php

messagesテーブルには、ユーザーID(user_id)、ボードID(board_id)、メッセージ本文(msg)を追加。

    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('user_id');
            $table->bigInteger('board_id');
            $table->string('msg');
            $table->timestamps();
        });
    }

リレーション

User.php

Userモデルは、たくさんのメッセージが紐づくので、hasMany。


    public function messages(){
        $this->hasMany('App\Message');
    }

    public function boards(){
        $this->hasMany('App\Board');
    }

Bord.php

Boardモデルは、ひとつのBoadにつき、送り元と送り先のユーザーがひとりずついるので、それぞれhasOneで指定。また、Boardはたくさんのメッセージが紐づくので、hasManyで指定。


    // 送り元ユーザーとの紐付け
    public function senderUser()
    {
        return $this->hasOne('App\User', 'id', 's_user_id');
    }

    // 送り先ユーザーとの紐付け
    public function recipientUser()
    {
        return $this->hasOne('App\User', 'id', 'r_user_id');
    }

    // DM相手を表示するためにotherUserを用意
    public function otherUser()
    {
        $user_id = Auth::id();
        $other_key = '';
        if ($user_id === $this->s_user_id) {
            $other_key = 'r_user_id';
        } else if ($user_id === $this->r_user_id) {
            $other_key = 's_user_id';
        }
        return $this->hasOne('App\User', 'id', $other_key);
    }

    // メッセージとの紐付け
    public function messages()
    {
        return $this->hasMany('App\Message');
    }

hasOne(‘第1引数:モデル名’, ‘第2引数:紐づけ先の外部キー’, ‘第3引数:紐づけ元のキー’);

Message.php

Messageモデルは、UserとBoardに属する関係だから、それぞれbelongsToで指定。

public function board(){
        return $this->belongsTo('App\Board');
    }
    public function user(){
        return $this->belongsTo('App\User');
    }

マイグレーション実行

うまくいけばデータベースにテーブルが作成されます。

$php artisan make:migrate;

コントローラー

BoardsController.php

BoardsControllerは、一覧表示用のindex、ボードの中身を見せるshow、新規ボード作成用のstoreアクションを追加。

public function index()
    {
  //ログインユーザーIDを取得し、そのユーザーが送った、または受け取ったboradを取得。
        $current_user_id = Auth::user()->id;
        $boards = Board::where('s_user_id', $current_user_id)->orwhere('r_user_id', $current_user_id)->get();
        return view('mypage', ['boards' => $boards]);
    }

    public function show($id)
    {
  //ボードモデルおよびメッセージモデルから、クリックしたboard_idの情報を取得する。
        $messages = Message::where('board_id', $id)->get();
        $board = Board::find($id);

        return view('messages', ['messages' => $messages, 'board' => $board]);
    }

    public function store(Request $request)
    {
        $board = new Board;
        $board->s_user_id = $request->s_user_id;
        $board->r_user_id = $request->r_user_id;
        $board->save();

  //boards一覧表示に戻る
        return redirect()->action(
            'BoardsController@index',
            ['board' => $board]
        );
    }

MessagesController.php

MessagesControllerは、メッセージ詳細表示用のindexと、新規メッセージ作成用のstoreを追加。

    public function store(Request $request)
    {
        $message = new Message;
        $message->msg = $request->msg;
        $message->bord_id = $request->bord_id;
        $message->user_id = $request->user_id;
        $message->save();

        $res = array(
            'message' => $message,
            'user_icon' => $message->user->icon,
            'user_name' => $message->user->name
        );

        return $res;
    }

ルーティング

Route::get('/mypage', 'BoardsController@index')->name('mypage');
Route::post('/mypage/store', 'BoardsController@store');

Route::resource(
    'boards.messages',
    'MessagesController',
    ['only' => ['index', 'store']]
);

Route::get('/messages/{id}', 'BoardsController@show')->name('messages');

ビュー

mypage.blade.php

mypage.blade.php ボード一覧表示、相手の名前を表示。

@if (count($boards) > 0)
            <div class="container">
                <div class="row">
                    @foreach ($boards as $board)
                        <div class="col-sm-6 col-lg-4">
                            <a href="{{ route('messages',$board->id) }}">
                                <h3 class="title">{{ $board->otherUser->name }}</h3>
                            </a>
                        </div>
                    @endforeach
                </div>
            </div>
        @endif

messages.blade.php

messages.blade.php 個別メッセージ一覧表示、相手の名前を表示。axiosで非同期処理!本当は相手と自分でCSSを分けたい…。

 <h2 class="title">Message for ...</h2>
        <p>{{ $board->otherUser->name }}</p>

        <div class="col-lg-6 text-center col-md-8 ml-auto mr-auto">
            <div class="form-group">
                <textarea name="content" class="form-control" placeholder="メッセージを入力...">{{ old('content') }}</textarea>
            </div>

            <input type="hidden" name="bord_id" value="{{ $board->id }}" class="board_id">
            <input type="hidden" name="sender_id" value="{{ $board->senderUser->id }}" class="sender_id">
            <input type="hidden" name="sender_name" value="{{ $board->senderUser->name }}" class="sender_name">
            <input type="hidden" name="sender_icon" value="{{ $board->senderUser->icon }}" class="sender_icon">
            <input type="hidden" name="recipient_id" value="{{ $board->recipientUser->id }}" class="recipient_id">
            <input type="hidden" name="recipient_name" value="{{ $board->recipientUser->name }}" class="recipient_name">
            <input type="hidden" name="recipient_icon" value="{{ $board->recipientUser->icon }}" class="recipient_icon">
            <input type="hidden" name="messages" value="{{ $messages }}" class="messages">
            <input type="hidden" name="user_id" value="{{ Auth::id() }}" class="user_id">
            <div class="form-group">
                <input type="submit" value="送信" class="btn btn-primary btn-round btn-block btn-lg submit_btn">
            </div>

            <ul class="msgArea list-unstyled"></ul>

app.js

async function message() {
    const messages = JSON.parse($('.messages').val());

    let content = "";
    messages.forEach(msg => {
        let user_icon = $('.sender_icon').val();
        let user_name = $('.sender_name').val();
        if (msg.user_id == $('.recipient_id').val()) {
            user_icon = $('.recipient_icon').val();
            user_name = $('.recipient_name').val();
        }
        const val = {
            'message': msg,
            'user_icon': user_icon,
            'user_name': user_name
        }
            content += tag(val)
    });

    $('.msgArea').html(content);
}

message();


$('.btn').on('click', function () {
    async function submit() {
        const params = {
            msg: $('.form-control').val(),
            board_id: $('.board_id').val(),
            user_id: $('.user_id').val(),
        }

        await axios.post(`/boards/${params.board_id}/messages`, params)
            .then(res => {
                const content = tag(res.data);
                $('.msgArea').append(content);
                $('.form-control').val('');
            })
            .catch(e => {
                alert(e.response);
            });
    }

    submit();
})

function tag(val) {
    return `<li class="mb-3 p-3">
<div class="container row">
<div class="col-3">
 <img src="/storage/icon/${val.user_icon}" alt="icon" class="icon rounded-circle img-fluid">
 <p class="user_name">${val.user_name}</p>
</div>
<div class="col-9 baloon">
 <p class="content says">${val.message.msg}</p>
</div>
<div class="col-12 text-left p-0">
 <p class="date">${val.message.created_at}</p>
</div>
            </li>`;
}

完成イメージ

CSSはサルワカさんを参考にしました!yukiのプロフ写真はアップしてないので表示されてません。。

まとめ

リレーションがよく分かってなかったことに気づけたし、axiosにも慣れてきた。あとは、またすっ飛ばしたバリデーションと、サーバーにアップする方法を調べてトライしてみたい!

参考

めちゃめちゃ参考にさせていただきました。

https://whatsupguys.net/programming-lartning-127/

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です