Vue.jsとFirestoreでお気に入り機能(Vuex)

2021.01.18

見出し画像

今日はお気に入り機能を実装したので、その振り返りをしていきます。

いいね機能とかも基本的に同じ流れで実装できるかなと。

目次

  1. 実装する事
  2. 簡単に流れを。。
  3. 保存、削除
  4. お気に入りした記事の取得
  5. マイページでお気に入りした記事を取得
  6. 記事の詳細ページでお気に入り
  7. 実装してみて

実装する事

記事にお気に入りできる。削除できる。(非同期)

マイページに自分のお気に入りした投稿の一覧を表示する。

記事の詳細ページからお気に入りできる(削除も)。

簡単に流れを。。

最初に、<template>とかcretae(){}などの表記は基本省略して書いてますのでよろしくお願いします。

記事に対してお気に入りするとFirestoreのデータベースにGoodsテーブルが追加される。

お気に入りのボタンがお気に入り解除に変わる。

実際に保存される内容は、記事のid,ユーザーのidになる。laravelとかでいう中間テーブルになるのかな?

お気に入りした記事はお気に入り一覧に登録される。

スーパーざっくりしてますが、大きくはこんなイメージです。

保存、削除

まずは、お気に入りする->データベースに保存、削除まで。

まずはコードを。

<v-btn v-if="!post.fav_status" @click.prevent="favorite(post)">
  <p>お気に入り</p>
</v-btn>
<v-btn v-else @click.prevent="favorite(post)">
  <p>お気に入り解除</p>
</v-btn>

favorite(post) {
   this.$store.dispatch('createFav', post)
}

やっている事はシンプルで。お気に入りボタンを押すとfavoriteメソッドが動く。dispatchでactionsのcreateFavを呼び出す。

気おつけるところは引数にはpost(お気に入りした記事)が含まれている事。

(注)下、create、deleteはmutations。createFavはactionsです。省略してます。

state: {
   myfavs: []
 },

create(state, post) {
     const user = firebase.auth().currentUser
     db.collection('favs').add({
       user_id: user.uid,
       post_id: post.id
     })
     .then(() => {
       console.log('お気に入り登録')
     })
     .catch(err => {
       console.log(err)
     })
   },
delete(state, post) {
     db.collection('favs').where('post_id', '==', post.id).get().then(snap => {
       snap.forEach(ele => {
         let delete_good_post = ele.data()
         delete_good_post.id = ele.id
         if(delete_good_post.post_id === post.id) {
           db.collection('favs').doc(delete_good_post.id).delete().then(() => {
             state.myfavs = state.myfavs.filter(fav => fav.id !== delete_good_post.id)
             console.log('お気に入り削除')
           })
         }
       })
     })
   }

createFav({commit, state}, post) {
     if(state.myfavs.length) {
       state.myfavs.forEach(ele => {
         if(post.id !== ele.id) {
           commit('create', post)
         } else {
           commit('delete', post)
         }
       })
     } else {
       commit('create', post)
     }
   }

createFavで処理を受け取ったら、条件分岐でcreateするのかdeleteするのか処理を分けている。

if(state.myfavs.length)はその記事がもう既にお気に入りされているかどうかを確認している。既にある場合はdeleteに飛ばしお気に入り解除の設定に飛ばしている。

deleteの処理はもっとイケメンなコードの書き方ないかな〜と思ってます。もしありましたらご教授ください。

お気に入りした記事の取得

ライフサイクルメソッドを使用して行います。

ここでは既にお気に入りされている記事とそれ以外の記事で区別をつけます。お気に入りボタンの表示を変えるためです。

created() {
   this.$store.dispatch('getposts')
   this.$store.dispatch('getFavs')
 },
