プログラミング教材で作ったチャットアプリに機能追加してみた

# はじめに
[こちら](https://www.techpit.jp/courses/195)の講座でチャットアプリを作成したので、追加のお勉強としてメッセージの削除機能を追加してみました。自分の備忘録を兼ねてやったことをメモ的に書いておきます。


# 環境
```
OS:MacOS 14.1.1（Sonoma）
Ruby：2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin23]
Vue.js:@vue/cli 5.0.8
```

# 仕様
* メッセージごとに削除ボタンを表示し、クリックすると該当メッセージが削除される
* 削除対象はチャットにログインしている本人が送信したメッセージのみ
    * 他人のメッセージには削除ボタンを表示させない
* メッセージ削除に成功するとメッセージ一覧が再取得され、削除したメッセージは画面から消える

# ルートを設定する
* メッセージ削除のルートを追加します
```config/routes.rb
resources :messages, only: ['index', 'destroy'] 
```

元々はメッセージ一覧取得用のindexだけ設定していたので今回はメッセージ削除のためにdestroyを追加しました。`resources :messages`だけだとCRUD全てのルートが作成されます。

# メッセージDELETEのルートが設定されていることを確認する
```
$ rails routes | grep message
message DELETE /messages/:id(.:format)                                                                  messages#destroy {:format=>:json}
```
messagesにDELETEのルーティングが追加されていることがわかります:relaxed:

# メッセージ削除のメソッドを実装する
* メッセージのコントローラに以下の通りコードを追加します
```app/controllers/messages_controller.rb
before_action :authenticate_user!, only: [ "destroy"]

 // ...省略

def destroy
    message = Message.find(params[:id])
    if (request.headers[:uid] == message.user.uid)
        if message.destroy
            render json: {id: message.id, email: message.user.email, message: 'メッセージの削除に成功しました'}, status: :ok
        else
            render json: {message: 'メッセージの削除に失敗しました', errors: message.errors.messages}, status: :badrequest
        end
    else 
        render json: {message: 'メッセージ作成者が違います'}, status: 401
    end
end
```
### 説明

```
before_action :authenticate_user!, only: [ "destroy"]
```
ここで削除の実行前に必ずユーザーがログイン済みであることをチェックしています

:::note info
* authenticate_user!とは？
	* deviseのメソッド
	* これをコントローラの先頭に書くことでユーザがログインしているかどうかを確認し、ログインユーザーのみ処理を実行できるようにする
:::

```
message = Message.find(params[:id])
```

メッセージのidをパラメタで渡すようにするので、パラメタのIDをもとにメッセージのレコードを検索し、messageという変数に格納しておきます。

```
if (request.headers[:uid] == message.user.uid)
 // ...省略
else 
    render json: {message: 'メッセージ作成者が違います'}, status: 401
end
```
削除条件の削除実行者とメッセージ作成者が一致することを確認しておきます。
ここで条件に一致しない場合(＝メッセージ作成者と削除リクエスト元のユーザーが一致しない場合)は、HTTPレスポンスとして401エラーを返すようにします。

:::note info
### HTTPの代表的なステータスコード一覧
* 200 OK：リクエスト成功
* 400 Bad Request：リクエスト不正、クライアントエラー
* 401 Unauthorized：認証に失敗
* 404 Not Found：Webページが見つからない
* 500 Internal：Server Error：サーバーエラー
:::

```
if message.destroy
    render json: {id: message.id, email: message.user.email, message: 'メッセージの削除に成功しました'}, status: :ok
```
メッセージの削除に成功した場合、成功のHTTPレスポンス(ok)を返します。200でも良いですがわかりやすくokにしておきます。

```
render json: {message: 'メッセージの削除に失敗しました', errors: message.errors.messages}, status: :badrequest
```
メッセージ削除に失敗した場合、Bad Requestというステータスとエラー内容を返します。

# PostmanでAPIの動作確認をする
ログイン情報をヘッダに付与した状態でメソッド：DELETEのAPIを叩いてみます。
`http://localhost:3000/messages/[message.id]`
以下のようなレスポンスが確認できました。
```json:本人の送信したメッセージを削除した場合
{"id": 3,"email": "test@gmail.com","message": "メッセージの削除に成功しました"}
```

```
Message Destroy (1.6ms)  DELETE FROM "messages" WHERE "messages"."id" = ?  [["id", 3]]
```
railsのログでもしっかり消えていることが確認できます
```json:本人以外のメッセージの削除を試行した場合
{"message": "メッセージ作成者が違います"}
```
```
Completed 401 Unauthorized in 12ms (Views: 0.3ms | ActiveRecord: 0.9ms | Allocations: 4127)
```
railsのログでもちゃんと401で怒られました。

# メッセージ削除ボタンを実装する
```vuejs:ボタン追加
<span class="deletebutton" @click="deleteMessage(message.id)">削除</span>
```
メッセージ一覧として画面に出力しているので、各メッセージに削除ボタンをspanタグで追加します。
クリックしたらdeleteMessageが発火します。その時(message.id)を引数として渡してあげるようにします。

```vuejs:メッセージ削除メソッド実装
methods: {
    async deleteMessage (messageId) {
        try {
            const res = await axios.delete(`http://localhost:3000/messages/${messageId}`,
                {
                    headers: {
                        uid: this.uid,
                        "access-token": window.localStorage.getItem('access-token'),
                        client: window.localStorage.getItem('client')
                    }
                })
            if (!res) {
                new Error('メッセージを削除できませんでした')
            }
            this.$emit('connectCable')
        } catch (error) {
            console.log(error)
        }
    },
        
```
deleteMessageのメソッドです。ヘッダーとしてログイン情報を付与し、DELETEのAPIを叩きます。


# CSSの設定
```CSS
.received .deletebutton {display: none;}
```
自分のメッセージか否かを見分けるようにしているので、受信した他人のメッセージは削除ボタン非表示にします。
```CSS
.sent .deletebutton {

    display: inline;

    background: #eee;

    border-radius: 30px;

    padding: 5px;

    color: #999;

    font-size: 10px;

    margin-bottom: 20px;

    margin-left: 4px;

    margin-right: 4px;

}
```
自分が送信したメッセージには削除ボタンを表示させます。
本当はメッセージの要素の下、送信時間の右横にボタンを表示させたかったのですが、うまくいかずとりあえずdisplay: inline;でメッセージ要素の右横にボタンを表示させてみました...

# できた画面
![スクリーンショット 2024-02-28 2.48.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629712/2a62be0d-62e8-eabc-46f1-125592482d09.png)
こんな感じ(CSS勉強します)
削除ボタンを押すとメッセージが削除され、メッセージ一覧が再読み込みされます。
これでメッセージの削除ボタンが実装できました。次は確認ダイアログを出すようにしてもいいかもしれません。

# 感想
[こちら](https://www.techpit.jp/courses/195)の講座の解説がわかりやすく、追加したコードの解説が必ず入るので初心者には大変嬉しい内容でした。UIも綺麗でやる気出るのでプログラミング学習はじめたばかりの方におすすめです！