laravel5.5 jTinderを使ってスワイプ機能 (マッチングアプリ)

## はじめに

<font color="red">DMM WEBCAMP Advent Calendar 2021</font> :snowman2:の14日目を担当させて頂く、yuukaです！(о´∀`о)

個人開発でマッチングアプリを作成した際にボタンのクリックだけでなく
スマホアプリのような滑らかなスワイプだったらかっこいいな〜
と考えて、実装した時のことを書いていこうと思います。

今回はjTinderを使って滑らかなスワイプを実装し、ajax通信でDBに値を保存していきす。☺️
## プレビュー
以下が完成イメージです。
[![Image from Gyazo](https://i.gyazo.com/1e44f75b5badf6d6d38dffc65e8a3e2c.gif)](https://gyazo.com/1e44f75b5badf6d6d38dffc65e8a3e2c)
## 前提
- laravel5.5を使用
- laravelの環境構築を終えている
- DBとの接続を終えている
- 新規登録、ログインの実装を終えている

## 実装手順
[1.ER図を確認する](#1er図を確認する)
[2.新規登録での画像アップロード機能](#2-新規登録での画像アップロード機能)
[3.jTinderの導入](#3-jtinderの導入)
[4.viewを作成](#4-viewを作成)
[5.jQueryの記述](#5-jqueryの記述)
[6.jQueryの説明](#6-jqueryの説明)
[7.DBを確認してみる](#7-dbを確認してみる)

#1.ER図を確認する

今回、マッチングの実装には触れずに`スワイプの部分の実装`になるのですが
イメージしやすいように全体のER図を作成しました。

マッチングはユーザ同士の繋がりになるので、**多対多**の関係です。
そのため、ここで作成しているMatchテーブルは、中間テーブルになります。
[![Image from Gyazo](https://i.gyazo.com/0efb3f982b6d3c5c811d835206e8dd6e.png)](https://gyazo.com/0efb3f982b6d3c5c811d835206e8dd6e)
Matchテーブルには、3つのカラムがあります。

| カラム名             |                                                      |
|--------------------|------------------------------------------------------|
| ***from_user_id*** | いいな,イマイチだなと判断するユーザのid (FK)                 　|
| ***to_user_id***   | いいな,イマイチだなと判断されるユーザのid (FK)                |
| ***is_like***      | いいなと思って貰えると**true**、イマイチだと**false**をDBに保存 　|

#2. 新規登録での画像アップロード機能

マッチングアプリですので、新規登録時に画像を登録する必要があります。

:::note warn
今回、新規登録での画像アップロード機能の実装は省略しています。
まだの方は先にそちらの実装からお願いします。
:::
以下のサイトは、とても分かりやすかったです。

https://biz.addisteria.com/user-register-avatar/

https://qiita.com/kei_Q/items/62cb8747280266956100

#3. jTinderの導入
*公式ドキュメント*

https://github.com/do-web/jTinder

1.公式のドキュメント内のjTinder.cssから**必要なcssの部分**をコピーする

公式のドキュメントで使用している画像は必要ないよ。
という方は、以下のようなコードはコピーしなくて大丈夫です。
可読性を高めるために、必要のないコードは省いていきましょう。

```css:css/jTinder.css
/* jTinder images */
#tinderslide .pane5 .img {
    background: url("../img/pane/pane5.jpg") no-repeat scroll center center;
    background-size: cover;
}
#tinderslide .pane4 .img {
    background: url("../img/pane/pane4.jpg") no-repeat scroll center center;
    background-size: cover;
}

~省略~

```
2.`resources/assets/sass`の配下に`_jTinder.scss`を作成

以下のような配置になっていれば、大丈夫です。
そして、作成した`_jTinder.scss`の中に1.でコピーした内容を貼り付けましょう。
[![Image from Gyazo](https://i.gyazo.com/7899cb27db5cc7f8a376e0ed81e5d767.png)](https://gyazo.com/7899cb27db5cc7f8a376e0ed81e5d767)
3.作成した`_jTinder.scss`をapp.scssでimportする

```diff_php:resources/assets/sass/app.scss
  // Variables
  @import "variables";
