【WordPress】特定の投稿を表示するウィジェットを自作する方法

PHP Wordress

こんにちは。あきのりです。
フリーランスプログラマーとしてwebページ・サービスを作っています。

今回はwordpressで特定の投稿を表示するウェジェットを作成する方法について見ていきます。

最初からあるウィジェットだと「最近の投稿」や「人気の投稿」などはあるんですが「特定の投稿」が無いようで、おそらくプラグインを利用しないといけないです。

プラグインを使っても良いのですが、できるなら機能を自作できた方がプラグインによる不具合も起こさずに済むので、今回はそんな投稿表示ウィジェットを作成していきましょう。

ファイルを編集していくので必ず元のファイルをコピーしておいてください。エラーが起きたときに元に戻せるようにしておいてください(ゼッタイ!!)。

下準備:ウィジェット用のファイルを作成しておく

wordpressをカスタマイズするには基本的にfunctions.phpというファイルを編集する必要があります。
が、いろんなものをこのファイルに直接書くと見づらくなってしまうので、別ファイルにコードを書きそのファイルをfunctions.phpで読み込むようにします。

まず、現在使っているテーマフォルダの中にfunctionsというフォルダを作成し、その中に以下の2つのファイルを作ります。

  • widgets.php : ウィジェットを作成するファイル
  • widgets-area.php : ウィジェットエリアを作成するファイル

そしてfunctions.phpでこれらファイルを読み込みましょう。
ファイルの一番上(<phpの直後)に書いてください。


//functions.php
<?php
/**
 * ファイルのインポート
 */
require_once get_template_directory() . '/functions/widgets.php';
require_once get_template_directory() . '/functions/widgets-area.php';

//~ 省略 ~

widgets-area.phpでウィジェットエリアを作成する

次はwidgets-area.phpにコードを書いていきます。
今回はヘッダー画像下にウィジェットエリアを作ります。
以下のように書いてください。


//widgets-area.php
<?php
/**
 * ウェジェットエリア登録
 */

//ヘッダー下
function below_header() {
    register_sidebar(array(
      'id' => 'below_header',
      'name' => 'ヘッダー下',
      'before_widget' => '<div class="recommended-posts">',
      'after_widget' => '</div>'
    ));
}
add_action( 'widgets_init', 'below_header' );

このように書くと、外観 -> ウィジェットで、画像のように「ヘッダー下」という欄が現れます。

プログラム解説(プログラマー向け)

ウィジェットエリアを作成するにはregister_sidebarという関数を使います。
この引数に配列で情報を渡します。

ここで渡した情報は以下の通りです。

  • id:ウィジェットエリアを特定するためのもの
  • name:管理画面に表示させるための名前
  • before_widget:このウィジェットエリアを囲うためのHTML開始タグ
  • after_widget: このウィジェットエリアを囲うためのHTML開始タグ

idやnameは分かりやすいものにしておき、タグに関しては汎用性を考えてdivタグにしています。

他にも設定はできるのですが詳しくは公式ドキュメントを読んでください。

wordpressのフックについて

また、wordpressでは作成した関数をどの時点で呼び出すかを決めることができます。フック関数っていうやつです。
今回の例では最後の部分ですね。

add_action( 'widgets_init', 'below_header' );

add_action('フックのタイミング', '呼び出す関数')という形で書きます。
今回は単純に「ウィジェットが読み込まれるタイミング」で呼び出しています。

widgets.phpで特定の投稿を表示する自作ウィジェットを作成する

次はwidgets.phpにコードを書いて、ウィジェットを作っていきます。
先ほど言った通り、特定の投稿を表示するウィジェットを作ります。


//widgets.php
<?php

//ウィジェットの登録
function theme_register_widget() {
    register_widget('PostWidget');
}
add_action('widgets_init', 'theme_register_widget');

//クラスの作成
class PostWidget extends WP_Widget {
    //初期設定
    public function __construct() {
        $widget_options = array(
            'classname' => 'widget-post',
            'description' => '特定の表示を表示する',
            'customize_selective_refresh' => true
        );

        $control_options = array();

        parent::__construct(
            'widget-post',
            '投稿表示',
            $widget_options.
            $control_options,
        );
    }

