コンテンツへスキップ

WordPressにdatabaseを作る

サイトでデータを貯めておきたい場合(例:不特定多数の人が編集可能な表を作ったり)、バックエンドのどこかに更新内容を保存できるデータベースが必要です。データベースがなければ、サイトに入力した内容は、サイトを閉じると消えてしまいます。

幸いにも、WordPressでは、サイトをホストしているFTP(サイトのデータが登録されているフォルダーのようなもの)にカスタムテーブルを作成することができます。

実際にデータベースを活用して作ったものはこちら。画像もテーブルに直接保存してます。

手順

  1. 自分のWordPressサイトをホストしているサービスにログインします。
    自分の場合は「ロリポップ!」というサービスです。
  2. ログインしたら、FTPにアクセスします。
  1. wp-content/pluginsのディレクトリに、新しいディレクトリcustom-tableを作成します。
  2. wp-content/plugins/custom-tableのディレクトリに、custom-table.phpというファイルを作成し、中身にテーブルを作成する下記を入力して保存する。文字コードはUTF-8にしておいた方がいいです。
<?php
/*
Plugin Name: My Custom Table Creator
Description: A simple plugin to create a custom database table.
Version: 1.0
Author: guregu321
*/

function create_custom_table() {
    global $wpdb;

    // The name of our custom table
    $table_name = $wpdb->prefix . 'custom_table_spatoon3_special'; 

    // The database charset and collate should be used.
    $charset_collate = $wpdb->get_charset_collate();

    // SQL statement to create the table
    $sql = "CREATE TABLE $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        special tinytext NOT NULL,
        name tinytext NOT NULL,
        score mediumint NOT NULL,
        PRIMARY KEY (id)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}

// Hook the function to the plugin activation hook
register_activation_hook(__FILE__, 'create_custom_table');

?>
  1. WordPressのダッシュボードにログインし、「インストール済みプラグイン」を開く。
  2. PHPで作成した「My Custom Table Creator」があるので有効化するとテーブルが作成される。(プラグインを無効化してもテーブルは削除されないので注意。削除する場合は、削除するPHPを実行しなければいけない。)
  3. 作成したテーブルは、phpMyAdminから確認ができます。自分は「ロリポップ!」からアクセスできました。なんならphpMyAdminから直接テーブル作成してもいいです。
  1. テーブルを開いたら、テスト用に「挿入」からレコードを追加しておく。
  2. 上のPHPファイルの中身をテーブルのデータを取得するコードに置き換えます。あとは、サイトのショートコードブロックまたはカスタムHTMLブロックに
    スペシャル 名前 回数
    アメフラシ やまち 7
    ウルトラショット ぐれ 12
    ウルトラハンコ ぐれ 12
    エナジースタンド ぐれ 12
    カニタンク ぐれ 11
    キューインキ ぐれ 10
    グレートバリア ぐれ 11
    サメライド ぐれ 9
    ジェットパック ぐれ 12
    ショクワンダー Keisuke 10
    テイオウイカ Keisuke,ぐれ 10
    デコイチラシ Keisuke 12
    トリプルトルネード Keisuke,ぐれ 13
    ナイスダマ Keisuke 13
    ホップソナー Keisuke 12
    マルチミサイル ぐれ 11
    メガホンレーザー5.1ch ぐれ 14
    を入れると、サイトを開くだけでテーブルからデータを表示してくれます。
function populate_custom_table() {
    global $wpdb;

    // Fetch data from custom table
    $table_name = $wpdb->prefix . 'custom_table_spatoon3_special';
    $results = $wpdb->get_results( "SELECT * FROM $table_name" );

    $style = '    .modern-table {
        border-collapse: collapse;
        background-color: #fff;
        color: #333;
        box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
    }

    .modern-table td,
    .modern-table th {
        padding: 0px 10px 0px 10px;
        text-align: left;
        border: none;
    }

    .modern-table td[contenteditable="true"] {
        background-color: #E8F4FF;
        border: 1px dashed #5AC8FA;
    }

    .modern-table tr:first-child th,
    .modern-table tr:first-child td {
        font-weight: bold;
    }

    .modern-table tr {
        background-color: #f6f6f6;
    }

    .modern-table input[type=text],
    .modern-table select {
        width: 100%;
        border: 1px solid #ccc;
        padding: 5px;
        font-size: 14px;
        border-radius: 5px;
        box-sizing: border-box;
        transition: border-color 0.3s ease;
    }

    .modern-table input[type=text]:focus,
    .modern-table select:focus {
        border-color: #5AC8FA;
        outline: none;
    }';

    // Start the table HTML
    $table_html = '<style>' . $style . '</style>';
    $table_html .= '<table class="modern-table">
        <tbody>
            <tr>
                <th>スペシャル</th>
                <th>名前</th>
                <th>回数</th>
            </tr>';

    // Loop through each result and add to table
    $row_id = 1;
    foreach( $results as $row ) {
        $table_html .= '<tr>
            <td>' . esc_html( $row->special ) . '</td>
            <td contenteditable="true" data-id="' . $row_id . '" data-column="name">' . esc_html( $row->name ) . '</td>
            <td contenteditable="true" data-id="' . $row_id . '" data-column="score">' . esc_html( $row->score ) . '</td>
        </tr>';
        $row_id++;
    }

    // Close the table HTML
    $table_html .= '</tbody></table>';

    return $table_html;
}