+ @import "jTinder";
```

4.jsも同じように作業

公式のドキュメントの
`js/jquery.jTinder.js`と`js/jquery.transform2d.js`の中身をコピーします。

`resources/assets/js`の配下に
`jquery.jTinder.js`と`jquery.transform2d.js`を作成します。

以下のような配置になっていれば、大丈夫です。
[![Image from Gyazo](https://i.gyazo.com/e30d445d4d7f9582f016e40e7e9d9db4.png)](https://gyazo.com/e30d445d4d7f9582f016e40e7e9d9db4)
5.作成した2つのファイルをapp.jsでrequireする

```diff_php:resources/assets/js/app.js
  require('./bootstrap');
+ require('./jquery.jTinder');
+ require('./jquery.transform2d');
```

#4. viewを作成

まずは、`resources/views/users`の中に**index.blade.php**を作成します。
`@foreach`を使用し、ユーザーの表示画面を実装していきます。

:::note to
1行目に書かれているid="tinderslide"でjsが発火。
4行目のdata-user_id="{{ $user->id }}"でユーザのidを送っています。
この２箇所を書き忘れないようにしましょう。
:::

```php:resources/views/users/index.blade.php
<div id="tinderslide">
    <ul>
        @foreach ($users as $user)
        <li data-user_id="{{ $user->id }}">
            <img src="{{ Storage::url($user->image) }}" />
            <div class="like">
                <img src="{{ asset('images/like.png')}}" alt="like" />
            </div>
            <div class="dislike">
                <img src="{{ asset('images/nope.png')}}" alt="dislike" />
            </div>
        </li>
        @endforeach
    </ul>

　　　　　　　　~省略~

```

ポイントは**likeクラス**と**dislikeクラス**です。
このクラスを呼び出すと[プレビュー](#プレビュー)のように顔写真の上に
LIKEやNOPEの画像をかっこよく表示してくれます。
今回、オリジナルの画像を使用していますが、icon, Font Awesomeなどを活用しても可愛いと思います！😊

表示する位置などを変更したい場合は`jTinder.css`の以下のコードを編集しましょう。

<details><summary>該当のcssコード</summary>
<div>

```css:resources/assets/sass/_jTinder.scss
/* Like & dislike badge images */
#tinderslide .like,
#tinderslide .dislike {
  opacity: 0;
  height: 80px;
  position: absolute;
  width: 170px;
  left: 45px; 
  top:40px;
  z-index: 1;
  overflow: hidden;
  -webkit-transform: translate3d(0%,0,0) scale3d(1,1,1);
  -moz-transform: translate3d(0%,0,0) scale3d(1,1,1);
  -ms-transform: translate3d(0%,0,0) scale3d(1,1,1);
  -o-transform: translate3d(0%,0,0) scale3d(1,1,1);
  transform: translate3d(0%,0,0) scale3d(1,1,1);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
```

</div>
</details>

#5. jQueryの記述

まだまだ、リファクタリングできると思いますので、アドバイス頂けると嬉しいです。
次の章で詳しく説明していきたいと思います。

```javascript
$("#tinderslide").jTinder({
    onDislike: function (user) {
        let user_id = user[0].dataset.id;
        postMatch(user_id, 0)
    },
    onLike: function (user) {
        let user_id = user[0].dataset.id;
        postMatch(user_id, 1)
    }
});

