baseof.htmlの作成 その4 階層型カテゴリーを実装する方法

こんにちはnasustです。前回の続きbaseof.htmlの解説をします。

right_aside.html 部分テンプレート

サイドバーにはプロフィール、カテゴリー、タグ一覧を表示します。 新しい関数が現れましたので解説します。

<div class="m-aside-widget__profile">
    <div class="m-aside-widget__profile-avatar">
        {{ $profile_image := "" }}
        {{ if fileExists "/assets/images/blank-profile.png" }}
            {{ $profile_image = resources.Get "images/blank-profile.png" }}
        {{ end }}
        {{ with .Site.Params.profile_image }}
            {{ if fileExists (printf "/assets/%s" . ) }}
                {{ $profile_image = resources.Get . }}
            {{ end }}
        {{ end }}
        {{ with $profile_image }}
            <img class="m-aside-widget__profile-avatar-img" src="{{ .Permalink }}" />
        {{ end }}
    </div>
    <div class="m-aside-widget__profile-author">
        {{ with .Site.Params.author }}
            {{ . }}
        {{ end }}
    </div>
    <div class="m-aside-widget__profile-description">
        {{ if fileExists "/content/profile.txt" }}
            {{ $file := readFile "/profile.txt" }}
            {{ $file | markdownify }}
        {{ end }}
    </div>
</div>
{{ if and .Section }}
    {{ $root_section_page := .Site.GetPage .Section }}
        {{ if eq $root_section_page.Kind "section" }}
        {{ $section_category_data := partial "function/get_all_category_pages" $root_section_page }}
        {{ if $section_category_data.child_section_category_data_list }}
            <div class="m-aside-widget__category">
                {{ template "category" $section_category_data.child_section_category_data_list }}
            </div>
        {{ end }}
    {{ end }}
{{ end }}
{{ define "category" }}
    <ul class="m-aside-widget__category-list" >
        {{ range . }}
            <li class="m-aside-widget__category-list-item">
                <a class="m-aside-widget__category-list-item-link" href="{{ .section.Permalink }}">
                    <span class="m-aside-widget__category-list-link-title" >{{ .section.Title }}
                    <span class="m-aside-widget__category-list-link-count"> ({{ len .all_child_pages }}) </span>
                </a>
            </li>
            {{ if .child_section_category_data_list }}
                {{ template "category" .child_section_category_data_list }}
            {{ end }}
        {{ end }}
    </ul>
{{ end }}

{{ if .Site.Taxonomies.tags }}
    <div class="m-aside-widget__tags">
        <ul class="m-aside-widget__tags-list">
            {{ range .Site.Taxonomies.tags }}
                <li class="m-aside-widget__tags-list-item">
                    <a class="m-aside-widget__tags-list-item-link" href="{{ .Page.Permalink }}">
                        <span class="m-aside-widget__tags-list-item-link-name">{{ .Page.Title }}</span>
                    </a>
                </li>
            {{ end }}
        </ul>
    </div>
{{ end }}
html

readFile

指定したパスのファイルを読みます。この場合はcontent/profile.txtです。

see also: readFile | Hugo

markdownify

渡されたテキストをMarkdownとして扱いHTMLに変換します。

see also: markdownify | Hugo

$section_category_data := partial “function/get_all_category_pages” $post_page

partialの説明は以前しましたが今回は使い方が違います。 部分テンプレートの中でreturn関数を呼ぶと変数を返すことができます。 今回はテンプレートを関数っぽく呼び出して結果を受け取っています。

see also: Partial Templates | Hugo

template

templateはdefineで定義された、そのテンプレート内でしか利用できないテンプレートを呼び出します。

see also: Base Templates and Blocks | Hugo

define

テンプレート内でテンプレートを定義します。 defineブロックで囲まれた部分がテンプレートとして扱われます。

defineの中で自分自身のテンプレートを呼び出すのは再帰処理を行う為です。

function/get_all_category_pages.htmlから取得するデータは階層構造を持ったカテゴリーです。

Hugoのカテゴリーは通常taxonomiesで設定します。 しかしtaxonomiesはブログのタグのように階層を持つことが出来ません。 今回のテンプレートでは、サブセクションをカテゴリーとして扱う為の処理を行っております。

例えば、post/generalというサブセクションがあればgeneralカテゴリーとして扱うようにします。 post/general/sub/とあれば、generalカテゴリーの下のsubはサブカテゴリーとして扱います。

このテンプレートが表示するカテゴリーの例:

- 雑記 ( 10 )
    - 映画 (3)
    - 旅行 (4)
  
- 開発 (7)
    - Go (3)
    - JavaScript (4)
bash

さらにカテゴリーの記事数は、サブカテゴリーの記事数と自身の記事数を合計したものになります。

雑記(3)+ 映画(3)+ 旅行(4)= 雑記(10)

この集計をfunction/get_all_category_pages.html 部分テンプレートで行っています。

function/get_all_category_pages.htmlから取得するデータは、 このカテゴリーのようにツリー状になっていますので、ツリーを辿るのに再帰処理を行っています。

see also: Base Templates and Blocks | Hugo

.Site.Taxonomies.tags

サイト全体のタグの配列です。

see also: Taxonomy Templates | Hugo

function/get_all_category_pages.html 部分テンプレート

セクションの構造を取得するテンプレートです。 テンプレートはlayout/partials/function/get_all_category_pages.htmlに作成しました。

{{ $section := . }}
{{ $scratch := newScratch }}
{{ $scratch.Set "all_child_pages" $section.CurrentSection.Pages }}

{{ if len $section.Sections  }}
    {{ range .Sections }}
        {{ $current_section := . }}
        {{ $child_section_category_data := partial "function/get_all_category_pages" $current_section }}
        {{ $scratch.Add "list" (slice $child_section_category_data )  }}
    {{ end }}
    {{ if ($scratch.Get "list") }}
        {{ range ($scratch.Get "list") }}
            {{ $scratch.Add "all_child_pages" .all_child_pages }}
        {{ end }}
    {{ end }}
{{ end }}

{{ return dict "section" $section "all_child_pages" ($scratch.Get "all_child_pages")  "child_section_category_data_list" ($scratch.Get "list")  }}
html

newScratch

スクラッチを作成する関数です。 Hugoの変数はスコープの問題などで扱いにくい事があります。

ですがスクラッチを使用すると簡単になります。

例えば、rangeの中で変数を配列に追加するには以下のようになります。

{{ $slice := 0 }}
{{ range xxx }}
    {{ if $slice }}
        {{ &slice = $slice | appned xx }}
    {{ else }}
        {{ $slice = slice xx }}
    {{ end }}
{{ end }}
html

というように面倒です。スクラッチを利用すると以下のようになります。

{{ $scratch := newScratch }}
{{ range xxx }}
    {{ $scratch.Add "slice" (slice xx) }}
{{ end}}
html

このように簡潔になります。

see also: .Scratch | Hugo

このテンプレートの処理について

セクションを再帰的に辿り以下の疑似コードのような構造で、 セクションのデータを格納します。

type section_category_data struct{
    section Page 
    all_child_pages Pages 
    child_section_category_data_list []section_category_data 
}
go

この構造を実現する為にdictを使用してMapで値を返しています。

see also: dict | Hugo

このように、ちょっとした処理を行う場合は、 処理内容をテンプレート纏めたおくと扱いやすいです。

次回はfooter.htmlを解説します。

prevnext