WordPress

世界最大シェアのCMS。豊富なテーマ・プラグインエコシステムと初心者にも優しいインターフェース。

CMSオープンソースブログPHPMySQL
ライセンス
GPL v2
言語
PHP
料金
完全無料(自己ホスト)

CMS

WordPress

概要

WordPressは、世界で最も人気のあるオープンソースのコンテンツ管理システム(CMS)です。2003年にブログプラットフォームとして誕生し、現在では全ウェブサイトの43.4%以上で使用されています。PHPとMySQLで構築され、その柔軟性と拡張性により、ブログから企業サイト、ECサイトまで幅広い用途に対応できます。

詳細

WordPressは、その使いやすさと豊富な機能で知られています。5分間の簡単なインストールプロセス、直感的な管理画面、強力なプラグインシステム、そして数千種類のテーマにより、技術的な知識が限られているユーザーでも本格的なウェブサイトを構築できます。

主な特徴:

  • プラグインエコシステム: 59,000以上の無料プラグインが利用可能
  • テーマシステム: 数千種類のテーマから選択可能
  • Gutenbergエディタ: ブロックベースの直感的なコンテンツ編集
  • マルチサイト機能: 一つのインストールで複数のサイトを管理
  • RESTful API: ヘッドレスCMSとしても利用可能
  • 多言語対応: 200以上の言語に翻訳
  • SEO最適化: URLの自動生成、メタデータ管理など

メリット・デメリット

メリット

  • 使いやすく、非技術者でも管理可能
  • 巨大なコミュニティとサポート体制
  • 豊富なプラグインとテーマ
  • 定期的なセキュリティアップデート
  • 無料で利用可能(オープンソース)
  • SEOに強い構造
  • モバイルレスポンシブ対応
  • カスタマイズの自由度が高い

デメリット

  • プラグインの多用によるパフォーマンス低下
  • セキュリティの脆弱性(人気ゆえの標的)
  • アップデート時の互換性問題
  • 大規模サイトでのパフォーマンス課題
  • データベース構造の制限
  • カスタマイズには PHP の知識が必要

参考ページ

書き方の例

1. Hello World(基本的なセットアップ)

functions.php(テーマ機能の追加)

<?php
// テーマのセットアップ
function my_theme_setup() {
    // タイトルタグのサポート
    add_theme_support('title-tag');
    
    // アイキャッチ画像のサポート
    add_theme_support('post-thumbnails');
    
    // カスタムメニューの登録
    register_nav_menus(array(
        'primary' => __('Primary Menu', 'my-theme'),
        'footer' => __('Footer Menu', 'my-theme')
    ));
}
add_action('after_setup_theme', 'my_theme_setup');

// スタイルシートとスクリプトの読み込み
function my_theme_scripts() {
    wp_enqueue_style('my-theme-style', get_stylesheet_uri());
    wp_enqueue_script('my-theme-script', 
        get_template_directory_uri() . '/js/script.js', 
        array('jquery'), '1.0.0', true
    );
}
add_action('wp_enqueue_scripts', 'my_theme_scripts');

2. テーマ開発

index.php(メインテンプレート)

<?php get_header(); ?>

<main id="main" class="site-main">
    <?php if (have_posts()) : ?>
        <div class="posts-container">
            <?php while (have_posts()) : the_post(); ?>
                <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
                    <header class="entry-header">
                        <h2 class="entry-title">
                            <a href="<?php the_permalink(); ?>">
                                <?php the_title(); ?>
                            </a>
                        </h2>
                        <div class="entry-meta">
                            <?php echo get_the_date(); ?> | 
                            <?php the_author(); ?> | 
                            <?php the_category(', '); ?>
                        </div>
                    </header>
                    
                    <?php if (has_post_thumbnail()) : ?>
                        <div class="entry-thumbnail">
                            <?php the_post_thumbnail('medium'); ?>
                        </div>
                    <?php endif; ?>
                    
                    <div class="entry-content">
                        <?php the_excerpt(); ?>
                    </div>
                    
                    <footer class="entry-footer">
                        <a href="<?php the_permalink(); ?>" class="read-more">
                            <?php _e('Read More', 'my-theme'); ?> →
                        </a>
                    </footer>
                </article>
            <?php endwhile; ?>
            
            <div class="pagination">
                <?php the_posts_pagination(array(
                    'mid_size' => 2,
                    'prev_text' => __('← Previous', 'my-theme'),
                    'next_text' => __('Next →', 'my-theme'),
                )); ?>
            </div>
        </div>
    <?php else : ?>
        <p><?php _e('No posts found.', 'my-theme'); ?></p>
    <?php endif; ?>
</main>

<?php get_sidebar(); ?>
<?php get_footer(); ?>

3. プラグイン開発

