はじめに:今日やったことをひとことで言うと
今日は、WordPressの「子テーマ」の大そうじをしました。
ひとことで言うと、「親テーマのファイルをコピーして書きかえる方法」をやめて、「決まった場所にパーツを差しこむ方法」に全部作りかえたという作業です。
これだけ聞いても「???」だと思うので、たとえ話を使って、ゼロから説明していきます。
そもそも「親テーマ」と「子テーマ」ってなに?
WordPressでサイトの見た目を決めているのが「テーマ」です。私は「Cocoon(コクーン)」という無料のテーマを使っています。
ここで、学校の教科書とノートでたとえてみます。
| WordPressの言葉 | たとえると | 説明 |
|---|---|---|
| 親テーマ(Cocoon) | 教科書 | 作者さんが作ったもの。ときどき新しい版(アップデート)が配られる |
| 子テーマ | 自分のノート | 自分で書きたしたいことを書く場所。教科書が新しくなっても消えない |
教科書(親テーマ)に直接落書きすると、新しい教科書が配られたときに全部消えてしまいます。だから自分の工夫はノート(子テーマ)に書くのがルールです。
何が問題だったの?
私の子テーマには、広告をたくさん入れるために、親テーマのファイルをまるごとコピーして、書きかえたものが11個も入っていました。
教科書でたとえると、教科書のページをコピーして、そのコピーに落書きして、教科書の上からはりつけていた状態です。
これの何がこまるかというと、
- 新しい教科書が配られても、上にはったコピーが古いままなので、新しい内容が読めない
- 教科書のまちがいが直っても、コピーのほうはまちがったまま
- 何年もたつと、「どこが自分の落書きで、どこが古い教科書の文なのか」が分からなくなる
実際に最新のCocoonとくらべてみたら、親テーマで直っているバグが、私のサイトでは直っていないところがいくつも見つかりました。
コピーして書きかえていたファイルはこの11個です。
| ファイル名 | ページのどこ? |
|---|---|
| header.php | ページのいちばん上の見えない部分 |
| footer.php | ページのいちばん下 |
| sidebar.php | 横のサイドバー |
| 404.php | 「ページが見つかりません」の画面 |
| tmp/body-top.php | ヘッダーの下あたり |
| tmp/entry-card.php | 記事一覧の1枚1枚のカード |
| tmp/content.php | 記事の本文まわり |
| tmp/single-contents.php | 投稿ページ全体 |
| tmp/page-contents.php | 固定ページ全体 |
| tmp/category-content.php | カテゴリページの上のほう |
| tmp/tag-content.php | タグページの上のほう |
解決のカギ「フック」ってなに?
ここで登場するのが「フック」というしくみです。
フックは、教科書に最初から用意されている「ふせんをはっていい場所」のようなものです。
Cocoonの作者さんは、「ここにパーツを差しこみたい人がいるだろうな」という場所に、あらかじめ目印をつけてくれています。たとえば、
- 記事の本文が終わった直後
- カルーセル(スライドする画像)のすぐ下
- フッターを読みこむ直前
などです。この目印に「ここに広告を出してね」というふせんをはれば、教科書をコピーしなくても、好きな場所にパーツを出せるんです。
しかも、新しい教科書が配られても、ふせんの場所(フック)はそのまま残るので、ふせんもそのまま使える。これが一番のメリットです。
実際のコードは、こんな感じの形をしています。
add_action( 'cocoon_part_after__tmp/carousel', 'my_after_carousel' );
function my_after_carousel() {
// ここに出したい広告を書く
}1行目が「カルーセルの後ろ(after)に、この中身を出してね」というお願いです。2行目から下が、出したい中身です。
実際にやった作業の流れ
全部で11ファイルあったので、1ファイルずつ「自分で書きたした部分だけ」を見つけて、ふせん(フック)に引っこしさせる作業をくり返しました。
| 手順 | やったこと |
|---|---|
| 1 | 子テーマをまるごとバックアップ |
| 2 | コピーしたファイルと、最新の親テーマのファイルを見くらべる |
| 3 | 「自分で書きたした部分」だけを取り出す |
| 4 | 同じ場所に出せるフックを探して、そこに引っこし |
| 5 | コピーしたファイルを削除 |
| 6 | PCとスマホで表示を確認。OKなら次のファイルへ |
いきなり全部やると、こわれたときにどこが原因か分からなくなるので、小さいファイルから1つずつ、確認しながら進めたのがポイントです。
見くらべる道具「WinMerge(ウィンマージ)」
手順2の「見くらべる」で便利なのが、WinMergeという無料のWindows用ソフトです。
2つのファイル(またはフォルダ)を左右にならべて開くと、ちがうところだけ色をつけて教えてくれます。まちがいさがしを自動でやってくれる道具、というイメージです。
- 左に「親テーマのファイル」、右に「子テーマにコピーしたファイル」を開く
- 色がついた行 = 自分で書きたした(または親テーマが新しくなって変わった)部分
- フォルダ同士をくらべれば、「同じ名前のファイルがどれか」も一目で分かる
何百行もあるファイルを目で見くらべるのは大変なので、この作業には欠かせない道具でした。
以前このブログで記事にしましたので参考までに見てみて