    //ウィジェットの管理画面上での設定表示
    public function form($instance) {
        $post_id = !empty($instance['post_ids']) ? esc_attr(implode(',', $instance['post_ids'])) : '1';
        ?>
        <!-- 管理画面での表示 -->
        <p>
            <label for="<?php echo $this->get_field_id('post_ids'); ?>"><?php _e('投稿ID(カンマ区切りで入力):'); ?></label>
            <input 
                type="text" 
                class="widefat" 
                id="<?php echo $this->get_field_id( 'post_ids' ); ?>"
                name="<?php echo $this->get_field_name( 'post_ids' ); ?>"
                value ="<?php echo $post_id; ?>" 
                required
            />
        </p>
        <!-- ここまで -->
        <?php
    }

    //入力フォームの更新
    public function update($new_instance, $old_instance) {
        $instance = $old_instance;

        $instance['post_ids'] = sanitize_text_field($new_instance['post_ids']);
        //数字、カンマのみを受け付ける
        $regex = "/[0-9\, ]+/";
        preg_match($regex, $instance['post_ids'], $matches);
        $matches[0] = str_replace(' ', '', $matches[0]);

        //文字列を配列化
        $post_ids = explode(',', $matches[0]);

        foreach($post_ids as $key => $value) {
            //空白は消す
            if($value == '') {
                unset($post_ids[$key]);
            } else {
                $value = (int)$value;
                //0は許可しない
                if($value == 0) {
                    unset($post_ids[$key]);
                } else {
                    $post_ids[$key] = $value;
                }
            }
        }

        $post_ids = array_values($post_ids);
        $instance['post_ids'] = $post_ids;

        return $instance;
    }

    // ウィジェットのページ上での出力処理
    public function widget($args, $instance) {
        $post_ids = (!empty($instance['post_ids'])) ? $instance['post_ids'] : array(1);
        $args = array(
            'post_type' => 'post',
            'post__in' => $post_ids,
            'ignore_sticky_posts' => true,
        );

        echo $args['before_widget'];
        //投稿データを取得
        $my_query = new WP_Query($args);
        if($my_query->have_posts());
        ?>

        <!-- オススメ記事一覧 -->
        <div class="row">
            <?php if($my_query->have_posts()) : while($my_query->have_posts()) : $my_query->the_post(); ?>
                <div class="col-4 position-relative mb-3">
                    <div class="img-container thumbnail-container">
                        <img src="<?= has_post_thumbnail() ? get_the_post_thumbnail_url('', 'full') : get_template_directory_uri() . '/images/no-image.jpg' ?>" alt="<?php the_title(); ?>">
                    </div>
                    <div>
                        <p><?php the_title() ?></p>
                    </div>
                    <a href="<?php the_permalink() ?>" class="stretched-link"></a>
                </div>
            <?php endwhile; endif; ?>
        </div>
        <!-- ここまで -->

        <?php
    }
}

するとウィジェット欄に「投稿表示」という項目が追加された思います。

これを「ヘッダー下」に追加してデータを入力して保存しましょう。今回は投稿IDをカンマ区切りで入力するように構築しています。

投稿IDはいちいち確認するのが面倒ですが、投稿一覧に表示させることで効率よく設定できます。こちらを確認してください。

ウィジェット情報を表示させる

最後にこのウィジェットをウェブページで表示させます。
今回はヘッダー下なので、そこに当たる場所に表示させるコードを書いていきます。

例として投稿一覧ページ(index.php)に書きます。


//index.php
<?php get_header(); ?>
<div class="container">
        <?php if(is_active_sidebar('below_header')) : ?>
            <?php dynamic_sidebar('below_header'); ?>
        <?php endif; ?>
    </div>
<?php get_footer(); ?>

するとIDで設定した投稿が表示されます。

今回のコードだと3投稿が一番見やすいです。これをスライダー形式で表示させればより多くの投稿を表示させることができ、「オススメ記事」が完成します。

コード解説(プログラマー向け)

ウィジェットの登録

widgets.phpの最初でウィジェットの登録を行っています。


