Meteor + Blaze で匿名質問サービスをつくろう:第4夜 UI作成 前編 から続きです。
Meteor と MongoDB
Meteor の空恐ろしいところは、サーバー側の DB の一部をコピーして
クライアントにも持っているということです。
初期状態では、クライアントで DB に加えた編集がサーバー側にも適用される
ようになっていますので、例えば他人に寄せられた質問を読んだり、
編集・削除したりできてしまうんですよね。
これではセキュリティ的に非常にまずいので、なんとかします。
パッケージを削除
なんでプレインストールされているんだろう、とぼやきながら
DB の同期を行っているパッケージを削除します。
(For prototyping only)
がついているふたつですね。
$ meteor list
accounts-twitter 1.4.1 Login service for Twitter accounts
accounts-ui 1.3.0 Simple templates to add login widgets to an app
autopublish 1.0.7 (For prototyping only) Publish the entire database to all clients
blaze-html-templates 1.1.2 Compile HTML templates into reactive UI with Meteor Blaze
ecmascript 0.10.0* Compiler plugin that supports ES2015+ in all .js files
es5-shim 4.7.3 Shims and polyfills to improve ECMAScript 5 support
insecure 1.0.7 (For prototyping only) Allow all database writes from the client
iron:router 1.1.2 Routing specifically designed for Meteor
meteor-base 1.3.0 Packages that every Meteor app needs
mobile-experience 1.0.5 Packages for a great mobile user experience
mongo 1.4.2* Adaptor for using MongoDB and Minimongo over DDP
reactive-var 1.0.11 Reactive variable
shell-server 0.3.1 Server-side component of the `meteor shell` command.
standard-minifier-css 1.4.0* Standard css minifier used with Meteor apps by default.
standard-minifier-js 2.3.1* Standard javascript minifiers used with Meteor apps by default.
tap:i18n 1.8.2 A comprehensive internationalization solution for Meteor
tracker 1.1.3 Dependency tracker to allow reactive callbacks
twitter-config-ui 1.0.0 Blaze configuration templates for Twitter OAuth.
* New versions of these packages are available! Run 'meteor update' to try to update those packages to their
latest versions. If your packages cannot be updated further, try typing `meteor add <package>@<newVersion>`
to see more information.
$ meteor remove insecure autopublish
Changes to your project's package version selections:
autopublish removed from your project
insecure removed from your project
insecure: removed dependency
autopublish: removed dependency
これで、DBの同期は止まりました。
ついでに、更新のきているパッケージのアップデートもしておきます。
$ meteor update
This project is already at Meteor 1.6.1, the latest release.
Changes to your project's package version selections from updating package versions:
babel-compiler upgraded from 7.0.0 to 7.0.5
base64 upgraded from 1.0.10 to 1.0.11
ecmascript upgraded from 0.10.0 to 0.10.5
ecmascript-runtime-client upgraded from 0.6.0 to 0.6.2
jquery upgraded from 1.11.10 to 1.11.11
meteor upgraded from 1.8.2 to 1.8.3
minifier-css upgraded from 1.3.0 to 1.3.1
minifier-js upgraded from 2.3.1 to 2.3.3
modules upgraded from 0.11.3 to 0.11.5
mongo upgraded from 1.4.2 to 1.4.4
promise upgraded from 0.10.1 to 0.10.2
rate-limit upgraded from 1.0.8 to 1.0.9
standard-minifier-css upgraded from 1.4.0 to 1.4.1
standard-minifier-js upgraded from 2.3.1 to 2.3.2
コレクションの準備
MongoDB コレクションを指定します。RDB ではテーブルに相当します。
サーバー・クライアントの双方からアクセスできるように、
shared
ディレクトリに新しくファイルを作成します。
Questions = new Mongo.Collection('questions');
Development 環境では DB の設定は Meteor がやってくれますので、
特に何もする必要はありません。
また、accounts-ui
パッケージを導入していれば、users
コレクションが自動的に
作成されていますので、明示的な宣言は不要です。
この記事ではMongoDB 自体については詳しく扱いません。
詳しく知りたい方は適宜検索してください。
スキーマの定義 (Optional)
MongoDB にスキーマという概念はありませんが、この時点で DB に格納する
データのスキーマを決めておくことは悪くないはずです。
パッケージの追加
$ meteor add aldeed:simple-schema
aldeed:simple-schema: A simple schema validation object with reactivity. Used by collection2 and autoform.
autoform
パッケージを入れるとついてくるのですが、今回は
autoform
を使わないので手動でインストールします。
定義
さきほどの Database.js
に追記します。
Questions.schema = new SimpleSchema({
userId: { type: String, regEx: SimpleSchema.RegEx.Id },
content: { type: String },
isPublic: { type: Boolean, defaultValue: false },
isAnswered: { type: Boolean, defaultValue: false },
isDeclined: { type: Boolean, defaultValue: false },
isReported: { type: Boolean, defaultValue: false },
createdAt: { type: Date }
});
このスキーマを適用すると、データベースに格納されるデータはこんなイメージになるでしょう。
{
_id: "0e1b8263277ee31c8b3f2691", // 質問ID。ユニーク。
content: "進捗どうですか?", // 質問の内容。
userId: "7f34348aa6fcfd21f499f27e", // 質問相手の user._id。
isPublic: false, // true なら大喜利モード。誰でも回答できる
isAnswered: false, // true なら回答済。
isDeclined: false, // 回答をスキップすると true にする。
isReported: false, // 運営に通報すると true にする。
createdAt: new Date() // 投稿日時。
}
ユーザーがコンテンツを送信できるサービスは、必ず事務局に通報するボタンがあるのが最近の流行りです。
このことをすっかり忘れていました。入れておきます。
Publish / Subscribe の設定
さきほど autopublish
insecure
を削除したので、
DB とクライアントの同期については明示しないといけません。
Meteor では、 publish / subscription という仕組みで同期を実現します。
- サーバー側
サーバーでは、DB のコレクションに名前をつけて publish します。
この名前はコレクション自体の名前とは無関係です。
中身は DB コレクションを返す関数なので、引数を受け取って特定のフィルターをかけることもできます。
- クライアント側
クライアントでは、先程の名前を使って subscribe します。
サーバー上で関数を実行して返り値をもらうようなイメージですが、
subscribe では DB が変更されたときに、クライアントから関数を呼ばなくても自動的にデータがプッシュされてきます。
Reactive programming というやつですね。
この機能が不要で、ただ単に関数を実行したいときは methods / call という別の仕組みを使いますが、この説明はのちほど。
それでは、サーバーが questions コレクションを publish する宣言をします。
Meteor.publish('Questions', (type, query, limit) => {
if (!limit) limit = 30;
switch (type) {
case 'public':
return Questions.find({ isPublic: true }, { limit: limit });
case 'specific':
return Questions.find({ _id: query }, { limit: limit });
case 'users':
return Questions.find({ userId: query }, { limit: limit });
}
});
必要な情報だけ絞って publish することで、表示速度とセキュリティの向上をはかります。
面倒でもコレクション全体を publish しないようにしましょう。
次にクライアントが subscribe する宣言を。
Router.route('/', function() {
+ this.subscribe('Questions', 'public');
this.render('Home');
});
Router.route('/user/:userid', function() {
+ this.subscribe('Questions', 'users', this.params.userid);
this.render('User');
});
Router.route('/user/:userid/q', function() {
this.render('Enqueue');
});
Router.route('/q/:qid', function() {
+ this.subscribe('Questions', 'specific', this.params.qid);
this.render('Question');
});
パスパラメーターは this.params.hoge
で取得できます。
ごめんなさい
前回、
第5夜ではモックアップと DB を結びつけていきます。
こんなことを書いたんですが、疲れたのでこのへんで……