【Laravel×Vue.js】非同期通信でお気に入り登録を実装したらエラーになった話と解決までの道のり

#画面をリロードしないとお気に入り数がカウントされない
今回実装をしたのがLaravelとVue.jsを使用したお気に入り登録機能です。
その際、発生したエラーとエラー解決までの道のりを備忘録的に残しておきます。
今回の記事では具体的にどんなお気に入り登録機能を実装したのかという説明の詳細は割愛しているのでご容赦ください。

##発生したエラー
__エラー内容__
・お気に入りボタンを押下するとお気に入り数がカウントされない
・画面を再度リロードすると正しく反映される

①押下してもお気に入り済みとなるだけで右側のカウントはされません
![スクリーンショット 2021-07-29 11.14.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/1578d5e5-11d0-6e33-7ab6-7288aab3836b.png)
②画面をリロードするとカウントされる
![¥ 8,888.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/39aae950-f286-b2af-0f37-18ab4fa79ccf.png)

__実装内容__
以下のような商品に紐づくお気に入り登録機能を実装しました。
・バックエンドはLaravel、フロントはVue.js
・「誰が」「どの商品を」お気に入りしているかはお気に入りテーブル(likesテーブル）で管理
・「あるユーザーがある商品をお気に入り済みであるかどうか」はisLikedByメソッドを使用
・VueコンポーネントにBlade経由で値を渡す
・お気に入りを押すと、VueからLaravelに対して非同期通信を行う
・Laravel側でお気に入りのリクエストを受け取ると、likesテーブルを更新して結果（お気に入り数）をレスポンス
・テーブル設計はこんな感じでusers、likes、itemsテーブルを用意
![スクリーンショット 2021-08-04 10.23.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/30c37ac2-1f83-841b-86e8-9c2f0a1cbd25.png)
##エラー解決までの道のり
結論からいうとバックエンドLaravel側でいいねを付け外しを行うcontrollerのメソッドにミスがありました。

お気に入り数を取得するアクセサの表記名を間違えていたことが原因です。
非常に初歩中の初歩的なミスで恥ずかしい限りです・・・。

このミスに気づくまでどのように解決したか順を追って書いていきます。
###Vueデバッグツールを用いてフロント側の動作を確認する
__①最初の商品表示__
下記の画像にあるようにVue側ではボタンを押した後に誰がお気に入り登録を押したかを判断するisLikedBy部分（バックエンド側と非同期連携しなくてもボタンの押下で変わる）はfalse → trueに変化してます。一方でバックエンド側に非同期通信を行なった後のレスポンスである『initialCountLikes』『initialIsLikedBy』は変化していないことがわかりました。
![スクリーンショット 2021-07-29 11.30.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/d5cd572d-8b54-d440-98d1-3dc2b685be37.png)
__②お気に入りをおしてみる__
dataはtrueになるがcountlikesは表示されていない
￼![スクリーンショット 2021-07-29 11.31.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/c68ba284-5cc7-4ec2-5338-a10f01f96358.png)
__③リロードすると正しく更新される__
![スクリーンショット 2021-07-29 11.31.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/eab8974e-9930-c691-98cf-e330cc3f0120.png)
￼
__【わかったこと】__
問題はバックエンド側にありそう。

###そもそもDBには正しく登録されているのか
リロードすると正しくカウント数が表示されていることからDBへの登録はうまく処理ができていそうです。
念の為ですが確認します。案の定DBには正しく登録されていることがわかりました。

![2021-07- 2135 2021-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/037faf08-d6e8-b4a3-323b-303541cd33b7.png)
￼
__【わかったこと】__
DBには登録されている。
###バックエンド側の処理を確認する
問題はバックエンド側にありそうなのでまずはどのような流れで非同期通信が行われて、likeテーブルに更新がかかるのかを確認します。
①ユーザーがお気に入りボタンを押下する
②そのアクションに合わせてVue側からLaravel側に非同期通信を行なってlikeテーブルへの更新をかける
③put/deleteを記述してcontroller側に渡す
④controller側でDB更新を行うメソッド（unlike/like）を実行して結果を返す
⑤結果をVue側で受け取る

__ItemLike.vue__

```js
// ...省略
async like() {
        const response = await axios.put(this.endpoint)
        this.isLikedBy = true
        this.countLikes = response.data.countLikes
      },
      async unlike() {
        const response = await axios.delete(this.endpoint)
        this.isLikedBy = false
        this.countLikes = response.data.countLikes
      },


```
__web.php__

```php
// ...省略
    Route::put('items/{item}/like', 'ItemsController@like')->name('item.like');
    Route::delete('items/{item}/like', 'ItemsController@unlike')->name('item.unlike');
```

__ItemsController.php__

```php
// ...省略
    // お気に入りをつける
     public function like(Request $request, Item $item)
    {
        $item->likes()->detach($request->user()->id);
        $item->likes()->attach($request->user()->id);

        return [
            'id' => $item->id,
            'countLikes' => $item->likes_count,
        ];
    }

    // お気にいりを外す
    public function unlike(Request $request, Item $item)
    {
        $item->likes()->detach($request->user()->id);

        return [
            'id' => $item->id,
            'countLikes' => $item->likes_count,
        ];
    }

```

参考：Laravel公式マニュアル
https://readouble.com/laravel/7.x/ja/eloquent-mutators.html

__【わかったこと】__
これまでの調査からリロードをすると正しく更新される=DB側には正しく登録されていることまでは分かりました。Laravel側からVue側にデータを渡す段階でエラーが発生していそう。

###お気に入りをつけはずすメソッドを精査
よくよく見てみるとItemモデル側に用意したアクセサを呼び出す際の記述が間違っています。初歩中の初歩的な間違いですね。


```php
//NG
return [
　　'id' => $item->id,　
　　'countLikes' => $item->likes_count,　//ここが間違っている
];
```

```php
//正解
return [
　　'id' => $item->id,　
　　'countLikes' => $item->count_likes,　//正しくはこちら
];
```

__Models/Item.php__

```php
// ...省略
    public function getLikesCountAttribute()
    {
        return $this->likes->count();
    }
```

###アクセサを呼び出す際のルール
アクセサを作成、呼び出す際には命名規則があります。
getOooXxxAttributeという形で作成して、呼び出す際はooo_xxxという形にする必要があります。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザーのファーストネームを取得
     *
     * @param  string  $value
     * @return string
     */
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }
}
```

```php
$user = App\User::find(1);

$firstName = $user->first_name;
```

##まとめ
エラーの内容自体は非常に初歩的な内容でした。一方で今回初めてフロント側でVueを使用したり、非同期処理を併用したことも相まってエラーの特定作業が煩雑になってしまいました。

特に僕のような学習中の未熟者だと、新しい技術やネットにある実装例を組み合わせて進めていく機会が多いと思います。

その際は必ずと言って良いほどエラーが発生するので少しずつエラー内容を紐解き、原因となりうる要素を焦らず一つずつ潰していく必要があると思いました。