Nuxtでfirestore触ったときの備忘録

# サクッとfirebaseを触ってみたい！！

# 前提とこの記事の書き方
- 画面はNuxt.jsで書いているがこの記事のメインはfirebase
- firebaseのプランはSpark
- プロジェクトのセットアップ等は[公式](https://firebase.google.com/docs/firestore/quickstart?hl=ja)などを見てやる

# まずはプラグインを作成しfirebaseへアクセスできるようにする。
- これで使う場所（action）でプラグインをimportして`fireApp.firestore()`とするとfirestoreへアクセスできるようになる
- 秘匿情報が含まれているためdotenvとかで設定ファイルより参照するようにする。
- 参考：[Nuxt.jsで.envファイルを扱う@nuxtjs/dotenv](https://qiita.com/taichi0514/items/3939af222dee21a44413)

```typescript:plugins/firebase.ts
import * as firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/auth'
// eslint-disable-next-line
let fireApp
if (!firebase.apps.length) {
  fireApp = firebase.initializeApp({
    apiKey: process.env.API_KEY,
    authDomain: process.env.AUTH_DOMAIN,
    databaseURL: process.env.DATABASE_URL,
    projectId: process.env.PROJECT_ID,
    storageBucket: process.env.STORAGE_BUCKET,
    messagingSenderId: process.env.MESSAGING_SENDER_ID,
    appId: process.env.APP_ID,
    measurementId: process.env.MEASUREMENT_ID
  })
} else {
  fireApp = firebase.app()
}

export default fireApp

```

# 基本的にfirebaseへのアクセスは`store/機能/action.ts`にしか書かない認識

# 登録処理（add）
- 例）書籍情報を登録する
- `collection('コレクション名').add(オブジェクト)`で追加できる

```action.ts
import fireApp from '~/plugins/firebase'
// ↑これでfireApp.firestore()とすることでfirestoreへアクセスが可能になる

/* 登録 */
  async add({ commit }, addFilter: AddFilter) {
    const db = fireApp.firestore()
    // 書籍を登録
    const booksRef = db.collection('books')
    await booksRef.add({
      title: addFilter.title,
      author: addFilter.author,
      isRental: addFilter.isRental,
      genre: addFilter.genre
    })
    // 登録処理なので状態管理は必要ないという思いからcommitは不要？？だと思う。
  }
```

- 無事に追加されると以下のようになる（IDはなんとなくマスクしている）
- 今回はフィールドにIDがないけど、なんだかんだでドキュメントにIDがあったほうが処理がラクだと思った。理由は後述する`参照(show)`の部分を参照。
<img width="1592" alt="スクリーンショット 2020-02-01 13.51.02.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/851c5d2e-474a-1753-1fdd-bdd44471d1bd.jpeg">



# 参照処理（show）
- 一覧情報の取得などでコレクションの情報を一括で取得したいときなど
- `collection('books').get()`でコレクション内全ての情報を取得できる。
- `forEach`の部分でそれぞれの図書情報を配列に格納してstore用のデータを作成しcommitしている。ドキュメントIDとフィールド（データの中身）はレイヤーが違うのでフィールドにもIDを付与した方が処理がラクになるかなーと思った。
- フィールドはdata()メソッドで取得する

```action.ts
/* 一覧表示 */
  async show({ commit }) {
    const db = fireApp.firestore()
    const books = []
    // 書籍データを取得
    const querySnapshot = await db.collection('books').get()

    // 書籍データをfetch
    querySnapshot.forEach((doc) => {
      books.push({
        id: doc.id, // ドキュメントID
        ...doc.data()
      })
    })
    // storeのデータを作成
    const booksInfo = []
    for (let i = 0; i < books.length; i++) {
      const bookInfo = books[i]
      booksInfo.push(bookInfo)
    }
    commit('describe', { defaultInfo: booksInfo })
  }
```

- firebaseから取れたてのデータ`querySnapshot`とstore用に詰め直した`booksInfo`をそれぞれコンソールへ出力した結果がコチラ
- querySnapshot（フィールドまで結構深い）
<img width="1680" alt="スクリーンショット 2020-02-01 14.36.27.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/42471ac0-44b1-5062-6ead-d0d62e8908d3.jpeg">

- booksInfo（これは別に見せる必要ないけど一応）
<img width="1318" alt="スクリーンショット 2020-02-01 14.38.27.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/51aa8792-5720-9429-661b-2701d7c5034d.jpeg">


# 1件取得処理（detail)
- ドキュメントIDを指定して`collection('books').doc(bookId).get()`とすれば取得できる

```action.ts
async detail({ commit }, bookId) {
    const db = fireApp.firestore()
    // クエスチョン1件にアクセス
    const book = await db
      .collection('books')
      .doc(bookId)
      .get()
    commit('book', { book })
  }
```

# 貸し出し処理（rental)：〜他のテーブルとの結合〜
- 例）登録処理（add）を応用し図書を貸りる処理をするときに`users`テーブルからユーザーの情報を登録する必要がある
- その場合パラメータにユーザIDを追加して`userRef`というusersコレクションへの参照を追加で登録する
- これでユーザ情報を紐付けることができる

```action.ts
/* レンタル処理 */
  async rental({ commit }, rentalFilter: rentalFilter) {
    const db = fireApp.firestore()
    // 書籍情報とユーザ情報を登録
    const booksRef = db.collection('books')
    await booksRef.add({
      title: rentalFilter.title,
      author: rentalFilter.author,
      isRental: rentalFilter.isRental,
      genre: rentalFilter.genre,
      userRef: db.collection("users").doc(rentalFilter.userId)
    })
    // 状態管理は必要ないという思いからcommitは不要？？だと思う。
  }
```

# これだけである程度の処理ができると思う。
- 複雑な結合はやっぱりRDBMSの方がいいだろうけどNoSQLは直感的でわかりやすいと思った。

# 随時追記していきます。