表示件数は1〜30、PC列数は1〜3、スマホ列数は1〜2です。複数RSSの同一URLは1件にまとめ、日付がない記事には日付を表示しません。
色んなジャンルの記事をまとめた技術ブログ「CGメソッド」
2026年からカテゴリーをサブディレクトリサイトに分けてみました。
その新着記事をまとめた表示を作りたかったのでSWELLで複数のサイトの新着記事をカードで表示するカスタマイズをしてみましたので、その備忘録です。

SWELLで複数のサイトの新着記事をカードで表示するカスタマイズ
SWELLには、すでにRSSブロックがありますが一つのサイトしか表示できません。
この表示を拡張して複数のサイトを登録して、その中の新着記事を表示させています。

やり方は簡単です。
functions.phpにコードをいれて、各サイトのRSSを登録。
あとはショートコードで表示するだけです。
SWELLのクラスを使っているので、カードの見た目はそのまま表示できています。

ショートコード
編集記事に、下記のショートコードブロックの内容を挿入します。
[multi_rss_cards limit="3" pc_col="3" sp_col="2"]limit … 全体の表示件数
pc_col / sp_col … -pc-col3 -sp-col2 の数字を変えられます
コード
functions.phpに下記のコードを挿入します。
RSSのURL入力は書き換えてください。
/**
* 複数RSSをまとめてカード表示(SWELLの見た目に寄せたHTML)
* 使い方: [multi_rss_cards limit="12" pc_col="3" sp_col="2"]
*/
add_shortcode('multi_rss_cards', function ($atts) {
$atts = shortcode_atts([
'limit' => 9, //全体の表示件数
'pc_col' => 3, //PCの列表示数
'sp_col' => 2, //スマホの列表示数
], $atts, 'multi_rss_cards');
$limit = max(1, min(30, (int) $atts['limit']));
$pc_col = max(1, min(3, (int) $atts['pc_col']));
$sp_col = max(1, min(2, (int) $atts['sp_col']));
// ▼ ここにRSSを追加/編集(URL => サイト名)
$feeds = [
'https://cg-method.com/unity/rss/' => 'Unityメソッド',
'https://cg-method.com/after-effects/rss/' => 'AEメソッド',
'https://cg-method.com/photoshop/rss/' => 'Photoshopメソッド',
'https://cg-method.com/illustrator/rss/' => 'Illustratorメソッド',
'https://cg-method.com/figma/rss/' => 'Figmaメソッド',
'https://cg-method.com/google/rss/' => 'Google Workspaceメソッド',
'https://cg-method.com/3dcg/rss/' => '3DCGメソッド',
'https://cg-method.com/video-editing/rss/' => '動画編集メソッド',
'https://cg-method.com/vtuber/rss/' => 'VTuberメソッド',
'https://cg-method.com/vr/rss/' => 'VRメソッド',
'https://cg-method.com/work-tools/rss/' => 'デジタルメソッド',
'https://cg-method.com/ai/rss/' => 'AIメソッド',
];
$cache_key = 'multi_rss_cards_v3_' . md5(serialize([$feeds, $limit, $pc_col, $sp_col]));
$cached = get_transient($cache_key);
if ($cached !== false) return $cached;
require_once ABSPATH . WPINC . '/feed.php';
$items = [];
$seen = [];
foreach ($feeds as $url => $site_title) {
$rss = fetch_feed($url);
if (is_wp_error($rss)) {
continue;
}
// 1フィードから取りすぎない(全体limitの2倍くらい取っておくと混ぜた時に綺麗)
$per_feed = min($limit * 2, 30);
$feed_items = $rss->get_items(0, $per_feed);
foreach ($feed_items as $item) {
$link = esc_url_raw((string) $item->get_link(), ['http', 'https']);
if (!$link || isset($seen[$link])) continue;
$seen[$link] = true;
$timestamp = (int) $item->get_date('U');
$items[] = [
'url' => $link,
'title' => $item->get_title(),
'date' => max(0, $timestamp),
'site' => $site_title,
'thumb' => multi_rss_pick_thumbnail($item),
];
}
}
// 日付で降順ソート
usort($items, function ($a, $b) {
return ($b['date'] ?? 0) <=> ($a['date'] ?? 0);
});
$items = array_slice($items, 0, $limit);
if (!$items) {
$message = '<p class="waza-rss-empty">記事を取得できませんでした。時間をおいて再読み込みしてください。</p>';
// 空の結果は1分だけ保持し、取得を繰り返すアクセスを抑えます。
set_transient($cache_key, $message, MINUTE_IN_SECONDS);
return $message;
}
// HTML生成
ob_start();
$wrap_class = sprintf(
'swell-block-rss'
);
$list_class = sprintf(
'p-postList is-rss -type-card -pc-col%d -sp-col%d',
$pc_col,
$sp_col
);
?>
<div class="<?php echo esc_attr($wrap_class); ?>">
<ul class="<?php echo esc_attr($list_class); ?>">
<?php foreach ($items as $it): ?>
<li class="p-postList__item">
<a href="<?php echo esc_url($it['url']); ?>" class="p-postList__link" target="_blank" rel="noopener noreferrer">
<?php if (!empty($it['thumb'])): ?>
<div class="p-postList__thumb c-postThumb">
<figure class="c-postThumb__figure">
<img decoding="async" class="c-postThumb__img u-obf-cover" src="<?php echo esc_url($it['thumb']); ?>" alt="">
</figure>
</div>
<?php endif; ?>
<div class="p-postList__body">
<h3 class="p-postList__title"><?php echo esc_html($it['title']); ?></h3>
<div class="p-postList__meta">
<div class="p-postList__site c-rssSite u-thin u-flex--aic">
<span class="c-rssSite__title"><?php echo esc_html($it['site']); ?></span>
</div>
<div class="p-postList__times c-postTimes u-thin">
<time class="c-postTimes__posted icon-posted">
<?php if ($it['date'] > 0) echo esc_html(wp_date('Y年n月j日', $it['date'])); ?>
</time>
</div>
</div>
</div>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php
$html = ob_get_clean();
// キャッシュ(10分)
set_transient($cache_key, $html, 10 * MINUTE_IN_SECONDS);
return $html;
});
/**
* RSSアイテムからサムネイルURLを抽出
* 優先: enclosure(media) > content内の最初のimg
*/
function multi_rss_pick_thumbnail($item) {
// 1) enclosure(OGP画像やアイキャッチが入っていることが多い)
$enclosure = $item->get_enclosure();
if ($enclosure) {
$thumb = $enclosure->get_thumbnail();
if ($thumb) return esc_url_raw($thumb);
$link = $enclosure->get_link();
if ($link && preg_match('~\.(jpg|jpeg|png|gif|webp)(\?.*)?$~i', $link)) {
return esc_url_raw($link);
}
}
// 2) content 内の最初の画像
$content = $item->get_content();
if ($content && preg_match('/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $m)) {
return esc_url_raw($m[1]);
}
// 3) それでも無ければ null
return null;
}
子テーマのfunctions.phpまたはスニペットをバックアップし、どちらか一方へ掲載コードを追加します。同じmulti_rss_cardsと補助関数を重複登録しないでください。RSSは管理者が確認したURLをコード内で指定します。読者の入力を取得先URLとして使う構成には変更しないでください。
カードがある場合のHTMLは10分、表示できる記事がない場合の案内は1分キャッシュします。
fetch_feed側にも別のフィードキャッシュがあるため、この間隔で配信元から最新記事を取得する保証ではありません。
全フィード取得失敗、画像なし、重複URL、日付なしを検証環境で確認してください。
戻すときは記事のショートコードを外してから登録コードを停止し、ページキャッシュを更新します。
外部RSSの実通信と画像表示、PCでのカード配置とEnterによるリンク移動は、後述の隔離環境で確認しました。
全取得先のタイムアウト動作とスマホでの配置は未検証です。
新しいタブで開くリンクのため、導入時は見出しなどにその旨を明示してください。
実際のRSSカードと画面幅による列数を確認する
| 条件(SWELL 2.19.0) | 3列指定の適用 |
|---|---|
| サイドバーなし | 画面幅1020px以上で3列 |
| サイドバーあり | 画面幅1320px以上で3列 |
| 今回の撮影:サイドバーあり・1280px | 2列で表示 |
表示数3・PC列数3・スマホ列数2の指定で、記事のPHPコードを隔離環境で実行しました。
12個のRSS取得先から集めた結果に3件のカードが表示され、3件ともサムネイル画像を読み込めました。
PC列数の指定は、すべてのPC画面幅でその列数になる保証ではありません。
SWELLのカード一覧用CSSが画面幅とサイドバーの有無に応じて配置を変更するため、1280pxの確認画面では2列になりました。
カードにTabキーで移動してEnterを押すと、新しいタブで配信元の記事が開きました。
導入時はタイトル・日付・配信元と移動先を照合し、表示中の画像も確認してください。
初回表示でRSS取得に時間がかかる場合と、保存済みのカードが表示される場合を分けて確認します。
カードのHTMLキャッシュ、WordPressのフィードキャッシュ、WP Rocketなどのページキャッシュは別の仕組みなので、変更直後に古い内容が見える場合はどの段階の内容が残っているかを確認してください。

取得できる記事がないときは案内を表示する
表示する記事がない場合は案内文を表示し、その結果を1分だけ保持します。
取得先が復旧しても、その1分間と別のフィード・ページキャッシュが残る間は直ちに表示が変わるとは限りません。
変更前のPHPを保存し、同名ショートコードと補助関数を重複登録せず、本文のコードへ置き換えます。
ページキャッシュを使う場合は対象ページを更新し、取得できる記事がない場合の案内と、通常時のカードを確認してください。
問題があれば原本のPHPへ戻します。
今回の検証は独立したWordPress 7.1.2 / SWELL 2.19.0で行いました。
検証用の取得先12個をすべて応答待ちのタイムアウトにすると案内が出て、続けてページを取得してもRSS通信を繰り返しませんでした。
検証用HTMLキャッシュを解除して1取得先を復旧させると、残り11取得先がHTTP 404でも復旧した1件のカードが出ました。
取得先URLと待ち時間は検証用に変更したものです。
実際の12サイトでの障害時間、WP Rocketとの組み合わせ、スマホ配置とリンク操作はこの検証に含みません。



