Not Only SQLという意味があるらしい

おまとめ三行

MySQLとかとは違うってことだけ把握しときましょう
変更した内容がリアルタイムに同期される
もはやPHPに頼らなくてもよくね?とか思っちゃいますね
今日はFirebaseのRealtime Databaseってやつを使ってみましょう。Firebaseのデータベースにはもう一つ、Cloud Firestoreというのもあるのですが、それは次回やります。

Realtime DatabaseはNoSQLのデータベースです。NoSQLが何かってのを説明しだすととてもながーくなってしまうのですが、というか僕自身、今までNoSQLって触ったことがないんでほとんど分かってませんが、とりあえずMySQLとかとは違うってことだけここでは把握しときましょう。



プロジェクトの開始

Hostingと同じようにinitします。データベースだけをinitする場合はこう書いた方が早いです。

$ firebase init database

無事にinitが完了すると、フォルダの中に「database.rule.json」というファイルができていると思います。またfirebase.jsonの中にこんな記述が追加されてるはず。

"database": {
  "rules": "database.rules.json"
}

database.rules.jsonの内容に従うみたいなことですね、きっと。


ちなみにdatabase.rules.jsonの中はこんな風になってると思います。

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

本来ならセキュリティを考慮してきちんとルールを書くべきなのですが、今回はテストなので、誰でも読み書きできるようにルールを書き換えてみます。

{
  "rules": {
    ".read": true,
    ".write": true
  }
}



基本的なデータの書き込み

それじゃあ適当なウェブページでデータをデータベースに書き込んでみましょう。

とりあえずものすごいシンプルに、名前を入力できるフォームでも作成してみましょう。

本来は自分で初期設定のconfigデータなどをjavascriptで書かなきゃいけないのですが、ホスティングをinitすると作成されたindex.htmlの中ですでにデータベースを使える状態が整ってます。なのでそれ使ってみましょう。

まず適当なhtmlファイルを作成します。今回は「database.html」としましょう。そこにindex.htmlをまるっとコピーして不要なコードを削り、簡単なフォームを設置する。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Realtime Database</title>
    <script defer src="/__/firebase/4.6.1/firebase-app.js"></script>
    <script defer src="/__/firebase/4.6.1/firebase-database.js"></script>
    <script defer src="/__/firebase/init.js"></script>
  </head>
  <body>
    <form>
      <input name="name" type="text" id="name" />
      <input name="comment" type="text" id="comment" />
      <button id="write" type="button">書き込む</button>
    </form>

    <script>
      document.getElementById('write').addEventListener('click', function() {
        firebase.database().ref().set({
          name: document.getElementById('name').value,
          comment: document.getElementById('comment').value
        }).then(function(){
          alert('書き込みました!');
        });
      });
    </script>
  </body>
</html>

デフォルトで用意されてるfireabase-database.jsやinit.jsを使えば「fireabase.database()」というインスタンスが使える状態になってます。

データの書き込みは「set()」メソッドを使う。書き込みたいデータは「キー:値」の形でオーケーです。

これをデプロイする。

$ firebase deploy

database.htmlにアクセス。

https://norm-nois.firebaseapp.com/database.html

この画面ではあかつきのお宿のCSSを使って少し見た目をいじってます。

実際に適当に名前を入力して書き込みボタンを押すと、ボタンを押しても画面上には何の変化もありませんが、データベースにデータが書き込まれます。

コンソール画面で確認してみるとこんな感じ。

データベースに書き込みました

「comment」と「name」が書き込まれてますね。



データの読み込み

じゃあ今度は読み取りボタンを画面に追加して、ボタンが押されたら先ほど書き込まれたデータを読み込んで画面上に表示するスクリプトを追加してみます。

//html
<p>名前:<span id="exp_name"></span></p>
<p>一言:<span id="exp_comment"></span></p>
<button id="read" type="button">読み込む</button>

//javascript
document.getElementById('read').addEventListener('click', function() {
  firebase.database().ref().on('value', function(snapshot) {
    values = snapshot.val();

    document.getElementById('exp_name').innerHTML = values['name'];
    document.getElementById('exp_comment').innerHTML = values['comment'];
  });
});

データの読み込みは「on()」メソッドを使います。「snapshot.val()」でデータをJSON形式で取得できます。

Realtime Databaseの面白いところは、名前の通り変更した内容がリアルタイムに同期されるところです。

