NTTドコモ様_dカーシェア
NTTドコモ様_dカーシェア
2018.03.20
#135
それいけ!フロントエンド

Keystone.jsで「追加で投稿機能をカスマイズ」する方法を試してみた

ハル

こんにちは。フロントエンドエンジニアのハルです。

今回は、keystone.jsで個人ブログを作った後、ポートフォリオを載せる機能を追加する想定でカスタマイズしてみたいと思います。

ベースの作成

ベースの作成の説明は、Qiitaで紹介した記事を投稿しました。
node.jsのCMSのkeystone.jsに触れてみようこちらを参考にしてください。

今回の設定項目

  • pugとlessを選択
  • galleryをNoにした
  • cloudinaryはEnterキーを押してスキップした
$ yo keystone
? What is the name of your project? sample-works
? Would you like to use Pug, Nunjucks, Twig or Handlebars for templates? [pug
| nunjucks | twig | hbs] pug
? Which CSS pre-processor would you like? [less | sass | stylus] less
? Would you like to include a Blog? Yes
? Would you like to include an Image Gallery? No
? Would you like to include a Contact Form? Yes
? What would you like to call the User model? User
? Enter an email address for the first Admin user: hoge@fuga.com
? Enter a password for the first Admin user:
 Please use a temporary password as it will be saved in plain text and change
it after the first login. admin
? Would you like to create a new directory for your project? No
? ------------------------------------------------
    Would you like to include Email configuration in your project?
    We will set you up with an email template for enquiries as well
    as optional mailgun integration No
