MovableTypeで検索ページを実現したかったのですが、DataAPIでは実現できなかったのでflexible Searchを利用して実現したときのメモ。
仕様(実現したいこと)
記事
- MovableTypeで登録
- カテゴリは4つ
- カスタムフィールド2つ(所属、団体名)
検索ページ
- カテゴリ、カスタムフィールド、キーワードで検索
- 結果を一覧に表示、ページングあり
- 一覧は全カテゴリを含むページと、各カテゴリごとの計5ページ
実装
基本的にはGitHubのページに載っているので、こちらを参照してもらえれば実現できます。
検索用JSONファイルの出力
検索対象のJSONファイルの出力が必要になります。これはMovableTypeで行います。
カスタムテンプレートを以下のような感じで作成します。
速度を少しでも早くしたい場合は、カテゴリごとに出力した方が良いです。
<mt:Unless name="compress" regex_replace="/^\s*\n/gm","">
<mt:SetVar name="items">
<mt:Entries include_blogs="1" lastn="0">
<mt:SetVarBlock name="item{date}"><$mt:EntryDate format="%Y.%m.%d"$></mt:SetVarBlock>
<mt:SetVarBlock name="item{title}"><mt:EntryTitle></mt:SetVarBlock>
<mt:SetVarBlock name="item{url}"><mt:EntryPermalink></mt:SetVarBlock>
<mt:SetVarBlock name="item{body}"><mt:EntryBody remove_html="1" regex_replace="/\n|\t| | /g",""></mt:SetVarBlock>
<mt:SetVarBlock name="item{excerpt}"><$mt:EntryExcerpt$></mt:SetVarBlock>
<mt:SetVarBlock name="item{more}"><mt:EntryMore remove_html="1" regex_replace="/\n|\t| | /g",""></mt:SetVarBlock>
<mt:SetVarBlock name="item{tag}">,<mt:EntryTags glue=","><mt:TagName regex_replace="/ | /g","%20"></mt:EntryTags>,</mt:SetVarBlock>
<mt:SetVarBlock name="item{category}"><mt:EntryCategories glue=","><mt:CategoryLabel></mt:EntryCategories></mt:SetVarBlock>
<mt:SetVarBlock name="item{group}"><$mt:CFGroup$></mt:SetVarBlock>
<mt:SetVarBlock name="item{circle}"><$mt:CFCircle$></mt:SetVarBlock>
<mt:SetVarBlock name="items" function="push"><mt:var name="item" to_json="1"></mt:SetVarBlock>
</mt:Entries>
{"items":[
<mt:Loop name="items" glue=","><mt:Var name="__value__"></mt:Loop>
]}
</mt:Unless>
-
<mt:SetVarBlock name="item{date}">
で指定しているitem{date}
のdate
の部分は後々使用するので、わかりやすい名前にしておいた方がよいです -
<$mt:CFGroup$>
、<$mt:CFCircle$>
はカスタムフィールドです
flexibleSearchの設定
今回オプションに設定したのは以下です。
検索用JSONファイルパス(searchDataPath)
MovableTypeで出力するパスです。
searchDataPath: '/data/entries.json',
フォームHTML(searchFormHTML)
検索フォーム部分のHTML。
searchFormHTML: [
'<div>\r\n',
'<form action="index.html" method="get">\r\n',
'<div>\r\n',
'<label for="group">所属</label>\r\n',
'<select name="g" id="group">\r\n',
'<option value="" selected="selected">選択してください</option>\r\n',
'<option value="1">所属1</option>\r\n',
'</select>\r\n',
'</div>\r\n',
'<div>\r\n',
'<label for="circle">団体名</label>\r\n',
'<select name="c" id="circle">\r\n',
'<option value="" selected="selected">選択してください</option>\r\n',
'</select>\r\n',
'</div>\r\n',
'<fieldset>\r\n',
'<legend>カテゴリ</legend>\r\n',
'<label><input type="checkbox" name="cat[]" id="category1" value="1"><span id="category1_label">カテゴリ1</span></label>\r\n',
'<label><input type="checkbox" name="cat[]" id="category2" value="2"><span id="category2_label">カテゴリ2</span></label>\r\n',
'<label><input type="checkbox" name="cat[]" id="category3" value="3"><span id="category3_label">カテゴリ3</span></label>\r\n',
'<label><input type="checkbox" name="cat[]" id="category4" value="4"><span id="category4_label">カテゴリ4</span></label>\r\n',
'</fieldset>\r\n',
'<div>\r\n',
'<label for="keyword">キーワード</label>\r\n',
'<input type="text" name="search" id="keyword" value="">\r\n',
'</div>\r\n',
'<div>\r\n',
'<input type="submit" id="submitButton" value="検索">\r\n',
'</div>\r\n',
'<input type="hidden" name="offset" value="0">\r\n',
'<input type="hidden" name="limit" value="6">\r\n',
'</form>\r\n',
'</div>\r\n',
].join(""),
-
<input type="hidden" name="offset" value="0">
は必ず必要 -
<input type="hidden" name="limit" value="6">
も必ず必要。valueは環境に合わせて。 -
<input type="text" name="search" id="keyword" value="">
も必ず必要。name=search
が大事。 - 末尾に改行(\r\n)を入れているのは見た目ではなく、このdivに「display: inline-block;」を指定している場合、改行を入れないとレイアウトが崩れるためです。
検索結果一覧(resultItemTmpl)
検索結果の一覧部分。
resultBlockId: "news-list",
resultItemTmpl: [,
'{{#items}}',
'<li>',
'<h3>{{group}}:{{circle}}</h3>',
'<h4><a href="{{url}}">{{title}}</a> ({{date}})</h4>',
'<p>{{&excerpt}}</p>',
'</li>',
'{{/items}}',
].join(""),
-
resultBlockId
は検索結果を挿入する要素のIDを指定します。jqueryでいうappend対象。 -
resultItemTmpl
は一覧部分。この辺はMT書いてる人ならなんとなくわかるはず。使われているurl、image等の名前は出力したjsonファイルのitem{url}
に合わせて指定します。
ページタイトル(resultMetaTitleTmpl)
ページタイトルが自動的に変更されるのでそれを修正。
resultMetaTitleTmpl: [
"{{metaTitle}}"
].join(""),
ページング(paginateTmpl)
paginateTmpl: [
'<div class="paging fs-paginate" id="fs-paginate">',
'<ul>',
'{{#showTurnPage}}',
'{{#exceptFirst}}',
'<li class="fs-prev"><a class="fs-prev-link fs-turn-page-link" href="#" title="{{prevPageText}}">{{&prevPageText}}</a></li>',
'{{/exceptFirst}}',
'{{/showTurnPage}}',
'{{#page}}',
'{{#checkRange}}',
'<li class="{{current}}"><a class="fs-page-link {{currentLink}}" href="#" title="{{pageNumber}}">{{pageNumber}}</a></li>',
'{{/checkRange}}',
'{{/page}}',
'{{#showTurnPage}}',
'{{#exceptLast}}',
'<li class="fs-next"><a class="fs-next-link fs-turn-page-link" href="#" title="{{nextPageText}}">{{&nextPageText}}</a></li>',
'{{/exceptLast}}',
'{{/showTurnPage}}',
'</ul>',
'</div>'
].join(""),
paginateCount: 6,
showTurnPage: true,
prevPageText: 'Prev',
nextPageText: 'Next',
maxPageCount: 10,
-
{{#exceptFirst}}
が「Prev」、{{#exceptLast}}
が「Next」の部分 - hrefは#でOK。flexibleSearchがよしなにしてくれる
- 自分だけなのかもしれないが、classとtitleを設定してあげないと正しくページングが動いてくれなかった
-
maxPageCount
はありがたい
独自の検索(customSearch)
flexibleSearchではキーワード検索は行えるが、カテゴリやカスタムフィールドの検索までは行えないので、その辺はこのエリアで独自に実装する必要がある。
return false;
にすると結果から除外される。
customSearch: function(item, paramObj){
var group = $('#group').val();
var circle = $('#circle').val();
var groupText = $("#group option:selected").text();
var circleText = $("#circle option:selected").text();
// Group
if (group != '') {
if (item.group != groupText) {
return false;
}
else {
// Circle
if (circle != '') {
if (item.circle != circleText) {
return false;
}
}
}
}
// Category Check
var arrCategory = new Array();
var bCategory = false;
for (var i = 1; i <= 4; i++) {
if ($('#category' + i).prop('checked')) {
arrCategory.push($('#category' + i + '_label')[0].innerText);
bCategory = true;
}
}
if (bCategory) {
var myCategory = item.category.split(',');
var bCategoryMatch = false;
for (var j = 0; j < myCategory.length; j++) {
if ($.inArray(myCategory[j], arrCategory) >= 0) {
// 対象
bCategoryMatch = true;
break;
}
}
if (!bCategoryMatch) {
// 対象外
return false;
}
}
return true;
},
ページングHTMLの変更(modifyPaginateHTML)
あまりないかもしれませんが、今回は表示中のページ番号はクリックできないようなHTMLだったので、変更する必要がありました。そういう場合はこのエリアで変更できます。
modifyPaginateHTML: function(html){
// current change
var searchStartStr = '<li class="fs-current">';
var searchEndStr = '</li>';
var start = html.indexOf(searchStartStr);
if (start != -1) {
var end = html.indexOf(searchEndStr, start);
start = parseInt(start) + searchStartStr.length;
var before = html.substring(start, end);
var title = $(before).attr('title');
html = html.replace(before, '<em>' + title + '</em>');
}
return html;
},
その他
- 初期表示で検索結果を表示したかったのですが、うまいこといきませんでした。なので、初期表示は独自でjqueryで実装しました。
- ページングのリンクは、クリックされたあとにflexibleSearchで自動で計算されるようです
- 検索対象データ(JSONファイル)が0件の場合、customSearchは動かないので、customSearch内で何か(例えばjqueryの関数の実行など)を行っている場合、実行されません。その場合、resultCompleteだと動くのでこちらを利用しましょう。
MovableTypeのDataAPIを断念した理由
- 検索速度が遅い
- キーワード検索したときに、記事のカテゴリが取得できない
- 1回のリクエストで複数カテゴリを指定して記事を取得できない
実現はできるが、遅いというのが気になった。
参考
flexible Search
便利なライブラリありがとうございます。