カスタム投稿タイプの投稿ページに、ACFで紐つけたユーザーの投稿を表示する
この記事は2019年11月11日に書かれたものです。情報が古い可能性がありますのでご注意ください。
なんだかよくわからないタイトルですが…
複数の会員がお知らせを投稿するサイトで、会員情報のページをカスタム投稿タイプで作成して、Advanced Custom Fields (ACF) プラグインを使ってこのカスタム投稿タイプの投稿(会員ページ)をユーザーに紐つけ、お知らせの一覧やシングルページにはカスタム投稿タイプで作成した会員ページのタイトル(会員名)とパーマリンク を、会員ページにはその会員を紐つけたユーザーが投稿したお知らせの一覧を取得して表示するということです。
ユーザープロフィールに ACF で各種フィールドを追加して、ユーザーアーカイブページで会員情報を表示するのが標準的で簡単な手法かと思いますが、今回ユーザー自身に標準のプロフィール項目以外を編集させたくないということで、カスタム投稿タイプで会員ページを作成することにしました。
また、多くのカスタムフィールドを追加するので、プロフィール画面より投稿画面の方が見やすいというのもありますし、標準のユーザーアーカイブを使わないので、URL からユーザー名がわかってしまうというセキュリティ上の問題も回避できます。
まず、会員情報を表示するページをカスタム投稿タイプで作ります。カスタム投稿名を「会員情報」、スラッグを「member」とし、タイトルに会員名を入力するようにし、ACF を使って会員情報を登録するための多くのカスタムフィールドを設定しています。
次に、このカスタム投稿タイプの投稿(会員ページ)とユーザープロフィールを紐つけるため、ユーザープロフィールに一つだけ、カスタム投稿タイプの投稿を指定するカスタムフィールドを追加します。逆(カスタム投稿タイプにユーザーを指定するフィールドを追加)でもいいのですが、今回は必ずしも全ての会員がユーザー権限を持つわけではないので、ユーザー側で指定するようにしました。
このカスタムフィールドのフィールド名を「user_member」とし、フィールドタイプを「投稿オブジェクト」、投稿タイプを「会員情報」で絞り込み、返り値のフォーマットは「投稿ID」とします。「投稿オブジェクト」を使うことにより、ユーザープロフィール画面では会員情報の一覧から会員名(タイトル)を選択するので、手入力による打ち間違いも防止できます。
これで準備完了。続いてテーマの編集です。
まず、お知らせの投稿一覧やシングルページに、ユーザーに紐つけたカスタム投稿タイプ(会員情報)の投稿IDから、タイトル(会員名)と会員ページへのパーマリンクを表示します。
通常、ACF のカスタムフィールドを取得する場合は、get_field('custom-field-name')
で取得できますが、ユーザープロフィールやタクソノミーに追加したカスタムフィールドの場合はこれでは取得できません。
ユーザーの場合は get_field('custom-field-name', 'user_(ユーザーID)')
、
タクソノミーの場合は get_field('custom-field-name', '(タクソノミースラッグ)_(タームID)')
のように記述する必要があります。今回はユーザーのカスタムフィールドなので、投稿者のユーザーIDが必要です。
single.php
<?php
$author_id = get_the_author_meta('ID');
$member_id = get_field('user_member', 'user_'. $author_id);
echo '<p class="memberPage"><a href="'. get_the_permalink($member_id). '">'. get_the_title($member_id). 'の紹介ページへ</a></p>'. "\n";
?>
次に、カスタム投稿タイプ(会員情報)で作成した会員ページに、その投稿IDを紐つけたユーザーによる投稿の一覧を表示します。
カスタム投稿タイプにユーザーを指定するフィールドを追加した場合は簡単にユーザーを特定できますが、今回は逆なので随分悩みました。
get_users()
でユーザーの一覧を取得し、その中から該当するユーザーを特定しています。
single-member.php
<?php
$post_id = get_the_ID(); // カスタム投稿の投稿ID…(1)
$author = 1; // 投稿者ユーザーが存在しない場合、暫定的に管理者のIDを設定
$users = get_users('role=author'); // 投稿者ユーザーの一覧を取得
foreach ( $users as $user ){
$member_id = get_field('user_member', 'user_'. $user->ID); // カスタムフィールドで取得した投稿ID…(2)
// (1)と(2)の投稿IDが一致したユーザーのIDを取得
if ( $member_id === $post_id ){
$author = $user->ID;
}
}
// ユーザーの投稿一覧を表示
$args = array(
'post_type' => 'post',
'author' => $author,
'post_status' => 'publish',
'posts_per_page' => -1
);
$my_query = new WP_Query( $args );
if ( $my_query -> have_posts() ):
?>
<section class="fromMember">
<h2><?php the_title(); ?>からのお知らせ</h2>
<ul>
<?php
while ( $my_query -> have_posts() ): $my_query -> the_post();
?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php
endwhile;
?>
</ul>
</section>
<?php
endif;
?>
全ての投稿者ユーザーの一覧を取得して、その中から条件に一致するユーザーを探し出しているので、ユーザー数が多い場合はパフォーマンスが落ちるかも。
その場合、運用が二度手間になりますが、カスタム投稿タイプにもユーザーを指定するフィールドを追加し、相互に紐つけるようにした方がいいかもしれません。
(2020年8月18日追記)
カスタム投稿タイプ(会員情報)の投稿IDと紐つけたユーザーが存在しない場合に、全てのユーザーの記事が表示されるのを回避するため、single-member.php のコードに、投稿者ユーザーが存在しない場合に暫定的に管理者のユーザーIDを設定する記述を追記しました。
“カスタム投稿タイプの投稿ページに、ACFで紐つけたユーザーの投稿を表示する”への 9 件のコメント
ken8さんより:
WEB制作で悩んでいる中、当該記事を拝見させていただき大変参考になりました。
一点ご質問なのですが、当該構成で組まれた場合投稿ユーザーについては
会員情報(カスタム投稿)ページを新規登録することができてしまい、一覧ページに勝手に自分以外の会員を掲載できてしまうかと思うのですが、その点はどのようにして回避されているのでしょうか?
基本的に投稿者に該当する会員情報ページのみの編集だけできるような状態を作れればベストなのですが
WPの特徴上、更新と新規登録が同じフェーズになっていることから非常に悩んでいます。
何かアドバイスをいただければ幸いです。
石輪さんより:
コメントありがとうございます。
こちらの記事で紹介したサイトの場合は、投稿者ユーザーに会員情報ページを編集させない仕様でしたので、User Role Editor プラグインを使って投稿者の権限を制限しています。
ただ、お知らせ(投稿)について、管理画面の投稿一覧でログインしている投稿者自身の投稿のみを表示し、他のユーザーの投稿は編集だけでなく表示もされないようにカスタマイズしていますので、同様の方法で、お問い合わせの「投稿者に該当する会員情報ページのみの編集だけできるように」ということも可能かと思います。
私の場合、こちらを参考にさせていただきました。
「WordPressの管理画面でログインユーザー以外のコンテンツを非表示にする – Qiita」
https://qiita.com/miya0001/items/7ecfff491efa4bceff2d
なお、編集画面に Advanced Custom Fields プラグインで作成したカスタムフィールドがあると、このフィールドも表示されなくなってしまいますので、この対策についてはこちらの記事で書いています。
「投稿一覧からログイン中の投稿者以外の投稿を隠すとカスタムフィールドが表示されないことの対策」
https://www.will3in.co.jp/frontend-blog/article/custom-fields-not-displayed-when-hiding-others-posts/
ken8さんより:
大変分かりやすいアドバイスありがとうございます。
カスタムフィールドが消えてしまう対策についても大変勉強になりました。
現在、当該記事と同じような仕組みでカスタム投稿内の投稿ごとに記者クラブページを作成し
そのページ内にその記者クラブに所属するメンバーの記事が表示されるような仕組みを作っています。
記者クラブ内のメンバーならそのクラブ内の記事変更ができ、当該カスタム投稿内のカスタムフィール情報も修正できるような仕様にするため奮闘しています。
同クラブ内のメンバー記者の記事を修正できるように権限を「他ユーザー記事の編集ができる」に設定すると、他クラブの記者の記事も修正できてしまい、「自分の記事だけの編集」にしてしまうと、同クラブ内のメンバーの記事が修正できなくなってしまう状態です。
ユーザー権限を「他メンバーの記事を編集できる」にし記者クラブページ(カスタム投稿)内にカスタムフィールを設け、同クラブメンバーの複数IDをカンマ区切りで保存し、$query->set('author', 同クラブメンバーの複数ID);で表示できるかと思っております。
ただ上記の場合だと確かに一覧表示には、同クラブメンバーの記事一覧しか表示されなくなるのですが、URLを修正すると他クラブの記事が修正できてしまうようになってしまいます。
↑ここに対して何か方法はないでしょうか?
案としては、同クラブメンバー以外に記事にアクセスがあると、編集画面が表示される時にその記事の投稿者IDがクラブメンバーID群の中に無い場合、不正アクセスとして他ページにリダイレクトされるような方法ができないかと思っています。
案としては、
ただ権限とし
石輪さんより:
そのような仕様は構築した経験がありませんので思い当たりませんが、以下の記事が参考になるのではないかと思います。
[WordPress] 別のユーザーグループが投稿した記事を編集出来ないようにする – KayaMemo
http://kayakuguri.github.io/blog/2015/11/12/wordpress-usergroup-edit/
また、Groups というプラグインが使えるのかもしれません。
Groups:ユーザーをグループ別で管理できる | WordPress活用術
https://www.hiskip.com/wp/plugin/site-manage/user-manage/16552.html
ビギナーさんより:
記事を拝見し参考にさせていただきました。
ひとつ挙動がおかしな箇所が発生してしまい質問なのですが、
店舗Aのページ…店舗Aの担当者が投稿した記事が表示される→◎
店舗Bのページ…店舗Bの担当者が投稿した記事が表示される→◎
A、B以外の店舗ページ… 店舗Aと店舗Bの記事が表示されている→×
と、このように、記事投稿がない店舗ページに全ての記事が表示されている状態です。
この点を修正すべく、そのような処理を加えたら良いか分からず質問させていただきました。
ご覧いただいていましたら知恵をお貸しください。
石輪さんより:
コメントありがとうございます。
確かに、紐つけたユーザーの投稿が無い場合はOKですが、ユーザーの紐つけそのものがない場合は全てのユーザーの投稿が表示されてしまいます。
対策として、この記事の single-member.php のコードの3行目に、投稿者ユーザーが存在しない場合に暫定的に管理者のユーザーIDを設定する記述を追記しましたので、参考にしてみてください。
ただし、管理者のユーザーIDが 1 で、管理者による投稿が無いことが前提です。管理者の投稿がある場合は、999 など存在しないユーザーIDを設定してもよいかと思います。
ビギナーさんより:
返信ありがとうございます!
なるほどです。教えていただいた方法で、求めていた状態で表示することができました。ありがとうございます。
付随する内容でお伺いしたいことがあります。
こちらの記事の方法で、複数店舗が投稿した記事一覧をトップページに数件表示させようと思っています。
その際、店舗ページ(カスタム投稿ページ)でそれぞれが所属するジャンル(タームスラッグ名)をclass名として取得して色分けをしようと考えました。
その際、スラッグ名はどのようにして取得するのが良いと思われますか?
「ユーザープロフィールページで紐づいているカスタム投稿ページのタイトル名を取得→そのページのジャンルタームを取得する」というような流れでスラッグを取得できないかと考えたのですが、具体的にどのようにコード化したら良いのかわかりませんでした。
また、他にもっと良い案があればお教え願います!!
石輪さんより:
カスタム投稿タイプの投稿IDからタームスラッグを取得できると思います。
カスタム投稿タイプの投稿IDの取得は、この記事の single.php と同じです。
カスタムタクソノミースラッグを 'genre' とします。
$author_id = get_the_author_meta('ID');
$member_id = get_field('user_member', 'user_'. $author_id);
$terms = get_the_terms($member_id, 'genre');
foreach( $terms as $term ) {
echo '<span class="'. $term->slug. '">'. $term->name. '</span>';
}
ビギナーさんより:
返信いただきありがとうございます。
教えていただいた内容でスラッグを取得できました。
やりたいことが全て実現でき、有意義な情報に感謝です。
これからも拝見させていただきます!