WEB・アプリ開発

middleman-blogでタグやカレンダーをカテゴリーの下階層に入れる方法

公式マニュアルではカスタム記事コレクションという機能を使って、カテゴリーを実現できるとしていますが、この機能だとタグと機能が同じなので、あまり存在意義を感じられません。

カテゴリーをタグやカレンダーより上位のレベルにあるものとして扱うにはどうすればよいのでしょうか?

具体的にいうとURLでいうと下のようにタグやカレンダーページを特定のカテゴリーの下に配置するにはどうすればよいのでしょうか。

http://example.com/カテゴリー名/タグ名/

http://example.com/カテゴリー名/2016/01/

これはこのブログの構成です。タグやカレンダーページをいくつか開いてみると構成がわかると思います。

2つの方法がある

  1. 複数ブログ機能を使う
  2. middleman-blogを拡張する

1の複数ブログ機能は一つのドメインでディレクトリを分けて、複数のブログを運営できる機能です。

この方法はまだ試していないのですが、私としては完全にブログを分けるのでは、次の条件のうち上の二つを実現できなさそうだったので、1ではなく2を採用しました。

  • ホーム画面では全カテゴリーの記事を表示したい
  • 各記事のURLはルート直下に配置したい
  • サイドバーの最新記事やタグやカレンダーはカテゴリー内の記事のみ対象にしたい

(やりようによっては1の方法でも実現できるかもしれません)

middleman-blogにモンキーパッチをあてる

middleman-blogの挙動を変えるには、単純にカスタム拡張を追加するのでは無理で、middleman-blogのclassにモンキーパッチを当てる必要があります。

カスタム拡張のafter_configurationコールバック内で、middleman-blogの3つのclassをリオープンしてモンキーパッチをあてます。
(リオープンとは宣言済みのクラスを再び宣言して変更を加えるという意味です。)

config.rb

下のコードをconfig.rbに追記します。(別ファイルのカスタム拡張にしてもOK。詳しくはカスタム拡張にて)

activate :blogよりも後ろに書きます。

config.rb

after_configurationでモンキーパッチする理由

カスタム拡張ではいくつかのコールバックが使えるのですが、このモンキーパッチはafter_configuration内で行う必要があります。

manipulate_resource_listでは既存のmiddleman-blogで記事が読み込み済みになってしまっています。

また、config.rbのreadyイベント時も同じように読み込み済みになってしまっています。(詳しくはconfig.rb の中でサイトマップを使う

config.rbのトップコンテキストでリオープンすることも考えられますが、そうすると逆にmiddleman-blogの一部のclassが読み込まれていないのでモンキーパッチできません。

なので、classが全て読み込み済み、記事の読み込み前のafter_configurationで行う必要があります。

スポンサーリンク

config.rbの解説

BlogDataTagPagesCalendarPagesを拡張しています。また、middleman-blogによって作られたhelperメソッド、blog_month_pathtag_pathを上書きしています。

BlogData

articles_categorized_bytags_categorized_byというメソッドを新たに追加しています。

これは、特定のカテゴリーに属する記事、タグをそれぞれ取得するメソッドです。引数にcategoryをとりますが、引数なしの場合は、全カテゴリーに属するものを返します。これはトップページでは、全カテゴリーに属する情報が必要だけど、インターフェイスを揃えるためです。

このメソッドはconfig.rb内でもtemplate側でも使えるように、template側に渡されるBlogDataというオブジェクトに追加しました。テンプレート側ではblogでアクセスできます。

TagPages

linkmanipulate_resource_listtag_page_resourceというメソッドを変更しています。

具体的にそれぞれのタグごとに

http://example.com/カテゴリー名/タグ名/

とうURLでページを生成するようにしています。テンプレート側にcategoryという名前でカテゴリーの種類を渡しています。

CalendarPages

ほぼTagPagesの拡張内容と同じです。

ただ、私は月ごとのカレンダーページしか必要としていないので、年と日のページ生成部分は削除しています。必要であればmiddleman-blogの元ファイルを参考に追加してください。

また、カレンダーページはカテゴリーごとの

http://example.com/カテゴリー名/2016/01/

というページももちろん作るのですが、

http://example.com/2016/01/

というように、全カテゴリーの記事についても作成するようにしています。これはトップページのサイドバーに表示するカレンダーは全カテゴリーを対象にしたいためです。

blog_month_pathとtag_path

こちらはmiddleman-blogで定義されるヘルパーですが、カテゴリーを引数にとり、カテゴリーが第一階層にくるパスを返すように上書きしています。カテゴリーを与えない場合は、ルート直下に配置されたパスを返します。

ja.ymlでタグとカテゴリーの階層関係を定義

タグとカテゴリーの階層関係をja.yml内で定義しています。

ja.yml

template側の書き方

サイドバーはトップページでは全カテゴリーの記事、カテゴリーページではそのカテゴリーだけの記事が反映されるようにします。

なので、サイドバーのpartialは例えばこんな感じです。(slimで書いています)


さきほど追加したarticles_categorized_bytags_categorized_byでカテゴリーごとの記事やタグを取り出しています。

また、メインのテンプレートでは、categoryという変数が渡されているので、メインメニューのどのカテゴリーをアクティブにするかの選択に使用できます。

参考

Middleman 3.0 extensions and execution order

さいごに

いかがでしたでしょうか。これでタグやカレンダーをカテゴリーの下に所属することができます。コードが長くわかりづらいかもしれませんが、middleman-blogの元コードをpryなどでデバッグしていくと動きがわかると思います。

なおバージョンは、

  • middleman (3.4.0)
  • middleman-blog (3.6.0.beta.2)

を使用しています。