a-blog cmsで記事に執筆者情報を表示する方法と構造化データについて
更新情報
- 2022-08-03 : 「エントリーを利用する」の内容を、エントリーIDとグローバル変数を利用する方法に改善
- 2022-01-08 : Twitterで ジェノベーゼ寺崎 (@genovese115) さんがご提案くださった素敵な実装アイデアを追記しました。
この記事について
この記事は、a-blog cms Advent Calendar 2021 - Adventar 10日目の記事です。
記事に執筆者情報を掲載するサイトが一般的になってきました。
国産CMS a-blog cmsでは、カスタムフィールドを柔軟に設定・表示することができるため、CMSが提供している標準機能で実装が可能です。
今回は、実装方法を3つ、考えてみました。
1と3は、実案件で実装検証済です。
2に関しても、テスト環境である程度実証済です。
- ユーザーを利用する
- カテゴリーを利用する
- エントリーを利用する
エントリーに表示させたい執筆者情報とは
まず、記事(エントリー)には、以下の情報が掲載できれば良いのではと思います。
- プロフィール写真
- 名前
- 肩書
- プロフィール文章
- 執筆者のウェブサイト・プロフィールページ
- 執筆者のSNS
それぞれの実装方法によって、表示できたり、できなかったり、工夫が必要だったりします。
共通のメリットとして検討したいこと
いずれの方法でも、執筆者の情報は一元管理する事が第一条件です。
ユーザーを利用する
概要
この方法では、エントリーを執筆したCMSのユーザー情報をエントリーに表示することができます。
a-blog cmsのユーザーには、デフォルトで以下のような情報が登録できます。
ここにない情報も、カスタムフィールドを作成することが可能です。
ユーザー情報にカスタムフィールドを用意するには、以下のファイルに入力用のコードを記述します。
themes\{your-theme}\admin\user\field.html
ここに記述した内容が、ユーザー管理のカスタム設定に表示されるようになります。
エントリーにユーザー情報を表示させる方法
CMSに登録したユーザー情報はカスタムフィールドを含め、エントリー本文(Entry_Body)
モジュールのuserField
ブロックで表示することができます。
Entry_Body
の変数表はこちら
https://developer.a-blogcms.jp/document/acms-code/vars/Entry_Body.html?cache=off
共通メリット : 執筆者の一元管理
この方法の場合、ユーザー管理の子ブログへの権限
にチェックを入れれば、子ブログ間でも執筆者情報を表示できるため、ユーザー情報の一元管理管理が可能です。
メリット01 : 管理面で、誰が書いたのかが分かりやすい
この方法のメリットの一つは、誰がそのエントリーを書いたのかが分かりやすいという点です。
管理画面にログインして、エントリーを確認した時に、「ああ、このエントリーはこの人が書いたのか」と、すぐに分かります。
例えば、複数人でサイトを運用していて、エントリーの修正が必要になった場合、誰に修正を依頼すれば良いのか、すぐにわかります。
一番分かりやすい運用方法なのではないかと思います。
メリット02 : エントリーにユーザーのカスタムフィールドを表示できる
エントリー本文(Entry_Body)
のuserField
ブロックでは、ユーザーのカスタムフィールドを表示できます。
冒頭に記載しましたエントリーに表示させたい情報のうち、デフォルトのフィールドとカスタムフィールドを分けるなら以下の通りです。
- プロフィール写真 (デフォルト)
- 名前 (デフォルト)
- 肩書 (カスタムフィールド)
- プロフィール文章 (カスタムフィールド)
- 執筆者のウェブサイト・プロフィールページ (デフォルト)
- 執筆者のSNS (カスタムフィールド)
いずれにしてもカスタムフィールドが使えるので、実装は楽です。
デメリット : ユーザー数の制限がある
デメリットとしては、プランによっては、ユーザー数の上限があることでしょうか。
プラン(ライセンス)に関してはこちら
ライセンス | 使いやすさで選ぶ国産CMS | a-blog cms
規模の大きなメディアサイトで、執筆者=CMSのユーザーでないという場合、かつ、低コストで運用を始めたいという場合は、別の方法の検討が必要になりそうです。
あるいは、ライターさんから記事を頂いて、それをサイト管理者が登録するという運用体制の場合がこれに当たるかと思います。
カテゴリーを利用する
概要
この方法では、執筆者をカテゴリーとして管理します。
a-blog cmsのカテゴリーには、デフォルトで以下のような情報が登録できます。
ここにない情報も、カスタムフィールドを作成することが可能です。
カテゴリーにカスタムフィールドを用意するには、以下のファイルに入力用のコードを記述します。
themes\{your-theme}\admin\category\field.html
ここに記述した内容が、カテゴリーのカスタム設定に表示されるようになります。
なお、カテゴリーに応じてカスタムフィールドを変更したい場合は、themes\{your-theme}\admin\category\field.html
のファイルに条件分岐を記述することで振り分けが可能になります。
あるいは、URLコンテキスト別に振り分ける場合は、field.html
に以下の記述をし、該当するディレクトリにそれぞれのファイルを用意します。
@include("/admin/category/bcd/%{BCD}.html")
@include("/admin/category/rccd/%{RCCD}.html")
@include("/admin/category/ccd/%{CCD}.html")
このあたりは、配布されているテーマファイルを色々と眺めていると非常に勉強になります。
カテゴリーを利用する場合の実例を考える
さて、この場合、エントリーのメインのカテゴリは別に存在していて、そのエントリーに執筆者情報を付与したいという運用が考えられます。
a-blog cms でサブカテゴリー機能をする場合、設定が必要です。
管理ページ > コンフィグ > 編集設定
で、「サブカテゴリー」を「有効」にしましょう。
詳しくは以下の公式ドキュメントにまとめられています。
新機能サブカテゴリーを使ってみよう | 2018秋合宿 | ハンズオン | a-blog cms developer
カテゴリー構造をどうするか
以下のような構造での実装を考えてみます。
- ルートブログ
- カテゴリー : ニュース
- カテゴリー : 製品情報
- カテゴリー : 執筆者 *グローバルにする
- カテゴリー : 山田太郎 *グローバルにする
- 子ブログ
- 様々なカテゴリ
エントリーにサブカテゴリー情報を表示させる方法
ここでも、エントリーを表示させる基本的なモジュールエントリー本文(Entry_Body)
を利用します。
この運用の場合、例えば、エントリーのメインのカテゴリーが製品情報
で、執筆者情報のためのサブカテゴリー山田太郎
が適用されると想定します。
エントリーに登録される執筆者情報は、サブカテゴリーになります。
エントリー本文(Entry_Body)
では、sub_category
のブロックでサブカテゴリー情報を表示できますが、メインのカテゴリーと異なり、サブカテゴリーの場合、カスタムフィールドを表示することができません。
そのため、エントリーには、以下の赤枠のように、カテゴリー名に設定した執筆者の名前のみが表示できます。
この運用の場合、執筆者の情報はカテゴリーに以下のようにして登録することとします。
そしてこの場合、エントリー本文(Entry_Body)
では、カスタムフィールドは表示できない、という事になります。
- プロフィール写真 (カスタムフィールド)
- 名前 (デフォルト)
- 肩書 (カスタムフィールド)
- プロフィール文章 (カスタムフィールド)
- 執筆者のウェブサイト・プロフィールページ (カスタムフィールド)
- 執筆者のSNS (カスタムフィールド)
では、カテゴリーのカスタムフィールドをどう表示させるか
これは、ひと工夫が必要になりそうです。
後述する、エントリーにを利用した実装と同じ工夫で実現可能かと思います。
共通メリット : 執筆者の一元管理
執筆者の一元管理はカテゴリーでも実現できます。
カテゴリーの設定では、そのカテゴリーを 下の階層のブログが利用することを許可する ためのグローバル
にする、という選択肢があります。
以下の赤枠の項目です。
これにより、子ブログを作成しているサイトであっても、執筆者情報を一元管理することができます。
サイト全体で管理したいカテゴリーがある場合は、この機能が便利です。
メリット01 : 執筆者の記事一覧を表示しやすい
カテゴリーを使うメリットは、エントリーサマリー : Entry_Summary
といった、記事の一覧を表示するモジュールを使って、執筆者別の記事を一覧表示しやすい事です。
メリット02 : 簡易的なプロフィールページが作れる
執筆者情報をカテゴリーで管理すると、一覧ページを簡単なプロフィールページにできます。
記事に適用する執筆者情報はサブカテゴリーとして登録するため、エントリー本文(Entry_Body)
で、サブカテゴリーのカスタムフィールドを表示することはできないのですが、カテゴリーそのものに優劣はありません。
そのため、執筆者カテゴリーのカスタムフィールドの情報は、エントリーサマリー : Entry_Summary
等で表示可能です。
例えば、記事Aがあったとします。
その記事には、以下のカテゴリーが登録されています。
- エントリー : 記事A
- メインカテゴリー : 製品情報
- サブカテゴリー : 執筆者カテゴリーの子カテゴリーの山田太郎
ここで気を付けたいのは、製品情報というカテゴリーも、山田太郎というカテゴリーも、メインやサブという優劣はなく、カテゴリーであるという事です。
サブカテゴリー機能とは、あくまで、1つのエントリーに対して、メインとサブを登録する機能です。
よって、エントリーサマリー : Entry_Summary
で、カテゴリー別のエントリー一覧を表示する場合は、どのカテゴリーでも、カスタムフィールドが表示できます。
例えば、執筆者カテゴリーの子カテゴリーの山田太郎のエントリー一覧を表示する場合は、以下のようなURLが考えられます。
https://domain/author/yamadataro/
エントリー一覧を表示するためにエントリーサマリー : Entry_Summary
モジュールを使えば、categoryField
ブロックでカスタムフィールドを表示できるので、このURLでアクセスした際に、執筆者のカテゴリーに用意した以下のようなカスタムフィールドも表示できます。
- プロフィール写真 (カスタムフィールド)
- 名前 (デフォルト *これはデフォルで用意されている名でもOKですし、カスタムフィールドで別途用意してもOK)
- 肩書 (カスタムフィールド)
- プロフィール文章 (カスタムフィールド)
- 執筆者のウェブサイト・プロフィールページ (カスタムフィールド)
- 執筆者のSNS (カスタムフィールド)
つまり、記事一覧ページに執筆者のプロフィールを表示できます。
デメリット : エントリーにカスタムフィールドを表示させるには工夫が必要
前述の通り、エントリー本文(Entry_Body)
では、サブカテゴリーのカスタムフィールドを表示することができないので、エントリーに執筆者の詳しい情報を表示するには工夫が必要です。
これは後述します。
エントリーを利用する
概要
この方法では、執筆者登録用のブログを作成し、執筆者情報をエントリーとして管理します。
2022-08-03加筆修正
理由は後述しますが、当初の記事の方法ではなく「執筆者ブログのエントリーID」を「執筆者情報を表示させる記事のカスタムフィールドとして渡し、表側では執筆者ブログのエントリーを検索して情報を表示させる」という方法に修正しました。
また、この方法では独自グローバル変数を利用します。
ブログの構造
以下のような構造を想定します。
- ルートブログ
- カテゴリー
- 執筆者情報のためのブログ :
執筆者ブログ
とします- 執筆者情報のためのエントリー
- 様々な子ブログ
- カテゴリー
エントリーに執筆者エントリーの情報を表示させる方法
この場合は、カスタムフィールドの活用が必須になります。
まず、執筆者ブログ
のエントリーに、以下のカスタムフィールドを用意します。
名前だけは、カスタムフィールドではなく、エントリーの名前とします。
- プロフィール写真
- 名前 (エントリーの名前にします)
- 肩書
- プロフィール文章
- 執筆者のウェブサイト・プロフィールページ
- 執筆者のSNS
エントリーのカスタムフィールドは、以下に記述するか、条件分岐で振り分けします。
\{your-theme}\admin\entry\field.html
URLコンテキストで条件分岐させる場合は、field.html
に以下の記述をします。
@include("{your-theme}/admin/entry/bcd/%{BCD}.html")
執筆者のブログコードをauthor
とした場合、以下のディレクトリにファイルを置いて、カスタムフィールドの記述をすることで条件分岐できます。
\{your-theme}\admin\entry\bcd\authors.html
記事の表示には、ここでもエントリー本文(Entry_Body)
を利用します。
執筆者ブログのエントリーに登録したカスタムフィールドは、他の記事のエントリーでは、そのままでは表示できません。
この2つを結びつける必要があります。
- 記事としてのエントリー (以降、
記事
とします) - 執筆者情報を登録する
執筆者ブログ
のエントリー (以降、執筆者ブログのエントリー
とします)
これを実現する方法のひとつとして、以下の方法を利用できます。
記事のエントリーに、執筆者情報を表示するためのカスタムフィールドを用意して、その値に執筆者ブログのエントリー
のカスタムフィールドの値を選択させる- 記事のエントリーに、執筆者情報を表示するためのカスタムフィールド
author_eid
を用意して、その値に執筆者ブログのエントリーID(EID)
を受け渡す
この方法を利用した場合、記事のエントリーの編集画面は以下のようになります。
これを実現するためには、執筆者ブログのエントリータイトルを、通常の記事のエントリーの編集画面で、Entry_List
などのモジュールで、一覧表示して選べるようにします。
そして、そこで選択されたEIDを、記事のエントリーに用意したカスタムフィールド author_eid
に格納します。
そして、表示側では逆に、author_eid
を使って、執筆者ブログのエントリーを検索して、該当のEIDのカスタムフィールド(肩書やプロフィール文章等)を表示させます。
実は、当初は、執筆者ブログのエントリーに用意した名前やプロフィール文章等のカスタムフィールドと同じ数のカスタムフィールドを、情報を表示したい記事にも用意し、それぞれ情報を受け渡していました。
例えば、 author_eid
に渡されるEIDをフックに、その他のカスタムフィールドをJavaScriptで自動的にセットするというようなことをしていました。
これのデメリットは、コード量が増えるだけではなく、執筆者ブログのエントリーに変更があった場合、その情報が、執筆者情報を表示させたい記事には自動的に反映されないという事でした。
それぞれ異なるカスタムフィールドなので当然ですね…。
では改良版のコードを紹介いたします。
実際のコード
まずは通常の記事に用意したカスタムフィールド author_eid
に、執筆者ブログのエントリーのEIDをセットするコードです。
今回は以下のファイルを作成して記述します。
\{your-theme}\admin\entry\field-author.html
中身は次のように、Entry_List
と外部コンテクストのctx
を利用して、執筆者ブログに限定してエントリー一覧を表示させます。
この例では、執筆者ブログのBIDは3です。
<table class="adminTable acms-admin-table-admin-edit">
<tr>
<th>執筆者ID(執筆者ブログのEID)
<i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="IDに対応する名前はセレクトボックスでご確認ください。"></i>
</th>
<td>
<select name="author_eid" class="acms-admin-form-width-full">
<!-- 執筆者が選択されていなければ任意のEIDを指定 -->
<!-- BEGIN_IF [{author_eid}/em] -->
<option value="1" {author_eid:selected#}1>1</option>
<!-- ELSE -->
<!-- 指定されていればその変数を指定 -->
<option value="{author_eid}" {author_eid:selected#}{author_eid}>{author_eid}</option>
<!-- END_IF -->
<!-- 執筆者一覧(執筆者ブログのエントリー)のセレクトボックスを作成 -->
<!-- BEGIN_MODULE Entry_List id="author_blog_entry_list" ctx="bid/3" -->
<!-- BEGIN entry:loop -->
<option value="{eid}" {author_eid:selected#}{eid}>{eid}({title})</option>
<!-- END entry:loop -->
<!-- END_MODULE Entry_List -->
</select>
<input type="hidden" name="field[]" value="author_eid" />
</td>
</tr>
<tr>
<th>登録されている執筆者一覧</th>
<td>
<!-- BEGIN_MODULE Entry_List id="author_blog_entry_list" ctx="bid/3" -->
<!-- BEGIN entry:loop -->
ID : {eid}({title})<br>
<!-- END entry:loop -->
<!-- END_MODULE Entry_List -->
</td>
</tr>
</table>
次に、表示側のコードです。
ここでもEntry_List
と外部コンテクストctx
を利用します。
ポイントは、「ctx="bid/3/eid/%{AUTHOR_EID}"
」とし、執筆者ブログのBIDと表示させたい執筆者のEIDを指定している事です。
執筆者ブログのEIDと通常の記事のカスタムフィールド author_eid
は同じ値が受け渡されているので、このように指定する事で該当の執筆者のエントリーを取得できます。
ただし…、「ctx="bid/3/eid/{author_eid}"
」では正しく動きません。
何故なら、執筆者ブログ(bid/3)のエントリーには、author_eid
のカスタムフィールドは存在しないためです(author_eid
は通常の記事に用意したカスタムフィールド)。
そのため、author_eid
をグローバル変数にし、どこからでも呼び出せるようにする必要があります。
まずは表示側のコードを先に記載します。
title
以外のprofile_url
などが、執筆者ブログのエントリーに用意されたカスタムフィールドです。
<!-- エントリーに登録された執筆者用カスタムフィールド「執筆者ID」(執筆者ブログのEID)から情報を取得 -->
<!-- BEGIN_MODULE Entry_List id="author_blog_entry_list" ctx="bid/3/eid/%{AUTHOR_EID}" -->
<!-- BEGIN entry:loop -->
<div>
<div>
<div>
<p>執筆者</p>
<p><!-- BEGIN_IF [{profile_url}/nem] --><a href="{profile_url}">{title}</a><!-- ELSE -->{title}<!-- END_IF --><!-- BEGIN name_kana:veil --> - {name_kana}<!-- END name_kana:veil --></p>
</div>
<div>
<p><!-- BEGIN org_main_name:veil -->{org_main_name}<!-- END org_main_name:veil --><!-- BEGIN org_main_role:veil --> {org_main_role}<!-- END org_main_role:veil --></p>
</div>
</div>
</div>
<!-- END entry:loop -->
<!-- END_MODULE Entry_List -->
次に、author_eid
をグローバル変数にする手順ですが、公式のドキュメントを参考にします。
グローバル変数の作り方 | 合宿 | ブログ | a-blog cms developer
まず、config.server.phpで以下のようにしてフックを有効にします。
define('HOOK_ENABLE', 1);
次に以下のファイルを編集します。
\extension\acms\Hook.php
ドキュメントでは/php/ACMS/User/Hook.php
と記載されていましたが、おそらく現在は上記のファイルが該当するのかと思います。
編集箇所はextendsGlobalVars
の箇所です。
次のように記述します。
/**
* グローバル変数の拡張
*
* @param array $globalVars
*/
public function extendsGlobalVars(&$globalVars)
{
// エントリーのカスタムフィールドの執筆者IDをグローバル変数化
if ( EID ) {
$Field = loadEntryField(EID);
$globalVars->setField('AUTHOR_EID', $Field->get('author_eid'));
}
}
これで完了です。
共通メリット: 執筆者の一元管理
執筆者情報を専用のブログで一元管理できます。
メリット01 : 執筆者のプロフィールページを作りやすい
この方法のメリットは、執筆者のプロフィールページを作りやすい点にあります。
執筆者のプロフィールページが、エントリーであるため、エントリーに用意しているユニットが利用できるためです。
詳細なプロフィールページを作る場合、エントリーを利用した方が、表現の幅が広がるため、この実装方法が有効かと思います。
デメリット : 執筆者の情報を表示するのに工夫が必要
上述の通り、記事のエントリーにカスタムフィールドを表示するには、工夫が必要です。
執筆者の記事一覧を表示させるには
この方法の場合、カテゴリーと異なり、ポストインクルードやカスタムフィールドの検索などで、エントリーを絞り込み表示させる必要があります。
コンビネーション (2022-01-08 追記)
Twitterでジェノベーゼ寺崎さんが、合わせ技を教えてくださいました。
a-blog cms awards 2021を受賞された清水さんの記事!
— ジェノベーゼ寺崎 (@genovese115) December 9, 2021
僕が実装するならあわせ技で
①執筆者情報はユーザで管理
②ユーザはライセンス制限のない「読者」権限で発行
③エントリーにユーザIDをカスタムフィールドで保存
④User_Fieldモジュール(ctxで③の値を渡す)で情報を表示
のようにするかな〜 https://t.co/8BYi19dMF1
読者権限
なら、ユーザー数制限がないため、とても効率的な方法だなと、目から鱗でした。
有難うございました!
(もしかしたらこの方法でも、グローバル変数の利用が必要になるかもしれません)
構造化データのマークアップ
さて、今回紹介した実装方法では、構造化データのマークアップが可能です。
a-blog cmsでは、配布されているテーマで、構造化データのマークアップ用のテンプレートファイルが提供されており、パンくずの構造化データに対応しています。
そのため、こちらをカスタマイズすることで、新たにデータを追加できます。
構造化データ用のテンプレートファイルは、以下に用意されています。
\{your-theme}\include\head\structured-data.html
Googleでは、検索結果の表示を拡張するためのリッチリザルトという仕組みを用意していますが、一般的な記事ページは、Article
の構造化データのマークアップが利用できます。
リッチリザルトや、それをさらに強化したエンリッチリザルトの情報は、以下の記事をご参照ください。
以下は、Article
タイプの構造化データと、パンくずの構造化データを組み合わせる例です。
{}
の中に、エントリーやユーザー、カテゴリーの情報を出力します。
[
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "{article-headline}",
"description": "{article-description}",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{article-url}"
},
"image": {
"@type": "ImageObject",
"url": "{article-image-path}",
"width": {article-image-width},
"height": {article-image-height}
},
"datePublished": "{article-published-date}",
"dateModified": "{article-updated-date}",
"author": {
"@type": "Person",
"name": "{article-author-name}",
"image": "{article-author-image-path}",
"url": "{article-author-url}",
"sameAs": [
"{article-author-sns-url-01}",
"{article-author-sns-url-02}"
],
"alternateName": "{article-author-other-name}",
"worksFor": {
"@type": "Corporation",
"name": [
"{article-author-organization-name-01}",
"{article-author-organization-name-02}"
],
"url": "{article-author-organization-url}",
"url": "{article-author-organization-other-url}"
}
}
},
{
"@context": "http://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
// デフォルトのテンプレートファイルの記述
]
}
]
構造化データのマークアップは、Article
の他にも、動画やレシピ、求人など、Googleが色々と採用していますが、a-blog cmsのカスタムフィールドと条件分岐を活用すると、様々なタイプに適用できるのではないかと思います。
余談
執筆者情報をポストインクルードで表示させる方法も試したのですが、この場合、執筆者情報を構造化データとして出力することができそうになかったため、検討から省きました。