FirebaseからVue.jsへデータを取り出すサンプルをいくつか投稿してみる

#はじめに
人生で初めてQiitaに投稿します。
Qiitaの記事にはいつも助けられてます。

将来的にはGCP使って働きたいかもしれない。まだ先のことは分からない。

#Firebaseのサンプルはまだまだ少ない
今回、FirebaseとVue.jsでウェブアプリを作ってみています。
一番最初の何も分からない段階のころを思い出すと、
**データベースからデータを取り出すサンプルがいっぱいあったら良かったのになぁ**と思っていたので、Vue.jsでFirebaseからデータを取り出すサンプルを投稿しようと思います。

#今回の環境
・Vue CLI
・NPM
・Firebase 公式ドキュメント(https://firebase.google.com/docs/web/setup/?hl=ja)

#用意したデータベース側の状況
<img width="1125" alt="スクリーンショット 2019-09-13 1.39.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447711/28a18bf7-25bb-4f2c-409b-40476aee0487.png">

commentsコレクションをサンプルにやっていきます。

##１　コンポーネントのcreatedでデータを全部取り出す
```test.vue
<template>
  <div>
    {{ allData }}
  </div>
</template>

<script>
import firebase from 'firebase'

export default {
  data() {
    return {
      allData: [],
    }
  },
  created() {
    allComments: {

        firebase.firestore().collection('comments').get().then(snapshot => {
          snapshot.forEach(doc => {
            this.allData.push(doc.data())
          })
        })
      }
  },
  methods: {

  },
  computed: {

  },
}

</script>
```
```表示
[ { "comment": "5", "comment_id": 5, "name": "５", "post_id": 5, "twitter_id": "dbwp" }, { "comment": "8", "comment_id": 4, "name": "8", "post_id": 4, "twitter_id": "8" }, { "comment": "9", "comment_id": 5, "name": "9", "post_id": 3, "twitter_id": "9" }, { "comment": "9", "comment_id": 6, "name": "9", "post_id": 4, "twitter_id": "9" }, { "comment": "6", "comment_id": 1, "name": "6", "post_id": 5, "twitter_id": "6" } ]
```

こんな感じで全て出力することができます。
1項目目なので体力があるので全て解説すると、

```
collection('comments')
```
この部分は、最初に画像を乗せたコレクションっていう項目の中から選択する部分となります。
コレクションとcollectionが一致するのに時間がかかりましたよ。はっはっは。

コレクションの中のデータ全てを「snapshot」として取り出した上、
それをforEachでドキュメントの数だけ「doc」に入れていきます。

ここで大事なのが、docに入っているのはデータの配列ではないことです。

``` test.vue
<template>
   <div>
    <h2>{{ allDataID }}</h2>
  </div>
</template>

//中略

data() {
    return {
      allData: [],
      allDataID: [],
    }
  },
created: {
allCommentsID: {
          firebase.firestore().collection('comments').get().then(snapshot => {
            snapshot.forEach(doc => {
              this.allDataID.push(doc.id)
            })
          })
        }
}
```

```kekka.death!!!
[ "0hJ3JH4ltH0Ppg2aMrPl", "Aqc6Suz0jjAjdshraRmd", "O25hMfl29sUK2AzBJ9LG", "b3eVipfRqDMPAxHLNRqp", "lOqVpEQKmhHitMGVbZgY" ]
```

最初は、　doc.data()　で全てを出力しましたが、
今回は、　doc.id　となっています。
これ、データベース内の固有に付けられたidなんです。
Firebaseのデータを消す時に使います。
where等でデータを特定した上で、このidを使ってデータベースから削除したりアップデートします。

つまり、docの段階では使いたいデータではないということです。

使いたいのは、doc.data()となります。ややこしい。

ちなみに、

```
doc.data().post_id
```

とすると、

```
[ 5, 4, 3, 4, 5 ]
```

こうなる。


##２　規則性をもってデータを取り出したい場合

```test.vue
//前略

firebase.firestore().collection('comments').where('post_id','>=',1).get().then(snapshot =>

//以下略

```

```kekka.death!!!
[ 3, 4, 4, 5, 5 ]

```

数字が小さい順から取り出せました。
whereを使って、post_idの1から順に取り出せます。
これとreverse()を使えば、例えばcreated_atなどで、最新の順に並べるとかするときに使えます。

ぶっちゃけこれだけ押さえておけば、とりあえず使える（はず）

##３　whereでデータを特定する
whereは、==で一致したやつや、true、falseなどでも絞り込みが可能です。
さきほどのサンプルで、

```test.vue

  firebase.firestore().collection('comments').where('post_id','==',3).get().then(snapshot => 

```

こうすると

```kekka.dajo
[ 3 ]
```
こうやって出力されるわけです。
ネタ切れ感。


##４　vuexで全部取り出してみよう
vuexが理解できていない人は、逆に読んでほしいかもしれない。
なぜなら５日前までは、自分も理解できていなかったから。

基本的に
Actionsでfirebaseからデータを取り出して
MutationsでStateにデータを格納して
Stateには、データを格納する入れ子（初期値）を用意しておいて
Gettersでコンポーネント側に持っていく形にする

（Stateでもデータは持っていけるけど、javascriptの王がgettersでできる限り絞ろうって感じの記事を書いていたので、リスペクトしてます。なので、できる限りgettersで絞っていきたい。）


ということで、全部取り出しをします。

``` store/comment.js
actions: {
    fetchComment ({ commit }){
      firebase.firestore().collection('comments').where('comment_id','>=',1).get().then(snapshot => {
        snapshot.forEach(doc => commit( 'addComment',doc.data()))
      })
    },

```

さきほど全取り出ししたのとほぼ同じです。
commitっていうのは、Mutationsに伝達しているっていうニュアンスで良いかと思います。

つまり、Mutations内のaddCommentっていう名前のMutationsに、doc.data()を伝達しているってことです。

では、Mutationsの方をみていきましょう。
Mutationsの役割は、Stateにデータを格納することでした。

``` store/comment.js
    addComment (state, comment){
      state.comments.push(comment)
    },
```

これまた何が何だかわからないですが、Actionsからcommitされてきた

doc.data()が、第二引数のcommentに入っています。

これを、stateに定義してある、commentsの中にpushされるというわけです。

stateはこんな感じ。

```store/comment.js
  state: {
    comments: [],
  },

```

結果

```kekka.airabutikyuu
[ { "comment": "6", "comment_id": 1, "name": "6", "post_id": 5, "twitter_id": "6" }, { "comment": "8", "comment_id": 4, "name": "8", "post_id": 4, "twitter_id": "8" }, { "comment": "5", "comment_id": 5, "name": "５", "post_id": 5, "twitter_id": "dbwp" }, { "comment": "9", "comment_id": 5, "name": "9", "post_id": 3, "twitter_id": "9" }, { "comment": "9", "comment_id": 6, "name": "9", "post_id": 4, "twitter_id": "9" } ]

```

最初と同じ結果になりました。
たとえばこれをgettersの方で、

``` store/comment.js
  getters: {
    reverseComments: state => state.comments.slice().reverse(),
```

こうかくと、さきほどのstateの結果と逆順で表示されます。

##５　postに紐づいたlikeを取得し消す
```
v-for="post in posts"
```

でpostに対して紐づいたLikeを削除したいとします。

1人１回しか同じpostにlikeできないので、

post_idとuser_idから特定してdoc.idを取得→そのlikeを削除するという流れです。


```store/like.js
actions:{
  deleteLike ( {getters, commit}, postId ) {
      if(getters.uid){
        firebase.firestore().collection("likes").where('post_id','==',postId).where('user_id','==',getters.uid).get().then(querySnapshot => {
          querySnapshot.forEach(doc => {
            console.log(doc.id);
            var data = doc.id;
            firebase.firestore().collection('likes').doc(data).delete().then(function(){
              console.log('いいねを取り消しました');
            });
          })
        });
        commit('deleteLike')
      }
    },
}
```
whereを2回使って絞り込んだ上でdoc.idを特定して削除。
引数postIdはコンポーネント側で数値を入れるために使います。
ActionsのあとはMutationsからstateの値を変更して画面の表示が変わるようにします。

```store/like.js
  state: {
    deleteLike: 0,
    likedDrawer: true,
  },

  mutations: {
    deleteLike ( state ){
        state.deleteLike ++
        state.likedDrawer = false
    },

```

表示で「いいねの数を１減らす」と「ハートマークの表示を変える」が実装できれば良いわけなので、
今までのようにデータをコミットする必要はないのでこうなりました。

コンポーネント側

```PostLike.vue
<v-icon 
v-if="$store.getters.userLikedPosts.includes(post.post_id) && $store.state.like.likedDrawer"
color="pink accent-2"
@click="deleteLikeConfirm(post.post_id)">mdi-heart</v-icon>
<v-icon
v-else
color="grey lighten-2"
@click="iine">mdi-heart</v-icon>
<span>{{ like_number_length[0] - $store.state.like.deleteLike}}</span>//いいねの数

〜〜〜中略〜〜〜

methods:{
    deleteLikeConfirm(postId){
        if(confirm('削除しますか？')){
          this.deleteLike(postId)
        } 
    },
}

```

![画面収録-2019-09-13-12.42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447711/16190440-5fa0-77d1-6c73-af547bbf3071.gif)

実装できました！

##【ラスト】postと同時にそれに付いているLikeも同時に消す

```store/post.js
    //↓POSTの削除
    deletePost({ getters ,commit }, postId){
      if(getters.uid){
        firebase.firestore().collection("posts").where('post_id', '==', postId).get().then(doc => {
          var data = doc.docs[0].id;

          firebase.firestore().collection("posts").doc(data).delete().then(function(){
            alert("Document successfully deleted!");
          }).catch(function(error) {
            console.error("Error removing document: ", error);
          });
      });
      firebase.firestore().collection("likes").where('post_id','==',postId).get().then(querySnapshot => {
        querySnapshot.forEach(doc => {
          var likeData = doc.id ;
          firebase.firestore().collection("likes").doc(likeData).delete().then();
        });
      });
        commit('deletePost', postId)
      }
    },//post削除ここまで

```

今までの総力を結して同時に2個消します。
likeの方は今回は複数あるのでforeachで回します。

```store/post.js
state: {
    posts: [],
},

mutations: {
    deletePost ( state, { postId }){
        const index = state.posts.findIndex(post => post.post_id === postId)
        //postのidにindexを付与し、indexから１番目のみを削除(splice)
        state.posts.splice(index, 1)
    },
}
```

これでポストと、ポストに紐づいたLikeを全て削除できて、stateの変更もできます。

めでたしめでたし。


##最後に補足
１　ポストに紐づくlikeやcommentといったものについては、公式のドキュメントでは
posts/{postId}/likes/likecount/
みたいな感じで、postコレクションのドキュメントの直下に配置させることを推奨しています。

２　v-forで回したpostの消去については周りくどいidの特定はいらないかもしれない。

３　読みやすい文章は心がけているつもりですが、読みにくいところや分かりにくいところはダメ出しをしてくれれば助かります。よろしくお願いします。