let postMatch = function (user_id, is_like) {
    $.ajax({
        type: "post",
        url: "/users",
        headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        },
        data: {
            to_user_id: user_id,
            is_like: is_like,
        },
    }).then((res) => {
        console.log(res);
    }).fail((error) => {
        console.log(error.statusText);
    });
};
```

#6. jQueryの説明

```javascript
$("#tinderslide").jTinder({
    onDislike: function (user) {
        let user_id = user[0].dataset.id; // $user->id が取得できる
        postMatch(user_id, 0)
    },
    onLike: function (user) {
        let user_id = user[0].dataset.id;
        postMatch(user_id, 1)
    }
});

~省略~

```
初めに、1行目の記述によって発火します。

そして、[viewを作成](#4-viewを作成)で記述した`<li data-id="{{ $user->id }}">`で
送った値は、datasetの中の`id`に入ります。
今回は、引数を**user**と指定しているため、`user[0].dataset.id`で値が取得できます。


```javascript
$("#tinderslide").jTinder({
    onDislike: function (user) {
      //
    },
    onLike: function (user) {    
      //
    },
```

左にスワイプさせると２行目の`onDislike:`
右にスワイプさせると５行目の`onLike:`に飛びます。
この部分の仕組みが気になる方は、`jquery.jTinder.js`の記述を調べてみてください。

```javascript
onLike: function (user) {
        let user_id = user[0].dataset.id;
        postMatch(user_id, 1)
    },
```

今回は、onLikeの中身を詳しく見ていきます。
１行目の` function (user) {`で引数を**user**と指定しています。
ここが**item**だった場合、`item[0].dataset.id`になります。

2行目で`user[0].dataset.id`を`user_id`に代入しています。

3行目で`postMatch`という関数を呼びだしています。
そして、2つの引数を送っています。
1つ目は`user_id`、２つ目は`is_like(boolean型)`の値を送っています。
`onLike`だと**1**、`onDislike`だと**0**を引数に設定しています。
**0** ... false
**1** ... true

続いて、`postMatch`という関数について説明していきたいと思います。
この関数の中で、ajax通信を行っています。

```javascript
const postMatch = function (user_id, is_like) {
};
```

先ほどの`postMatch(user_id, 1)`が
第一引数と第二引数の`(user_id, is_like)`に入ります。

次に、ajax通信の書き方になります。

:::note warn
LaravelでajaxのPOST送信を行うときは、上の4行目で記述しているように
CSRFトークンを追加する必要があります。
忘れてしまうと419エラーなどが起きてしまいます。
:::

```javascript
$.ajax({
    type: "post", // dbに新規登録するのでpostと指定
    url: "/users", // 保存する記述をしているコントローラーのurlを指定
    headers: { // CSRFトークンを追加
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    },
    data: { // 左側にカラム名、右側に保存したいデータ
        to_user_id: user_id,
        is_like: is_like,
    },
}).then((res) => {
    console.log(res);
}).fail((error) => {
    console.log(error.statusText);
});
```

ajax通信が終わり、dbに保存が成功した時は`.then`
どこかで予期せぬエラーが起きてしまった時は`.fail`にとぶように設定します。

#7. DBを確認してみる
スワイプした後にデータがきちんと入っているか確認してみましょう。
`is_like`のtureかfalseの判定が間違えていないかもチェックしてみてください。
[![Image from Gyazo](https://i.gyazo.com/ee574e3668851571c2f10542bcef23bd.png)](https://gyazo.com/ee574e3668851571c2f10542bcef23bd)

# 終わりに
今回は、laravelというよりもjQueryがメインになってしまいました😔
ajax通信やjTinderは、初めての実装だったので楽しかったです！
ぜひ、参考にしてもらえたら嬉しいです😳
今回は、マッチング機能には触れていないのでルーティングやコントローラーの記述は省略しています。
参考文献のYoutubeがとてもわかりやすく説明してくださっているので、実装したいな！と思った方はのぞいてみてください💁

## 参考文献

この記事は以下の情報を参考にして執筆しました。
*jTinderについての実装*

https://github.com/do-web/jTinder

*マッチングアプリの要件や実装の考え方*

https://www.youtube.com/watch?v=BscMulSaqCw