サイドバー(sidebar.php)
一番小さいファイルからスタート。自分で足していたのは「広告」と「星5評価ランキング」の2つだけでした。WordPressにもともとある「サイドバーのウィジェットを出す直前」のフックに引っこしさせて、終わり。
星5評価ランキングってなに?
訪問してくれた人が記事に星1から5で評価をつけられるしくみを作りました。
- 記事のタイトルの下に星がならんでいて、クリックするだけで投票できる
- 集まった評価から、人気の記事をランキングにして表示
- 投票数が少ない記事がいきなり1位にならないよう、票数も考えた計算で順位を決めている
- 前の日とくらべて順位が上がったか下がったかを矢印で表示
このしくみは自作のもので、プラグイン化して次回公開予定です。お楽しみに。
ヘッダー・ボディ上部・フッター(header.php / tmp/body-top.php / footer.php)
ここが一番むずかしかったところです。
サイトはPCで3列(3カラム)になっていて、その3列目を作るために、
- body-top.php で「箱をあける」
- footer.php で「箱をしめて、となりに3列目を作る」
という、2つのファイルでセットになった作りになっていました。
お弁当箱でたとえると、ふたを開ける係と閉める係が別々の人なので、片方だけやめると、ふたが開けっぱなし(レイアウトがくずれる)になってしまいます。
なので、「箱をあけるフック」と「箱をしめるフック」を同時に用意して、3つのファイルを同時に削除しました。
記事一覧のカード(tmp/entry-card.php)
カードに「カテゴリとタグのリンク」を出していただけでした。抜粋文の直後に出せるフックがあったので、そこに引っこし。ついでに、親テーマで直っていたバグ(まだ作っていない変数を使っていたミス)も、自動的に直りました。
記事本文まわり(tmp/content.php)
記事タイトルの下に「お気に入りボタン」と「星5評価」を出していた部分だけ。アイキャッチ画像の直前のフックに引っこし。
投稿ページ・固定ページ(single-contents.php / page-contents.php)
ここは中身の9割が自分で書いた広告でした。量が多いので、
- 投稿ページ用の広告 → ad-single.php
- 固定ページ用の広告 → ad-page.php
- 何度も出てくる「リンク表」→ parts/link-table.php(1つにまとめて使い回し)
というふうに、ファイルを役割ごとに分けて整理しました。
カテゴリ・タグページ(category-content.php / tag-content.php)
カテゴリ用とタグ用で、ほとんど同じ中身でした。なので1つの共通の部品にまとめて、両方から使う形にしました(ad-archive.php)。同じものを2回書かなくていいので、次に直すときも1か所ですみます。
404ページ(404.php)
「商品がない場合はコチラ」のボタンと、星5評価ランキングだけ。フッターを読みこむ直前のフックで、「404ページのときだけ出す」という条件をつけて引っこし。
途中で起きたトラブルと、その解決
もちろん、すんなりとはいきませんでした(笑)。
トラブル1:星5ランキングの見た目がくずれた
サイドバーを引っこしさせたら、ランキングのデザイン(CSS)が全部消えてしまいました。
原因は、ランキングのCSSが「ページの中で最初にランキングが呼ばれたときに1回だけ出す」というしくみだったこと。フックに引っこしたことで、その「1回目」が画面に出ないタイミングになってしまい、「CSSはもう出したつもり」なのに実際には出ていないという状態になっていました。
解決は、CSSをページの一番最初(head)で必ず1回出すように変えること。これで、どこでランキングを使ってもくずれなくなりました。
トラブル2:スマホでトップページだけ横にはみ出る
スマホでトップページを見ると、画面が横に広がってしまう問題が出ました。
Chromeの開発者ツールで、「画面からはみ出している部品だけを赤い線でかこむ」という調べ方をして、犯人を探しました。
犯人は、一番上に出していたスマホ用広告の「表(table)」の外わく。表は、中身が大きいと自分もいっしょに広がる性質があるので、大きめの広告が出たときだけ、ページごと横に広がっていました。
表をやめて、「中身がはみ出したら、はみ出た分はかくす」わくに変えて、ページが横に広がる問題は解決。さらに、1つの広告会社のバナーが大きすぎたので、別の会社の広告に入れかえました。
これで一件落着…かと思いきや、入れかえた広告会社のほうにも大きなバナーがあったんです。わくのおかげでページがくずれることはないものの、はみ出た右側がスパッと切れた形で表示されていて、どうも気になります。
そこで、広告が画面より大きいときだけ、画面の幅に合わせてまるごと縮小するしくみを作りました。
- 広告を「中箱」に入れて、表示されたら中箱の横幅を測る
- 画面より大きければ、ちょうど収まる比率で縮小(画像もクリックできる範囲もいっしょに縮む)
- 画面に収まる広告なら、縮小せずにそのまま中央に表示
- 広告は少し遅れて表示されるので、表示された瞬間・読みこみ完了時・数秒後・画面を回転したときにも測り直す
コピー機の「縮小コピー」のようなイメージです。これで見た目がすっきりして、気持ちよくなりました。
実際のソースはこちらです(広告タグは伏せています)。
add_action( 'cocoon_part_after__tmp/carousel', 'my_after_carousel' );
function my_after_carousel() {
// スマホのときだけ
if ( ! wp_is_mobile() ) return;
// ランダムで出す広告の候補
$banners = array(
'(広告会社Aの広告タグ)',
'(広告会社Bの広告タグ)',
);
?>
<div id="sp-top-banner" style="background:#000; text-align:center; max-width:100%; overflow:hidden;">
<div id="sp-top-banner-in" style="display:inline-block; transform-origin:top left; vertical-align:top;">
<?php echo $banners[ array_rand( $banners ) ]; ?>
</div>
</div>
<script>
/* 広告が画面幅より大きいときだけ縮小する */
(function(){
var box = document.getElementById('sp-top-banner');
var inner = document.getElementById('sp-top-banner-in');
if (!box || !inner) return;
function fit(){
inner.style.transform = 'none';
box.style.height = '';
var w = inner.scrollWidth;
var max = box.clientWidth;
if (w > max) {
var s = max / w;
inner.style.transform = 'scale(' + s + ')';
box.style.height = Math.ceil(inner.offsetHeight * s) + 'px';
}
}
/* 広告が差しこまれた瞬間に測り直す */
if (window.MutationObserver) {
new MutationObserver(fit).observe(inner, { childList: true, subtree: true });
}
window.addEventListener('load', fit);
window.addEventListener('resize', fit);
setTimeout(fit, 1000);
setTimeout(fit, 3000);
})();
</script>
<?php
}ついでに直したバグたち
1つずつファイルを見ていったおかげで、今まで気づかなかったミスもたくさん見つかりました。
| 見つかったミス | どう直した? |
|---|---|
| 投稿ページで、見出しは「4番目のタグの商品」なのに、中身は「6番目のタグ」で検索していた。しかも2番目のタグはどこにも使われていなかった | 見出しどおりのタグで検索するように。使われていなかった枠に2番目のタグを入れて、6個のタグを全部活用 |
| カテゴリページなのに、「一覧の1件目の記事」のカテゴリで商品を検索していた | 表示中のカテゴリ名で検索するように |
| 商品情報を取ってくる処理が、アクセスのたびに毎回外のサーバーに問い合わせていた(重い!) | 一度取ったら数時間おぼえておく「キャッシュ」を追加 |
| 商品が取れなかったとき、「取得できませんでした」というエラー文が訪問者に見えていた | 失敗したときは何も出さないように |
| 商品情報を取る処理に時間制限がなく、相手のサーバーが重いとページごと止まるおそれがあった | 時間制限つきの取り方(wp_remote_get)に変更 |
| HTMLのタグの閉じ方の順番がまちがっていた所がいくつも | 正しい順番に |
できあがったファイルの形
最終的に、子テーマはこんな形になりました。
| ファイル | 役割(たとえると) |
|---|---|
| functions.php | 目次。どのファイルを読むかを書いてある |
| ad-hooks.php | 全ページ共通の広告のふせん |
| ad-single.php | 投稿ページ専用の広告のふせん |
| ad-page.php | 固定ページ専用の広告のふせん |
| ad-archive.php | カテゴリ・タグページの広告のふせん |
| dmm-widget.php | DMMの商品を取ってくる道具箱 |
| display-hooks.php | 広告以外の「見た目」のふせん |
| parts/link-table.php | 使い回すリンク表 |
そして一番大事なのは、親テーマと同じ名前のファイル(コピーして書きかえたもの)がゼロになったことです。
広告のふせんファイルの中身(かんたん版)
実際のファイルは広告がたくさん入っていて長いので、ここではしくみが分かるように、かんたんにしたものをのせます。「(広告タグ)」のところに、広告会社からもらったタグを入れる形です。
ad-hooks.php(全ページ共通の広告のふせん)
全部のページに出す広告を書くファイルです。ほかの広告ファイル(投稿・固定・一覧ページ用)も、ここから読みこんでいます。
<?php
if ( !defined( 'ABSPATH' ) ) exit;
/* ページの種類ごとの広告ファイルも読みこむ */
require_once get_stylesheet_directory() . '/ad-single.php';
require_once get_stylesheet_directory() . '/ad-page.php';
require_once get_stylesheet_directory() . '/ad-archive.php';
/* カルーセルの直後に広告を出す */
add_action( 'cocoon_part_after__tmp/carousel', 'my_after_carousel' );
function my_after_carousel() {
echo '<div>(広告タグ)</div>';
}
/* サイドバーのウィジェットの直前に広告を出す */
add_action( 'dynamic_sidebar_before', 'my_sidebar_top', 10, 2 );
function my_sidebar_top( $index, $has_widgets ) {
if ( $index !== 'sidebar' ) return; // メインのサイドバーだけ
echo '<div>(広告タグ)</div>';
}
/* メインの一番最後(フッターの直前)に広告を出す */
add_action( 'get_footer', 'my_main_bottom' );
function my_main_bottom() {
echo '<div>(広告タグ)</div>';
}ad-single.php(投稿ページ専用の広告のふせん)
投稿ページのときだけ出す広告です。「is_single()」で「今、投稿ページかな?」を確認してから出しています。
<?php
if ( !defined( 'ABSPATH' ) ) exit;
/* 記事の本文が終わった直後に広告を出す */
add_action( 'cocoon_part_after__tmp/content', 'my_single_after_content' );
function my_single_after_content() {
if ( !is_single() ) return; // 投稿ページ以外では何もしない
echo '<div>(広告タグ)</div>';
}
/* 関連記事の直後に広告を出す */
add_action( 'cocoon_part_after__tmp/related-entries', 'my_single_after_related' );
function my_single_after_related() {
if ( !is_single() ) return;
echo '<div>(広告タグ)</div>';
}ad-page.php(固定ページ専用の広告のふせん)
固定ページは、自作のページテンプレート(お気に入り一覧など)もあるので、「ふつうの固定ページを表示している間だけ」目印を立てて、そのときだけ広告を出すようにしています。
<?php
if ( !defined( 'ABSPATH' ) ) exit;
/* 目印:ふつうの固定ページを表示している間だけ true */
$GLOBALS['my_in_page'] = false;
add_action( 'cocoon_part_before__tmp/page-contents', function() {
$GLOBALS['my_in_page'] = true;
} );
add_action( 'cocoon_part_after__tmp/page-contents', function() {
$GLOBALS['my_in_page'] = false;
}, 99 );
/* 本文が終わった直後に広告を出す */
add_action( 'cocoon_part_after__tmp/content', 'my_page_after_content' );
function my_page_after_content() {
if ( empty( $GLOBALS['my_in_page'] ) ) return; // 目印がないときは何もしない
echo '<div>(広告タグ)</div>';
}ad-archive.php(カテゴリ・タグページの広告のふせん)
カテゴリページとタグページは中身がほとんど同じなので、共通の部品(my_archive_ads)を1つ作って、両方から呼び出す形にしています。表示しているカテゴリ名・タグ名を受け取って、その名前で広告や商品を出します。
<?php
if ( !defined( 'ABSPATH' ) ) exit;
/* カテゴリページ:タイトル・説明の直後 */
add_action( 'cocoon_part_after__tmp/category-content', 'my_category_ads' );
function my_category_ads() {
$term = get_queried_object(); // 表示中のカテゴリ
my_archive_ads( $term->name );
}
/* タグページ:タイトル・説明の直後 */
add_action( 'cocoon_part_after__tmp/tag-content', 'my_tag_ads' );
function my_tag_ads() {
$term = get_queried_object(); // 表示中のタグ
my_archive_ads( $term->name );
}
/* カテゴリ・タグ共通の広告ブロック */
function my_archive_ads( $keyword ) {
echo '<h2>' . esc_html( $keyword ) . 'の人気商品</h2>';
echo '<div>(広告タグ)</div>';
}functions.php(目次)
最後に、functions.php の一番下で広告ファイルと見た目のファイルを読みこめば完成です。
require_once get_stylesheet_directory() . '/ad-hooks.php';
require_once get_stylesheet_directory() . '/display-hooks.php';これで、Cocoonが何回アップデートしても、広告や改造が消えたり、くずれたりする心配がなくなりました。
今回わかった大事なルール
| ルール | 理由 |
|---|---|
| 親テーマのファイルはコピーしない | 新しい版の修正がとどかなくなるから |
| 差しこみたいときはフックを使う | アップデートしても場所が変わらないから |
| ファイルは役割ごとに分ける | 「あの広告どこだっけ?」がなくなるから |
| 同じものは1つにまとめて使い回す | 直すときに1か所ですむから |
| 作業前に必ずバックアップ | 失敗してもすぐ元にもどせるから |
| 1つずつ直して、1つずつ確認 | こわれたときに原因がすぐ分かるから |
おわりに
正直、最初は「ごちゃごちゃで手がつけられない…」と思っていました。でも、1ファイルずつ、自分で書いた部分だけを取り出して、正しい場所に引っこしさせるというのをくり返していったら、ちゃんと全部片づきました。
WordPressの子テーマに、親テーマのファイルをコピーしてたくさん置いている人は、アップデートでこまる前に、一度見直してみるのがおすすめです。
次回は、星5評価のしくみを「プラグイン」にして公開する予定です。お楽しみに!





コメント