? ------------------------------------------------
    KeystoneJS integrates with Cloudinary for image upload, resizing and
    hosting. See http://keystonejs.com/docs/configuration/#services-cloudinary
 for more info.

    CloudinaryImage fields are used by the blog template.

    You can skip this for now (we'll include demo account details)

    Please enter your Cloudinary URL:
? ------------------------------------------------
    Finally, would you like to include extra code comments in
    your project? If you're new to Keystone, these may be helpful. Yes

ここまでで、ブログ機能は整っていますので、ポートフォリオ機能を追加していきます。

管理画面作成

こちらを参考にhttp://keystonejs.com/docs/database/#listsしながらやってみます。

models/Work.js

var keystone = require('keystone');
var Types = keystone.Field.Types;

// ListでWorkを作成、オプションはドキュメントを参照
var Work = new keystone.List('Work', {
	map: { name: 'title' },
	autokey: { path: 'slug', from: 'title', unique: true },
	defaultSort: '-createdAt',
});

Work.add({
	title: { label: 'タイトル', type: String, required: true },
	manMonth: { label: '制作日数', type: Number },
	content: { label: '説明文', type: Types.Html, wysiwyg: true, height: 400 },
	message: { label: '制作ポイント', type: Types.Textarea, height: 200 },
	image: { label: '画像', type: Types.CloudinaryImage },
	publishedDate: { label: '投稿日', type: Date, default: Date.now },
});

Work.register();

models/Work.jsファイルを作成します。

new keystone.List()でリストを作成し、第一引数にはWorkというキーを指定します。第二引数にオプションを指定します。

基本的にはデフォルトであるブログ機能のPostを参考にしているので必要最低限です。

Work.add()の部分で、入力項目を設定しています。『 label: ○○ 』で管理画面表示用の名前を設定できます。

typeで何を入力するかを指定し、それにあったフォームが用意されます。今回は、シンプルなものを使用しました。

  • type: Stringで1行入力用の欄が用意できます
  • type: Numberで数字を入力できる欄が用意できます
  • type: Types.Html, wysiwyg: trueでWordPressのビュジュアルエディターのような複数行テキスト欄が用意できます
  • type: Types.Textareで複数行入力できる欄が用意できます
  • type: Types.CloudinaryImageで 画像設定が用意できます
  • type: Dateで日付設定が用意できます

keystone.js

// Configure the navigation bar in Keystone's Admin UI
keystone.set('nav', {
	posts: ['posts', 'post-categories'],
	works: 'works', // 追記: 管理画面の順番設定とラベル設定
	enquiries: 'enquiries',
	users: 'users',
});

すでにある、keystone.jsファイルを編集します。

これは任意ですが、管理画面トップ時に項目一覧のラベルと順番を指定できます。指定がない場合ラベルがotherになります。

ここで動作確認します。keystoneを実行します。

$ node keystone

管理画面にログインしてみると、Worksが追加されているかと思います。

keystone.jsカスタマイズ 1

この時点でWorksの『 + 』ボタンから追加もできるので、いくつか追加してみてください。

keystone.jsカスタマイズ 2

一覧作成

次に一覧画面を作成していきます。

routes/middleware.js

exports.initLocals = function (req, res, next) {
	res.locals.navLinks = [
		{ label: 'Home', key: 'home', href: '/' },
		{ label: 'Blog', key: 'blog', href: '/blog' },
		{ label: 'Works', key: 'works', href: '/works' }, // 追記: メニューへ追加
		{ label: 'Contact', key: 'contact', href: '/contact' },
	];
	res.locals.user = req.user;
	next();
};

『 routes/middleware.js 』を編集します。コメントがある行を追記することで、メニューの追加を行っています。

/routes/index.js

// Setup Route Bindings
exports = module.exports = function (app) {
	// Views
	app.get('/', routes.views.index);
	app.get('/blog/:category?', routes.views.blog);
	app.get('/blog/post/:post', routes.views.post);
	app.get('/works', routes.views.works); // 追記: 一覧のルーティング設定
	app.all('/contact', routes.views.contact);
};

コメントの行を追記することで、一覧ページ用のルーティング設定を行っています。『 /works 』にアクセスがあった場合『 routes/views/ 』の『 works.js 』ファイルを指定しています。

routes/views/works.js

var keystone = require('keystone');

exports = module.exports = function (req, res) {
	var view = new keystone.View(req, res);
	var locals = res.locals;

	// ローカルの設定
	locals.section = 'works';

	// mongodbから全件取得し worksへセットする
	view.query('works', keystone.list('Work').model.find());

	// 使用するテンプレートの指定
	view.render('works');
};

mongodbからWorkを全件取得し、値を受け渡しています。『 view.render() 』でテンプレートファイルの指定をしています。

templates/views/works.pug

extends ../layouts/default

block content
	.container
		h1
			| 制作物
		.row
			.col-md-12.col-lg-12
				.row
					each work in works
						.col-md-4.col-lg-4
							.thumbnail
								if work.image.exists
									img.img-responsive(src=work._.image.fit(640,640))
								.caption
									h3
										!= work.title
									p
										a.btn.btn-success.btn-block(href='/works/' + work.slug)
											| 詳細へ

ここは表示部分のコードです。pugを指定したので、pugの記法だとこのようになります。worksにデータが渡ってきているので『 each work in works 』としてループし、『 work.title 』でタイトルを取得しています。

画像は『 work.image.exists 』で保存されている画像があるかを確認し『 work._.image.fit(640,640) 』は、『._(ドットとアンダースコア)』をつけることでアンダースコアメソッドというものが用意されている便利機能が使えるようになります。

そして『 .image 』で画像のデータにアクセスして便利機能の『 .fit() 』で好きな画像サイズで画像を取得できます。

リンクはこの後の詳細で固有の情報のslugを元に、mongodbから詳細情報を取得するので『 '/works/' + work.slug 』とリンクを作成しています。

また、ここで動作確認します。さきほどの実行中だったものは止めて、再度、keystoneを実行します。

$ node keystone

下記URLにアクセスすると……。

http://localhost:3000/works

登録したものが表示されたかと思います。

keystone.jsカスタマイズ 3

詳細

routs/index.js

// Setup Route Bindings
exports = module.exports = function (app) {
	// Views
	app.get('/', routes.views.index);
	app.get('/blog/:category?', routes.views.blog);
	app.get('/blog/post/:post', routes.views.post);
	app.get('/works/:work', routes.views.work);// 追記: 詳細のルーティング設定
	app.get('/works', routes.views.works); // 一覧のルーティング設定
	app.all('/contact', routes.views.contact);
};

コメントの追記となっている部分を追記することで、詳細のルーティング設定を行っています。『/worls/xxx』でアクセスがあった場合、『 routes/views/ 』 『 works.js 』を指定しています。

さっきと違うのは『:work』でパラメータを受け取るように記述しています。

routs/views/work.js

var keystone = require('keystone');

exports = module.exports = function (req, res) {
	var view = new keystone.View(req, res);
	var locals = res.locals;

	// ローカルの設定
	locals.section = 'works';

	// req.params.workでパラメーターを受けっと他ものを格納
	locals.filters = {
		work: req.params.work,
	};

	// 格納先の用意
	locals.data = {
		works: [],
	};

	// mongodbからslugで一致するものを取得しデータを受け渡し
	view.on('init', function (next) {
		var q = keystone.list('Work').model.findOne({
			slug: locals.filters.work,
		});

		q.exec(function (err, result) {
			locals.data.work = result;
			next(err);
		});
	});

	// 使用するテンプレートの指定
	view.render('work');
};

詳細では、slugの情報が入っているパラメーターを受け取りそのパラメーターを使って、mongodbからslugで一致するデータを取得しテンプレートにデータを受け渡しています。

テンプレートファイルは『 work 』と指定しているのでwork.pugを追加します。

templates/views/work.pug

extends ../layouts/default

block content
	.container
		.row
			.col-md-12.col-lg-12
				article
					p
						a(href="/works")
							| ← Back to works
					hr
					header
						h1 #{data.work.title}
					.row
						.col-md-6.col-lg-6
							if data.work.image
								img(src=data.work._.image.fit(750,450)).img-responsive
						.col-md-6.col-lg-6
							ul.list-group
								li.list-group-item.active
									strong
										| 制作情報
								li.list-group-item
									strong
										| 制作時間:
									| #{data.work.manMonth}日
								li.list-group-item
									strong
										| 制作ポイント:
									!= data.work.message
								li.list-group-item
									!= data.work.content

最後に詳細の表示部分です。一覧のとき同様、各項目を表示しています。Bootstrapが読み込まれてるのでいい感じにクラス名を指定しつつ表示させてみました。

詳細が見れるか確認してみます。実行中だったものは止めて、再度、keystoneを実行します。

$ node keystone

先ほど作っておいた、Worksの一覧ページから『 詳細へ 』ボタンをクリックして詳細を見ます。登録したものが表示されたかと思います。

keystone.jsカスタマイズ 4

やったぜ! 見られました。

まとめ

公式ドキュメントとすでにあるPostを見ながら手探りでやってみましたが、慣れればWordPressのように簡単にカスタマイズができそうでした。

手探りしながらだったので雑な記事になってしまいましたが、今後もKeystone.jsを触っていきたいと思います。

最終的には自分好みにカスタマイズしたBlogサイトを公開したいです。