海未「今回はMVCの最後、モデルについて見てみましょう」
にこ「話は聞かせてもらったわ!」
穂乃果「うわっ!にこちゃん!?」
海未「またですか・・・もうオチが見えているのですが」
にこ「モデルと聞いてはこのにこにーが黙っているわけにはいかないじゃない」
海未「いえ、モデルというのは・・・」
ことり「でもにこちゃん、モデルやるには背も胸も足りないよね♪」
にこ「・・・うぐっ」
海未「・・・っ」
穂乃果「わー、ことりちゃんストレート」
にこ「・・・こ、今回は見逃してあげるわ。覚えてなさいよーーーっ!!」
ことり「あ、行っちゃった」
穂乃果「まあ、それだけ恵まれたプロポーションのことりちゃんに言われたら、ねえ」
海未「・・・胸・・・」
海未「気を取り直してモデル、すなわちデータを扱う層を見ていきます。今回、データベースとしてMongoDBというものを使ってみます」
穂乃果「データベースって、全然知らないけど・・・」
海未「MongoDBの解説が趣旨ではありませんから、今回のアプリケーションに必要な部分だけ説明することにします。特に前提知識は必要ないと思います」
海未「まずはMongoDBと、Expressから扱うためのモジュールをインストールしましょう」
$ sudo apt-get install mongodb
$ sudo apt-get install build-essential
$ sudo apt-get install libkrb5-dev
$ npm install kerberos –save
$ npm install mongoose –save
海未「Ubuntuなら、これで。他の環境では適宜読み替えてください」
海未「ではモデルのコードを書いていきます。model.js
を以下のように実装してください」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var mongoose = require('mongoose'); var url = 'mongodb://localhost/polls'; var db = mongoose.createConnection(url, function(err, res) { if (err) { console.log(`error: ${err}`); } else { console.log(`success connected ${url}`); } }); var GroupSchema = new mongoose.Schema({ name: String, votes: Number }); exports.Group = db.model('Group', GroupSchema); |
海未「ここではMongoDBへの接続と、アプリケーション内で使うデータ構造の定義をしています。接続はとりあえず定型文と考えてもよいでしょう」
穂乃果「ということは、重要なのは下半分、と」
海未「そうですね。ここでスキーマという概念が出てきます。RDBであればテーブル定義ですが・・・ここではJavaScriptのオブジェクトの定義と考えてください」
ことり「name
とvotes
ってプロパティを持ったオブジェクトができるってこと?」
海未「そんな感じです。String
とかNumber
は、DBに格納するときの型なのでJavaScriptではそれほど意識する必要はありません」
穂乃果「じゃあ、とりあえず各グループの名前と得票数がデータベースに保存されるって思えばいいんだね」
海未「routes/add.js
に、POSTの時のデータ登録の処理を書きます」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var express = require('express'); var router = express.Router(); var Group = require('../model.js').Group; router.get('/', function(req, res, next) { res.render('add'); }); router.post('/', function(req, res, next) { var data = req.body; data['votes'] = 0; var newGroup = new Group(data); newGroup.save(function(err) { if (err) { console.log(err); res.redirect('back'); } else { res.redirect('/'); } }); }); module.exports = router; |
海未「さきほどのmodel.js
でexport
しているGroupを使ってデータを扱います。new Group
で新しいグループを作成しますが、その引数はオブジェクト、リテラルで書くと{name: 'μ\'s', votes: 0}
のようなものです」
ことり「さっきのスキーマと同じだね」
海未「はい。フォームから送信されたデータ、req.body
にはグループ名が{name: 'μ's'}
のように入っていますから、それにvotes
を付け足したものを基にGroupを作っています」
穂乃果「うん、大体わかるよ」
海未「作ったGroupオブジェクトのsave
関数を呼び出すと、データがDBに保存されます。コールバック関数を登録するので、その中で保存後の処理をします。ここではエラーがなければトップページへ飛ばしていますね」
穂乃果「これで、フォームからDBにデータが登録できるようになったの?」
海未「はい。といっても、それを表示する部分はまだですが。では、次はroutes/index.js
を」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var express = require('express'); var router = express.Router(); var Group = require('../model.js').Group; router.get('/', function(req, res, next) { Group.find({}, {}, {sort: {votes: -1}}, function(err, data) { if (err) { console.log(err); return next(err); } else { res.render('index', {groups: data}); } }); }); module.exports = router; |
海未「Groupオブジェクトのfind
関数は、DBからデータを取り出すものです。引数が色々ありますが第1引数は検索条件、第2引数は取得するカラムというかプロパティというか。どちらも未指定なので無条件、つまり全件を取得します」
穂乃果「ふむ、ふむ・・・」
海未「第3引数は検索オプションですが、ここではソート順を指定しています。得票数の降順ですね」
ことり「得票数の多いグループを上に表示、って仕様だったね」
海未「最後はコールバックで、データの取得が完了したら取得したデータが渡されてきます。ここで、データをindex.jade
に渡しています」
ことり「このデータって、何が入ってるの?」
海未「このようなJSON形式になっています」
1 2 3 4 |
[ { __v: 0, _id: 5683cdd0bc0ed56cd0935ca4, votes: 0, name: 'Aqours' }, { __v: 0, _id: 5683ca12976da328cb44dfee, votes: 0, name: 'μ\'s' }, { __v: 0, _id: 5683cdc6bc0ed56cd0935ca3, votes: 0, name: 'A-RISE' } ] |
穂乃果「なるほど、これなら簡単に扱えそうだね」
ことり「_id
っていうのは、ユニークになる感じ?」
海未「全てのデータを一意に識別できるように自動生成されるものです。投票機能のところで、これを使いますね」
海未「では、このデータをビューに表示してみましょう。views/index.jade
を編集します」
1 2 3 4 5 6 7 8 9 10 11 12 |
extends layout block content h1 School Idol Festival h2 エントリー一覧 ul each val in groups li a(href='#') #{val.name} p a(href='/add') 新規エントリーはこちら |
海未「groups
はコントローラからパラメータとして渡しましたね。配列ですからJadeのeach
を使って反復処理ができます」
穂乃果「それで、さっきのデータからname
を取り出して、っと。書き方はJavaScriptと同じ感じなんだね」
海未「これでデータの登録とその表示ができるようになりましたから、実際に登録して反映されることを確認してみてください」
穂乃果「えーと、こっちの画面で登録ってやると・・・おおっ、できてるできてる!」
ことり「大したことじゃないのかもしれないけど、なんか楽しいね♪」
海未「では次回は、これに投票機能を付けてみましょう。ここまでに学んだMVCの仕組みが理解できていれば、そう難しい話ではないはずです」