wordpressのウィジェット「最近の投稿」にサムネイルを表示させる

前回の続きで、今回はもう少しまともに使えるウィジェットを作ってみます。

wordpressで最初から用意されているウィジェットの一つに「最近の投稿」があります。

f:id:fullstack-baby:20190920160934p:plain

ウィジェットのタイトルと、何件表示するか、投稿日を表示するか、だけを設定できるシンプルな作りです。

フロント画面で表示させるとこのようになります。

f:id:fullstack-baby:20190920171454p:plain
最近の投稿のウィジェットの表示

サムネイルとかを表示させてもうちょいリッチにしたいですよね。通常wordpressのコアをカスタマイズするにはフックを探すのが王道なのですが、ソースコードを見ると使えそうなフックが用意されていませんでした。

仕方がないのでWP_Widget_Recent_Postsというクラスを継承して、新しいウィジェットを作ることにしましょう。WP_Widget_Recent_Postsの継承だと、コンストラクタのオーバーライドが出来なかったので、WP_Widget_Recent_Postsを参考にしつつ、WP_Widgetを継承したウィジェットを作っていくことにします。

ちなみに、皆がやりたいと思うことは大体プラグイン化されています。急ぎの人はこちらを使って感想聞かせてください。

WP_Widgetを継承してMy_Recent_Postsクラスを作成する

<?php
/**
 * WP_Widgetを継承して、サムネイル表示できる最近の投稿ウィジェットを作る
 */
class My_Recent_Posts extends WP_Widget {

    /**
    * WP_Widget_Recent_Postsとidや名前が被らないように注意して、親のコンストラクタ関数に引数を渡します
    */
    public function __construct() {
        $widget_ops = array(
            'classname'                   => 'widget-recent-entries-with-thumbnail',
            'description'                 => __( 'Your site&#8217;s most recent Posts.' ),
            'customize_selective_refresh' => true,
        );
        parent::__construct( 'recent-posts-with-thumbnail', __( 'Recent Posts With Thumbnail' ), $widget_ops );
        $this->alt_option_name = 'widget_recent_entries_with_thumbnail';
    }

    /**
    * フロントサイドの表示部分です
    * サムネイルと記事タイトルを表示できるようにします
    *
    * @param array $args     Display arguments including 'before_title', 'after_title',
    *                        'before_widget', and 'after_widget'.
    * @param array $instance Settings for the current Recent Posts widget instance.
    */
    public function widget( $args, $instance ) {
        if ( ! isset( $args['widget_id'] ) ) {
            $args['widget_id'] = $this->id;
        }
        $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Posts' );
        /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
        $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
        $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5;
        if ( ! $number ) {
            $number = 5;
        }
        $show_date = isset( $instance['show_date'] ) ? $instance['show_date'] : false;

        /**
        * カスタムポイント
        * ウィジェット設定で「サムネイルを表示」にチェックが入っていればtrue
        */
        $show_thumbnail = isset( $instance['show_thumbnail'] ) ? $instance['show_thumbnail'] : false;

        /**
        * Filters the arguments for the Recent Posts widget.
        *
        * @see WP_Query::get_posts()
        *
        * @param array $args     An array of arguments used to retrieve the recent posts.
        * @param array $instance Array of settings for the current widget.
        */
        $r = new WP_Query(
            apply_filters(
                'widget_posts_args',
                array(
                    'posts_per_page'      => $number,
                    'no_found_rows'       => true,
                    'post_status'         => 'publish',
                    'ignore_sticky_posts' => true,
                ),
                $instance
            )
        );
        if ( ! $r->have_posts() ) {
            return;
        }
        ?>
        <?php echo $args['before_widget']; ?>
        <?php
        if ( $title ) {
            echo $args['before_title'] . $title . $args['after_title'];
        }
        ?>
        <ul>
            <?php foreach ( $r->posts as $recent_post ) : ?>
                <?php
                $post_title   = get_the_title( $recent_post->ID );
                $title        = ( ! empty( $post_title ) ) ? $post_title : __( '(no title)' );
                $aria_current = '';
                if ( get_queried_object_id() === $recent_post->ID ) {
                    $aria_current = ' aria-current="page"';
                }
                ?>
                <li>
                    <?php
                    /**
                    * カスタムポイント
                    * テンプレートタグを使用してサムネイルを表示させます。
                    * cssでデザイン調整しやすいように適当なクラス名を与えていますが任意です。
                    */
                    ?>
                    <?php if ($show_thumbnail): ?>
                        <div class="recent-post-thumbnail-wrap">
                            <?php echo get_the_post_thumbnail( $recent_post->ID, 'post-thumbnail', array('class' => 'recent-post-thumbnail') ); ?>
                        </div>
                    <?php endif; ?>

                    <div class="recent-post-body">
                        <a href="<?php the_permalink( $recent_post->ID ); ?>"<?php echo $aria_current; ?>><?php echo $title; ?></a>
                        <?php if ( $show_date ) : ?>
                            <span class="post-date"><?php echo get_the_date( '', $recent_post->ID ); ?></span>
                        <?php endif; ?>
                    </div>
                </li>
            <?php endforeach; ?>
        </ul>
        <?php
        echo $args['after_widget'];
    }

