MENU

問い合わせ


    【WordPressで作るキュレーションメディア 第0回】キュレーションメディアの要件と「低品質まとめ」の教訓 ― Docker で WordPress 6.8 の土台を作る

    特定のテーマについて、信頼できる情報を選び、確かめ、解説して届ける「キュレーションメディア(=情報を集めて整理・解説するWebメディア)」を、WordPress で作る連載を始めます。自社の専門分野で情報発信をしたい、オウンドメディアを「まとめ記事」中心で立ち上げたい、という相談は少なくありません。

    「WordPress でキュレーションメディアを作りたいけれど、プラグインをたくさん入れるだけでいいの?品質や著作権の面で気を付けることはある?」

    結論から言うと、キュレーションメディアの品質は「機能」より先に「編集方針」で決まります。そのうえで、WordPress 6.8 の標準機能(ブロックテーマ・theme.json・パターン・REST API)と、子テーマ1つ+小さな自作プラグイン1つで、方針を仕組みとして支える構成にします。第0回の今回は、要件の整理と「低品質まとめ」から学ぶ教訓、そして Docker で動く開発環境の土台づくりまでを扱います。

    目次

    キュレーションメディアをWordPressで作る前に決める要件

    キュレーションメディアは、ニュースサイトやブログと同じく記事を公開する仕組みですが、次の点が特徴です。

    • 他者の情報を扱う割合が高い:公式発表、統計、他の記事などを参照・引用しながら記事を作る
    • ページの「型」が複数ある:通常の記事に加えて、複数の情報を並べる「まとめ」、比較軸で並べる「ランキング」
    • 書き手が多い:社内外のライターが書き、編集者が確認して公開する体制になりやすい

    このため、最初に決めておく要件は「どんな画面にするか」よりも、「誰が書き、誰が確認し、何を出典として認めるか」です。

    「低品質まとめ」から学ぶ3つの教訓

    過去には、キュレーション型のサイトが大量の記事を短期間で公開し、内容の正確さや他サイトからの無断転載が問題となって、記事の公開停止に至った例が複数ありました。特定のサイトを挙げるのは控えますが、一般的な教訓は次の3つにまとめられます。

    教訓起きたこと(一般論)仕組みでの対策(本連載で実装)
    無断転載をしない他サイトの文章・画像を出典なしで、または引用の範囲を超えて載せる出典URL・出典名・確認日を必須にした「引用元カード」ブロック(第4回)
    根拠の薄い情報を載せない医療・健康・お金など、生活に影響する分野で専門家の確認がない記事を公開する著者・監修者の表示(第6回)、下書きレビューの工程(第11回)
    量産を目的にしない記事数を目標にし、1本あたりの確認・独自の解説が薄くなる公開権限を編集者に限定し、ライターは「レビュー待ち」までにする(第11回)

    ポイントは、ツールは「書き忘れ」「確認漏れ」を防ぐだけで、品質を保証するものではないことです。仕組みで守れる範囲と、人が判断すべき範囲を最初に分けておきます。

    編集方針として最初に合意しておくこと

    連載のサンプルでは、次の編集方針を前提に機能を作っていきます。

    • 一次情報を確認する:公式発表・公的機関の資料・原典を確認し、確認した日付を記録する
    • 独自の解説を主にする:他者の情報は材料であり、記事の中心は自社の視点による整理・比較・解説にする
    • 引用のルールを決める:引用部分を明確に区別し、出所を必ず示す。画像・スクリーンショットの転載はしない
    • 広告は広告と分かるようにする:アフィリエイトや広告案件を含む記事には、その旨を表示する

    引用については、著作権法第32条が「公表された著作物は、引用して利用することができる」と定め、その引用は「公正な慣行に合致するもの」であり、かつ「報道、批評、研究その他の引用の目的上正当な範囲内」で行われなければならないとしています。

    📰 出典:e-Gov法令検索 著作権法(第32条・第48条)

    文化庁の著作権テキストでは、引用の条件の例として、引用の「必然性」があること、カギ括弧などで引用部分が明確になっていること、引用部分とそれ以外の「主従関係」が明確であること、出所の明示(第48条)などが挙げられています。

    📰 出典:文化庁「著作権テキスト(令和6年度版)」

    本連載で紹介するのは、こうした一般的な考え方を「入力欄」や「表示」で支える実装です。個別の記事で引用が適法かどうかの判断はここでは行いません。迷うケースは弁護士などの専門家に確認してください。

    広告の表示については、2023年10月1日から、いわゆるステルスマーケティング(広告であることを隠した宣伝)が景品表示法の規制対象になっています。実装は第9回で扱います。

    📰 出典:消費者庁 ステルスマーケティング規制について

    連載の全体像:子テーマ1つと小さなプラグイン1つで作る

    完成時の構成は次のとおりです。見た目は子テーマ、機能はプラグイン、と役割を分けます。

    部品役割主な中身
    親テーマ Twenty Twenty-FiveWordPress 標準のブロックテーマ。編集しないテンプレート・パターン・スタイル
    子テーマ curation-child見た目(デザインとページの型)theme.json、テンプレート、テンプレートパーツ、パターン
    プラグイン curation-tools機能(テーマを替えても残すもの)特集タクソノミー、引用元カード、ランキング、構造化データ、OGP、権限
    MySQL記事・設定・分類の保存投稿、メタ情報、タクソノミー
    WP-CLI管理画面を使わない操作インストール、初期データ投入、バックアップ

    「機能をプラグインに置く」理由は、特集の分類や構造化データ(=検索エンジン向けに内容を機械が読める形で書いたデータ)のように、デザインを変えても失われてはいけないものをテーマに入れると、テーマ変更のたびに作り直しになるからです。

    連載の目次(予定)

    各回のタイトルは予定です。前回までのコードに差分を積み上げていきます。

    回タイトル(予定)
    第0回キュレーションメディアの要件と「低品質まとめ」の教訓 ― Docker で WordPress 6.8 の土台を作る(この記事)
    第1回theme.json でデザイントークンを定義する(子テーマの基本)
    第2回カテゴリ・タグ・「特集」― タクソノミー設計
    第3回記事・まとめ・ランキングのテンプレートとパターン
    第4回「引用元カード」ブロック ― 著作権法第32条の引用を仕組みで守る
    第5回「まとめリスト」と「比較表」をパターンとブロックスタイルで作る
    第6回著者・監修者プロフィールを表示する(E-E-A-T の考え方)
    第7回人気記事ランキングと関連記事を小さなプラグインで作る
    第8回目次と構造化データ(JSON-LD: Article / BreadcrumbList / ItemList)
    第9回OGP・SNS カードと「広告・PR表記」― ステマ規制への対応
    第10回表示速度の改善 ― 画像サイズ・キャッシュ・計測
    第11回権限・編集フロー・運用 ― 下書きレビュー、バックアップ、更新

    使うバージョン(2025年5月時点)

    区分採用理由
    CMSWordPress 6.82025年4月リリースの最新版。ブロックテーマ前提で進める
    親テーマTwenty Twenty-FiveWordPress 6.7 から同梱されている標準テーマ
    PHPPHP 8.3公式 Docker イメージで選べる版。プラグインは PHP 8.2 以上で動く書き方にする
    DBMySQL 8.4(LTS)長期サポート版
    CLIWP-CLI 2初期セットアップと運用作業を手順化するため

    📰 出典:WordPress.org News「WordPress 6.8 “Cecil”」

    PHP のバージョンについて補足します。2025年5月時点の WordPress 公式の互換表では、WordPress 6.8 は PHP 7.2〜8.4 に対応しており、そのうち PHP 8.3 と 8.4 は「beta support(ベータ対応)」と表示されています。WordPress 本体は動作しますが、プラグインを含めた広い検証が続いている段階という位置付けです。本番環境では、利用するプラグインとホスティングの対応状況を見て、8.2 系にするか 8.3 系にするかを決めてください。

    📰 出典:Make WordPress Core「PHP Compatibility and WordPress Versions」

    Docker で WordPress 6.8 の開発環境を作る

    ここから実装です。サンプル一式は次の構成です(第0回時点)。

    code/
    ├─ compose.yaml                … WordPress・MySQL・WP-CLI の3サービス
    ├─ .env.example                … パスワードなどの見本(実値は .env に書く)
    ├─ .gitignore
    ├─ scripts/setup.sh            … インストール〜初期設定を1コマンドで
    └─ wp-content/
       ├─ themes/curation-child/   … 子テーマ(style.css / functions.php / theme.json)
       └─ plugins/curation-tools/  … 自作プラグイン(curation-tools.php)

    compose.yaml:バージョンを固定した3つのサービス

    compose.yaml(抜粋)

    name: wp-curation
    
    services:
      db:
        image: mysql:8.4.5
        environment:
          MYSQL_DATABASE: ${DB_NAME:-wordpress}
          MYSQL_USER: ${DB_USER:-wordpress}
          MYSQL_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD を .env に設定してください}
          MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?DB_ROOT_PASSWORD を .env に設定してください}
        command: ["--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"]
        volumes:
          - db_data:/var/lib/mysql
        healthcheck:
          test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u root -p\"$$MYSQL_ROOT_PASSWORD\" --silent"]
    
      wordpress:
        image: wordpress:6.8.1-php8.3-apache
        depends_on:
          db:
            condition: service_healthy
        ports:
          - "${WP_PORT:-8088}:80"
        environment:
          WORDPRESS_DB_HOST: db:3306
          WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
          # 連載の再現性のため、サンプル環境ではコア自動更新を止めている。
          # 本番では止めずにセキュリティ更新を適用すること(第11回で扱う)。
          WORDPRESS_CONFIG_EXTRA: |
            define( 'WP_ENVIRONMENT_TYPE', 'local' );
            define( 'WP_AUTO_UPDATE_CORE', false );
            define( 'DISALLOW_FILE_EDIT', true );
        volumes:
          - wp_core:/var/www/html
          - ./wp-content/themes/curation-child:/var/www/html/wp-content/themes/curation-child
          - ./wp-content/plugins/curation-tools:/var/www/html/wp-content/plugins/curation-tools
    
      # WP-CLI は必要なときだけ起動する: docker compose run --rm wpcli wp <command>
      wpcli:
        image: wordpress:cli-2.12.0-php8.3
        profiles: ["cli"]
        user: "33:33"
        volumes:
          - wp_core:/var/www/html
          - ./wp-content/themes/curation-child:/var/www/html/wp-content/themes/curation-child
          - ./wp-content/plugins/curation-tools:/var/www/html/wp-content/plugins/curation-tools

    設計のポイントは次の4つです。

    • イメージのタグを固定する:wordpress:6.8-... のような浮動タグは、将来のマイナー版に自動で進みます。記事どおりに再現できるよう、サンプルではタグを固定しています。
    • 自作部分だけをマウントする:WordPress 本体は名前付きボリューム(wp_core)に置き、子テーマとプラグインのフォルダだけをホスト側と共有します。Git で管理するのは自作のコードだけです。
    • パスワードは .env から渡す:${DB_PASSWORD:?...} の書き方で、未設定のまま起動しようとするとエラーで止まります。
    • WP-CLI は profiles で分ける:常時起動はせず、コマンドを打つときだけ一時的にコンテナを起動します。公式イメージの WordPress 側と WP-CLI 側でファイル所有者の UID が異なるため、user: "33:33" で合わせています。

    📰 出典:Docker Hub 公式イメージ wordpress

    WP_AUTO_UPDATE_CORE を false にしているのは、連載の途中でバージョンが変わらないようにするためです。本番環境ではセキュリティ更新を止めないでください。更新の手順は第11回で扱います。DISALLOW_FILE_EDIT は、管理画面からテーマ・プラグインのファイルを直接編集する機能を無効にする設定です。

    .env.example:実値は .env にだけ書く

    .env.example

    # cp .env.example .env して値を変更する。.env はコミットしない。
    DB_NAME=wordpress
    DB_USER=wordpress
    DB_PASSWORD=change-me-db
    DB_ROOT_PASSWORD=change-me-root
    
    # ホスト側のポート(80/8080 が使用中の環境が多いため 8088 を既定にしている)
    WP_PORT=8088
    WP_DEBUG=1
    
    # scripts/setup.sh が使うサイト初期値
    SITE_TITLE=キュレーションメディア(サンプル)
    ADMIN_USER=admin
    ADMIN_PASSWORD=change-me-admin
    ADMIN_EMAIL=admin@example.com

    .gitignore には .env とアップロードファイルの保存先(wp-content/uploads/)を登録しています。

    scripts/setup.sh:インストールと初期設定を手順化する

    管理画面で1つずつ設定すると、担当者によって設定が微妙に違う環境ができてしまいます。WP-CLI(=WordPress をコマンドで操作するツール)で手順をスクリプトにしておけば、誰が実行しても同じ状態になります。

    scripts/setup.sh(抜粋)

    wp() { docker compose --progress quiet run --rm -T wpcli wp "$@"; }
    
    if wp core is-installed 2>/dev/null; then
      echo "==> インストール済みのためスキップ"
    else
      wp core install \
        --url="http://localhost:${WP_PORT:-8088}" \
        --title="${SITE_TITLE}" \
        --admin_user="${ADMIN_USER}" \
        --admin_password="${ADMIN_PASSWORD}" \
        --admin_email="${ADMIN_EMAIL}" \
        --locale=ja \
        --skip-email
    fi
    
    wp language core install ja --activate || echo "(言語パックの取得に失敗。英語のまま続行)"
    wp option update timezone_string 'Asia/Tokyo'
    wp rewrite structure '/%postname%/'
    
    wp theme is-installed twentytwentyfive
    wp theme activate curation-child
    wp plugin activate curation-tools
    
    create_cat web-dev   'Web制作・開発' 'サイト制作・開発まわりの情報を厳選して解説'
    create_cat cloud     'クラウド・インフラ' 'クラウドサービスやサーバー運用の情報'
    create_cat security  'セキュリティ' '脆弱性情報や対策の一次情報をまとめて解説'
    create_cat ai-tools  'AI・業務ツール' '業務で使えるAI・SaaSの比較と使いどころ'
    create_cat marketing 'Webマーケティング' 'SEO・SNS・広告運用の基礎と最新動向'

    すでにインストール済みなら飛ばす、カテゴリが存在すれば作らない、というように何度実行しても同じ結果になる書き方にしています(create_cat は既存チェック付きの小さな関数です。全体は code/ を参照してください)。

    📰 出典:WP-CLI Commands wp core install

    子テーマとプラグインの空の雛形を作る

    子テーマ:style.css と functions.php

    子テーマは style.css のヘッダーに Template: twentytwentyfive と書くことで、親テーマを継承します。

    wp-content/themes/curation-child/style.css(ヘッダー部分の抜粋)

    /*
    Theme Name:        Curation Child
    Description:       「WordPressでキュレーションメディアを作る」連載用の Twenty Twenty-Five 子テーマ。
    Template:          twentytwentyfive
    Version:           0.1.0
    Requires at least: 6.8
    Requires PHP:      8.2
    Text Domain:       curation-child
    */

    ブロックテーマでは、子テーマの style.css は自動では読み込まれません。functions.php で明示的に読み込みます。

    wp-content/themes/curation-child/functions.php(コメントの一部を省略)

    <?php
    if ( ! defined( 'ABSPATH' ) ) {
    	exit;
    }
    
    /**
     * 子テーマの style.css を読み込む。
     *
     * 親テーマの style.css は親の functions.php が 'twentytwentyfive-style' として読み込むので、
     * それに依存させて親の後に出力されるようにする。
     */
    function curation_child_enqueue_styles() {
    	$theme = wp_get_theme();
    	wp_enqueue_style(
    		'curation-child-style',
    		get_stylesheet_uri(),
    		array( 'twentytwentyfive-style' ),
    		$theme->get( 'Version' )
    	);
    }
    add_action( 'wp_enqueue_scripts', 'curation_child_enqueue_styles' );

    📰 出典:WordPress Theme Handbook「Child Themes」

    theme.json は今の時点ではコンテンツ幅と行間だけを設定しています。次回、ここに色・文字サイズ・余白を定義していきます。

    wp-content/themes/curation-child/theme.json

    {
    	"$schema": "https://schemas.wp.org/wp/6.8/theme.json",
    	"version": 3,
    	"settings": {
    		"appearanceTools": true,
    		"layout": {
    			"contentSize": "720px",
    			"wideSize": "1200px"
    		}
    	},
    	"styles": {
    		"typography": {
    			"lineHeight": "1.8"
    		}
    	}
    }

    プラグイン:curation-tools.php

    プラグインは、今回はヘッダーと定数だけの空の状態です。第2回以降、includes/ にファイルを追加してここから読み込みます。

    wp-content/plugins/curation-tools/curation-tools.php(抜粋)

    <?php
    /**
     * Plugin Name:       Curation Tools
     * Description:       キュレーションメディア用の小さな機能群(引用元カード・ランキング・関連記事・構造化データなど)。連載の各回で機能を追加していく。
     * Version:           0.1.0
     * Requires at least: 6.8
     * Requires PHP:      8.2
     * Text Domain:       curation-tools
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
    	exit;
    }
    
    define( 'CURATION_TOOLS_VERSION', '0.1.0' );
    define( 'CURATION_TOOLS_FILE', __FILE__ );
    define( 'CURATION_TOOLS_DIR', plugin_dir_path( __FILE__ ) );
    define( 'CURATION_TOOLS_URL', plugin_dir_url( __FILE__ ) );

    連載のコードは WordPress Coding Standards に合わせ、関数名には curation_child_ / curation_tools_ の接頭辞を付けます。他のテーマ・プラグインと名前が衝突するのを防ぐためです。各 PHP ファイルの先頭にある ABSPATH のチェックは、ファイルへの直接アクセスで処理が走らないようにするための定番の書き方です。

    動作確認の方法

    次の手順で起動し、確認します。筆者の環境でも、すべての項目を確認しました。

    cp .env.example .env        # パスワード類を変更する
    docker compose up -d
    ./scripts/setup.sh
    
    docker compose run --rm wpcli wp core version     # 6.8 系が表示される
    docker compose run --rm wpcli wp theme list       # curation-child が active、twentytwentyfive が parent
    docker compose run --rm wpcli wp plugin list      # curation-tools が active
    curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8088/   # 200
    curl -s http://localhost:8088/ | grep "curation-child-style-css"   # 子テーマCSSの link タグ
    curl -s 'http://localhost:8088/wp-json/wp/v2/categories?_fields=slug,name'
    • トップページが 200 で表示され、HTML に子テーマの CSS(id='curation-child-style-css')が読み込まれている
    • REST API(/wp-json/)のカテゴリ一覧に、サンプルカテゴリ5件と「未分類」が返る
    • 終了は docker compose down、データごと消す場合は docker compose down -v

    つまずきやすい点

    • ポートが使われている:80番や8080番はほかの開発環境と衝突しやすいため、サンプルは 8088 番にしています。変える場合は .env の WP_PORT を変更し、setup.sh をやり直してください(サイトURLにポート番号が入るため)。
    • WP-CLI でファイルの書き込みに失敗する:WordPress 側と WP-CLI 側のコンテナでユーザーIDが違うと、言語ファイルなどの書き込みで権限エラーになります。user: "33:33" はそのための設定です。
    • 親テーマを直接編集してしまう:親テーマは更新で上書きされます。変更は必ず子テーマ(同名のテンプレートや theme.json)とフックで行います。
    • サンプルの設定を本番に持ち込む:WP_DEBUG=1 や自動更新の停止は開発用の設定です。本番の設定は第11回で整理します。

    発注者向けメモ:機能より先に「編集方針」を合意する

    キュレーションメディアを開発会社に依頼するとき、画面デザインや機能の一覧より先に、次の点を社内で決めておくと手戻りが減ります。

    • 誰が書き、誰が確認して公開するか:公開権限を持つ人を絞るかどうかで、権限設計とレビューの仕組み(第11回)の作り込みが変わります。
    • 何を出典として認めるか:公式発表・公的機関の資料を優先するのか、他メディアの記事も出典にするのか。引用のルールと、画像を使う場合の権利確認の方法も含めて決めます。
    • 扱う分野のリスク:医療・健康・お金・法律などを扱うなら、専門家の監修体制が必要か。監修者を表示する場合は、実際に監修している事実が前提です。
    • 広告・アフィリエイトの有無:収益化の方法によって、PR表記の仕組み(第9回)が必要になります。
    • 公開後の運用体制:WordPress 本体・プラグインの更新、バックアップ、障害時の復旧を誰が担当するか。

    機能は後から足せますが、品質の低い記事が大量に公開された後で方針を変えるのは、記事の修正・削除・リダイレクト対応が発生するため高コストです。

    打ち合わせでは、次のように聞いてみてください。

    • 「追加するプラグインの一覧と、それぞれを入れる理由、更新が止まった場合の代替案を教えてください」
    • 「見た目と機能をどう分けて作りますか?テーマを変えたときに失われる機能はありますか?」
    • 「開発環境と本番環境の PHP・WordPress のバージョンはそろえますか?本番の更新はどなたがどの頻度で行う想定ですか?」
    • 「ライターが公開ボタンを押せない運用にできますか?レビューの流れを画面で見せてもらえますか?」

    まとめと次回予告

    第0回では、キュレーションメディアの要件と「低品質まとめ」からの教訓を整理し、Docker で動く WordPress 6.8 の土台を作りました。

    • キュレーションメディアの品質は、機能より先に「誰が書き・確認し・何を出典とするか」で決まる
    • 無断転載・根拠の薄い情報・量産という失敗は、入力欄・表示・権限の仕組みで「防ぐ手助け」ができる
    • 見た目は子テーマ、機能はプラグインに分け、親テーマは編集しない
    • compose.yaml でバージョンを固定し、初期設定は WP-CLI のスクリプトで再現可能にする

    次回は「theme.json でデザイントークンを定義する(子テーマの基本)」です。色・文字サイズ・余白を theme.json にまとめ、管理画面の「スタイル」から一貫して変更できる子テーマにします。

    この連載の記事一覧

    この記事は連載「WordPressで作るキュレーションメディア」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      コメント

      コメント一覧 (1件)

      目次