Laravelで非同期チャットアプリを作ろう（１）

# はじめに
この記事では、片方がコメントを送信したら、もう片方の人はリロードしなくても、コメントが表示されるチャットアプリを作っていきたいと思います。

[Laravelで非同期チャットアプリを作ろう（２）](https://qiita.com/Alesion30/items/dddb3f162e60b16fdc6d)

# 完成物

![chat2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/6190d885-41a3-01b2-59ba-d73eebcb1f41.gif)

ソースコード：https://github.com/Alesion30/ChatApp

# 認証機能の実装

### 雛形の生成

```terminal
$ laravel new ChatApp
```

### マイグレーションの実行

```terminal
$ cd ChatApp
$ php artisan migrate
```

### laravel/uiのインストール

```terminal
$ composer require laravel/ui
```

### ログイン機能の実装

```terminal
$ php artisan ui vue --auth
$ npm install
$ npm run dev
```

##### resources/views/welcome.blade.php

<img width="1390" alt="スクリーンショット 2020-01-06 10.00.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/fbb77667-14b5-b3e4-ff43-e35eddcb4023.png">

##### resources/views/auth/login.blade.php

<img width="1390" alt="スクリーンショット 2020-01-06 10.01.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/82843dec-3837-2df4-1d5c-58fdf61e8dc8.png">

##### resources/views/auth/register.blade.php

<img width="1390" alt="スクリーンショット 2020-01-06 10.01.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/0346349d-216f-dba0-bb67-aaf25fa757c2.png">

### パスワードを8文字以上から4文字以上に変更する

app/Http/Controllers/Auth/RegisterController.phpの55行目の箇所を以下のように書き換える。

```app/Http/Controllers/Auth/RegisterController.php
'password' => ['required', 'string', 'min:4', 'confirmed']
```

### DBとの接続をする

phpMyAdminで、ChatAppというデータベースを作成する。

.envファイルの`APP_NAME`と`DB_USERNAME`と`DB_PASSWORD`にそれぞれ対応した値を記述する。

```
APP_NAME=ChatApp
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ChatApp
DB_USERNAME=(phpMyAdminのユーザー名)
DB_PASSWORD=(phpMyAdminのパスワード)
```

# 見た目を作る

<img width="1390" alt="スクリーンショット 2020-01-06 12.34.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/adefb9c9-c7e0-975c-b8de-75279aa9d4e4.png">

### home.blade.phpを編集する

ホーム画面に、コメントを表示するボードとコメントを入力する欄と送信ボタンをつける。

```html:resources/views/home.blade.php
@extends('layouts.app')

@section('content')
<div class="chat-container row justify-content-center">
    <div class="chat-area">
        <div class="card">
            <div class="card-header">Comment</div>
            <div class="card-body chat-card">
                
            </div>
        </div>
    </div>
</div>

<div class="comment-container row justify-content-center">
    <div class="input-group comment-area">
        <textarea class="form-control" placeholder="input massage" aria-label="With textarea"></textarea>
        <button type="input-group-prepend button" class="btn btn-outline-primary comment-btn">Submit</button>
    </div>
</div>

@endsection
```

### cssでデザインを整える

public/cssフォルダに新しくview.cssを作る。

```public/css/view.css
.chat-container {
  width: 100%;
  height: 100%;
}

.chat-card {
  height: 67vh;
  overflow: auto;
}

.chat-area {
  width: 70%;
}

.comment-container {
  position: fixed;
  bottom: 20px;
  text-align: center;
  width: 100%;
}

.comment-area {
  width: 70%;
}

.comment-btn {
  margin: 0px 10px;
}

.comment-body {
  padding: 5px 30px 20px 30px;
}

.comment-body:hover {
  background-color: #dfdfdf;
}

.comment-body-user {
  font-weight: bold;
  font-size: 20px;
}

.comment-body-time {
  font-size: 10px;
  margin-top: 10px;
  margin-left: 5px;
  color: #a0a0a0;
}
/*# sourceMappingURL=view.css.map */
```

view側で、view.cssを読み込む。resources/views/layouts/app.blade.phpに下のコードを追加する。

```html:resources/views/layouts/app.blade.php
<link href="{{ asset('css/view.css') }}" rel="stylesheet">
```

### コメントを表示する部品を作る

<img width="1027" alt="スクリーンショット 2020-01-06 12.45.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519787/28c3158b-f9d2-9846-5028-f470daa67755.png">

resources/viewsフォルダに新しくcomponentsフォルダを作り、その中にcomment.blade.phpを作成する。

```html:resources/views/components/comment.blade.php
<div class="media">
    <div class="media-body comment-body">
        <div class="row">
            <span class="comment-body-user">TestName</span>
            <span class="comment-body-time">2020-01-06 12:16:45</span>
        </div>
        <span class="comment-body-content">
            Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin. Cras purus odio,
            vestibulum in vulputate at, tempus viverra turpis. Fusce condimentum nunc ac nisi vulputate fringilla. Donec
            lacinia congue felis in faucibus.
        </span>
    </div>
</div>
```

### home.blade.phpにコメントを表示する部品を埋め込む

```html:resources/views/home.blade.php
@extends('layouts.app')

@section('content')
<div class="chat-container row justify-content-center">
    <div class="chat-area">
        <div class="card">
            <div class="card-header">Comment</div>
            <div class="card-body chat-card">
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
                @include('components.comment')
            </div>
        </div>
    </div>
</div>

<div class="comment-container row justify-content-center">
    <div class="input-group comment-area">
        <textarea class="form-control" placeholder="input massage" aria-label="With textarea"></textarea>
        <button type="input-group-prepend button" class="btn btn-outline-primary comment-btn">Submit</button>
    </div>
</div>

@endsection
```

# コメントを保存するテーブルを作る

### マイグレーションの作成

```terminal
$ php artisan make:migration create_comments_table
```

```database/migrations/2020_01_05_064658_create_comments_table.php
<?php

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

class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('login_id');
            $table->string('name');
            $table->string('comment');
            $table->timestamps();
        });
    }

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

### モデルの作成

```terminal
$ php artisan make:model Comment
```

```app/Comment.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $fillable = [
        'login_id', 'name', 'comment'
    ];

    protected $guarded = [
        'create_at', 'update_at'
    ];
}
```

# データベースからデータを取得して、画面に反映させる

HomeController.phpを開いて、`use App\Comment;`を追加し、index関数を以下のように書き換える。

```app/Http/Controllers/HomeController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Comment;
use Illuminate\Support\Facades\Auth;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
        $comments = Comment::get();
        return view('home', ['comments' => $comments]);
    }
}
```

foreachで、コメントを表示させる。comment.blade.phpにも、データを渡す。

```html:resources/views/home.blade.php
<div class="card-body chat-card">
    @foreach ($comments as $item)
    @include('components.comment', ['item' => $item])
    @endforeach
