何が言いたいのか分からねーと思うが。要は、固定ページが何千ページもあって階層がたくさんある時に、一覧からページを探すのがしんどいので、親ページを指定してその子ページだけを表示させる、というものを実現したい。
やりたいこと
絞り込み検索に、こんなセレクトボックス↓を追加して、選択した親ページの子ページのみを表示させたい。
セレクトボックスには、「子を持つページ」のみが表示される。自分自身が子ページであったとしても、さらに自分が子ページを持っているならば表示される。
「テスト親」という名前のページを選択してみた。このように、選択したページの子ページに絞られて表示される。素晴らしい!←
ちなみに、孫ページは表示されない。あくまで子ページ(直近の子孫)に絞り込まれる。そうじゃないとあんま意味ない。
完成コード
以下を functions.php
にコピペするだけでとりあえず使える。
<?php
class WP_Filter_To_Children {
private $post_type; //管理画面の一覧の投稿タイプ
private $key = 'child_of'; // $_GETに指定する、親ページのIDのキー
public function __construct() {
$this->set_post_type();
add_action('restrict_manage_posts', array($this, 'show_selectbox'));
add_filter('posts_where', array($this, 'posts_where'));
}
// 投稿タイプを取得
private function set_post_type() {
if(isset($_GET) && isset($_GET['post_type'])) {
$this->post_type = $_GET['post_type'];
}
}
// 子を持つページオブジェクトのみを返す
private function get_parents() {
// 全件取得
$args = array(
'numberposts' => -1,
'post_type' => $this->post_type,
'sort_order' => 'ASC',
'sort_column' => 'menu_order'
);
$all_posts = get_pages($args);
if($all_posts) {
// 親(子どもがいる人)リスト
$parents = [];
// 子どもがいるなら親リストに追加
foreach($all_posts as $post) {
if($this->has_children($post->ID)) {
$parents[] = $post;
}
}
return $parents;
}
}
// 子どもがいれば$idを返す
private function has_children($id) {
$args = array(
'post_type' => $this->post_type,
'child_of' => $id
);
$posts = get_pages($args);
return $posts ? $id : false;
}
// 親ページのセレクトボックス
public function show_selectbox() {
$posts = $this->get_parents();
if($posts) {
$html = '<select name="child_of" id="parent" class="postform" title="選択したページの子ページ一覧を表示します">';
$html .= '<option value="" disabled>子ページ一覧を表示</option>';
$html .= '<option value="">すべて表示</option>';
$html .= sprintf('<option value="0"%s>一番上の階層のみ</option>', selected($_GET[$this->key], 0, false));
foreach($posts as $post) {
$depth = count(get_post_ancestors($post->ID));
$prefix = sprintf('%s ', str_repeat('−', $depth));
$checked = selected($_GET[$this->key], $post->ID, false);
$html .= sprintf('<option value="%d"%s>%s%s</option>', $post->ID, $checked, $prefix, $post->post_title);
}
$html .= '</select>';
echo $html;
}
}
// 問い合わせクエリに親ページIDを追加
public function posts_where($where) {
if(is_admin()) {
global $wpdb;
if(isset($_GET) && isset($_GET[$this->key])) {
$value = $_GET[$this->key];
if($value !== '') {
$where .= " AND {$wpdb->posts}.post_parent='{$_GET[$this->key]}'";
}
}
}
return $where;
}
}
$wp_filter_to_children = new WP_Filter_To_Children();
解説
↓を魔改造させていただいた。
上記記事の、親ページIDを静的に記述したり、深い階層の親ページを取得できなかったりするあたりを解消。
魔改造ポイント
- 固定ページと、階層をもつカスタム投稿タイプすべてで使える
- 子ページをもつ全ての親ページを自動的に取得しセレクトボックスに表示
- 階層がめっちゃ深くても取得できる
- 絞り込み検索した時にセレクトボックスが選択状態になるように
- 一番上の階層のみの表示もできるように
コードについての解説
「子をもつすべてのページ」オブジェクトを取得し、その一覧をセレクトボックスの選択肢として、選択された親ページのIDをもとに子ページに絞り込んで表示するというもの。
まず悩ましいのは、has_children()
みたいな子どもがいるかチェックする関数がないこと。なので、力技で自作。
それを使って子どもを持つページのみの一覧が取得できたら、セレクトボックスを作成して、アクションフック restrict_manage_posts
に引っかける。これでGETパラメータに選択したページのIDを送ることができるようになった。
ちなみに、セレクトボックスのページタイトルで階層を表現しているが、これは get_post_ancestors
で先祖の数を取得して、その数だけダッシュ記号を反復表示させている。
あとは、そのGETパラメータから取得したIDをもとに、MySQLの問い合わせ文を変更し絞り込めばOK。
問い合わせ分文の変更は、フィルターフック posts_where
に引っかける。以上。
暇があったらプラグイン化するから使ってね!