my-plugin.php(基本的なプラグイン構造)

<?php
/**
 * Plugin Name: My Custom Plugin
 * Plugin URI: https://example.com/my-plugin
 * Description: カスタム機能を追加するプラグイン
 * Version: 1.0.0
 * Author: Your Name
 * Author URI: https://example.com
 * License: GPL v2 or later
 * Text Domain: my-plugin
 */

// 直接アクセスを防ぐ
if (!defined('ABSPATH')) {
    exit;
}

// プラグインのアクティベーション
register_activation_hook(__FILE__, 'my_plugin_activate');
function my_plugin_activate() {
    // データベーステーブルの作成
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_plugin_data';
    
    $charset_collate = $wpdb->get_charset_collate();
    $sql = "CREATE TABLE $table_name (
        id int(11) NOT NULL AUTO_INCREMENT,
        name varchar(255) NOT NULL,
        value text NOT NULL,
        created_at datetime DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) $charset_collate;";
    
    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}

// ショートコードの登録
add_shortcode('my_custom_shortcode', 'my_custom_shortcode_handler');
function my_custom_shortcode_handler($atts) {
    $atts = shortcode_atts(array(
        'title' => 'Default Title',
        'color' => 'blue'
    ), $atts);
    
    ob_start();
    ?>
    <div class="my-custom-box" style="border-color: <?php echo esc_attr($atts['color']); ?>">
        <h3><?php echo esc_html($atts['title']); ?></h3>
        <p>This is custom content from my plugin!</p>
    </div>
    <?php
    return ob_get_clean();
}

// 管理画面メニューの追加
add_action('admin_menu', 'my_plugin_admin_menu');
function my_plugin_admin_menu() {
    add_menu_page(
        'My Plugin Settings',
        'My Plugin',
        'manage_options',
        'my-plugin',
        'my_plugin_admin_page',
        'dashicons-admin-generic',
        20
    );
}

function my_plugin_admin_page() {
    ?>
    <div class="wrap">
        <h1>My Plugin Settings</h1>
        <form method="post" action="options.php">
            <?php settings_fields('my_plugin_settings'); ?>
            <?php do_settings_sections('my_plugin_settings'); ?>
            <?php submit_button(); ?>
        </form>
    </div>
    <?php
}

4. カスタム投稿タイプ

custom-post-type.php(カスタム投稿タイプの登録)

<?php
// カスタム投稿タイプ「製品」の登録
add_action('init', 'register_product_post_type');
function register_product_post_type() {
    $labels = array(
        'name' => '製品',
        'singular_name' => '製品',
        'add_new' => '新規追加',
        'add_new_item' => '新しい製品を追加',
        'edit_item' => '製品を編集',
        'new_item' => '新しい製品',
        'view_item' => '製品を表示',
        'search_items' => '製品を検索',
        'not_found' => '製品が見つかりません',
        'not_found_in_trash' => 'ゴミ箱に製品はありません',
        'menu_name' => '製品'
    );
    
    $args = array(
        'labels' => $labels,
        'public' => true,
        'has_archive' => true,
        'menu_icon' => 'dashicons-cart',
        'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'),
        'rewrite' => array('slug' => 'products'),
        'show_in_rest' => true // Gutenbergエディタのサポート
    );
    
    register_post_type('product', $args);
}

// カスタムタクソノミー「製品カテゴリー」の登録
add_action('init', 'register_product_taxonomy');
function register_product_taxonomy() {
    $labels = array(
        'name' => '製品カテゴリー',
        'singular_name' => '製品カテゴリー',
        'search_items' => 'カテゴリーを検索',
        'all_items' => 'すべてのカテゴリー',
        'parent_item' => '親カテゴリー',
        'parent_item_colon' => '親カテゴリー:',
        'edit_item' => 'カテゴリーを編集',
        'update_item' => 'カテゴリーを更新',
        'add_new_item' => '新しいカテゴリーを追加',
        'new_item_name' => '新しいカテゴリー名',
        'menu_name' => '製品カテゴリー'
    );
    
    $args = array(
        'labels' => $labels,
        'hierarchical' => true,
        'public' => true,
        'show_in_rest' => true,
        'rewrite' => array('slug' => 'product-category')
    );
    
    register_taxonomy('product_category', 'product', $args);
}

// カスタムフィールドの追加(ACF不使用)
add_action('add_meta_boxes', 'add_product_meta_boxes');
function add_product_meta_boxes() {
    add_meta_box(
        'product_details',
        '製品詳細',
        'product_details_callback',
        'product',
        'normal',
        'high'
    );
}