add_shortcode( 'custom_table', 'populate_custom_table' );
  1. 次は、データの変更をデータベースに反映するための設定をします。PHPと同じディレクトリにedit-table.jsを作成し、中身に下記を入力して保存する。
document.addEventListener('DOMContentLoaded', function () {
    const editableCells = document.querySelectorAll('td[contenteditable="true"]');

    editableCells.forEach(function (cell) {
        cell.addEventListener('blur', function () {
            let password = prompt("パスワードを入力してね!");
            if (password === null) return;

            let id = cell.getAttribute('data-id');
            let column = cell.getAttribute('data-column');
            let value = cell.innerText;

            jQuery.post(ajax_object.ajax_url, {
                action: 'save_table_data',
                nonce: ajax_object.nonce,
                password: password,
                id: id,
                column: column,
                value: value
            }, function (response) {
                alert(response);
            });
        });
    });
});
  1. そしてPHPに下記のコードを追加します。これでサイト上でデータを更新したときに、データベースにも反映されるようになります。
function enqueue_custom_scripts() {
    wp_enqueue_script('edit-table-script', plugin_dir_url(__FILE__) . '/edit-table.js', array('jquery'), '1.0', true);
    wp_localize_script('edit-table-script', 'ajax_object', array('ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('edit_table_nonce')));
}

add_action('wp_enqueue_scripts', 'enqueue_custom_scripts');

function save_table_data() {
    global $wpdb;

    // Check the nonce for security
    $nonce = $_POST['nonce'];
    if (!wp_verify_nonce($nonce, 'edit_table_nonce')) {
        die('Permission denied.');
    }

    // Password check
    $input_password = sanitize_text_field($_POST['password']);
    $correct_password = "パスワード";  // Set your desired password here
    if ($input_password !== $correct_password) {
        echo "パスワードが違います!";
        wp_die();
    }

    $id = intval($_POST['id']);
    $column = sanitize_text_field($_POST['column']);
    $value = sanitize_text_field($_POST['value']);

    $table_name = $wpdb->prefix . 'custom_table_spatoon3_special';
    $result = $wpdb->update($table_name, array($column => $value), array('id' => $id));

    if ($result === false) {
        echo "Error: " . $wpdb->last_error;
        wp_die();
    }

    echo "データが更新されました!";
    wp_die();
}

add_action('wp_ajax_save_table_data', 'save_table_data'); // For logged-in users
add_action('wp_ajax_nopriv_save_table_data', 'save_table_data'); // For non-logged-in users

※スマホだと、ブラウザかWordPressのキャッシュ機能で最新のデータを毎回取得してくれませんでした。なので、Ajax経由でコードが毎回実行されるようにしました。まずはPHPに以下を追加する。

function load_custom_table_data() {
    echo populate_custom_table();
    wp_die();
}

add_action('wp_ajax_load_custom_table_data', 'load_custom_table_data'); // For logged-in users
add_action('wp_ajax_nopriv_load_custom_table_data', 'load_custom_table_data'); // For non-logged-in users

そして、JavaScriptを以下に書き換える。

document.addEventListener('DOMContentLoaded', function () {
    
    // Load table data
    loadTableData();
    
    function loadTableData() {
        // Show a loading message or spinner here (optional)
        document.querySelector('#tableContainer').innerHTML = "Loading...";

        jQuery.post(ajax_object.ajax_url, {
            action: 'load_custom_table_data',
            nonce: ajax_object.nonce,
        }, function (response) {
            // Replace the content of a container (e.g., div) with the table data
            document.querySelector('#tableContainer').innerHTML = response;
            
            attachEditableEvents(); // Once the table is loaded, attach the editable events to the cells
        });
    }

    function attachEditableEvents() {
        const editableCells = document.querySelectorAll('td[contenteditable="true"]');

        editableCells.forEach(function (cell) {
            cell.addEventListener('blur', function () {
                let password = prompt("パスワードを入力してね!");
                if (password === null) return;

                let id = cell.getAttribute('data-id');
                let column = cell.getAttribute('data-column');
                let value = cell.innerText;

                jQuery.post(ajax_object.ajax_url, {
                    action: 'save_table_data',
                    nonce: ajax_object.nonce,
                    password: password,
                    id: id,
                    column: column,
                    value: value
                }, function (response) {
                    alert(response);
                });
            });
        });
    }

});

※画像を処理するPHPは以下にしました。

// ##################################
// ############# Load Image #############
// ##################################

function fetch_image_data()
{
    global $wpdb;

    $id = intval($_POST['id']);

    $table_name = $wpdb->prefix . 'custom_table_spatoon3_special';
    $row = $wpdb->get_row($wpdb->prepare("SELECT image_mime_type, image_data FROM $table_name WHERE id = %d", $id));

    if ($row) {
        wp_send_json_success([
            'mime_type' => $row->image_mime_type,
            'image_data' => base64_encode($row->image_data),
        ]);
    } else {
        wp_send_json_error([
            'message' => 'Image not found.',
        ]);
    }
}

add_action('wp_ajax_fetch_image_data', 'fetch_image_data'); // For logged-in users
add_action('wp_ajax_nopriv_fetch_image_data', 'fetch_image_data'); // For non-logged-in users




// ##################################
// ############# Upload Image #############
// ##################################

function upload_image_data()
{
    global $wpdb;

    // Check the nonce for security
    $nonce = $_POST['nonce'];
    if (!wp_verify_nonce($nonce, 'edit_table_nonce')) {
        die('Permission denied.');
    }

    // Password check
    $input_password = sanitize_text_field($_POST['password']);
    $correct_password = "パスワード";  // Set your desired password here
    if ($input_password !== $correct_password) {
        echo "グルの名前が違います!";
        wp_die();
    }

    $id = $_POST['id'];
    $file = $_FILES['image_data'];

    // Get the image data and mime type
    $image_data = file_get_contents($file['tmp_name']);
    $image_mime_type = $file['type'];

    $table_name = $wpdb->prefix . 'custom_table_spatoon3_special';
    $result = $wpdb->update($table_name, array('image_data' => $image_data, 'image_mime_type' => $image_mime_type), array('id' => $id));

    if ($result === false) {
        echo "Error: " . $wpdb->last_error;
        wp_die();
    }

    echo "データが更新されました!";
    wp_die();
}

add_action('wp_ajax_upload_custom_table_image', 'upload_image_data');
add_action('wp_ajax_nopriv_upload_custom_table_image', 'upload_image_data');

画像を処理するJavaScriptは以下の通り。

jQuery(document).on('click', '.image-link', function (e) {
    e.preventDefault();

    const imageId = jQuery(this).data('id');

    jQuery.post(ajax_object.ajax_url, {
        action: 'fetch_image_data',
        nonce: ajax_object.nonce,
        id: imageId
    }, function (response) {
        if (response.success && response.data) {
            openImageInNewTab(event, response.data.mime_type, response.data.image_data);
        } else {
            alert(response.data);
        }
    });

    function openImageInNewTab(event, mimeType, base64Data) {
        event.preventDefault();

        // Convert base64 data to blob
        const byteCharacters = atob(base64Data);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray], { type: mimeType });

        // Create object URL and open in new tab
        const objectUrl = URL.createObjectURL(blob);
        window.open(objectUrl);
    }

});

