//tips
//smartcontract
Firebaseには2つのデータベースがあり、
Realtimedatabase:使い方が簡単。クライアントのリアルタイム同期ができるが、クライアントとサーバ間のやりとり遅延時間が長い。
Cloud firestorage:高速なアクセスとスケーリング機能を備えており、こちらがデータベースとしては最適
Cloud firestorageを新たに作成。
このストレージは
コレクション:データを格納する土台
ドキュメント:コレクションに保管されるデータの1セット。ランダムなキーが割り当てられその中にドキュメントで保管すべき情報を組み込んでいく。
フィールド:値を保管するもの。ドキュメントの中に保管され、キーと値がセットで用意される。
という3要素の組み合わせでデータが作られている。別途ドキュメントにコレクションを追加して階層化することも可能。
データをmyadminのように追加していく。
セキュリティルールというものも下記のように設定されており、データベースのどこにアクセスしたときに読み書きをどのように許可するかを記述したもの。ただif falseならとあり、常にfalseとなることからallow read, writeは実行されない、アクセスが許可されない状態となっている。アプリからアクセスもできない。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
//ここで読み書きの許可が行われている
match /{document=**} {
allow read, write: if false;
}
}
}
allow read, write;のように変更しておく。もちろんこの状態では危ないのでfirebaseの認証機能を後で追加する必要がある。
先にnext_appへの連動から進めていく。npmでのインストールの方が設定作業が少ないようなので今回はそちらを利用。
npm install firebase
バージョンがアップデートされているようなので
import firebase from 'firebase/compat/app';
としてインポートするのを忘れない。
Firebaseを利用するための初期化処理を行う。
Componentsフォルダにfire.jsを作成し、
import firebase from 'firebase'
// ☆各プロジェクトの設定を記述
const firebaseConfig = {
apiKey: "……APIキー……",
authDomain: "プロジェクト名.firebaseapp.com",
databaseURL: "データベースのURL",
projectId: "プロジェクト名",
storageBucket: "プロジェクト名.appspot.com",
messagingSenderId: "……メッセージ用ID……",
appId: "……アプリケーションID……"
}
if (firebase.apps.length == 0) {
firebase.initializeApp(firebaseConfig)
}
の設定を行う。
また、databaseURの記述はないので探しているとfirestorageには必要ないので削除で問題ないとのこと。
参考:
https://stackoverflow.com/questions/65601040/databaseurl-not-found-in-firestore-sdk-snippet
Mydataコレクションにアクセスして早速データの方取得していく。
Fireフォルダにindex.jsを作成。
Firebaseの仕様でエラー発生。バージョンを古いものに戻す。
Package.jsonを書き直し、npm install firebase@8.9.1をインストールすることで対応できた。
const db = firebase.firestore()でfirestoreのオブジェクトを用意。
この取り出したオブジェクトを使ってfirestoreのアクセスを行う。
取り出したデータのでたを保管する変数を用意。dataはmydataにまとめたデータをステートに保管しておくもの。
const mydata = []
const [data, setData] = useState(mydata)
const [message, setMessage] = useState('wait...')
データ取り出しを db.collection('mydata').get()で実行。mydataコレクションのドキュメントを取り出す。
このGetは非同期メソッドなので、続けて完了後の処理を書く必要がある。
db.collection('mydata').get().then((snapshot)=> {
Snapshotにはアクセスした時点でのfirestoreの状態をまとめたオブジェクトが渡される。mydataコレクションを先に取り出しているのでsnapshotのforeachをすることでmydataコレクション内の要素を取り出していくことになる。
snapshot.forEach((document)=> {
ただ、documentはドキュメント情報を含んだオブジェクトであるため、そこからドキュメントデータを取り出し処理する必要があり、
const doc = document.data()
dataメソッドでデータを取り出している。フィールドの名前と値をまとめたものを取り出せる。このオブジェクトから各フィールドの値を取り出していくことになる。
mydata.push(
<tr key={document.id}>
<td><a href={'/fire/del?id=' + document.id}>
{document.id}</a></td>
<td>{doc.name}</td>
<td>{doc.mail}</td>
<td>{doc.age}</td>
</tr>
)
})
setData(mydata)
setMessage('Firebase data.')
})
}, [])
また、useEffect(() => { }, [])とすることで最初にアクセスした時のみ更新し、表示中のその後は更新させないことができるため無駄なデータベースへのアクセスを防ぐことができる。
Firebaseはアクセス数で課金額が決められるのでuseEffect(() => { }, [])の使い方が非常に重要になっている。
Addでfirebaseにデータベースを追加するスクリプトも確認。
import { useRouter } from 'next/router'
import '../../components/fire'
const db = firebase.firestore()
export default function Add() {
const [message, setMessage] = useState('add data')
const [name, setName] = useState('')
const [mail, setMail] = useState('')
const [age, setAge] = useState(0)
const router = useRouter()
今回はuseRouter()を仕様し他のページに移動する機能も組み込む。
新しいドキュメントを追加する処理はdoaction関数の中で行っており、db.collection('mydata').add(ob)としてコレクションのオブジェクトからaddメソッドを呼び出し、新たな項目を追加している。シンプルで分かりやすい。ただ、addするとドキュメントのidはランダムなテキストとなるため別途設定が必要。
コレクションにドキュメントを追加した後に/fireに移動しており、このリダイレクト処理はnext.jsに用意されているrouterというモジュール機能を使っている。
useRouterはパスごとにコンポーネントを割り当てる機能であるルーティングを実施できる。
Addを使用した後に router.push('/fire’)とすることでrouterオブジェクトに移動のための情報を追加し、その情報をもとに移動を行う。現時点のURLに/fireパスが追加されるわけではないので注意。
const doAction = ((e)=> {
const ob = {
name:name,
mail:mail,
age:age
}
db.collection('mydata').add(ob).then(ref=> {
router.push('/fire')
})
})
/fireからidを押すとそのドキュメントが削除されるようにした。
if (router.query.id != undefined) {
router.query.が空でない場合には、得られた情報をもとにfirestoreからドキュメントを取得しそれを表示させている。
また、ページアクセス後に値を得られるのは若干時間ラグがあるため空であったらmessageテキストに.を追加し更新させ、その更新をuseeffectに感知させてループを繰り返させるようにしている。値が得られたら確定するのでループから抜ける。
Docメソッドでidから対象のドキュメントを取得。ここで得られたobをdata
ステートに保管している。
これで指定したidの内容がデータから得られるようになる。
useEffect(() => {
if (router.query.id != undefined) {
setMessage('Delete id = ' + router.query.id)
db.collection('mydata').doc(router.query.id).get().then(ob=>{
setData(ob.data())
})
} else {
setMessage(message + '.')
}
}, [message])
削除は簡単でdocで対象を取得しdeleteメソッドを実行するだけ。
const doAction = (e)=> {
db.collection('mydata').doc(router.query.id)
.delete().then(ref=> {
router.push('/fire')
})
}
ここに検索機能も追加する。
今回はfindステートに入力フィールドの値を保管し、それを利用して検索を行っている。
where('name', '==', find).get()でnameの値がfindと等しいものを取り出している。findbaseではあまり高度な検索はできないとのこと。なのでその検索範囲で問題ないようなアプリを制作することが大事になる。
db.collection('mydata').where('name', '==', find).get().then(snapshot=> {
snapshot.forEach((document)=> {
const doc = document.data()
Authでのユーザー認証も行っていく。firebaseの機能で各snsなどの認証もサポートされるとのこと。これはありがたい。
早速こちらの機能を使ってみる。今回はgoogle認証機能で進める。twitterやfacebookを利用する際にはアプリケーションidやapiキーが別途必要なよう。
Authenticationサービスはfirebaseオブジェクトにあるauthというメソッドでオブジェクトを取得。このオブジェクトにサービスが用意されているので利用すれば良い。
認証を行う時には認証プロバイダーというものを作成する必要があり、認証プロバイダーとはさまざまな認証方式の基本的な仕組みを管理するオブジェクト。googleアカウントを使う場合にはgoogleようの認証プロバイダーを利用して認証を行うことになる。
変数=new firebase.auth.GoogleAuthProvider()
Googleアカウントによる認証はいくるかの方法が用意されており、シンプルなのはポップアップウィンドウで認証する方法、authオブジェクトにあるsignInWithPopupメソッドで行える。