例えば読み込みボタンを押して一度データを読み込んだ後、名前や一言コメントを適当に書き込んでみてください。読み込むボタンを押さなくても内容が書き換わってるのが分かると思います。こちらで書き換えに関する処理は何も書いていません。

なので、もしも偶然にも今このページに他の人がアクセスしていたとして、その人がこっそり書き込みボタンを押したら、あなたが見ている画面の内容もすぐに変わります。「何もしてないのに急に知らない名前が出てきたぞ!?」ってなっても慌てないように。バグではないので。もちろんPCが乗っ取られたわけでもない。

自分でブラウザを二つ開いて、それぞれで書き込みをしてみれば同期してることが確認できると思います。



データの削除

データの削除は「remove()」メソッドを使います。試しに一言コメントを削除してみましょう。

上の二つと同じようにボタンとjavascriptを追加してみる。

//ボタン
<button id="remove" type="button">削除ボタン</button>

//javascript
document.getElementById('remove').addEventListener('click', function() {
  firebase.database().ref('comment').remove().then(function(){
    alert('削除しました!');
  });
});

これでcommentのデータが削除されます。nameの方を消したいなら「ref(‘name’)」とすればオーケー。

削除した場合もリアルタイムに同期されます。先にデータの読み込みを行ってから削除ボタンを押すと、一言の方がundefinedに変わるのが確認できると思います。



データの階層化

Realtime Databaseは、MySQLみたいにテーブルを作成して、テーブルごとにカラムを設定して……という概念がありません。

なので、例えばTwitterみたいなサイトがあったとして、ユーザーデータとツイートデータを別々に管理するにはどうしたら良いんだろって話ですが、基本的には階層化して管理する形になるのかなと思います。

例えばこんな感じ。

データはネスト構造で管理

データの持ち方の良し悪しは別として、こういう風にデータを階層にすることができます。この場合「users」とか「tweets」がテーブルみたいな感覚になるでしょうか。

じゃあこういうデータの読み書きはどうやって行うかってーと、「ref()」メソッドのところで指定できます。

例えば上記のusersのところに3番目のユーザーを追加したいならこうです。

firebase.database().ref('users/3').set({
  name: '小林',
  age: '50',
  sex: '男性',
});

同様にtweetsの1番のデータを読み込みたいならこう。

firebase.database().ref('tweets/1').on('value', function(snapshot) {
  values = snapshot.val();
});

削除も同じ。

firebase.database().ref('tweets/1').remove();

階層はもっと深くすることもできるので、ユーザー単位でツイートを管理したいとかだったら、「ref(‘users/1/tweets/1’).set()」みたいにデータを登録することもできます。



注意が必要なのは、usersにいくつもデータが入ってるところに、うっかり「ref(‘users’).set()」とかやってしまうと、usersのデータが全て書き換わってしまうことです。

firebase.database().ref('users/1').set({
  name: '田中',
  age: '20',
  sex: '男性',
});

firebase.database().ref('users/2').set({
  name: '山田',
  age: '30',
  sex: '女性',
});

firebase.database().ref('users').set({
  name: '小林',
  age: '50',
  sex: '男性',
});

例えばこの処理を上から順番に行った場合、最終的には小林さんのデータしか残りません。田中さんと山田さんのデータは上書きされて消えてしまいます。

「ref(‘tweets’).remove()」とかやってしまった場合も同様です。

この辺のうっかりはセキュリティルールの書き方次第で防ぐこともできますが、ルールの詳細な書き方を僕がまだきちんと把握できないので、今回は省略。すみません。





NoSQLは非リレーショナルデータベースとも言われ、MySQLでテーブルどうしをやたらめったらリレーションするような使い方とはちょっと勝手が違います。だから例えばCakePHPでHABTMしまくってるようなウェブサービスをこのRealtime Databaseに乗せ替えようと思ったら大変かもしれません。でもそこまで複雑なリレーションを必要としないのであれば、HostingとRealtime Databaseだけでもいっぱしのウェブサイトは作れると思います。

たぶん、あかつきのお宿程度のサイトなら余裕なんじゃないかなー。小説とかは「ref(‘novels/four/a/1’)(四行小説のあ行の1つ目)」みたいな感じで階層管理できるし。

僕はプログラマーになってからずっとPHPしか触ってこなかったので、DBを使ったシステムを構築すると言えばPHPを使うのが当たり前な感覚でした。でもHTMLとjavascriptだけでここまでできるんだと、もはやPHPに頼らなくてもよくね?とか思っちゃいますね。



その他のFirebaseに関する記事はこちら