jQuery(document).ready(function () {
    // Handle the upload button click
    jQuery(document).on('click', '.upload-image-btn', function () {
        var rowId = jQuery(this).data('id');
        console.log("Initial Row ID:", rowId);
        var targetInput = jQuery('.image-upload-input[data-id="' + rowId + '"]');
        console.log(targetInput.length);  // This should log '1' if the input is found.
        targetInput.click();
    });

    // Handle the actual image selection
    jQuery(document).on('change', '.image-upload-input', function (event) {
        event.stopPropagation();

        var rowId = jQuery(this).data('id');
        console.log("Second Row ID:", rowId);

        var file = this.files[0];
        if (file) {

            let password = prompt("グルの名前を入力してね!");
            if (password === null) return;

            let special = jQuery(this).attr('data-special');
            let isConfirmed = window.confirm(special + "の画像を更新しますか?");
            if (!isConfirmed) {
                return;
            }

            var formData = new FormData();
            formData.append('image_data', file);
            formData.append('nonce', ajax_object.nonce);
            formData.append('action', 'upload_custom_table_image');
            formData.append('password', password);
            formData.append('id', rowId);

            jQuery.ajax({
                url: ajax_object.ajax_url,
                type: 'POST',
                data: formData,
                contentType: false,
                processData: false,
                success: function (response) {
                    if (response === "グルの名前が違います!") {
                        jQuery('.image-upload-input[data-id="' + rowId + '"]').val(''); // Reset the file input
                    }
                    alert(response);
                }
            });
        } else {
            console.log("File was not properly selected");
        }
    });
});