    /**
    * サムネイル表示切り替え用に、show_thumbnailというフラグを保存させるようにします。
    *
    * @param array $new_instance New settings for this instance as input by the user via
    *                            WP_Widget::form().
    * @param array $old_instance Old settings for this instance.
    * @return array Updated settings to save.
    */
    public function update( $new_instance, $old_instance ) {
        $instance              = $old_instance;
        $instance['title']     = sanitize_text_field( $new_instance['title'] );
        $instance['number']    = (int) $new_instance['number'];
        $instance['show_date'] = isset( $new_instance['show_date'] ) ? (bool) $new_instance['show_date'] : false;
        $instance['show_thumbnail'] = isset( $new_instance['show_thumbnail'] ) ? (bool) $new_instance['show_thumbnail'] : false;
        return $instance;
    }
    /**
    * サムネイル表示を切り替えるチェックボックスを表示させます
    *
    * @param array $instance Current settings.
    */
    public function form( $instance ) {
        $title     = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
        $number    = isset( $instance['number'] ) ? absint( $instance['number'] ) : 5;
        $show_date = isset( $instance['show_date'] ) ? (bool) $instance['show_date'] : false;
        $show_thumbnail = isset( $instance['show_thumbnail'] ) ? (bool) $instance['show_thumbnail'] : false;
        ?>
        <p><label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
        <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $title; ?>" /></p>

        <p><label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of posts to show:' ); ?></label>
        <input class="tiny-text" id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" type="number" step="1" min="1" value="<?php echo $number; ?>" size="3" /></p>

        <p><input class="checkbox" type="checkbox"<?php checked( $show_date ); ?> id="<?php echo $this->get_field_id( 'show_date' ); ?>" name="<?php echo $this->get_field_name( 'show_date' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_date' ); ?>"><?php _e( 'Display post date?' ); ?></label></p>

        <p><input class="checkbox" type="checkbox"<?php checked( $show_thumbnail ); ?> id="<?php echo $this->get_field_id( 'show_thumbnail' ); ?>" name="<?php echo $this->get_field_name( 'show_thumbnail' ); ?>" />
        <label for="<?php echo $this->get_field_id( 'show_thumbnail' ); ?>"><?php _e( 'Display thumbnail?' ); ?></label></p>
        <?php
    }
}

WP_Widget_Recent_Postsのソースコードをコピペして、必要な部分だけ加筆修正しました。

結果を確認する

管理画面

f:id:fullstack-baby:20190920165239p:plain
今回作ったウィジェットの設定画面

サムネイルを切り替えるためのチェックボックスが表示されました。翻訳をあてていないため英語表記のままです。

フロント画面

f:id:fullstack-baby:20190920180212p:plain
サムネイルが表示された

見た目を整えるために以下のcssをあてています。

.widget-recent-entries-with-thumbnail li {
    display: flex;
}
.widget-recent-entries-with-thumbnail .recent-post-thumbnail {
    width: 100px;
    height: auto;
    margin-right: 0.5rem;
}

以上です。よきwordpressライフを!