computed: {
   posts() {
     let array = this.$store.state.myfavs
     let publicPost = []
     this.$store.state.posts.forEach(element => {
       if(element.public === true) {
         publicPost.push(element)
       function checkAlreadyFavs(arr, id) {
         return arr.some(function(value) {
           return id === value.id
         })
       }
       if(checkAlreadyFavs(array, element.id)) {
         element.fav_status = true
       } else {
         element.fav_status = false
       }
       publicPost.push(element)
     });
     return publicPost
   }

ページにアクセスした際にdispatchでgetpostsとgetFavsが処理される用にします。

処理が通った後は記事一覧とお気に入りしている投稿一覧を取得し、既にお気に入りされている記事にはelement.fav_status = trueを与え、それ以外の記事にはelement.fav_status = falseを与え区別してからreturnしています。

element.fav_statusの値によってお気に入りボタンの表示を切り替えるようにしています。1番上のコードに書いてます。

もしtrueだったらボタンはお気に入り解除を。falseだったらお気に入りボタンを表示させるというような形ですね。

state: {
   posts: [],
   myfavs: []
 },

mutationsです
getfav(state, favs_post) {
   state.myfavs = favs_post
},

getpost(state, posts) {
     state.posts = posts
   },

actionsです
getFavs({commit}){
     db.collection('favs').onSnapshot(snapshot => {
       let get_favs = []
       snapshot.forEach(ele => {
         let fav = ele.data()
         fav.id = ele.id
         const user = firebase.auth().currentUser
         if(fav.user_id === user.uid) {
           db.collection('posts').doc(fav.post_id).onSnapshot(snap => {
             let fav_post = snap.data()
             fav_post.id = snap.id
             get_favs.push(fav_post)
           })
         }
       })
       commit('getfav', get_favs)
     })
   },

getposts({commit}) {
     db.collection('posts').onSnapshot(snapshot => {
       let posts = []
       snapshot.forEach(element => {
         let post = element.data()
         post.id = element.id
      posts.push(post)
       });
       commit('getpost', posts)
     })

getFavsはfavを取得し、fav.post_idをもとにログインしているユーザーがfavしている記事を取得しています。取得した値はgetfavでstateのmyfavsに追加されます。

getpostsはpostを全て取得し、取得した値を持ってgetpostを呼び出しています。値はgetpostでstateのpostsに追加されます。

マイページでお気に入りした記事を取得

基本的に難しい操作はしません。お気に入りしている記事をデータベースから取得し、表示させるだけです。ライフサイクルメソッド使ってます。

 <h3>お気に入りした投稿</h3>
 <div v-for="fav in myfavs" :key="fav.id">
  <p>{{fav.title}}</p>
  <p>{{fav.text}}</p>
 </div>

this.$store.dispatch('getFavs')

myfavs() {
  return this.$store.state.myfavs
}

myfavsをeachで回して、お気に入りした記事を表示させている。

こちらもdispatchでmypostsを実行させ、処理がうまくいった場合にはstate.myfavsから値を取得する流れになっています。

ここの処理は以前説明しているコードを使用しているだけです。

state: {
   myfavs: []
 },

actionsです。これは2つくらい前にあるコードで使用しているものと同じです。
getFavs({commit}){
     db.collection('favs').onSnapshot(snapshot => {
       let get_favs = []
       snapshot.forEach(ele => {
         let fav = ele.data()
         fav.id = ele.id
         const user = firebase.auth().currentUser
         if(fav.user_id === user.uid) {
           db.collection('posts').doc(fav.post_id).onSnapshot(snap => {
             let fav_post = snap.data()
             fav_post.id = snap.id
             get_favs.push(fav_post)
           })
         }
       })
       commit('get', get_favs)
     })
   },

onSnapshotメソッドを使うことによってリアルタイムリスナーになってくれるのでfavsに変更があるとその都度更新してくれます。

非同期みたいな処理をしてくれていると、認識しています。

記事の詳細ページでお気に入り

Object.assignの便利さに救われた実装でした。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

<v-btn
       color="green lighten-1"
       class="mx-0"
       outlined
       v-if="!post.fav_status"
       @click.prevent="favorite(post)"
       >
       <p>お気に入り</p>
     </v-btn>
     
     <v-btn
       color="green lighten-1"
       class="mx-0"
       outlined
       v-else
       @click.prevent="favorite(post)"
       >
       <p>お気に入り解除</p>
     </v-btn>

 computed: {
   post() {
     let array = this.$store.state.myfavs
     let publicPost = []
     this.$store.state.posts.forEach(element => {
       function checkAlreadyFavs(arr, id) {
         return arr.some(function(value) {
           return id === value.id
         })
       }
       if(checkAlreadyFavs(array, element.id)) {
         element.fav_status = true
       } else {
         element.fav_status = false
       }
       publicPost.push(element)
     });

     let detailpost = {}
     publicPost.forEach(ele => {
       let id = this.$route.params.postId
       let detail_post = {}
       if(ele.id === id) {
         detail_post = ele
       }
       Object.assign(detailpost, detail_post)
     })
     return detailpost
   },
}

自分のお気に入り一覧を取得。この記事に対して既にお気に入りしているかどうかを確認、処理。checkAlreadyFavsでsomeメソッドを使います。Array.prototype.some()some() メソッドは、配列の少なくとも 1 つの要素が、渡された関数によって実施されるテストに通るかどうかをテストしまdeveloper.mozilla.org

その処理が終わったら詳細ページを開いているので、urlからthis.$route.params.postIdを使用してpost.idを取得します。

publicPostの中にpost.idと一致するものがあれば、それを返り値として返すようにしています。

個人的にはここが少し時間かかりました。Object.assignに出会うまで。。

実装してみて

個人的にテンションが上がったのは、データベースで外部キーを使用して値を取得したりするのが出来たことです。

後はjavascriptのメソッド便利なやつあるじゃーーーん!!!って発見があります。

いいね機能も同じ流れで作れると思ってます。

次はフォロー機能とか実装できるようになってみたい!!って感じです。w

今回はここら辺で、

以上!