function product_details_callback($post) {
    wp_nonce_field('product_details_nonce', 'product_details_nonce');
    $price = get_post_meta($post->ID, '_product_price', true);
    $sku = get_post_meta($post->ID, '_product_sku', true);
    ?>
    <table class="form-table">
        <tr>
            <th><label for="product_price">価格</label></th>
            <td>
                <input type="text" id="product_price" name="product_price" 
                       value="<?php echo esc_attr($price); ?>" class="regular-text" />
            </td>
        </tr>
        <tr>
            <th><label for="product_sku">SKU</label></th>
            <td>
                <input type="text" id="product_sku" name="product_sku" 
                       value="<?php echo esc_attr($sku); ?>" class="regular-text" />
            </td>
        </tr>
    </table>
    <?php
}

// カスタムフィールドの保存
add_action('save_post_product', 'save_product_details');
function save_product_details($post_id) {
    if (!isset($_POST['product_details_nonce']) || 
        !wp_verify_nonce($_POST['product_details_nonce'], 'product_details_nonce')) {
        return;
    }
    
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    
    if (!current_user_can('edit_post', $post_id)) {
        return;
    }
    
    if (isset($_POST['product_price'])) {
        update_post_meta($post_id, '_product_price', 
                        sanitize_text_field($_POST['product_price']));
    }
    
    if (isset($_POST['product_sku'])) {
        update_post_meta($post_id, '_product_sku', 
                        sanitize_text_field($_POST['product_sku']));
    }
}

5. データベース操作

database-operations.php(WPDBを使用したデータベース操作)

<?php
// グローバルな$wpdbオブジェクトを使用
global $wpdb;

// 1. データの挿入
function insert_custom_data($name, $value) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_custom_table';
    
    $result = $wpdb->insert(
        $table_name,
        array(
            'name' => $name,
            'value' => $value,
            'created_at' => current_time('mysql')
        ),
        array('%s', '%s', '%s') // データ型の指定
    );
    
    if ($result === false) {
        error_log('Database insert failed: ' . $wpdb->last_error);
        return false;
    }
    
    return $wpdb->insert_id;
}

// 2. データの取得
function get_custom_data($limit = 10) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_custom_table';
    
    // 準備されたステートメントを使用(SQLインジェクション対策)
    $results = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT * FROM $table_name ORDER BY created_at DESC LIMIT %d",
            $limit
        )
    );
    
    return $results;
}

// 3. 特定のデータを取得
function get_custom_data_by_name($name) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_custom_table';
    
    $result = $wpdb->get_row(
        $wpdb->prepare(
            "SELECT * FROM $table_name WHERE name = %s",
            $name
        )
    );
    
    return $result;
}

// 4. データの更新
function update_custom_data($id, $name, $value) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_custom_table';
    
    $result = $wpdb->update(
        $table_name,
        array(
            'name' => $name,
            'value' => $value,
            'updated_at' => current_time('mysql')
        ),
        array('id' => $id),
        array('%s', '%s', '%s'),
        array('%d')
    );
    
    return $result !== false;
}

// 5. データの削除
function delete_custom_data($id) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_custom_table';
    
    $result = $wpdb->delete(
        $table_name,
        array('id' => $id),
        array('%d')
    );
    
    return $result !== false;
}

// 6. カスタムクエリの実行
function get_statistics() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'my_custom_table';
    
    // 統計情報の取得
    $stats = $wpdb->get_results("
        SELECT 
            COUNT(*) as total_records,
            COUNT(DISTINCT name) as unique_names,
            MAX(created_at) as last_created
        FROM $table_name
    ");
    
    return $stats[0];
}

// 7. トランザクション処理(WordPress 5.5以降)
function complex_database_operation($data_array) {
    global $wpdb;
    
    // トランザクション開始
    $wpdb->query('START TRANSACTION');
    
    try {
        foreach ($data_array as $data) {
            $result = insert_custom_data($data['name'], $data['value']);
            if (!$result) {
                throw new Exception('Insert failed');
            }
        }
        
        // コミット
        $wpdb->query('COMMIT');
        return true;
        
    } catch (Exception $e) {
        // ロールバック
        $wpdb->query('ROLLBACK');
        error_log('Transaction failed: ' . $e->getMessage());
        return false;
    }
}

6. API連携

rest-api.php(REST APIの実装)

