michimani.net

[Hugo] 記事下に同じタグが付けられている記事一覧を表示する

2020-02-27

前に同一カテゴリ記事の一覧を記事下に表示する方法を書きましたが、今回はそのタグ版です。複数のタグが付けられている場合は、それぞれのタグに紐づく記事の一覧を表示するようにしてみます。

目次

前提

やること

基本的には同一カテゴリの記事一覧を表示したときと同じです。

各テーマディレクトリ内にある layouts/_default/single.html に修正を加えます。
今回も前回と同様に indigo のテーマで実装するので、対象のファイルは themes/indigo/layouts/_default/single.html となります。

現在記事のタグ名を取得する

同一タグの記事一覧を取得するために、現在の記事のタグ名を取得する必要があります。カテゴリの時と同様に、次のような記述でタグの リスト を取得できます。

{{ .Params.tags }}

前回は、 一つの記事にカテゴリは一つしか設定しない という暗黙のマイルールに基づいて、カテゴリ名は次のような記述で取得していました。

{{ $c := index .Params.categories 0 }}

ただし、今回は複数のタグが付けられていることを想定して、すべてのタグに関して関連する記事一覧を表示するので、次のような記述でタグのリストからタグ名を再帰的に取得します。

{{ range .Params.tags }}
  {{ $t := . }}
{{ end }}

この range の中で、各タグに関連する記事一覧を取得し、表示していきます。

対象のタグが付けられた記事一覧を取得する

$t にはタグ名が格納されるので、その値を用いて記事の一覧を取得します。具体的には次のような形で取得します。

{{ $tposts := index $site_obj.Taxonomies.tags (replace (lower $t) " " "-") }}

これで $tposts 変数には、タグ名 $t が付けられた記事の一覧が格納されます。

とは言っても急に出てきた部分がいくつかあるので、それぞれ説明していきます。

変数 $site_obj

まずは $site_obj という変数です。
これは range の外で次のように定義しておきます。

{{ $site_obj := .Site }}

range の中では .Site を新たに定義した変数として使用しています。理由としては、 range の中では . が示すのは個別のタグ名なので、 .Site を参照することができないからです。

試しに .Site のまま使用しようとすると、下記のようなエラーとなります。 (適宜 改行しています)

render of "page" failed: "/path/to/michimani.net/themes/indigo/layouts/_default/single.html:106:33": 
execute of template failed: template: _default/single.html:106:33: executing "_default/single.html" at <.Site.Taxonomies.tags>: 
can't evaluate field Site in type string
Rebuild failed:

Failed to render pages: render of "page" failed: "/path/to/michimani.net/themes/indigo/layouts/_default/single.html:106:33": 
execute of template failed: template: _default/single.html:106:33: executing "_default/single.html" at <.Site.Taxonomies.tags>: 
can't evaluate field Site in type string

なので、 range の外で .Site を変数に代入しておいて、 range の中では変数を介して .Site の情報を使用します。

半角スペースの置換

タグ名には半角スペースを含めることができますが、 .Site.Taxonomies.tags オブジェクトのキーとなっているタグ名では英数字が小文字に、半角スペースがハイフン - に、それぞれ置換されています。
例えば Amazon S3 というタグ名であれば、キー名としては amazon-s3 となっています。

一方で range で回している .Params.tags のタグ名は Amazon S3 といった本来のタグ名なので、次の記述で英数字の小文字化と半角スペースの置換を行っています。

{{ (replace (lower $t) " " "-") }}

記事の一覧を表示させる

記事の一覧が取得できたので、あとは表示するだけです。
この部分はカテゴリの場合と同じなので、特に新しいことはありません。前回と同様に、該当する記事一覧の最初の 5 件のみを表示します。

{{ range first 5 $tposts }}
    {{ .Render "li" }}
{{ end }}

最終的にどうなったのか

ここまでやったことをまとめると、各タグの記事一覧を表示する部分は次のような形になりました。

{{ if and (.Params.tags) }}
    {{ $site_obj := .Site }}
    {{ range .Params.tags }}
        {{ $t := . }}

        {{ $tposts := index $site_obj.Taxonomies.tags (replace (lower $t) " " "-") }}
        {{ if gt (len $tposts) 1 }}
        <div class="same-category-tags-posts-area">
            <h2>Other posts tagged by "{{ $t }}"</h2>
            {{ range first 5 $tposts }}
                {{ .Render "li" }}
            {{ end }}
            {{ if gt (len $tposts) 5 }}
            <a class="more-btn" href="/tags/{{ lower $t }}/">more ...</a>
            {{ else }}
            <a class="more-btn" href="/tags/">other tags</a>
            {{ end }}
        </div>
        {{ end }}
    {{ end }}
{{ end }}

リストの細かい表示条件はカテゴリの場合と同じで、同一タグの記事が 5 件以上あればタグ別記事一覧ページへのリンクを more 、 5 件より少なければタグ一覧ページへのリンクを othre tags で、それぞれ表示するようにしています。

実際には次のような表示になります。

the same tags post list

まとめ

Hugo で作ったブログの記事下に、その記事と同じタグが付けられている記事の一覧を表示してみた話でした。

今までは各記事のタイトル部分にタグ別一覧へのリンクを置いていましたが、今回 記事の一覧を表示することでタグ別一覧ページへ遷移するというステップがなくなり、回遊がしやすくなりました。
このブログに関しては自分でも見返すことが多いので、関連する情報が追いかけやすくなったなーという印象です。

同じようなことを実現したいと考えている方は、是非参考にしてみてください。


comments powered by Disqus