WordPressで「最近の投稿」の代わりに使える!更新順の「最近の更新」ウィジェットを自作する方法

WordPress の標準「最近の投稿」は公開日の新しい順である。だが実運用では、過去記事をリライトして再公開せずに更新だけする場面がある。

そこで更新日時(最終更新日)で並べ直した「最近の更新」リストをサイドバーに出す小さなウィジェットを作った。

プラグインに頼らず、functions.php に追加するスタイル。アイキャッチも表示し、狭いサイドバーでもタイトル直後に更新日を添えるレイアウトにしている。

このブログも「最近の投稿」から、このウィジェットに置き換えてみた。

完成コード(functions.php)

  • 更新日の新しい順に取得(orderby => 'modified'
  • アイキャッチ(サムネイル)を表示
  • タイトルのすぐ後ろに更新日を表示
  • ウィジェットとショートコードの両対応

ショートコード例

[recently_updated_posts count="5" post_type="post" title="最近の更新"]

phpソースコード

/**
 * 最近の「更新」投稿リスト(更新日時で並べ替え)
 * - ウィジェット名: 「最近の更新投稿」
 * - ショートコード: 

最近の更新

*/ if ( ! class_exists( 'WP_Widget_Recently_Updated_Posts' ) ) { // テーマがアイキャッチに対応していない場合の保険 add_action( 'after_setup_theme', function () { add_theme_support( 'post-thumbnails' ); } ); class WP_Widget_Recently_Updated_Posts extends WP_Widget { public function __construct() { parent::__construct( 'recently_updated_posts', '最近の更新投稿', array( 'description' => '更新日時(最終更新日)の新しい順に投稿を表示' ) ); } public function widget( $args, $instance ) { $widget_title = apply_filters( 'widget_title', $instance['title'] ?? '最近の更新' ); $count = ! empty( $instance['count'] ) ? (int) $instance['count'] : 5; $post_type = ! empty( $instance['post_type'] ) ? sanitize_text_field( $instance['post_type'] ) : 'post'; echo $args['before_widget']; if ( $widget_title ) { echo $args['before_title'] . esc_html( $widget_title ) . $args['after_title']; } $q = new WP_Query( array( 'post_type' => $post_type, 'post_status' => 'publish', 'ignore_sticky_posts' => true, 'posts_per_page' => $count, 'orderby' => 'modified', 'order' => 'DESC', 'no_found_rows' => true, ) ); if ( $q->have_posts() ) { echo '<ul class="recently-updated-posts">'; while ( $q->have_posts() ) { $q->the_post(); $permalink = get_permalink(); $post_title = get_the_title(); $modified_at = get_the_modified_time( get_option( 'date_format' ) ); echo '<li class="recently-updated-item">'; // アイキャッチ(フィルタの影響を避け、素の <img> を出力) $thumb_id = get_post_thumbnail_id(); if ( $thumb_id ) { $img = wp_get_attachment_image_src( $thumb_id, 'thumbnail' ); if ( $img ) { echo '<a href="' . esc_url( $permalink ) . '" class="rup-thumb">'; echo '<img class="rup-thumb-img" src="' . esc_url( $img[0] ) . '" width="' . (int) $img[1] . '" height="' . (int) $img[2] . '" alt="' . esc_attr( $post_title ) . '">'; echo '</a>'; } } // タイトル直後に更新日 echo '<div class="rup-text">'; echo '<a class="rup-title" href="' . esc_url( $permalink ) . '">' . esc_html( $post_title ) . '</a>'; echo ' <span class="updated-at">(更新: <time datetime="' . esc_attr( get_the_modified_time('c') ) . '">' . esc_html( $modified_at ) . '</time>)</span>'; echo '</div>'; echo '</li>'; } echo '</ul>'; wp_reset_postdata(); } else { echo '<p>更新された投稿はまだありません。</p>'; } echo $args['after_widget']; } public function form( $instance ) { $title = $instance['title'] ?? '最近の更新'; $count = isset( $instance['count'] ) ? (int) $instance['count'] : 5; $post_type = $instance['post_type'] ?? 'post'; ?> <p> <label>タイトル: <input class="widefat" name="<?php echo esc_attr( $this->get_field_name('title') ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>"> </label> </p> <p> <label>表示数: <input name="<?php echo esc_attr( $this->get_field_name('count') ); ?>" type="number" min="1" step="1" value="<?php echo esc_attr( $count ); ?>"> </label> </p> <p> <label>投稿タイプ(post / page / カスタム投稿など): <input class="widefat" name="<?php echo esc_attr( $this->get_field_name('post_type') ); ?>" type="text" value="<?php echo esc_attr( $post_type ); ?>"> </label> </p> <?php } public function update( $new_instance, $old_instance ) { $instance = array(); $instance['title'] = sanitize_text_field( $new_instance['title'] ?? '' ); $instance['count'] = max( 1, (int) ( $new_instance['count'] ?? 5 ) ); $instance['post_type'] = sanitize_text_field( $new_instance['post_type'] ?? 'post' ); return $instance; } } // ウィジェット登録 add_action( 'widgets_init', function() { register_widget( 'WP_Widget_Recently_Updated_Posts' ); } ); // 同等のショートコード add_shortcode( 'recently_updated_posts', function( $atts ) { $atts = shortcode_atts( array( 'count' => 5, 'post_type' => 'post', 'title' => '最近の更新', ), $atts, 'recently_updated_posts' ); ob_start(); the_widget( 'WP_Widget_Recently_Updated_Posts', array( 'title' => $atts['title'], 'count' => (int) $atts['count'], 'post_type' => $atts['post_type'], ), array( 'before_widget' => '', 'after_widget' => '', 'before_title' => '<h3>', 'after_title' => '</h3>' ) ); return ob_get_clean(); } ); }

スタイルシート(追加CSS)

サイドバー幅を想定したシンプルな見た目。サムネ左/テキスト右、タイトル直後に更新日。極端に幅が狭いときは更新日だけ次行に落とせるようにしてある。

/* 最近の更新ウィジェット */
.recently-updated-posts { list-style: none; margin: 0; padding: 0; }
.recently-updated-posts .recently-updated-item {
  display: flex;
  gap: .6rem;
  align-items: flex-start;
  margin: .4rem 0;
}

.recently-updated-posts .rup-thumb { flex: 0 0 auto; }
.recently-updated-posts .rup-thumb-img {
  display: block;
  width: 60px;
  height: 60px;
  object-fit: cover;
  border-radius: 6px;
}

.recently-updated-posts .rup-text { flex: 1 1 auto; min-width: 0; }
.recently-updated-posts .rup-title { font-weight: 600; text-decoration: none; }
.recently-updated-posts .rup-title:hover { text-decoration: underline; }

.recently-updated-posts .updated-at {
  margin-left: .4em;
  color: #666;
  font-size: .9em;
}

/* 幅がかなり狭い場合は更新日を次行へ */
@media (max-width: 360px) {
  .recently-updated-posts .updated-at { display: block; margin-left: 0; }
}

設置

  1. 子テーマの functions.php に上の PHP を追記(推奨は子テーマ)。
  2. 外観 → ウィジェット(ブロックテーマならサイトエディターのサイドバー)で「最近の更新投稿」を追加。
  3. 表示数や投稿タイプを設定。
  4. 外観 → カスタマイズ → 追加CSS(またはテーマのCSS)に上の CSS を貼る。

うまく表示されないときのメモ

  • アイキャッチが出ない テーマが add_theme_support('post-thumbnails') を持っているか確認。CPT なら add_post_type_support( 'news', 'thumbnail' ); のようにサムネ対応を付ける。

  • 画像が「読み込めません」になる 画像直リンクでは見えるのにページ内でだけ失敗するなら、ホットリンク防止(Refererチェック)が原因のことが多い。wp-content/uploads/.htaccess の許可ドメインに自サイトのドメインや開発用の IP を追加する(例:RewriteCond %{HTTP_REFERER} !^https?://(www\.)?example\.com [NC])。ローカルや UserDir(~user)環境だと引っかかりやすい。

  • 右端が詰まって改行が変 本CSSではタイトルと更新日を同じ行に並べつつ自然に折り返す。常に別行にしたければ .updated-at { display:block; margin-left:0; } にする。