<?php
// カスタムREST APIエンドポイントの登録
add_action('rest_api_init', function () {
    // 製品一覧の取得
    register_rest_route('myapi/v1', '/products', array(
        'methods' => 'GET',
        'callback' => 'get_products_api',
        'permission_callback' => '__return_true', // 公開API
        'args' => array(
            'per_page' => array(
                'default' => 10,
                'sanitize_callback' => 'absint',
            ),
            'page' => array(
                'default' => 1,
                'sanitize_callback' => 'absint',
            ),
        ),
    ));
    
    // 単一製品の取得
    register_rest_route('myapi/v1', '/products/(?P<id>\d+)', array(
        'methods' => 'GET',
        'callback' => 'get_single_product_api',
        'permission_callback' => '__return_true',
        'args' => array(
            'id' => array(
                'validate_callback' => function($param, $request, $key) {
                    return is_numeric($param);
                }
            ),
        ),
    ));
    
    // 製品の作成(認証必須)
    register_rest_route('myapi/v1', '/products', array(
        'methods' => 'POST',
        'callback' => 'create_product_api',
        'permission_callback' => function() {
            return current_user_can('edit_posts');
        },
        'args' => array(
            'title' => array(
                'required' => true,
                'sanitize_callback' => 'sanitize_text_field',
            ),
            'content' => array(
                'required' => true,
                'sanitize_callback' => 'wp_kses_post',
            ),
            'price' => array(
                'required' => false,
                'sanitize_callback' => 'sanitize_text_field',
            ),
        ),
    ));
});

// 製品一覧を取得するAPIコールバック
function get_products_api($request) {
    $per_page = $request->get_param('per_page');
    $page = $request->get_param('page');
    
    $args = array(
        'post_type' => 'product',
        'posts_per_page' => $per_page,
        'paged' => $page,
        'post_status' => 'publish',
    );
    
    $query = new WP_Query($args);
    $products = array();
    
    if ($query->have_posts()) {
        while ($query->have_posts()) {
            $query->the_post();
            $products[] = array(
                'id' => get_the_ID(),
                'title' => get_the_title(),
                'content' => get_the_content(),
                'excerpt' => get_the_excerpt(),
                'price' => get_post_meta(get_the_ID(), '_product_price', true),
                'sku' => get_post_meta(get_the_ID(), '_product_sku', true),
                'featured_image' => get_the_post_thumbnail_url(get_the_ID(), 'full'),
                'date' => get_the_date('c'),
                'link' => get_permalink(),
            );
        }
    }
    
    wp_reset_postdata();
    
    return new WP_REST_Response(array(
        'products' => $products,
        'total' => $query->found_posts,
        'pages' => $query->max_num_pages,
        'current_page' => $page,
    ), 200);
}

// 単一製品を取得するAPIコールバック
function get_single_product_api($request) {
    $id = $request->get_param('id');
    $post = get_post($id);
    
    if (!$post || $post->post_type !== 'product') {
        return new WP_Error(
            'product_not_found',
            'Product not found',
            array('status' => 404)
        );
    }
    
    $product = array(
        'id' => $post->ID,
        'title' => $post->post_title,
        'content' => apply_filters('the_content', $post->post_content),
        'excerpt' => $post->post_excerpt,
        'price' => get_post_meta($post->ID, '_product_price', true),
        'sku' => get_post_meta($post->ID, '_product_sku', true),
        'featured_image' => get_the_post_thumbnail_url($post->ID, 'full'),
        'gallery' => get_post_meta($post->ID, '_product_gallery', true),
        'categories' => wp_get_post_terms($post->ID, 'product_category', 
                                         array('fields' => 'names')),
        'date' => get_the_date('c', $post),
        'modified' => get_the_modified_date('c', $post),
        'author' => get_the_author_meta('display_name', $post->post_author),
        'link' => get_permalink($post->ID),
    );
    
    return new WP_REST_Response($product, 200);
}

// 製品を作成するAPIコールバック
function create_product_api($request) {
    $title = $request->get_param('title');
    $content = $request->get_param('content');
    $price = $request->get_param('price');
    
    $post_data = array(
        'post_title' => $title,
        'post_content' => $content,
        'post_type' => 'product',
        'post_status' => 'draft', // 下書きとして作成
        'post_author' => get_current_user_id(),
    );
    
    $post_id = wp_insert_post($post_data);
    
    if (is_wp_error($post_id)) {
        return new WP_Error(
            'product_creation_failed',
            'Failed to create product',
            array('status' => 500)
        );
    }
    
    // カスタムフィールドを保存
    if ($price) {
        update_post_meta($post_id, '_product_price', $price);
    }
    
    return new WP_REST_Response(array(
        'id' => $post_id,
        'message' => 'Product created successfully',
        'link' => get_permalink($post_id),
    ), 201);
}

// JWT認証の実装例(別途JWT認証プラグインが必要)
add_filter('rest_authentication_errors', function($result) {
    // すでにユーザーがログインしている場合はスキップ
    if (!empty($result)) {
        return $result;
    }
    
    // Authorizationヘッダーを確認
    $auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    
    if (strpos($auth_header, 'Bearer ') === 0) {
        $token = substr($auth_header, 7);
        
        // JWTトークンの検証(実装は省略)
        $user_id = verify_jwt_token($token);
        
        if ($user_id) {
            wp_set_current_user($user_id);
            return true;
        }
    }
    
    return $result;
});