</div>
```

```html:resources/views/components/comment.blade.php
<div class="media">
    <div class="media-body comment-body">
        <div class="row">
            <span class="comment-body-user">{{$item->name}}</span>
            <span class="comment-body-time">{{$item->created_at}}</span>
        </div>
        <span class="comment-body-content">{!! nl2br(e($item->comment)) !!}</span>
    </div>
</div>
```

コメントは複数行表示出来るように、e()でエスケープ→nl2brで改行をbrタグに変換→タグがエスケープされないように{!! !!}で囲んでいる。

# コメントを送信できるようにする

### コメントをデータベースに保存する関数を作る

HomeController.phpでadd関数を作る。

```app/Http/Controllers/HomeController.php
public function add(Request $request)
{
    $user = Auth::user();
    $comment = $request->input('comment');
    Comment::create([
        'login_id' => $user->id,
        'name' => $user->name,
        'comment' => $comment
    ]);
    return redirect()->route('home');
}
```

### add関数をweb.phpに登録する

```routes/web.php
Route::post('/add', 'HomeController@add')->name('add');
```

### フォームを作る

home.blade.phpのコメントを送信する部分を、以下のように書き換える。

```html:resources/views/home.blade.php
<form method="POST" action="{{route('add')}}">
    @csrf
    <div class="comment-container row justify-content-center">
        <div class="input-group comment-area">
            <textarea class="form-control" id="comment" name="comment" placeholder="input massage"
                aria-label="With textarea"></textarea>
            <button type="submit" class="btn btn-outline-primary comment-btn">Submit</button>
        </div>
    </div>
</form>
```

### Shift+Enterでコメントを送信できるようにする。

home.blade.phpのコメントを送信する部分を、以下のように書き換える。

```html:resources/views/home.blade.php
<form method="POST" action="{{route('add')}}">
    @csrf
    <div class="comment-container row justify-content-center">
        <div class="input-group comment-area">
            <textarea class="form-control" id="comment" name="comment" placeholder="push massage (shift + Enter)"
                aria-label="With textarea"
                onkeydown="if(event.shiftKey&&event.keyCode==13){document.getElementById('submit').click();return false};"></textarea>
            <button type="submit" id="submit" class="btn btn-outline-primary comment-btn">Submit</button>
        </div>
    </div>
</form>
```

今回はここまで。次回は、本題である非同期通信を用いて、リアルタイムでチャットができるようにしたいと思います。