Backbone.Model の fetch や save はサーバーと通信するけど、データをサーバーではなくローカルにだけ保存したいときがある。例えば PhoneGap でスタンドアロンアプリを作るときとか。
localStorage にアクセスするライブラリは、backbone-localstorage っていうのが Backbone.js のリポジトリ内にあるけど、Web SQL Database にアクセスするライブラリは見つからなかった。使いたいのは Web SQL Database なんだけどな。
ネットで探してみたけど見つからなかったので、自作してみた。
/** * データベース名 */ Backbone.DATABASE_NAME = "backbone"; /** * データベースのバージョン */ Backbone.DATABASE_VERSION = "0.1"; /** * データベースの表示名 */ Backbone.DATABASE_DISPLAY_NAME = "backbone"; /** * データベースの予測サイズ */ Backbone.DATABASE_SIZE = 1024 * 1024; /** * Web SQL Database 上のテーブルを表します。 * * @class * @param name テーブル名 * @param columns 列の定義 * @example * var table = new Table("user", { * name: "TEXT", * email: "TEXT" * }); */ var Table = function(name, columns) { this.name = name; this.columns = columns; }; /** * データベースを開きます。 */ Table.openDatabase = function() { return openDatabase( Backbone.DATABASE_NAME, Backbone.DATABASE_VERSION, Backbone.DATABASE_DISPLAY_NAME, Backbone.DATABASE_SIZE); }; /** * データベースを開いて SQL を実行します。 * * @param sql 実行する SQL * @param params プレースフォルダに渡すパラメータ * @param options オプション * @example * Table.executeSql("SELECT * FROM users", [], { * success: function(result) { * alert(result); * } * }); */ Table.executeSql = function(sql, params, options) { var options = options || {}; var db = Table.openDatabase(); db.transaction(function(tx) { // 発行する SQL とパラメータをログに出力 console.log(sql); console.log(params); tx.executeSql(sql, params, function(tx, rs) { if (options.success) { options.success(rs); } }, function(tx, error) { if (options.error) { options.error(error); } }); }); }; /** * テーブルを作成します。 * * @param options オプション * @example * table.createTable({ * success: function(result) { * alert("テーブルの作成に成功しました。"); * } * }); */ Table.prototype.createTable = function(options) { var columnNames = ""; _.each(this.columns, function(type, col) { columnNames += " , " + col + " " + type; }); var sql = "CREATE TABLE IF NOT EXISTS " + this.name + " (id INTEGER PRIMARY KEY " + columnNames + " ) "; Table.executeSql(sql, [], options); }; /** * テーブルを削除します。 * * @param options オプション * @example * table.dropTable({ * success: function(result) { * alert("テーブルの削除に成功しました。"); * } * }); */ Table.prototype.dropTable = function(options) { var sql = "DROP TABLE IF EXISTS " + this.name; Table.executeSql(sql, [], options); }; /** * モデルを1件取得します。 */ Table.prototype.find = function(model, options) { var sql = "SELECT * FROM " + this.name + " WHERE id = ?"; var params = [model.id]; // success を差し替える if (options.success) { var originalSuccess = options.success; options.success = function(result) { var row = result.rows.item(0); originalSuccess(row); }; } Table.executeSql(sql, params, options); }; /** * モデルを複数件取得します。 */ Table.prototype.findAll = function(collection, options) { // SQL の組み立て var sql = "SELECT * FROM " + this.name; var params = []; if (collection.filterString) { sql += " WHERE " + collection.filterString; if (collection.filterParams) { params = collection.filterParams; } } if (collection.orderString) { sql += " ORDER BY " + collection.orderString; } // success を差し替える if (options.success) { var originalSuccess = options.success; options.success = function(result) { var models = []; for (var i = 0, len = result.rows.length; i < len; i++) { models.push(result.rows.item(i)); } originalSuccess(models); }; } Table.executeSql(sql, params, options); }; /** * モデルを新しく作成します。 */ Table.prototype.create = function(model, options) { // SQL を組み立てる var columnNames = ""; var paramNames = ""; var params = []; var first = true; _.each(this.columns, function(type, col) { if (!first) { columnNames += " , "; paramNames += " , "; } first = false; columnNames += col; paramNames += "?"; var value = model.get(col); params.push(value); }, this); var sql = "INSERT INTO " + this.name + " ( " + columnNames + " ) VALUES ( " + paramNames + " ) "; // success を差し替える if (options.success) { var originalSuccess = options.success; options.success = function(result) { originalSuccess(model); }; } // SQL を実行する Table.executeSql(sql, params, options); }; /** * モデルを更新します。 */ Table.prototype.update = function(model, options) { // SQL を組み立てる var columnNames = ""; var params = []; var first = true; _.each(this.columns, function(type, col) { if (!first) { columnNames += " , "; } first = false; columnNames += col + " = ? "; var value = model.get(col); params.push(value); }, this); var sql = "UPDATE " + this.name + " SET " + columnNames + " WHERE id = ? "; params.push(model.id); // success を差し替える if (options.success) { var originalSuccess = options.success; options.success = function(result) { originalSuccess(model); }; } // SQL を実行する Table.executeSql(sql, params, options); }; /** * 指定したモデルを削除します。 */ Table.prototype.destroy = function(model, options) { var sql = "DELETE FROM " + this.name + " WHERE id = ?"; var params = [model.id]; // success を差し替える if (options.success) { var originalSuccess = options.success; options.success = function(result) { originalSuccess(model); }; } Table.executeSql(sql, params, options); }; /** * オリジナルの sync メソッド。 */ Backbone.originalSync = Backbone.sync; Backbone.sync = function(method, model, options) { var table = model.table || model.model.prototype.table; switch (method) { case "read": if(model.id) { table.find(model, options); } else { table.findAll(model, options); } break; case "create": table.create(model, options); break; case "update": table.update(model, options); break; case "delete": table.destroy(model, options); break; } }
とりあえず、自分が作ろうとしているアプリに必要な機能に絞って実装した。
使い方を簡単に説明すると、
var Book = Backbone.Model.extend({ table: new Table("books", { title: "TEXT", author: "TEXT", price: "INTEGER" } });
という風にすれば、fetch や save で Web SQL Database にアクセスするようになる。
不具合が見つかったら(多分)修正するけど、機能追加は必要に迫られるまでしないと思う。Fork、Pull Request は大歓迎。
というか、誰かがもっとちゃんとしたライブラリを作ってくれたらいいな。