//ウィジェットの登録
function theme_register_widget() {
    register_widget('PostWidget');
}
add_action('widgets_init', 'theme_register_widget');

これはウィジェットエリアを作ったときとほぼ同じです。

ウィジェットクラスの作成

ウィジェットを作成するにはウィジェット用のクラスを作る必要があります。

クラスの中身は決まっていて大枠として以下のようになっています。


//クラスの作成
class クラス名 extends WP_Widget {
    //初期設定
    public function __construct() {
        parent::__construct(
            'id',
            'name',
        );
    }

    //ウィジェットの管理画面上での設定表示
    public function form($instance) {

    }

    //入力フォームの更新
    public function update($new_instance, $old_instance) {

    }

    // ウィジェットのページ上での出力処理
    public function widget($args, $instance) {

    }
}

それぞれのメソッド(関数)にコードを書いて自作をしていきます。

ウィジェットの初期設定

まず、初期設定です。__construct()の部分です。

ここでは、管理画面でウィジェット項目を表示させるための処理を行います。
さっきの例を見てみるとこんな感じ。


    public function __construct() {
        $widget_options = array(
            'classname' => 'widget-post',
            'description' => '特定の表示を表示する',
            'customize_selective_refresh' => true
        );

        $control_options = array();

        parent::__construct(
            'widget-post',
            '投稿表示',
            $widget_options,
            $control_options,
        );
    }

いろいろ書いてありますが、最後のparent::__constructの部分で初期設定を行っています。
ここで設定するものは次の通り

  • ID : ウィジェットを区別するためのID
  • name : ウィジェットの名前
  • widget_options : ウィジェットの追加情報(説明文など)
  • control_options : ダッシュボード上で展開した際の幅や高さを指定

上2つは文字列で、下2つは配列で定義します。

ウェジェットのフォーム作成

次は、ウェジェットに情報を入力する部分、formについてです。
ここはシンプルにフォームを作成するだけでOKです。

