Firebase Cloud Firestore + Vueのデータ連携とCRUD機能

## 概要

現在開発中の[個人サービス](https://twitter.com/XI_no_me/status/1497172021188255745)において、プロトタイプ化とトライアンドエラーをメインの目的としてVue.js + Firebaseを開発環境で使用しています。
別の活動もあるため自分のペースにはなりますが当初イメージしていたデータ構成や機能イメージの実現性にも変化が出てきたので、今回の記事にまとめようと思います。



### 参考文献

- [Cloud Firestore にデータを追加する](https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja)
- [Vue.jsで、親コンポーネントからもらった変数を子コンポーネントで更新したいときの対処法](https://qiita.com/masatomix/items/ab4f0488083554f5fceb)
- [Firestoreのデータ操作(取得, 追加, 更新, 削除)](https://www.wakuwakubank.com/posts/723-firebase-firestore-query/)
- https://devsakaso.com/vue-firebase-get-doc-id-before-adding/



## Firestore側の構成
最終的にフロント側（Vue）での表示、CRUD機能を実装することが必要になるので、まずFirestoreの構成を見てみます。
[公式サイト](https://firebase.google.com/docs/firestore/data-model?hl=ja)にもありますがFirestoreは「NoSQL ドキュメント指向データベース」というものになるので、MySQLなどの一般的なSQL構造とは似て非なるものと考えた方がいいです。

![スクリーンショット 2022-04-19 15.15.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76115/22061568-17c3-e5d6-6729-c9c08469b227.png)
上記キャプチャがSQLの編集画面ですが、

- 「コレクション」：テーブル群
- 「ドキュメント」：テーブル
というイメージになり、これらを取りまとめるDBとして「プロジェクト」単位で定義する形になります。

ドキュメントは追加した際にユニークのドキュメントID（自動採番 or カスタム）が振られ、フロント側でこの値を取得することも可能です。


## Firestoreデータの取得

CLIでFirebaseへログイン、連携すると次のような形でフロント側にFirestoreのデータを連携することができるようになります。

```javascript:src/firebase.js
import firebase from "firebase/compat/app";

// Firestore参照
export const db = firebase.firestore();
```

複数のコンポーネントで使うパターンがほとんどだと思うので、firebase用のJSファイルを用意して汎用的に使えるようconst関数`db`を定義。


```javascript:src/views/Project/Detail.vue
<script>
// Firebase認証
import { db } from "@/firebase";

export default {
  data() {
    return {
      datas_project: [],
      
    };
  },

  created() {
    // ポートフォリオ
    this.getProjectData();
  },

  methods: {
    // プロジェクト
    getProjectData() {
      console.log(this.$route.query);
      const project_id = this.$route.query.id;

      db.collection("tbl_project")
        .where("project_id", "==", project_id)
        .get()
        .then((snapshot) => {
          snapshot.forEach((doc) => {
            console.log("ドキュメントID: ", doc.id);
            this.datas_project.push(doc.data());
          });
        });
    },
  },
};

```

画面側のソース一例。
Firebaseデータを扱う各コンポーネントで、先ほど定義したconst関数`db`をインポートして使用します。

```javascript
datas_project: [],
```
`data()` ではデータ格納用に「datas_project」を定義します。

```javascript
db.collection("tbl_project")
  .where("project_id", "==", project_id)
  .get()
  .then((snapshot) => {
    snapshot.forEach((doc) => {
      console.log("ドキュメントID: ", doc.id);
      this.datas_project.push(doc.data());
    });
  });
```

ドキュメントの参照方法は[公式サイト](https://firebase.google.cn/docs/firestore/query-data/get-data?hl=ja)を参照。
上記の記述では、`tbl_project`ドキュメントを参照後、`project_id`が特定値であることを条件にフィルタリング。
条件に合ったものを`snapchat`でループ処理し、data()内で定義した`datas_project`に格納するという流れになります。

```vue
<div v-for="data in datas_project" :key="data">
 {{ data.project_name }}
</div>
```

あとはこんな感じで、Firestoreで定義したデータ構成をもとに参照できるようになります。


## Cloud Firestore にデータを追加する

Vueで作成したフォーム項目→Firestoreへデータを連携し、Firestore側で追加するにはこちらを参照。

https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja


基本的にはこのような流れになります。

- コレクションを指定
- ドキュメントIDを指定（自動で割り振り or 任意名）
- 入力するデータ項目・参照元を指定
- `set()` メソッドを使用（ID指定を省略した`add()`メソッドでも可能）

`ドキュメントID`はFirestoreの編集ページでこのように表示されます。
ユニークな値になる必要がありますが、指定した場合はその値が、指定なしの場合はランダム文字列で自動生成されます。

![スクリーンショット 2022-04-30 11.02.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76115/a6e7d8b7-ab8e-40d3-c781-ee392bedd738.png)

```javascript
// Add a new document in collection "cities"
db.collection("cities").doc("LA").set({
    name: "Los Angeles",
    state: "CA",
    country: "USA"
})
.then(() => {
    console.log("Document successfully written!");
})
.catch((error) => {
    console.error("Error writing document: ", error);
});
```

ドキュメントのソースを解釈するとこのようになります。
- コレクション名： cities
- ドキュメント名: LA(任意指定)
- データ項目: name、state、country

任意指定ではなく自動生成のドキュメントIDを使うにはドキュメント名の指定を外す、または`add()`メソッドを使えばOKです。
```javascript
db.collection("cities").doc().set({
    name: "Los Angeles",
    state: "CA",
    country: "USA"
})
```

```javascript
db.collection("cities").add({
    name: "Los Angeles",
    state: "CA",
    country: "USA"
})
```

また、これを応用して[自動生成IDをIDとして格納したドキュメントを作成する方法](https://devsakaso.com/vue-firebase-get-doc-id-before-adding/)もあります。
ドキュメントを取得した際にIDが発行されるので、これを関数として格納するという流れになります。
```javascript
// 自動発行されるID名を関数化
const newDoc = db.collection("cities").doc().id;
// 取得したID名をドキュメント名、追加項目の「id」として指定
db.collection("cities").doc(newDoc).set({
    id: newDoc,
    name: "Los Angeles",
    state: "CA",
    country: "USA"
})
```

## Cloud Firestore のデータを更新する

[ドキュメントを更新する](https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja#update-data)

すでに作成しているドキュメントを更新する場合は `update()`メソッドを使用します。

```javascript
var washingtonRef = db.collection("cities").doc("DC");

// Set the "capital" field of the city 'DC'
return washingtonRef.update({
    capital: true
})
.then(() => {
    console.log("Document successfully updated!");
})
.catch((error) => {
    // The document probably doesn't exist.
    console.error("Error updating document: ", error);
});
```