今回だとこんな感じ。


    //ウィジェットの管理画面上での設定表示
    public function form($instance) {
        $post_id = !empty($instance['post_ids']) ? esc_attr(implode(',', $instance['post_ids'])) : '1';
        ?>
        <!-- 管理画面での表示 -->
        <p>
            <label for="<?php echo $this->get_field_id('post_ids'); ?>"><?php _e('投稿ID(カンマ区切りで入力):'); ?></label>
            <input 
                type="text" 
                class="widefat" 
                id="<?php echo $this->get_field_id( 'post_ids' ); ?>"
                name="<?php echo $this->get_field_name( 'post_ids' ); ?>"
                value ="<?php echo $post_id; ?>" 
                required
            />
        </p>
        <!-- ここまで -->
        <?php

一番最初の部分


$post_id = !empty($instance['post_ids']) ? esc_attr(implode(',', $instance['post_ids'])) : '1';

では、初期値をを設定しています。
以降のpタグから始まる部分は、おなじみのフォームです。

入力データの更新

次は、このフォームで入力されたデータを更新する部分、updateのについてです。
これもフォームを作成した人なら分かるかと思います。

データを取得して保存するだけです(違いは、$_POSTではなく$new_instance変数を使います)。


    //入力フォームの更新
    public function update($new_instance, $old_instance) {
        $instance = $old_instance;

        $instance['post_ids'] = sanitize_text_field($new_instance['post_ids']);
        //数字、カンマのみを受け付ける
        $regex = "/[0-9\, ]+/";
        preg_match($regex, $instance['post_ids'], $matches);
        $matches[0] = str_replace(' ', '', $matches[0]);

        //文字列を配列化
        $post_ids = explode(',', $matches[0]);

        foreach($post_ids as $key => $value) {
            //空白は消す
            if($value == '') {
                unset($post_ids[$key]);
            } else {
                $value = (int)$value;
                //0は許可しない
                if($value == 0) {
                    unset($post_ids[$key]);
                } else {
                    $post_ids[$key] = $value;
                }
            }
        }

        $post_ids = array_values($post_ids);
        $instance['post_ids'] = $post_ids;

        return $instance;
    }

今回は数字のカンマ区切りで入力してほしいので、正規表現を使ってデータを確認しています。
また、空白も取り除いています。


        //数字、カンマのみを受け付ける
        $regex = "/[0-9\, ]+/";
        preg_match($regex, $instance['post_ids'], $matches);
        //空白を除く
        $matches[0] = str_replace(' ', '', $matches[0]);

これをカンマを切るポイントにして配列に変換しています。


//文字列を配列化
$post_ids = explode(',', $matches[0]);

ただ、カンマ(,)が続いたときに空白の要素が含まれてしまうので、それを消去します。
また、IDが0の投稿は無いので、0があったらそれも抜き取ります。


        foreach($post_ids as $key => $value) {
            //空白は消す
            if($value == '') {
                unset($post_ids[$key]);
            } else {
                $value = (int)$value;
                //0は許可しない
                if($value == 0) {
                    unset($post_ids[$key]);
                } else {
                    $post_ids[$key] = $value;
                }
            }
        }

webページに実際に表示させる

最後は、実際に表示させる部分、widgetについてです。

ここでは、入力されたデータを受けて、(必要な処理があればして)表示させればOKです。


  // ウィジェットのページ上での出力処理
    public function widget($args, $instance) {
        $post_ids = (!empty($instance['post_ids'])) ? $instance['post_ids'] : array(1);
        $args = array(
            'post_type' => 'post',
            'post__in' => $post_ids,
            'ignore_sticky_posts' => true,
        );

        echo $args['before_widget'];
        //投稿データを取得
        $my_query = new WP_Query($args);
        if($my_query->have_posts());
        ?>

        <!-- オススメ記事一覧 -->
        <div class="row">
            <?php if($my_query->have_posts()) : while($my_query->have_posts()) : $my_query->the_post(); ?>
                <div class="col-4 position-relative mb-3">
                    <div class="img-container thumbnail-container">
                        <img src="<?= has_post_thumbnail() ? get_the_post_thumbnail_url('', 'full') : get_template_directory_uri() . '/images/no-image.jpg' ?>" alt="<?php the_title(); ?>">
                    </div>
                    <div>
                        <p><?php the_title() ?></p>
                    </div>
                    <a href="<?php the_permalink() ?>" class="stretched-link"></a>
                </div>
            <?php endwhile; endif; ?>
        </div>
        <!-- ここまで -->

        <?php
    }

今回は、配列にした投稿IDを元に、投稿データを取得しています。


        $query_args = array(
            'post_type' => 'post',
            'post__in' => $post_ids,
            'ignore_sticky_posts' => true,
        );
        //投稿データを取得
        $my_query = new WP_Query($query_args);

投稿データを取得するのにWP_Queryというもの使っていて、条件を引数として設定しています。
今回の条件については以下の通り

  • post_type : 投稿タイプ
  • post__in:投稿ID
  • ignore_sticky_posts:「トップに固定」の記事を無効

取得した投稿データはループ処理で表示させています。これは投稿一覧ページなどで使っているものと同じです。


        <div class="row">
            <?php if($my_query->have_posts()) : while($my_query->have_posts()) : $my_query->the_post(); ?>
                <div class="col-4 position-relative mb-3">
                    <div class="img-container thumbnail-container">
                        <img src="<?= has_post_thumbnail() ? get_the_post_thumbnail_url('', 'full') : get_template_directory_uri() . '/images/no-image.jpg' ?>" alt="<?php the_title(); ?>">
                    </div>
                    <div>
                        <p><?php the_title() ?></p>
                    </div>
                    <a href="<?php the_permalink() ?>" class="stretched-link"></a>
                </div>
            <?php endwhile; endif; ?>
        </div>

最後に表示させたい場所に以下のコードを書きました。


        <?php if(is_active_sidebar('below_header')) : ?>
            <?php dynamic_sidebar('below_header'); ?>
        <?php endif; ?>

まとめ

というわけで以上です。
これで特定の投稿を表示させることができました。

また、新しくウィジェットを自作することもできると思うので挑戦してみてください。

それでは、良い1日を。

スポンサードサーチ

オススメ英語学習用SNS "Our Dictionary"

人気記事英語学習用SNSをLaravelで作ってみた【システム解説あり】