プログラム研究ブログ

propansystemの研究ブログ

これまでに作成したプラグインは、データ登録画面で、入力フォームを表示し、次の画面で内容チェック、その後に完了画面を表示する。という一連の動作になっています。

この時、入力フォームから確認画面へ遷移する際のセキュリティとして、CSRF攻撃がされる場面が想定されます。

このCSRF攻撃に対応する為に、対策を入れます。
具体的には、入力画面のform開始タグの直下に、以下の命令を書きます。

<?php wp_nonce_field("my-nonce-key", "my-form"); ?>

また、確認画面側では、次のように書いて、CSRFのチェックを行います。

//CSRF対策用のチェック
if (isset($_POST["my-form"]) && $_POST["my-form"]) {
    if (check_admin_referer("my-nonce-key", "my-form")) {
        //問題がなければ、ここで処理を続行
        
    }
}

ポイントとなる箇所だけを書きましたが、フォーム内に「wp_nonce_field」を、確認画面のチェック処理で「check_admin_referer」を、それぞれ使うということで、覚えておいてよいかと思います。

前回の投稿ではメディアアップローダの画面を利用したファイルのアップロードを書きましたが、今回は単純なプログラムによるファイルのアップロードを書きます。

プラグインを作成する方向性としては、無意味な実装になるかもしれませんが、ここは実験がてらやってみます。

まず、作成中のプラグインのTOP画面にファイルアップロード用のボタンを追加します。

    function disp()
    {
        echo <<< EOL
<form action="" method="post">

    <h2>データ一覧</h2>
    <input type='submit' name='submit[regist]' class='button-primary' value='新規登録' />
    <input type='submit' name='submit[regist_file]' class='button-primary' value='新規登録(ファイルアップロード)' />

    <div class="wrap">

    <table class="wp-list-table widefat striped posts">
        <tr>
            <th nowrap>ID</th>
            <th nowrap>名前</th>
            <th nowrap>テキスト</th>
            <th nowrap>チェック</th>
            <th nowrap>ラジオ</th>
            <th nowrap>セレクト</th>
            <th nowrap>テキストエリア</th>
            <th nowrap>登録日時</th>
            <th nowrap>詳細</th>
            <th nowrap>編集</th>
        </tr>
EOL;

以下、省略します…

すると次のような画面になります。

続いて、ボタンを押下した際の動作を決める関数に、ファイルアップロード用画面と、アップロードボタンを押された際のメソッドを追加します。

function sample_plugin()
{
    if (isset($_REQUEST["submit"]["detail"])) {
        //他の処理は記述を省略します…

    } else if (isset($_REQUEST["submit"]["regist_file"])) {
        //ファイルアップロード用画面
        self::regist_file();
    } else if (isset($_REQUEST["submit"]["regist_file_up"])) {
        //ファイルアップロード処理
        self::regist_file_up();

    } else {
        self::disp();
    }
}

次に、画面上の「新規登録(ファイルアップロード)」ボタンを押した時の処理を書きます。

function regist_file($error_message_flg = null)
{
    echo <<< EOL
<h2>ファイルアップロード</h2>
<div class="wrap">
<div class="wrap">
<table class="wp-list-table widefat striped posts">
    <tr>
        <td>ファイルアップロード</td>
        <td>
            <form method="post" action="" enctype="multipart/form-data">
                CSVを選択してアップロードボタンを押してください<br />
                <input type="file" name="upfilename" />
                <input type="submit" value="アップロード">
                <input type="hidden" name="submit[regist_file_up]" value="on">
            </form>

        </td>
    </tr>
</table>
</div>
<form action="" method="post">
    <input type='submit' name='submit[file_upload]' class='button-primary' value='戻る' />
    <input type="hidden" name="form_id" value="{$form_id}">
    <input type="hidden" name="create_date" value="{$create_date}">
</form>
</div>
EOL;

}

今はデザインやレイアウトについては、割愛して書いています。
画面はこのようになります。

画面上から、ファイルを選択してアップロードボタンを押した後の処理は次のように書きました。

function regist_file_up()
{
    //CSVファイルがアップロードされた場合
    if (is_uploaded_file($_FILES["upfilename"]["tmp_name"])) {
        $upload_dir = wp_upload_dir();
        $upload_file_name = $upload_dir['basedir'] . "/" . $_FILES["upfilename"]["name"];
        if (move_uploaded_file($_FILES["upfilename"]["tmp_name"], $upload_file_name)) {
            chmod($upload_file_name, 0777);
        }
        $message = "ファイルをアップロードいたしました";
    } else {
        $message = "ファイルのアップロードが失敗しました";
    }

    //完了メッセージの出力
    echo <<< EOL
<h2>ファイルアップロード完了</h2>
<form action="" method="post">
<div class="wrap">
    {$message}
</div>
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
</form>
EOL;

}

書き方は一般的なファイルアップロードによる処理と、変わらないですが、「wp_upload_dir();」というwordpressが用意している関数を使っている点がポイントになります。

この関数をコールし、「$upload_dir[‘basedir’]」という値をとることにより、wordpressの「uploads」ディレクトリまでのフルパスを参照できます。

そうすることで、uploadsディレクトリ直下にファイルをアップロードすることができています。

完了後は、次のような画面になります。

これで、ファイルをアップロードが完了します。
工夫や改善の余地はありますが、基本的な流れはできているので、あとはどのように応用するかになります。

管理画面側に、wordpressに搭載されているメディアアップローダを組み込んでみます。

今回、非常に参考にしたサイトはこちらのサイト様になります。

そもそも管理画面にファイルをアップロードする機能はphp標準のファイルアップロードの処理を書くことでも実装が可能です。
ただ、その場合はアップロード先ディレクトリをどうするか、仕様を決めておかないといけない為、配慮すべき項目が多くなりがちです。

そこでメディアアップローダを使うことで、アップロードとファイル保存先についてはある程度wordpressまかせにすることができます。

以下、実装手順を記載します。

まず、__construct()等に、必要となるjavascript関連のファイルを読み込みます。

wp_enqueue_script('media-upload');
wp_enqueue_script('thickbox');

次に、スタイルシート関連のファイルも読み込みます。これは同じくコンストラクタ内で問題ありませんが、独自にスタイルシート読み込み用に関数を分けておいてもいいかもしれません。

wp_enqueue_style('thickbox');

続いて、登録画面に対して、ファイルアップロードの為のフォームを記載します。(ここは上記参考サイト様の書き方を引用させていただきました)

<input type="text" id="my_media_1" name="my_media_1" value="" />
<a class="media-upload" href="JavaScript:void(0);" rel="my_media_1">ファイル選択</a>

そして、フォーム内の「ファイル選択」リンクを押下した時に、
メディアアップロードのダイアログを立ち上げる為に、以下のjavascriptを記載します。

<script type="text/javascript">
jQuery("document").ready(function(){
    jQuery(".media-upload").each(function(){
        var rel = jQuery(this).attr("rel");
        jQuery(this).click(function(){
            window.send_to_editor = function(html) {
                imgurl = jQuery("img", html).attr("src");
                jQuery("#"+rel).val(imgurl);
                tb_remove();
            }   
            formfield = jQuery("#"+rel).attr("name");
            tb_show(null, "media-upload.php?post_id=0&type=image&TB_iframe=true");
            return false;
        });
    });
});
</script>

という記載をフォームが表示される画面内のHTMLタグのどこかに記載します。

以上の記述をすることで、フォーム画面からメディアアップロードの画面を出し、そこでファイルを選択して、URL等をフォームにセットすることができます。

以下、これまでに作成してきたサンプルプラグインの「新規登録」画面に対して、ファイルアップロードの項目を追加してみたので、そのキャプチャを貼ります。

赤枠部分が実際に操作をしてみて、ファイルのアップロード(例ではアップロード済みのファイルを選択しました)、ファイル名取得、フォーム項目への設置、という一連の動きになっています。

①新規登録画面へ

②登録画面から「ファイル選択」をクリック

③既にアップロード済みのファイルを選択

④該当のファイル(例では「00.png」を選択)し、詳細画面を表示。その後「ファイルのURL」を取得するボタンを押す

⑤ファイル名が取得できます

⑥同画面の下にある「投稿に挿入」ボタンを押して、元の新規登録フォームへ戻ると、ファイル名がセットされています。

ページャの見直し

前回のページャを設置した記事をおさらいを含めて、見直してみます。

前回投稿した記事http://propansystem.net/blog/?p=1022では、登録データ数の無関係にページャ表示をしていました。

この場合、1画面内に10記事のデータ数を超えている場合は問題ありませんが、10記事以内のデータの場合にもページャが1ページ分のみ表示されてしまいます。

ページャをクリックしても何も起きず、ページャを表示していまうことで余計に操作しずらくなります。

そこで、データ一覧を出力する箇所のページャ表示部分に件数制限のロジックを入れて、10件未満の場合にはページャを表示しないようにします。

データ一覧を表示する関数部分を抜粋します。

	function disp()
	{
		//データ一覧
		echo <<< EOL
<form action="" method="post">
<h2>データ一覧</h2>
<input type='submit' name='submit[regist]' class='button-primary' value='新規登録' />
<div class="wrap">
<table class="wp-list-table widefat striped posts">
	<tr>
		<th nowrap>ID</th>
		<th nowrap>名前</th>
		<th nowrap>テキスト</th>
		<th nowrap>チェック</th>
		<th nowrap>ラジオ</th>
		<th nowrap>セレクト</th>
		<th nowrap>テキストエリア</th>
		<th nowrap>登録日時</th>
		<th nowrap>詳細</th>
		<th nowrap>編集</th>
	</tr>
EOL;

		//現在ページ取得
		$pageid = filter_input(INPUT_GET, 'pageid');

		//1ページあたりの件数
		$limit = 10;

		//DBオブジェクトを用意
		global $wpdb;

		//全件数取得
		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT count(*) AS CNT FROM {$tbl_name}";
		$rows = $wpdb->get_results($sql);
		$recordcount = $rows[0]->CNT;

		$offset = $pageid * $limit;

		//offset と limitによる画面表示用のデータ取得
		$sql = "SELECT * FROM {$tbl_name} ORDER BY id limit {$offset}, {$limit};";

		//通常の取得方法(SQL実行結果を、オブジェクトとして取得)
		$rows = $wpdb->get_results($sql);

		foreach($rows as $row) {
			echo "<tr>";
			echo "<td>" . $row->id . "</td>";
			echo "<td>" . $row->sample_name . "</td>";
			echo "<td>" . $row->sample_text . "</td>";
			echo "<td>" . $row->sample_check . "</td>";
			echo "<td>" . $row->sample_radio . "</td>";
			echo "<td>" . $row->sample_select . "</td>";
			echo "<td>" . $row->sample_textarea . "</td>";
			echo "<td>" . $row->create_date . "</td>";
			echo "<td>";
			echo "<input type='submit' name='submit[detail][" . $row->id . "]'";
			echo " class='button-primary' value='詳細' />";
			echo "</td>";
			echo "<td>";
			echo "<input type='submit' name='submit[edit][" . $row->id . "]'";
			echo " class='button-primary' value='編集' />";
			echo "</td>";
			echo "</tr>";
		}
		echo "</table>";
		echo "</div>";
		echo "</form>";

		//データ数が1画面あたりの記事数を超える場合のみ、ページャを表示する
		if ($recordcount > $limit) {
			$args = array(
				'label' => __('Per Page'),
				'default' => 10,
				'option' => 'disp'
			);
			$page_html = self::pagination($recordcount);

			//ページ部分の表示
			echo "<div class='admin_pagination'>";
			echo "<ul>";
			foreach ($page_html as $key => $value) {
				echo "<li>" . $value . "</li>";
			}
			echo "</ul>";
		}

	}

関数の最後で「if ($recordcount > $limit) {」という分岐を行い、
10件に満たない場合の制御をしています。

こうすることにより、データ数が少ない場合は、以下のような表示になります。

画面表示の書き方はもっとシンプルで、わかりやすい書き方はあると思いますが、今は不恰好ですが動作しているので次に進めていきます。

wordpress流の書き方については、今後の研究課題にして追求していく予定です。

クイックリターン関数について、調べてみます。
下記のようなコードを例にします。

add_filter("test_filter", "sample_func");
function sample_func() {
    //何らかの処理
    return true;
}

上記のコードは「test_filter」としてフィルターフックを指定し、sample_func関数をコールバックしています。

sample_func関数内で何らかの処理を行う関数の場合、次のような戻り値の時に簡略化して書くことができます。

「true」「false」「array()」(空の配列を返す)「””」「null」「0」

上記の戻り値の場合、

function sample_func() {
    //何らかの処理
    return true;
}

の関数定義を書かずに、

add_filter("test_filter", "__return_true");

のように一行で書くことできます。
下記のそれぞれのケースでは、
「false」「array()」(空の配列を返す)「””」「null」「0」
次のように書けます。

//false
add_filter("test_filter", "__return_false");
//array()
add_filter("test_filter", "__return_empty_array");
//""
add_filter("test_filter", "__return_empty_string");
//null
add_filter("test_filter", "__return_null");
//0
add_filter("test_filter", "__return_zero");

実際にフィルターを使う時に試してみるほうが良さそうです。

前の投稿ではプラグインを有効化した時に実行される関数についてでしたが、今回はプラグインを無効化した時に実行される関数について調べてみます。

無効化する時に実行される関数は以下になります。

register_deactivation_hook($file, $callback);

第一引数は、有効化した時の関数と同じく、実行されるプログラムのファイル名を指定します。
「__FILE__」等と指定しても問題なく動作します。

第二引数は無効化した時に実行されるコールバック関数を指定します。

例として、以下のように書きます。

register_deactivation_hook(__FILE__, 'test_de_action');

function test_de_action() {
    //ここで何らかの処理
}

関数側がクラスを使っている場合は、次のように書きます。

register_activation_hook(__FILE__, array('test_class', 'test_de_action');
 
class test_class {
    function test_de_action() {
        //ここで何らかの処理
    }
}

有効化と同じように書けるので、有効化の処理が問題なく動作すれば、そのまま無効化も同じように書くことができます。

プラグインの機能として、プラグインをインストールした後に、「有効化」を行うタイミングの時に1度だけ実行される関数が用意されています。

以前、プラグインを作成した際、過去のブログ(http://propanmode.net/blog/?p=884)でも取り上げていましたが、もう少し深く調べてみます。

プラグインの有効化のタイミングで実行される関数は、以下のようになります。

register_activation_hook($file, $callback);

第一引数はプラグインのメインPHPファイルのパスを、
第二引数にはプラグインを有効化した時に実行されるコールバック関数を指定します。

例として、次のようになります。

register_activation_hook(__FILE__, 'test_action');

function test_action() {
    //ここで何らかの処理
}

関数側がクラスを使っている場合は、第二引数の書き方が次のように変わります。

register_activation_hook(__FILE__, array('test_class', 'test_action');

class test_class {
    function test_action() {
        //ここで何らかの処理
    }
}

実際にプラグイン作成時に書いた処理なので、動作検証については既に完了しています。
あとは処理の中で何をどうプログラムするのかで様々な操作をすることが可能になるので、そこは設計次第になります。

アクションフックにアクションを登録する方法は以下になります。

※wordpressの公式ドキュメントからの抜粋になりますが、若干説明を簡易にしています。

add_action(アクションを適用するフック名, コールバック関数, 優先順位, フックした関数が受け入れられる引数の数);

この登録したアクションの中身は以下のように記述します。
※これもwordpressの公式ドキュメントから参照したコードです。

function email_friends( $post_ID )  {
   $friends = 'bob@example.org, susie@example.org';
   wp_mail( $friends, "sally's blog updated", 'I just put something on my blog: http://blog.example.com' );

   return $post_ID;
}
add_action( 'publish_post', 'email_friends' );

上記のコードは「ブログを投稿しときに、メール通知を行う」という動作のようです。

add_actionの第一引数で「アクションを呼ぶきっかけとなる動作」を決定しています。
この第一引数には、様々な操作を設定できるようで、以下の公式ドキュメントにまとめられています。
http://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7

上記URLを見ると「一般的リクエスト中に実行されるアクション」や「管理画面リクエスト中に実行されるアクション」等といったアクションのきっかけがまとめれています。

この情報を使って第一引数を決定し、特定の動作を行う関数を用意する。
というような実装の流れになると思われます。(実際の実装は今後の課題予定とします)

アクションフックについて

do_action()が実行されている場所をアクションフックと呼びます。

アクションフックはフィルターフックと似ています。
どちらもフック(きっかけ)があった場合に処理が動きます。

アクションフックは、管理画面などで、投稿記事の公開、テーマ変更、などが行われた際に始動されます。

また、フィルターフックについてはwordpressのサイト側を表示する際に、特定の処理を通して表示を変更したい場合、等に利用されます。

アクションについて

アクションは特定のイベントが発生した際に実行される処理を言います。
例えば、wp_headアクションはよく利用されるアクションです。

テンプレート内でwp_headが呼び出されたら、cssやjsの読み込みを一括して行う。といった処理で利用される例があります。

apply_filters()にフィルターを追加するにはadd_filter()関数を使います。

これは実際に書いてみないと分からないので、簡単なサンプルプログラムを作ります。

前回の投稿で、簡単なサンプルを作りました。
それをおさらいしてみます。

フィルターフックを設置する
function sample_func1() {
    return apply_filters("sample_filter", "hoge");
}

//「sample_filter」フックに対して、コールバック関数(sample_call)を指定する
add_filter("sample_filter", "sample_call");
 
//コールバック関数
function sample_call($value) {
    return "fuga";
}

applry_filtersはフィルターフックを設置するための関数です。
第一引数がフック名、第二引数がコールバック関数への引数になります。

フィルターフックについて

フィルターの使い方について、学びます。

フィルターフックについて

wordpressはHTMLの出力や、各種設定、環境変数、フォーム入力値などは、apply_filters()という関数を経由します。

例えば、ある関数を定義して、それをフィルターフックを設置する場合は、次のように書きます。

function sample_func1() {
	return "test";
}

この関数をフィルターフックを設置します。

function sample_func1() {
	return apply_filters("sample_filter", "hoge");
}

上記のフィルターフックを、プラグインやテーマのfunctions.phpで次のように適用します。

add_filter("sample_filter", "sample_call");

function sample_call($value) {
	return "fuga";
}

実際にこのフィルターフックを利用したプログラムを作成してみます。

これまでの管理画面の作りは、ほぼphpのみで実装してきました。

次にこの管理画面にjavascirpt(jquery)を動作させるように設定してみます。

まず、プラグインフォルダの中に、jsファイルを設置する為のディレクトリを作成します。

/ワードプレス設置ディレクトリ/wp-content/plugins/SamplePlugin1

上記のディレクトリに対し、jsディレクトリを作成します。

/ワードプレス設置ディレクトリ/wp-content/plugins/SamplePlugin1/js/

次に、そのディレクトリ内に、javascript用のファイルを設置します。

/ワードプレス設置ディレクトリ/wp-content/plugins/SamplePlugin1/js/sample1_script.js

jsの中は、jqueryが使用できるので、以下のようなサンプルコードを書きます。

(function($){
    alert("hello jquery !");
})(jQuery);

次に、プラグイン本体のphpのコンストラクタの中に、以下の読み込み命令文を書きます。

wp_register_script('sample1js', plugins_url( './js/sample1_script.js', __FILE__ ));
wp_enqueue_script('sample1js');

こうすることで、プラグインの画面にアクセスした時にjsがロードされます。
例では、アラートを表示するだけのシンプルなjsですが、画面にアクセスすると以下のようになります。

アラート表示だけのプログラムなので、あまり意味がないですが、jsが動作することを確認できました。

登録画面を追加する

前回までのソースコードを元に、新規登録画面の一連の動きを書きます。

	/**
	 * 登録
	 */
	function regist($error_message_flg = null)
	{
		if ($error_message_flg == false) {
			$sample_name = esc_attr($_REQUEST["sample_name"]);
		}

		if (isset($_REQUEST["form_id"])) {
			//確認画面の「戻る」ボタンから遷移してきた場合
			$form_id = esc_attr($_REQUEST["form_id"]);

			$sample_name = esc_attr($_REQUEST["sample_name"]);
			$sample_text = esc_attr($_REQUEST["sample_text"]);
			$sample_check = $_REQUEST["sample_check"];
			$sample_radio = esc_attr($_REQUEST["sample_radio"]);
			$sample_select = esc_attr($_REQUEST["sample_select"]);
			$sample_textarea = esc_attr($_REQUEST["sample_textarea"]);

			//チェックボックスの判定
			foreach ($sample_check as $key => $value) {
				$sample_check_checked[$value] = "checked";
			}
			
			//ラジオボタンの判定
			for ($i=0; $i<3; $i++) {
				if ($sample_radio == $i) {
					$sample_radio_checked[$i] = "checked";
				}
			}
			
			//セレクトボックスの判定
			$sample_select_array = array("a", "b", "c", "d", "e");
			foreach ($sample_select_array as $key => $value) {
				if ($sample_select == $value) {
					$sample_select_selected[$key] = "selected";
				}
			}

			$create_date = esc_attr($_REQUEST["create_date"]);
		} else {
			
			//一覧画面から「編集」ボタンを押下した場合
			
			//押されたボタンのIDを取得する
			if (array_search("編集", $_REQUEST["submit"]["edit"])) {
				$form_id = array_search("編集", $_REQUEST["submit"]["edit"]);
			}

			global $wpdb;
			$tbl_name = $wpdb->prefix . 'sample_mst';
			$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
			$prepared = $wpdb->prepare($sql, $form_id);
			$rows = $wpdb->get_results($prepared, ARRAY_A);

			$sample_name = $rows[0]["sample_name"];
			$sample_text  = $rows[0]["sample_text"];
			$sample_check  = $rows[0]["sample_check"];
			$sample_radio  = $rows[0]["sample_radio"];
			$sample_select  = $rows[0]["sample_select"];
			$sample_textarea = $rows[0]["sample_textarea"];

			$sample_check = explode(",", $sample_check);

			//チェックボックスの判定
			foreach ($sample_check as $key => $value) {
				$sample_check_checked[$value] = "checked";
			}
			
			//ラジオボタンの判定
			for ($i=0; $i<3; $i++) {
				if ($sample_radio == $i) {
					$sample_radio_checked[$i] = "checked";
				}
			}
			
			//セレクトボックスの判定
			$sample_select_array = array("a", "b", "c", "d", "e");
			foreach ($sample_select_array as $key => $value) {
				if ($sample_select == $value) {
					$sample_select_selected[$key] = "selected";
				}
			}

			$create_date = $rows[0]["create_date"];

		}

		echo <<< EOL
<form action="" method="post">
<h2>データ登録</h2>
EOL;

		if (strlen($error_message_flg)) {
			echo "<div class='updated fade'><p><strong>";
			echo _e('NAMEを入力してください');
			echo "</strong></p></div>";
		}

		echo <<< EOL

<div class="wrap">

<form action="" method="post">
	<h2>データ編集</h2>

	<div class="wrap">

	<table class="wp-list-table widefat striped posts">
		<tr>
			<td>ID</td>
			<td>{$form_id}</td>
		</tr>
		<tr>
			<td>NAME</td>
			<td>
				<input type="text" name="sample_name" value="{$sample_name}">
			</td>
		</tr>

		<tr>
			<td>テキスト</td>
			<td>
				<input type="text" name="sample_text" value="{$sample_text}">
			</td>
		</tr>
		<tr>
			<td>チェックボックス</td>
			<td>
				チェック値1<input type="checkbox" name="sample_check[]" value="0" {$sample_check_checked[0]}>
				チェック値2<input type="checkbox" name="sample_check[]" value="1" {$sample_check_checked[1]}>
				チェック値3<input type="checkbox" name="sample_check[]" value="2" {$sample_check_checked[2]}>
			</td>
		</tr>
		<tr>
			<td>ラジオボタン</td>
			<td>
				ラジオ値1<input type="radio" name="sample_radio" value="0" {$sample_radio_checked[0]}>
				ラジオ値2<input type="radio" name="sample_radio" value="1" {$sample_radio_checked[1]}>
				ラジオ値3<input type="radio" name="sample_radio" value="2" {$sample_radio_checked[2]}>
			</td>
		</tr>
		<tr>
			<td>セレクトボックス</td>
			<td>
				<select name="sample_select">
					<option value="a" {$sample_select_selected[0]}>選択a</option>
					<option value="b" {$sample_select_selected[1]}>選択b</option>
					<option value="c" {$sample_select_selected[2]}>選択c</option>
					<option value="d" {$sample_select_selected[3]}>選択d</option>
					<option value="e" {$sample_select_selected[4]}>選択e</option>
				</select>
			</td>
		</tr>
		<tr>
			<td>テキストエリア</td>
			<td>
				<textarea name="sample_textarea" cols="30" rows="5">{$sample_textarea}</textarea>
			</td>
		</tr>

		<tr>
			<td>登録日時</td>
			<td>{$create_date}</td>
		</tr>
	</table>

	<input type='submit' name='submit[regist_check]' class='button-primary' value='登録内容を確認する' />
	<input type='submit' name='submit[]' class='button-primary' value='戻る' />

	<input type="hidden" name="form_id" value="{$form_id}">
	<input type="hidden" name="create_date" value="{$create_date}">

	</div>

</form>

EOL;
	}

	/**
	 * 登録確認
	 */
	function regist_check()
	{
		if (!strlen($_REQUEST["sample_name"])) {
			self::regist(false);
			return;
		}
		
		$sample_name = esc_attr($_REQUEST["sample_name"]);

		$sample_name = esc_attr($_REQUEST["sample_name"]);
		$sample_text = esc_attr($_REQUEST["sample_text"]);
		$sample_check = $_REQUEST["sample_check"];
		$sample_radio = esc_attr($_REQUEST["sample_radio"]);
		$sample_select = esc_attr($_REQUEST["sample_select"]);
		$sample_textarea = esc_attr($_REQUEST["sample_textarea"]);

		//チェックボックスの値を処理
		if (is_array($_REQUEST["sample_check"])) {
			foreach ($_REQUEST["sample_check"] as $key => $value) {
				$disp_checkbox .= "チェック値" . $value . "<br />";
				$hidden_checkbox .= '<input type="hidden" name="sample_check[]" value="' . $value . '">';
			}
		}

		//テキストエリア整形(表示用)
		$sample_textarea_disp = nl2br($sample_textarea);

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ編集確認</h2>

	<div class="wrap">

	    <table class="wp-list-table widefat striped posts">
			<tr> 
				<td>ID</td>
				<td>{$form_id}</td>
			</tr>
			<tr> 
				<td>NAME</td>
				<td>{$sample_name}</td>
			</tr>
			<tr> 
				<td>テキスト</td>
				<td>{$sample_text}</td>
			</tr>
			<tr> 
				<td>チェックボックス</td>
				<td>
					{$disp_checkbox}
				</td>
			</tr>
			<tr> 
				<td>ラジオボタン</td>
				<td>{$sample_radio}</td>
			</tr>
			<tr> 
				<td>セレクトボックス</td>
				<td>{$sample_select}</td>
			</tr>
			<tr> 
				<td>テキストエリア</td>
				<td>{$sample_textarea_disp}</td>
			</tr>
		</table>

	<input type="hidden" name="form_id" value="{$form_id}">

	<input type="hidden" name="sample_name" value="{$sample_name}">
	<input type="hidden" name="sample_text" value="{$sample_text}">
	{$hidden_checkbox}
	<input type="hidden" name="sample_radio" value="{$sample_radio}">
	<input type="hidden" name="sample_select" value="{$sample_select}">
	<input type="hidden" name="sample_textarea" value="{$sample_textarea}">

	<input type="hidden" name="create_date" value="{$create_date}">

	</div>

	<input type='submit' name='submit[regist_exec]' class='button-primary' value='登録する' />
	<input type='submit' name='submit[regist]' class='button-primary' value='戻る' />

</form>
EOL;

	}

	/**
	 * 登録実行
	 */
	function regist_exec()
	{
		global $wpdb;

		$sample_name = esc_attr($_REQUEST["sample_name"]);
		$sample_text = esc_attr($_REQUEST["sample_text"]);
		$sample_radio = esc_attr($_REQUEST["sample_radio"]);
		$sample_select = esc_attr($_REQUEST["sample_select"]);
		$sample_textarea = esc_attr($_REQUEST["sample_textarea"]);

		//チェックボックスの値を処理
		if (is_array($_REQUEST["sample_check"])) {
			foreach ($_REQUEST["sample_check"] as $key => $value) {
				$sample_check .= $value . ",";
			}
		}

		//投稿を登録
		$table_name = $wpdb->prefix . 'sample_mst';
		$result = $wpdb->insert(
			$table_name,
			array(
				'sample_name' => $sample_name,
				'sample_text' => $sample_text,
				'sample_check' => $sample_check,
				'sample_radio' => $sample_radio,
				'sample_select' => $sample_select,
				'sample_textarea' => $sample_textarea,
				'create_date' => current_time('mysql')
			)
		);

		//データ一覧
		echo <<< EOL
<form action="" method="post">
<h2>データ登録</h2>
<div class='updated fade'><p><strong>
EOL;

		echo _e('登録が完了しました');

		echo <<<EOL
</strong></p></div>
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

更新系ですので、修正の処理を同じような流れになります。
登録フォームがあり、確認画面があり、完了メッセージ画面があります。

詳細画面の項目を追加する

これまでに追加した項目を編集画面で自由に編集ができるようになりました。

次は詳細画面の項目を追加し、表示項目を合わせようと思います。
改修したソースは以下のようになります。


/**
 * 詳細表示
 */
function detail()
{
	//押されたボタンのIDを取得する
	if (array_search("詳細", $_REQUEST["submit"]["detail"])) {
		$form_id = array_search("詳細", $_REQUEST["submit"]["detail"]);
	}

	//データ一覧
	echo <<< EOL
<form action="" method="post">
<h2>データ詳細</h2>

EOL;

	global $wpdb;

	$tbl_name = $wpdb->prefix . 'sample_mst';
	$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
	$prepared = $wpdb->prepare($sql, $form_id);
	$rows = $wpdb->get_results($prepared, ARRAY_A);

	$sample_name = $rows[0]["sample_name"];
	$sample_text  = $rows[0]["sample_text"];
	$sample_check  = $rows[0]["sample_check"];
	$sample_radio  = $rows[0]["sample_radio"];
	$sample_select  = $rows[0]["sample_select"];
	$sample_textarea = $rows[0]["sample_textarea"];

	$create_date = $rows[0]["create_date"];

	echo <<<EOL

<div class="wrap">

<table class="wp-list-table widefat striped posts">
	<tr>
		<td>ID</td>
		<td>{$form_id}</td>
	</tr>

	<tr>
		<td>NAME</td>
		<td>{$sample_name}</td>
	</tr>

	<tr>
		<td>テキスト</td>
		<td>{$sample_text}</td>
	</tr>
	<tr>
		<td>チェック</td>
		<td>{$sample_check}</td>
	</tr>
	<tr>
		<td>ラジオ</td>
		<td>{$sample_radio}</td>
	</tr>
	<tr>
		<td>セレクト</td>
		<td>{$sample_select}</td>
	</tr>
	<tr>
		<td>テキストエリア</td>
		<td>{$sample_textarea}</td>
	</tr>

	<tr>
		<td>登録日時</td>
		<td>{$create_date}</td>
	</tr>
</table>

</div>

<input type="hidden" name="form_id" value="{$form_id}">

<input type='submit' name='submit[delete_check]' class='button-primary' value='削除確認する' />
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
	echo "</form>";
}

実行した画面は以下のようになります。

DBの内容をそのまま表示しているので、チェックボックス、ラジオボタン、セレクトボックスの箇所は工夫が必要になります。
何を出力させたいのか、を明確にして形づくるのがよいです。

編集ボタン押下時の処理を書く

前回までは、一覧画面から該当のデータを選択し、編集フォームにDBの値をセットし、その値を編集して確認画面を出力するところまで実装しました。

次は、編集確認画面から、編集ボタンを押下し、編集処理をするプログラムを書きます。

編集実行時のプログラムは以下のようになります。

/**
 * 編集実行
 */
function edit_exec()
{
	global $wpdb;

	$form_id = esc_attr($_REQUEST["form_id"]);

	$sample_name = esc_attr($_REQUEST["sample_name"]);
	$sample_text = esc_attr($_REQUEST["sample_text"]);
	$sample_radio = esc_attr($_REQUEST["sample_radio"]);
	$sample_select = esc_attr($_REQUEST["sample_select"]);
	$sample_textarea = esc_attr($_REQUEST["sample_textarea"]);
	$update_date = date("Y-m-d H:i:s");

	//チェックボックスの値を処理
	if (is_array($_REQUEST["sample_check"])) {
		foreach ($_REQUEST["sample_check"] as $key => $value) {
			$disp_checkbox .= "チェック値" . $value . "<br />";
			$sample_check .= $value . ',';
		}
	}

	//投稿を更新
	$tbl_name = $wpdb->prefix . 'sample_mst';
	$result = $wpdb->update(
		$tbl_name,
		array(
			'sample_name' => $sample_name,
			'sample_text' => $sample_text,
			'sample_check' => $sample_check,
			'sample_radio' => $sample_radio,
			'sample_select' => $sample_select,
			'sample_textarea' => $sample_textarea,
			'update_date' => $update_date,
		),
		array('id' => $form_id,),
		array(
			'%s',
			'%s',
			'%s',
			'%s',
			'%s',
			'%s'
		),
		array('%d')
	);

	//データ一覧
	echo <<< EOL
<form action="" method="post">
	<h2>データ修正</h2>
	<div class='updated fade'><p><strong>
EOL;
	echo _e('更新が完了しました');
	echo <<<EOL
</strong></p></div>
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
</form>
EOL;
}

この処理を実行すると、DB内には、編集画面で入力された値が格納されます。

$wpdbの使い方は、さらに研究する必要がありますが、今のところ上記の書き方で更新されるので、この書き方で進めます。

また、前回までに書いた編集画面の値のセットの仕方がいまいちな部分があったので、それも合わせて変更しました。

/**
 * 修正
 */
function edit()
{
	if (isset($_REQUEST["form_id"])) {
		//確認画面の「戻る」ボタンから遷移してきた場合
		$form_id = esc_attr($_REQUEST["form_id"]);

		$sample_name = esc_attr($_REQUEST["sample_name"]);
		$sample_text = esc_attr($_REQUEST["sample_text"]);
		$sample_check = $_REQUEST["sample_check"];
		$sample_radio = esc_attr($_REQUEST["sample_radio"]);
		$sample_select = esc_attr($_REQUEST["sample_select"]);
		$sample_textarea = esc_attr($_REQUEST["sample_textarea"]);

		//チェックボックスの判定
		foreach ($sample_check as $key => $value) {
			$sample_check_checked[$value] = "checked";
		}
		
		//ラジオボタンの判定
		for ($i=0; $i<3; $i++) {
			if ($sample_radio == $i) {
				$sample_radio_checked[$i] = "checked";
			}
		}
		
		//セレクトボックスの判定
		$sample_select_array = array("a", "b", "c", "d", "e");
		foreach ($sample_select_array as $key => $value) {
			if ($sample_select == $value) {
				$sample_select_selected[$key] = "selected";
			}
		}

		$create_date = esc_attr($_REQUEST["create_date"]);
	} else {
		
		//一覧画面から「編集」ボタンを押下した場合
		
		//押されたボタンのIDを取得する
		if (array_search("編集", $_REQUEST["submit"]["edit"])) {
			$form_id = array_search("編集", $_REQUEST["submit"]["edit"]);
		}

		global $wpdb;
		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
		$prepared = $wpdb->prepare($sql, $form_id);
		$rows = $wpdb->get_results($prepared, ARRAY_A);

		$sample_name = $rows[0]["sample_name"];
		$sample_text  = $rows[0]["sample_text"];
		$sample_check  = $rows[0]["sample_check"];
		$sample_radio  = $rows[0]["sample_radio"];
		$sample_select  = $rows[0]["sample_select"];
		$sample_textarea = $rows[0]["sample_textarea"];

		$sample_check = explode(",", $sample_check);

		//チェックボックスの判定
		foreach ($sample_check as $key => $value) {
			$sample_check_checked[$value] = "checked";
		}
		
		//ラジオボタンの判定
		for ($i=0; $i<3; $i++) {
			if ($sample_radio == $i) {
				$sample_radio_checked[$i] = "checked";
			}
		}
		
		//セレクトボックスの判定
		$sample_select_array = array("a", "b", "c", "d", "e");
		foreach ($sample_select_array as $key => $value) {
			if ($sample_select == $value) {
				$sample_select_selected[$key] = "selected";
			}
		}

		$create_date = $rows[0]["create_date"];
	}

	//データ一覧
	echo <<< EOL
<form action="" method="post">
	<h2>データ編集</h2>

	<div class="wrap">

	<table class="wp-list-table widefat striped posts">
		<tr>
			<td>ID</td>
			<td>{$form_id}</td>
		</tr>
		<tr>
			<td>NAME</td>
			<td>
				<input type="text" name="sample_name" value="{$sample_name}">
			</td>
		</tr>

		<tr>
			<td>テキスト</td>
			<td>
				<input type="text" name="sample_text" value="{$sample_text}">
			</td>
		</tr>
		<tr>
			<td>チェックボックス</td>
			<td>
				チェック値1<input type="checkbox" name="sample_check[]" value="1" {$sample_check_checked[0]}>
				チェック値2<input type="checkbox" name="sample_check[]" value="2" {$sample_check_checked[1]}>
				チェック値3<input type="checkbox" name="sample_check[]" value="3" {$sample_check_checked[2]}>
			</td>
		</tr>
		<tr>
			<td>ラジオボタン</td>
			<td>
				ラジオ値1<input type="radio" name="sample_radio" value="1" {$sample_radio_checked[0]}>
				ラジオ値2<input type="radio" name="sample_radio" value="2" {$sample_radio_checked[1]}>
				ラジオ値3<input type="radio" name="sample_radio" value="3" {$sample_radio_checked[2]}>
			</td>
		</tr>
		<tr>
			<td>セレクトボックス</td>
			<td>
				<select name="sample_select">
					<option value="a" {$sample_select_selected[0]}>選択a</option>
					<option value="b" {$sample_select_selected[1]}>選択b</option>
					<option value="c" {$sample_select_selected[2]}>選択c</option>
					<option value="d" {$sample_select_selected[3]}>選択d</option>
					<option value="e" {$sample_select_selected[4]}>選択e</option>
				</select>
			</td>
		</tr>
		<tr>
			<td>テキストエリア</td>
			<td>
				<textarea name="sample_textarea" cols="30" rows="5">{$sample_textarea}</textarea>
			</td>
		</tr>

		<tr>
			<td>登録日時</td>
			<td>{$create_date}</td>
		</tr>
	</table>

	<input type='submit' name='submit[edit_check]' class='button-primary' value='編集内容を確認する' />
	<input type='submit' name='submit[]' class='button-primary' value='戻る' />

	<input type="hidden" name="form_id" value="{$form_id}">
	<input type="hidden" name="create_date" value="{$create_date}">

	</div>

</form>
EOL;
}

変更した箇所は、一覧画面から該当のデータを選択した際、チェックボックス、ラジオボタン、セレクトボックスのそれぞれの値がうまくセットされなかったので、次のように記述し、処理を追加しました。

//チェックボックスの判定
foreach ($sample_check as $key => $value) {
	if ($value != "") {
		$sample_check_checked[$key] = "checked";
	}
}

//ラジオボタンの判定
for ($i=0; $i<3; $i++) {
	if ($sample_radio == $i) {
		$sample_radio_checked[$i] = "checked";
	}
}

//セレクトボックスの判定
$sample_select_array = array("a", "b", "c", "d", "e");
foreach ($sample_select_array as $key => $value) {
	if ($sample_select == $value) {
		$sample_select_selected[$key] = "selected";
	}
}

こうすることにより、既存のデータをフォームにセットすることができるようになります。
フォーム関連の書き方はこの限りではないので、もっとうまい書き方や、効率的な書き方、ライブラリを使った書き方など、数多くの方法があると思われます。
そこはおいおいの研究課題にします。

編集の一連の動作は、次のような画面になります。

■編集一覧

■編集ボタン押下後

■値の変更

■編集確認画面

■編集実行画面

これで修正画面の一連の動作ができるようになったので、次は詳細画面、削除画面へとりかかっていきます。

編集画面の確認画面を作成する

前回作成した修正画面に対して、確認画面を拡張します。

これまでは、確認画面に「sample_text」の項目1つだけしかありませんでしたが、ここに「テキスト」「チェックボックス」「ラジオボタン「セレクトボックス」「テキストエリア」の項目を追加します。

修正画面と、修正確認画面のメソッドを変更したソース全体を記載します。

    /**
     * 修正
     */
    function edit()
    {
        if (isset($_REQUEST["form_id"])) {
            //確認画面の「戻る」ボタンから遷移してきた場合
            $form_id = esc_attr($_REQUEST["form_id"]);

            $sample_name = esc_attr($_REQUEST["sample_name"]);
            $sample_text = esc_attr($_REQUEST["sample_text"]);
            $sample_check = $_REQUEST["sample_check"];
            $sample_radio = esc_attr($_REQUEST["sample_radio"]);
            $sample_select = esc_attr($_REQUEST["sample_select"]);
            $sample_textarea = esc_attr($_REQUEST["sample_textarea"]);

            //チェックボックスの判定
            foreach ($sample_check as $key => $value) {
                if ($value != "") {
                    $sample_check_checked[$key] = "checked";
                }
            }
            
            //ラジオボタンの判定
            for ($i=0; $i<3; $i++) {
                if ($sample_radio == $i) {
                    $sample_radio_checked[$i] = "checked";
                }
            }
            
            //セレクトボックスの判定
            $sample_select_array = array("a", "b", "c", "d", "e");
            foreach ($sample_select_array as $key => $value) {
                if ($sample_select == $value) {
                    $sample_select_selected[$key] = "selected";
                }
            }

            $create_date = esc_attr($_REQUEST["create_date"]);
        } else {
            
            //一覧画面から「編集」ボタンを押下した場合
            
            //押されたボタンのIDを取得する
            if (array_search("編集", $_REQUEST["submit"]["edit"])) {
                $form_id = array_search("編集", $_REQUEST["submit"]["edit"]);
            }

            global $wpdb;
            $tbl_name = $wpdb->prefix . 'sample_mst';
            $sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
            $prepared = $wpdb->prepare($sql, $form_id);
            $rows = $wpdb->get_results($prepared, ARRAY_A);

            $sample_name = $rows[0]["sample_name"];
            $sample_text  = $rows[0]["sample_text "];
            $sample_check  = $rows[0]["sample_check "];
            $sample_radio  = $rows[0]["sample_radio "];
            $sample_select  = $rows[0]["sample_select "];
            $sample_textarea = $rows[0]["sample_textarea"];

            $create_date = $rows[0]["create_date"];
        }

        //データ一覧
        echo <<< EOL
<form action="" method="post">
    <h2>データ編集</h2>

    <div class="wrap">

    <table class="wp-list-table widefat striped posts">
        <tr>
            <td>ID</td>
            <td>{$form_id}</td>
        </tr>
        <tr>
            <td>NAME</td>
            <td>
                <input type="text" name="sample_name" value="{$sample_name}">
            </td>
        </tr>

        <tr>
            <td>テキスト</td>
            <td>
                <input type="text" name="sample_text" value="{$sample_text}">
            </td>
        </tr>
        <tr>
            <td>チェックボックス</td>
            <td>
                チェック値1<input type="checkbox" name="sample_check[]" value="1" {$sample_check_checked[0]}>
                チェック値2<input type="checkbox" name="sample_check[]" value="2" {$sample_check_checked[1]}>
                チェック値3<input type="checkbox" name="sample_check[]" value="3" {$sample_check_checked[2]}>
            </td>
        </tr>
        <tr>
            <td>ラジオボタン</td>
            <td>
                ラジオ値1<input type="radio" name="sample_radio" value="1" {$sample_radio_checked[0]}>
                ラジオ値2<input type="radio" name="sample_radio" value="2" {$sample_radio_checked[1]}>
                ラジオ値3<input type="radio" name="sample_radio" value="3" {$sample_radio_checked[2]}>
            </td>
        </tr>
        <tr>
            <td>セレクトボックス</td>
            <td>
                <select name="sample_select">
                    <option value="a" {$sample_select_selected[0]}>選択a</option>
                    <option value="b" {$sample_select_selected[1]}>選択b</option>
                    <option value="c" {$sample_select_selected[2]}>選択c</option>
                    <option value="d" {$sample_select_selected[3]}>選択d</option>
                    <option value="e" {$sample_select_selected[4]}>選択e</option>
                </select>
            </td>
        </tr>
        <tr>
            <td>テキストエリア</td>
            <td>
                <textarea name="sample_textarea" cols="30" rows="5">{$sample_textarea}</textarea>
            </td>
        </tr>

        <tr>
            <td>登録日時</td>
            <td>{$create_date}</td>
        </tr>
    </table>

    <input type='submit' name='submit[edit_check]' class='button-primary' value='編集内容を確認する' />
    <input type='submit' name='submit[]' class='button-primary' value='戻る' />

    <input type="hidden" name="form_id" value="{$form_id}">
    <input type="hidden" name="create_date" value="{$create_date}">

    </div>

</form>
EOL;
    }

    /**
     * 編集確認
     */
    function edit_check()
    {
        $form_id = esc_attr($_REQUEST["form_id"]);
        $sample_name = esc_attr($_REQUEST["sample_name"]);

        $sample_text = esc_attr($_REQUEST["sample_text"]);
        $sample_check = esc_attr($_REQUEST["sample_check"]);
        $sample_radio = esc_attr($_REQUEST["sample_radio"]);
        $sample_select = esc_attr($_REQUEST["sample_select"]);
        $sample_textarea = esc_attr($_REQUEST["sample_textarea"]);

        $create_date = esc_attr($_REQUEST["create_date"]);

        if (!strlen($sample_name)) {
            echo _e('NAMEが未入力です');
        }

        //チェックボックスの値を処理
        if (is_array($_REQUEST["sample_check"])) {
            foreach ($_REQUEST["sample_check"] as $key => $value) {
                $disp_checkbox .= "チェック値" . $value . "<br />";
                $hidden_checkbox .= '<input type="hidden" name="sample_check[]" value="' . $value . '">';
            }
        }

        //テキストエリア整形(表示用)
        $sample_textarea_disp = nl2br($sample_textarea);

        //データ一覧
        echo <<< EOL
<form action="" method="post">
    <h2>データ編集確認</h2>

    <div class="wrap">

        <table class="wp-list-table widefat striped posts">
            <tr> 
                <td>ID</td>
                <td>{$form_id}</td>
            </tr>
            <tr> 
                <td>NAME</td>
                <td>{$sample_name}</td>
            </tr>
            <tr> 
                <td>テキスト</td>
                <td>{$sample_text}</td>
            </tr>
            <tr> 
                <td>チェックボックス</td>
                <td>
                    {$disp_checkbox}
                </td>
            </tr>
            <tr> 
                <td>ラジオボタン</td>
                <td>{$sample_radio}</td>
            </tr>
            <tr> 
                <td>セレクトボックス</td>
                <td>{$sample_select}</td>
            </tr>
            <tr> 
                <td>テキストエリア</td>
                <td>{$sample_textarea}</td>
            </tr>
        </table>

    <input type="hidden" name="form_id" value="{$form_id}">

    <input type="hidden" name="sample_name" value="{$sample_name}">
    <input type="hidden" name="sample_text" value="{$sample_text}">
    {$hidden_checkbox}
    <input type="hidden" name="sample_radio" value="{$sample_radio}">
    <input type="hidden" name="sample_select" value="{$sample_select}">
    <input type="hidden" name="sample_textarea" value="{$sample_textarea_disp}">

    <input type="hidden" name="create_date" value="{$create_date}">

    <input type='submit' name='submit[edit_exec]' class='button-primary' value='編集する' />
    <input type='submit' name='submit[edit]' class='button-primary' value='戻る' />

    </div>

</form>
EOL;
    }

値のチェック方法や、受け渡しの方法など、細かい箇所は修正する必要があると思いますが、全体の入力→確認への流れはこのような形で作りました。

これを実行すると、次のような入力画面と確認画面になります。

まず、一覧画面になります。

次に、一覧画面から「編集」を選択したデータを表示します。

編集内容を入力します。

「編集内容を確認する」ボタンを押下すると、確認画面に入力した値が表示されます。

確認画面から「戻る」ボタンを押しても、入力値を保持したまま、前画面へ遷移することもできます。

編集画面の管理項目を追加する

前回追加したテーブルの値を編集する為に、管理画面の「編集時」の表示を変更します。

これまでは「NAME」という値の編集しかできませんでしたが、
ここに管理項目として「テキスト」「チェックボックス」「ラジオボタン」「セレクトボックス」「テキストエリア」という項目を追加します。

修正時のメソッド全体は以下のようになります。

	/**
	 * 修正
	 */
	function edit()
	{

		if (isset($_REQUEST["form_id"])) {
			$form_id = esc_attr($_REQUEST["form_id"]);
			$sample_name = esc_attr($_REQUEST["sample_name"]);
			$create_date = esc_attr($_REQUEST["create_date"]);
		} else {
			//押されたボタンのIDを取得する
			if (array_search("編集", $_REQUEST["submit"]["edit"])) {
				$form_id = array_search("編集", esc_attr($_REQUEST["submit"]["edit"]));
			}

			global $wpdb;

			$tbl_name = $wpdb->prefix . 'sample_mst';
			$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
			$prepared = $wpdb->prepare($sql, $form_id);
			$rows = $wpdb->get_results($prepared, ARRAY_A);

			$sample_name = $rows[0]["sample_name"];

			$sample_text  = $rows[0]["sample_text "];
			$sample_check  = $rows[0]["sample_check "];
			$sample_radio  = $rows[0]["sample_radio "];
			$sample_select  = $rows[0]["sample_select "];
			$sample_textarea = $rows[0]["sample_textarea"];

			$create_date = $rows[0]["create_date"];
		}

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ編集</h2>

	<div class="wrap">

	<table class="wp-list-table widefat striped posts">
		<tr>
			<td>ID</td>
			<td>{$form_id}</td>
		</tr>
		<tr>
			<td>NAME</td>
			<td>
				<input type="text" name="sample_name" value="{$sample_name}">
			</td>
		</tr>

		<tr>
			<td>テキスト</td>
			<td>
				<input type="text" name="sample_text" value="{$sample_text}">
			</td>
		</tr>
		<tr>
			<td>チェックボックス</td>
			<td>
				チェック値1<input type="checkbox" name="sample_check" value="1">
				チェック値2<input type="checkbox" name="sample_check" value="2">
				チェック値3<input type="checkbox" name="sample_check" value="3">
			</td>
		</tr>
		<tr>
			<td>ラジオボタン</td>
			<td>
				ラジオ値1<input type="radio" name="sample_radio" value="1">
				ラジオ値2<input type="radio" name="sample_radio" value="2">
				ラジオ値3<input type="radio" name="sample_radio" value="3">
			</td>
		</tr>
		<tr>
			<td>セレクトボックス</td>
			<td>
				<select name="sample_select">
					<option value="a">選択a</option>
					<option value="b">選択b</option>
					<option value="c">選択c</option>
					<option value="d">選択d</option>
					<option value="e">選択e</option>
				</select>
			</td>
		</tr>
		<tr>
			<td>テキストエリア</td>
			<td>
				<textarea name="sample_textarea" cols="30" rows="5">{$sample_textarea}</textarea>
			</td>
		</tr>

		<tr>
			<td>登録日時</td>
			<td>{$create_date}</td>
		</tr>
	</table>

	<input type='submit' name='submit[edit_check]' class='button-primary' value='編集内容を確認する' />
	<input type='submit' name='submit[]' class='button-primary' value='戻る' />

	<input type="hidden" name="form_id" value="{$form_id}">
	<input type="hidden" name="create_date" value="{$create_date}">

	</div>

</form>
EOL;
	}

上記のように書くと、画面は以下のように表示されます。

この増やした項目に対して、登録や修正を行えるようにしていきます。

管理項目を追加する

これまで作ってきたプラグインは、項目が「sample_name」という1つの項目だけを取り扱ってきました。

今後は、sample_nameという項目以外にも、チェックボックス、ラジオボタン、セレクトボックス、テキストエリア、を取り扱う管理画面を作ってみるので、プラグインインストール時に実行されるテーブル作製のcreate table文を拡張してみます。

これまでは、以下のcreate文です。

CREATE TABLE {$table_name} (
id				INT NOT NULL AUTO_INCREMENT,
sample_name		VARCHAR(128),
create_date		DATETIME,
PRIMARY KEY(id)
) {$charset_collate};

これを、次のように拡張します。

CREATE TABLE {$table_name} (
id              INT NOT NULL AUTO_INCREMENT,
sample_name     VARCHAR(128),
sample_text     VARCHAR(256),
sample_check    VARCHAR(256),
sample_radio    VARCHAR(256),
sample_select   VARCHAR(256),
sample_textarea text,
create_date     DATETIME,
PRIMARY KEY(id)
) {$charset_collate};

実行されるタイミングはプラグインをインストールした時に一度実行されるので、メソッド全体では次のようになります。

/**
 * プラグインインストール時
 */
function create_tables_sample_mst()
{
    global $wpdb;

    $charset_collate = "";

    //接頭辞の追加(socal_count_cache)
    $table_name = $wpdb->prefix . 'sample_mst';

    //charsetを指定する
    if (!empty($wpdb->charset)) {
        $charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset} ";
    }

    //照合順序を指定する(ある場合、通常デフォルトのutf8_general_ci)
    if (!empty($wpdb->collate)) {
        $charset_collate .= "COLLATE {$wpdb->collate}";
    }

    $sql = <<< EOL
CREATE TABLE {$table_name} (
id              INT NOT NULL AUTO_INCREMENT,
sample_name     VARCHAR(128),
sample_text     VARCHAR(256),
sample_check    VARCHAR(256),
sample_radio    VARCHAR(256),
sample_select   VARCHAR(256),
sample_textarea text,
create_date     DATETIME,
PRIMARY KEY(id)
) {$charset_collate};
EOL;

    //dbDeltaを実行する為に必要
    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);

    $sample_name = 'テストデータ';

    $table_name = $wpdb->prefix . 'sample_mst';

    $wpdb->insert(
        $table_name,
        array(
            'sample_name' => $sample_name,
            'create_date' => current_time('mysql')
        ) 
    );
}

次回以降、この追加した項目に対して、「登録」「修正」「削除」「詳細」ができるように、管理画面を拡張していきます。

前回まで作った管理テーブルに対し、データ件数が増えてきた場合を想定して、ページング処理を入れます。

前回までの、テーブル一覧部分のコードは以下のとおりです。

	/**
	 * 初期表示
	 */
	function disp()
	{
		//データ一覧
		echo <<< EOL
<form action="" method="post">

	<h2>データ一覧</h2>
	<input type='submit' name='submit[regist]' class='button-primary' value='新規登録' />

	<div class="wrap">

	<table class="wp-list-table widefat striped posts">
		<tr>
			<th nowrap>ID</th>
			<th nowrap>名前</th>
			<th nowrap>登録日時</th>
			<th nowrap>詳細</th>
			<th nowrap>編集</th>
		</tr>
EOL;

		global $wpdb;

		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} ORDER BY id;";
		$rows = $wpdb->get_results($sql);

		foreach($rows as $row) {
			echo "<tr>";
			echo "<td>" . $row->id . "</td>";
			echo "<td>" . $row->sample_name . "</td>";
			echo "<td>" . $row->create_date . "</td>";
			echo "<td>";
			echo "<input type='submit' name='submit[detail][" . $row->id . "]'";
			echo " class='button-primary' value='詳細' />";
			echo "</td>";
			echo "<td>";
			echo "<input type='submit' name='submit[edit][" . $row->id . "]'";
			echo " class='button-primary' value='編集' />";
			echo "</td>";
			echo "</tr>";
		}
		echo "</table>";
		echo "</div>";
		echo "</form>";
	}

このコードに対し、ページ処理を入れていきます。

まず、現在のページ番号をGETパラメータで受け渡す為、パラメータ取得用の変数を用意し、取得します。

$pageid = filter_input(INPUT_GET, 'pageid');

次に、1ページあたり、何件表示して次のページに移るのか、上限を決定します。

//1ページあたりの件数
$limit = 10;

次に、対象となるデータの総件数を取得します

//DBオブジェクトを用意
global $wpdb;

//全件数取得
$tbl_name = $wpdb->prefix . 'sample_mst';
$sql = "SELECT count(*) AS CNT FROM {$tbl_name}";
$rows = $wpdb->get_results($sql);
$recordcount = $rows[0]->CNT;

ここで、$recordcountという変数に総件数が格納されます。

次に、ページ遷移した時に、データベースからオフセットした値を取得する為に、SQL用にoffset値を決定します。

//offsetの値を決定
$offset = $pageid * $limit;

最後に、データベースにクエリを発行し、データを取得します。
ここのタイミングではlimit件数(10件ごと)にデータを区切って取得しています。

//offset と limitによる画面表示用のデータ取得
$sql = "SELECT * FROM {$tbl_name} ORDER BY id limit {$offset}, {$limit};";

//通常の取得方法(SQL実行結果を、オブジェクトとして取得)
$rows = $wpdb->get_results($sql);

次にページ処理用のメソッドと、それに応じたパラメータを書いておきます。

$args = array(
	'label' => __('Per Page'),
	'default' => 10,
	'option' => 'disp'
);
$page_html = self::pagination($recordcount);

paginationというメソッド名は、function disp()の外側の任意の箇所に記述します。

function pagination($recordcount)
{
	$count = $recordcount;
	$limit = 10;

	//レコード総数がゼロのときは何も出力しない
	if (0 === $count) {
		return '';
	}

	//現在表示中のページ番号(ゼロスタート)
	$intCurrentPage = self::getCurrentPage();

	//ページの最大数
	$intMaxpage = ceil($count / $limit);

	//現在ページの前後3ページを出力
	$intStartpage = (2 < $intCurrentPage) ? $intCurrentPage - 3 : 0;
	$intEndpage = (($intStartpage + 7) < $intMaxpage) ? $intStartpage + 7 : $intMaxpage;

	//url組み立て
	$urlparams = filter_input_array(INPUT_GET);

	$items = [];

	//ページURLの生成
	//最初
	$urlparams['page'] = filter_input(INPUT_GET, 'page');
	$urlparams['pageid'] = 0;
	$items[] = sprintf('<span><a href="?%s">%s</a></span>'
		, http_build_query($urlparams)
		, '最初'
	);

	//表示中のページが先頭ではない時
	if (0 < $intCurrentPage) {
		$urlparams['pageid'] = $intCurrentPage - 1;
		$items[] = sprintf('<span><a href="?%s">%s</a></span>'
			, http_build_query($urlparams)
			, '前へ'
		);
	}

	for ($i = $intStartpage; $i < $intEndpage; $i++) {
		$urlparams['pageid'] = $i;
		$items[] = sprintf('<span%s><a href="?%s">%s</a></span>'
			, ($intCurrentPage == $i) ? ' class="current"' : ''
			, http_build_query($urlparams)
			, $i + 1
		);
	}

	//表示中のページが最後ではない時
	if ($intCurrentPage < $intMaxpage) {
		$urlparams['pageid'] = $intCurrentPage + 1;
		$items[] = sprintf('<span><a href="?%s">%s</a></span>'
			, http_build_query($urlparams)
			, '次へ'
		);
	}

	//最後
	$urlparams['pageid'] = $intMaxpage - 1;
	$items[] = sprintf('<span><a href="?%s">%s</a></span>'
		, http_build_query($urlparams)
		, '最後'
	);

	return $items;
}

最終的に、$page_htmlという変数に、ページング出力用の情報が入るので、それを画面出力用に整形して、出力します。

//ページ部分の表示
echo "<div class='admin_pagination'>";
echo "<ul>";
foreach ($page_html as $key => $value) {
	echo "<li>" . $value . "</li>";
}
echo "</ul>";

このままでは、文字列のみのページ表示になるので、cssでスタイルを調整します。

/* ページング処理 */
.admin_pagination ul li {
	display: inline-block;
	padding: 0px 1px 0px 1px;
	margin: 0px 3px 0px 3px;
	background-color: #DBDBDB;
}
.admin_pagination ul li .current a{
	color: #FFFFFF;
	background-color: #666666;
}
.admin_pagination ul a {
	display: block;
	padding: 8px 10px 8px 10px;
}

これで、テーブルに関連するページング処理の表示ができました。

出力結果は以下のようになります。

試しに3ページ目にアクセスする。

このように表示されます。

作っている最中に、気づきが遅かった部分がありました。
$wpdbのオブジェクトに対し、get_resultsメソッドを使ったSQL実行で、
offset、limitの処理を書いた場合、一般的なSQLの書き方では、
思うような抽出結果が得られませんでした。

SELECT * FROM テーブル名 OFFSET 10 LIMIT 5;

などと書くのはNGで、

SELECT * FROM テーブル名 LIMIT 10, 5;

のように書かないと、想定した出力結果が得られませんでした。
wpdbのget_resultsの仕様ページを見たのですが、明確な記載はありませんでした。
気づくまでに時間がかかってしまいました。

管理画面全体に対して、独自に用意したcssファイルを読み込ませます。

有効化しているテーマのディレクトリの直下に、独自cssファイルを配置します。

有効化しているディレクトリの場所は以下のようになります。

/ワードプレス設置ディレクトリ/wp-content/themes/有効化しているディレクトリ/

ここに、独自cssとして、「my_admin_style.css」を用意する(ファイル名は任意でOKです。

/ワードプレス設置ディレクトリ/wp-content/themes/有効化しているディレクトリ/my_admin_style.css

このように配置します。

次に、テーマディレクトリ直下のfunctions.phpを編集します。
ファイルの一番した等に次のように記述します。

//管理画面に独自CSSファイルを読み込ませる
function my_admin_style()
{
    wp_enqueue_style( 'my_admin_style', get_template_directory_uri().'/my_admin_style.css' );
}
add_action( 'admin_enqueue_scripts', 'my_admin_style' );

ここでポイントとなるのは、追加したcssファイル名「my_admin_style」と同じ要素でadd_actionを記述することです。

これで、管理画面を再読み込みすると、cssが適用されます。

管理画面のデータ一覧表示部分のテーブルが味気のないものだったので、簡単なstyleを記述して少し見やすくしました。

style適用前は以下のような画面でしたが、以下のような表示になっています。

この表示にstyleを適用して見やすくします。

まずは、tableタグを次のようなdivタグで囲います。

<div class="wrap">

次に、tableタグに次のようなクラスを記述します。

class="wp-list-table widefat striped posts"

最後にwrapで囲んだdivを閉じます。

tableを使っている箇所でstyleを整えたいところに適宜、記述していくとよいかと思います。

最初に画面を調整した結果、表示する関数全体としては以下のようになりました。

function disp()
{
    //データ一覧
    echo <<< EOL
<form action="" method="post">

<h2>データ一覧</h2>
<input type='submit' name='submit[regist]' class='button-primary' value='新規登録' />

<div class="wrap">

<table class="wp-list-table widefat striped posts">
    <tr>
        <th nowrap>ID</th>
        <th nowrap>名前</th>
        <th nowrap>登録日時</th>
        <th nowrap>詳細</th>
        <th nowrap>編集</th>
    </tr>
EOL;

    global $wpdb;

    $tbl_name = $wpdb->prefix . 'sample_mst';
    $sql = "SELECT * FROM {$tbl_name} ORDER BY id;";
    $rows = $wpdb->get_results($sql);

    foreach($rows as $row) {
        echo "<tr>";
        echo "<td>" . $row->id . "</td>";
        echo "<td>" . $row->sample_name . "</td>";
        echo "<td>" . $row->create_date . "</td>";
        echo "<td>";
        echo "<input type='submit' name='submit[detail][" . $row->id . "]'";
        echo " class='button-primary' value='詳細' />";
        echo "</td>";
        echo "<td>";
        echo "<input type='submit' name='submit[edit][" . $row->id . "]'";
        echo " class='button-primary' value='編集' />";
        echo "</td>";
        echo "</tr>";
    }
    echo "</table>";
    echo "</div>";
    echo "</form>";
}

表示される結果は次のようになります。

少しだけ、おしゃれになりました。

これまでに作ったプラグインは主に管理画面での操作用に作ってきました。

大まかな動きとしては、DBの登録/修正/削除が管理画面からできていましたが、今度はそれをユーザ画面側に表示することを行います。

ユーザ画面側では、「投稿」「固定ページ」へ自由に表示する為に「ショートコード」という書き方をすることで、DBの値を表示することができます。

これまでに作ったプラグインの中身を表示する為に、以下のような書き方をします。
(便宜上、プラグインプログラムの一番下に書き足します)

/**
 * ショートコード
 */
function samplelistFunction()
{
	//データ一覧
	$disp_html =<<< EOL
<form action="" method="post">
<h2>データ一覧(ショートコードテスト)</h2>
<table>
	<tr>
		<th nowrap>ID</th>
		<th nowrap>名前</th>
		<th nowrap>登録日時</th>
	</tr>
EOL;

	global $wpdb;

	$tbl_name = $wpdb->prefix . 'sample_mst';
	$sql = "SELECT * FROM {$tbl_name} ORDER BY id;";
	$rows = $wpdb->get_results($sql);

	foreach($rows as $row) {

$disp_html .=<<<EOL
	<tr>
		<td>{$row->id}</td>
		<td>{$row->sample_name}</td>
		<td>{$row->create_date}</td>
		<td>
	</tr>
EOL;
	}

	$disp_html .=<<< EOL
	</table>
	</form>
EOL;

	return $disp_html;
}
//「[samplelist]という記述を投稿画面や固定ページに記述する」
add_shortcode('samplelist', 'samplelistFunction');

上記の書き方をすることにより、ユーザ側で、ショートコードタグ「[samplelist]」を記述すると、その部分にDBアクセスした一覧表示テーブルを表示することができます。

色々と応用することができますが、まずは表示できるところまでで、一区切りとします。

データの更新系を考えてみます。

データを更新する場合、当然のように入力値をチェックしないと、整合性が取れないデータがユーザからの入力されてしまいます。

今回はデータの新規登録時に値をチェックする仕組みを入れてみます。

まずは、データをチェックするタイミングを考えます。
新規登録からデータを登録する「登録内容を確認する」(下記、画像ファイルを参照)というボタンを押した時の動作をきっかけにチェックを入れます。

次にソース上で、そのボタンが押された箇所の処理をみます。

    /**
     * 登録確認
     */
    function regist_check()
    {
        $sample_name = $_REQUEST["sample_name"];

        //データ一覧
        echo <<< EOL
<form action="" method="post">
    <h2>データ登録確認</h2>
<table border="1">
    <tr>
        <td>NAME</td>
        <td>{$sample_name}</td>
    </tr>
</table>

<input type="hidden" name="sample_name" value="{$sample_name}">

<input type='submit' name='submit[regist_exec]' class='button-primary' value='登録する' />
<input type='submit' name='submit[regist]' class='button-primary' value='戻る' />
EOL;
        echo "</form>";
    }

単純に入力画面で入力した文字列をそのまま変数に代入して、確認画面を出していることがわかります。

このままでは空白や、危険な文字列が入力された場合に、意図しない動作になってしまいます。

次のように修正します。

    /**
     * 登録確認
     */
    function regist_check()
    {
        //入力値が空白の場合の処理(ここでは単純に0バイトだったら)
        if (!strlen($_REQUEST["sample_name"])) {
            self::regist(false);
            return;
        }
        
        $sample_name = esc_attr($_REQUEST["sample_name"]);

        //データ一覧
        echo <<< EOL
<form action="" method="post">
    <h2>データ登録確認</h2>
<table border="1">
    <tr>
        <td>NAME</td>
        <td>{$sample_name}</td>
    </tr>
</table>

<input type="hidden" name="sample_name" value="{$sample_name}">

<input type='submit' name='submit[regist_exec]' class='button-primary' value='登録する' />
<input type='submit' name='submit[regist]' class='button-primary' value='戻る' />
EOL;
        echo "</form>";
    }

チェック方法が少し荒っぽいですが、未入力チェックと、< > & ” ‘ (小なり、大なり、アンパサンド、ダブルクォート、シングルクォート) 文字参照をエンコードする関数「esc_attr」を処理に入れています。

また、もし入力された値が空白だった場合「self::regist(false)」という形で、また登録画面を呼び出しています。

引数にfalseを渡し、チェック時に何が起きたのかを新規登録画面へ伝えます。

新規登録画面側の処理は、以下のように修正します。

    /**
     * 登録
     */
    function regist($error_message_flg = null)
    {
        if ($error_message_flg !== false) {
            $sample_name = esc_attr($_REQUEST["sample_name"]);
        }

        echo <<< EOL
<form action="" method="post">
    <h2>データ登録</h2>
EOL;

        //エラーメッセージのフラグがfalseの場合、メッセージを表示する
        if ($error_message_flg == false) {
            echo "<div class='updated fade'><p><strong>";
            echo _e('NAMEを入力してください');
            echo "</strong></p></div>";
        }

        echo <<< EOL
    <table border="1">
        <tr>
            <td>NAME</td>
            <td>
                <input type="text" name="sample_name" value="{$sample_name}">
            </td>
        </tr>
    </table>

    <input type='submit' name='submit[regist_check]' class='button-primary' value='登録内容を確認する' />
    <input type='submit' name='submit[]' class='button-primary' value='戻る' />
</form>
EOL;
    }

このように処理を書くことで、未入力の場合には「NAMEを入力してください」
というメッセージが表示されるようになります。

今回は未入力チェックのみの実装でしたが、その他にも入力項目の種類に合わせたチェック方法を実装する場面が出てきます。
当然のごとくエラーの表示場所や、文言、レイアウトなどはもっと工夫する必要があります。

書き方は数多くあるので、もっと良い書き方を考えて改良する予定です。

前回までのプログラムに、新規登録画面を追加します。

まずは、「登録画面」「登録確認画面」「登録完了画面」の処理の為に分岐を作っておきます。
そしてそれぞれ違うメソッドを呼ぶように関数を追加。

	/**
	 * 管理画面のHTMLの生成と表示
	 */
	function sample_plugin()
	{
		if (isset($_REQUEST["submit"]["detail"])) {
			//詳細
			self::detail();
		} else if (isset($_REQUEST["submit"]["edit"])) {
			//修正
			self::edit();
		} else if (isset($_REQUEST["submit"]["edit_check"])) {
			//修正確認
			self::edit_check();
		} else if (isset($_REQUEST["submit"]["edit_exec"])) {
			//修正実行
			self::edit_exec();
		} else if (isset($_REQUEST["submit"]["delete_check"])) {
			//削除確認
			self::delete_check();
		} else if (isset($_REQUEST["submit"]["delete_exec"])) {
			//削除実行
			self::delete_exec();
		} else if (isset($_REQUEST["submit"]["regist"])) {
			//新規登録
			self::regist();
		} else if (isset($_REQUEST["submit"]["regist_check"])) {
			//新規登録確認
			self::regist_check();
		} else if (isset($_REQUEST["submit"]["regist_exec"])) {
			//新規登録
			self::regist_exec();
		} else {
			//初期表示
			self::disp();
		}
	}

次に、プログラム内の任意の箇所に、以下の関数を追加します。

	/**
	 * 登録
	 */
	function regist()
	{
		if (isset($_REQUEST["sample_name"])) {
			$sample_name = $_REQUEST["sample_name"];
		}

		echo <<< EOL
<form action="" method="post">
	<h2>データ登録</h2>
	<table border="1">
		<tr>
			<td>NAME</td>
			<td>
				<input type="text" name="sample_name" value="{$sample_name}">
			</td>
		</tr>
	</table>

	<input type='submit' name='submit[regist_check]' class='button-primary' value='登録内容を確認する' />
	<input type='submit' name='submit[]' class='button-primary' value='戻る' />
</form>
EOL;
	}

	/**
	 * 登録確認
	 */
	function regist_check()
	{
		$sample_name = $_REQUEST["sample_name"];

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ登録確認</h2>
EOL;

		echo <<<EOL
<table border="1">
	<tr>
		<td>NAME</td>
		<td>{$sample_name}</td>
	</tr>
</table>

<input type="hidden" name="sample_name" value="{$sample_name}">

<input type='submit' name='submit[regist_exec]' class='button-primary' value='登録する' />
<input type='submit' name='submit[regist]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}


	/**
	 * 登録実行
	 */
	function regist_exec()
	{
		global $wpdb;

		$sample_name = $_REQUEST["sample_name"];

		//投稿を登録
		$table_name = $wpdb->prefix . 'sample_mst';
		$result = $wpdb->insert(
			$table_name,
			array(
				'sample_name' => $sample_name,
				'create_date' => current_time('mysql')
			)
		);

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ登録</h2>
	<div class='updated fade'><p><strong>
EOL;
		echo _e('登録が完了しました');
		echo <<<EOL
</strong></p></div>
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

これで、新規登録ボタンを押した後に、登録フォーム→確認画面→完了画面へ遷移し、最後にDB内にデータを保存する流れができます。

■新規登録ボタン追加

■登録フォームの表示

■確認画面の表示

■完了画面の表示

ちなみに入力値チェックは確認画面のタイミングで書く必要がありますので、次の課題として進めます。

かなり簡単な登録画面(項目も1個のみ)ですが、文字列の登録が管理画面上から可能になりました。

作成中のプログラムに、削除機能をつけてみます。

まずは、function sample_plugin()に対して、削除確認画面と、
削除実行画面のメソッドをつけます。

	/**
	 * 管理画面のHTMLの生成と表示
	 */
	function sample_plugin()
	{
		if (isset($_REQUEST["submit"]["detail"])) {
			//詳細
			self::detail();
		} else if (isset($_REQUEST["submit"]["edit"])) {
			//修正
			self::edit();
		} else if (isset($_REQUEST["submit"]["edit_check"])) {
			//修正確認
			self::edit_check();
		} else if (isset($_REQUEST["submit"]["edit_exec"])) {
			//修正実行
			self::edit_exec();
		} else if (isset($_REQUEST["submit"]["delete_check"])) {
			//削除確認
			self::delete_check();
		} else if (isset($_REQUEST["submit"]["delete_exec"])) {
			//削除実行
			self::delete_exec();
		} else {
			//初期表示
			self::disp();
		}
	}

次に関数delete_checkとdelete_execをそれぞれ新規作成します。

削除確認画面は以下のように書きます。
これは修正確認画面の応用なので、書き方によっては同一にできるかと思います。

	/**
	 * 削除確認
	 */
	function delete_check()
	{
		$form_id = $_REQUEST["form_id"];

		global $wpdb;

		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
		$prepared = $wpdb->prepare($sql, $form_id);
		$rows = $wpdb->get_results($prepared, ARRAY_A);

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ削除確認</h2>
	<table border="1">
		<tr>
			<td>ID</td>
			<td>{$form_id}</td>
		</tr>
		<tr>
			<td>NAME</td>
			<td>{$rows[0]["sample_name"]}</td>
		</tr>
		<tr>
			<td>登録日時</td>
			<td>{$rows[0]["create_date"]}</td>
		</tr>
	</table>

	<input type="hidden" name="form_id" value="{$form_id}">

	<input type='submit' name='submit[delete_exec]' class='button-primary' value='削除する' />
	<input type='submit' name='submit[edit]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

次に削除実行の関数を追加します。

	/**
	 * 削除実行
	 */
	function delete_exec()
	{
		$form_id = $_REQUEST["form_id"];

		//データを削除
		global $wpdb;
		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "DELETE FROM {$tbl_name} WHERE id = %s;";
		$dlt = $wpdb->query($wpdb->prepare($sql, $form_id));

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ編集認</h2>
EOL;

		echo "<div class='updated fade'><p><strong>";
		echo _e('削除が完了しました');
		echo "</strong></p></div>";

		echo <<<EOL
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

上記の機能を書くことで、データ一覧画面から詳細画面→削除確認画面→削除実行の流れができています。
書き方は千差万別でもっと効率のよい書き方もあると思います。
そこは今度プラグインの調整を続けていく際に強化していきます。

前回作った詳細一覧に対し、「編集」ボタンをつけて、
1レコードづつデータ編集できるようにする。

各ボタンごとにメソッドを分けて処理を書いたので、ソースは少し長めになります。
ボタンごとにname属性に違う記述(やりたい役割ごとに名称を変える)し、
そのname属性をもとにfunction sample_plugin()の中で呼び出すメソッドを分けています。

なにも押下されなかった場合は、初期表示となり、その後、詳細表示(一覧)、
編集、編集確認、編集完了、というそれぞれのメソッドを用意しています。

もっと良い書き方があると思いますが、ひとまず更新はできているのでブログ記事化します。

	/**
	 * 管理画面のHTMLの生成と表示
	 */
	function sample_plugin()
	{
		if (isset($_REQUEST["submit"]["detail"])) {
			//詳細
			self::detail();
		} else if (isset($_REQUEST["submit"]["edit"])) {
			//修正
			self::edit();
		} else if (isset($_REQUEST["submit"]["edit_check"])) {
			//修正確認
			self::edit_check();
		} else if (isset($_REQUEST["submit"]["edit_exec"])) {
			//修正実行
			self::edit_exec();
		} else {
			//初期表示
			self::disp();
		}
	}

	/**
	 * 初期表示
	 */
	function disp()
	{
		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ一覧</h2>
	<table>
		<tr>
			<th nowrap>ID</th>
			<th nowrap>名前</th>
			<th nowrap>登録日時</th>
			<th nowrap>詳細</th>
			<th nowrap>編集</th>
		</tr>
EOL;

		global $wpdb;

		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} ORDER BY id;";
		$rows = $wpdb->get_results($sql);

		foreach($rows as $row) {
			echo "<tr>";
			echo "<td>" . $row->id . "</td>";
			echo "<td>" . $row->sample_name . "</td>";
			echo "<td>" . $row->create_date . "</td>";
			echo "<td>";
			echo "<input type='submit' name='submit[detail][" . $row->id . "]'";
			echo " class='button-primary' value='詳細' />";
			echo "</td>";
			echo "<td>";
			echo "<input type='submit' name='submit[edit][" . $row->id . "]'";
			echo " class='button-primary' value='編集' />";
			echo "</td>";
			echo "</tr>";
		}
		echo "</table>";
		echo "</form>";
	}

	/**
	 * 詳細表示
	 */
	function detail()
	{
		//押されたボタンのIDを取得する
		if (array_search("詳細", $_REQUEST["submit"]["detail"])) {
			$form_id = array_search("詳細", $_REQUEST["submit"]["detail"]);
		}

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ詳細</h2>
EOL;

		global $wpdb;

		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
		$prepared = $wpdb->prepare($sql, $form_id);
		$rows = $wpdb->get_results($prepared, ARRAY_A);

		echo <<<EOL
<table border="1">
	<tr>
		<td>ID</td>
		<td>{$rows[0]["id"]}</td>
	</tr>
	<tr>
		<td>NAME</td>
		<td>{$rows[0]["sample_name"]}</td>
	</tr>
	<tr>
		<td>登録日時</td>
		<td>{$rows[0]["create_date"]}</td>
	</tr>
</table>
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

	/**
	 * 修正
	 */
	function edit()
	{
		if (isset($_REQUEST["form_id"])) {
			$form_id = $_REQUEST["form_id"];
			$sample_name = $_REQUEST["sample_name"];
			$create_date = $_REQUEST["create_date"];
		} else {
			//押されたボタンのIDを取得する
			if (array_search("編集", $_REQUEST["submit"]["edit"])) {
				$form_id = array_search("編集", $_REQUEST["submit"]["edit"]);
			}

			global $wpdb;

			$tbl_name = $wpdb->prefix . 'sample_mst';
			$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
			$prepared = $wpdb->prepare($sql, $form_id);
			$rows = $wpdb->get_results($prepared, ARRAY_A);

			$sample_name = $rows[0]["sample_name"];
			$create_date = $rows[0]["create_date"];
		}

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ編集</h2>
EOL;


		echo <<<EOL
<table border="1">
	<tr>
		<td>ID</td>
		<td>{$form_id}</td>
	</tr>
	<tr>
		<td>NAME</td>
		<td>
			<input type="text" name="sample_name" value="{$sample_name}">
		</td>
	</tr>
	<tr>
		<td>登録日時</td>
		<td>{$create_date}</td>
	</tr>
</table>

<input type="hidden" name="form_id" value="{$form_id}">
<input type="hidden" name="create_date" value="{$create_date}">

<input type='submit' name='submit[edit_check]' class='button-primary' value='編集内容を確認する' />
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

	/**
	 * 編集確認
	 */
	function edit_check()
	{
		$form_id = $_REQUEST["form_id"];
		$sample_name = $_REQUEST["sample_name"];
		$create_date = $_REQUEST["create_date"];

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ編集確認</h2>
EOL;

		echo <<<EOL
<table border="1">
	<tr>
		<td>ID</td>
		<td>{$form_id}</td>
	</tr>
	<tr>
		<td>NAME</td>
		<td>{$sample_name}</td>
	</tr>
</table>

<input type="hidden" name="form_id" value="{$form_id}">
<input type="hidden" name="sample_name" value="{$sample_name}">
<input type="hidden" name="create_date" value="{$create_date}">

<input type='submit' name='submit[edit_exec]' class='button-primary' value='編集する' />
<input type='submit' name='submit[edit]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

	/**
	 * 編集実行
	 */
	function edit_exec()
	{
		global $wpdb;

		$form_id = $_REQUEST["form_id"];
		$sample_name = $_REQUEST["sample_name"];
		$update_date = date("Y-m-d H:i:s");

		//投稿を更新
		$tbl_name = $wpdb->prefix . 'sample_mst';
		$result = $wpdb->update(
			$tbl_name,
			array('sample_name' => $sample_name,),
			array('id' => $form_id,),
			array('%s'),
			array('%d')
		);

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ編集認</h2>
EOL;

		echo "<div class='updated fade'><p><strong>";
		echo _e('更新が完了しました');
		echo "</strong></p></div>";

		echo <<<EOL
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

上記のコードを実行した時の画面を張っておきます。
一覧表示から、編集、確認、完了という、一般的な編集処理ができます。

見た目のレイアウト調整については別な機会にまとめて、整理しようと思っています。
まずは確実に動くものを仕上げていきます。

■一覧画面

■編集を押した時

■編集確認画面

■編集完了画面

管理画面に詳細画面を追加する

先日追加した「function sample_plugin()」には、テーブルの値をだた一覧表示しただけでしたので、今回はその一覧から詳細画面を表示する。という部分を作ってみます。

まず、sample_plugin()という関数は以下のように書いていました。

	/**
	 * 管理画面のHTMLの生成と表示
	 */
	function sample_plugin()
	{
		echo <<< EOL
<h1>sample_plugin メインページ</h1>
ここにHTMLテンプレートを生成していきます。
EOL;

		//データ一覧
		echo <<< EOL
		<h2>データ一覧</h2>
		<table>
			<tr>
				<th nowrap>ID</th>
				<th nowrap>名前</th>
				<th nowrap>登録日時</th>
				<th nowrap>詳細</th>
			</tr>
EOL;

		global $wpdb;
		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} ORDER BY id;";
		$rows = $wpdb->get_results($sql);

		foreach($rows as $row) {
			echo "<tr>";
			echo "<td nowrap>" . $row->id . "</td>";
			echo "<td nowrap>" . $row->sample_name . "</td>";
			echo "<td nowrap>" . $row->create_date . "</td>";
			echo "<td nowrap>";
			echo "<input type='submit' name='submit[detail][" . $row->id . "]'";
			echo " class='button-primary' value='詳細' />";
			echo "</tr>";
		}
		echo "</table>";
	}

上記の部分を、別な関数に移し、ボタンによって表示する情報を切り替えるように改造します。

ボタンごとに画面を切り替える為、function sample_plugin()を以下のように変更しました。

/**
 * 管理画面のHTMLの生成と表示
 */
function sample_plugin()
{
	if (isset($_REQUEST["submit"]["detail"])) {
		self::detail(); //詳細
	} else if (isset($_REQUEST["submit"]["edit"])) {
		self::edit(); //修正
	} else {
		self::disp(); //初期表示
	}
}

「$_REQUEST[“submit”][“detail”]」という部分が、HTML上にあるボタンのname属性によって切り替えるポイントとなります。(詳細か編集か初期表示かを分けています)
もっと良い方法があるかもしれませんが、間違いなく動作するので、一旦これで進めます。

次に「詳細画面」を表示する為の関数function detail()を新規に書きます。

	/**
	 * 詳細表示
	 */
	function detail()
	{
		//押されたボタンのIDを取得する
		if (array_search("詳細", $_REQUEST["submit"]["detail"])) {
			$form_id = array_search("詳細", $_REQUEST["submit"]["detail"]);
		}

		//データ一覧
		echo <<< EOL
<form action="" method="post">
	<h2>データ詳細</h2>
EOL;

		global $wpdb;

		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} WHERE id = %d;";
		$prepared = $wpdb->prepare($sql, $form_id);
		$rows = $wpdb->get_results($prepared, ARRAY_A);

		echo <<<EOL
<table border="1">
	<tr>
		<td>ID</td>
		<td>{$rows[0]["id"]}</td>
	</tr>
	<tr>
		<td>NAME</td>
		<td>{$rows[0]["sample_name"]}</td>
	</tr>
	<tr>
		<td>登録日時</td>
		<td>{$rows[0]["create_date"]}</td>
	</tr>
</table>
<input type='submit' name='submit[]' class='button-primary' value='戻る' />
EOL;
		echo "</form>";
	}

上記の関数を用意することで、一覧表示の「詳細」ボタンを押した後に、詳細画面へ遷移し、各データの詳細を表示することが可能になります。

応用次第では、管理画面の色々な場面で編集用の画面が作れるようになります。

管理画面にアクセスした際、初期表示を行います。

あらかじめテーブル「wp_sample_mst」にテスト用データを3件程登録しておきます。
前回の投稿で作成した関数「function sample_plugin()」に、下記のようにデータベースにアクセスしてデータを取得するSQLを書きます。

	/**
	 * 管理画面のHTMLの生成と表示
	 */
	function sample_plugin()
	{
		echo <<< EOL
<h1>sample_plugin メインページ</h1>
ここにHTMLテンプレートを生成していきます。
EOL;
		//データ一覧
		echo <<< EOL
<h2>データ一覧</h2>
<table>
<tr>
<th nowrap>ID</th>
<th nowrap>名前</th>
<th nowrap>登録日時</th>
			</tr>
EOL;
		global $wpdb;
		$tbl_name = $wpdb->prefix . 'sample_mst';
		$sql = "SELECT * FROM {$tbl_name} ORDER BY id;";
		$rows = $wpdb->get_results($sql);
		foreach($rows as $row) {
			echo "<tr>";
			echo "<td nowrap>" . $row->id . "</td>";
			echo "<td nowrap>" . $row->sample_name . "</td>";
			echo "<td nowrap>" . $row->create_date . "</td>";
			echo "</tr>";
		}
		echo "</table>";
	}

上記の例では、テストデータを3件取得して表示しています。

実行結果は以下のようになります。

テーブルに登録したテストデータ3件の表示がされました。
今後、このデータを修正、削除、登録ができるように進めます。

前回までの投稿で、wordpress管理画面のメニューに「SamplePlugin1」というメニューが追加されました。

(下記画像を参照)

001

今回はこのメニューをクリックした時、右側のページにHTMLを生成して表示する動作を作ってみます。

コードは以下のように記述します。
関数「add_pages」の部分の「add_menu_page」の引数に
「array($this, ‘sample_plugin’)」と書いている部分があります。

	function add_pages()
	{
		add_menu_page(
			'SamplePlugin1 Plugin Settings',
			'SamplePlugin1',
			'manage_options',
			'SamplePluginMenu',
			array($this, 'sample_plugin')
		);
	}

この「sample_plugin」という名前の関数(メソッド)を新規作成します。
例えば、以下のように書きます。

	/**
	 * 管理画面のHTMLの生成と表示
	 */
	function sample_plugin()
	{
		
		echo <<< EOL



<h1>sample_plugin メインページ</h1>


ここにHTMLテンプレートを生成していきます。

EOL;
		
	}

上記のように記述して、管理メニューの「SamplePlugin1」を押すと、画面は以下のように表示されます。

002

このようにして、管理項目などを表示するHTMLを生成していきます。

プラグインのアンインストール時の動きを書きます。

まず、前回のプラグイン停止時の動きと同じように、クラスの外で
次のように書きます。

//アンインストール時の設定
register_uninstall_hook(__FILE__, array('SamplePlugin1', 'myplugin_uninstall'));

続いて、クラスの中で次のように書きます。

	/**
	 * 停止時の実行
	 */
	function myplugin_unactivate()
	{
		global $wpdb;

		//削除するテーブル名の決定
		$table_name = $wpdb->prefix . 'sample_mst';

		//テーブル削除
		$sql_drop = "DROP TABLE " . $table_name . ";";
		$wpdb->query($sql_drop);
	}

こうすることで、プラグインのアンインストール時に、メソッド「myplugin_unactivate」が呼び出されることになります。

プラグイン停止時の動き

前回の投稿では、プラグインのインストール時にテーブル作成を行いました。

今回はプラグイン停止時の処理を追加します。

停止時はプログラムの先頭で以下の処理を書いてから

//停止時の設定
register_deactivation_hook(__FILE__, array('SamplePlugin1', 'myplugin_unactivate'));

↓のように停止時用の関数を作成します。

/**
* 停止時の実行
*/
function myplugin_unactivate()
{
	//テーブルデータ消去などなど
	global $wpdb;

	//削除するテーブルの決定
	$table_name = $wpdb->prefix . 'sample_mst';

	//テーブル削除
	$sql_delete = "DELETE FROM " . $table_name . ";";
	$wpdb->query($sql_delete);
}

こうする事で、プラグイン停止時の動きを制御します。

また、上記の例では、deleteを行ってテーブルの内容をクリアしていますが、
AUTO_INCREMENTを使用しているテーブルの場合などの場合は

$sql_delete = "DELETE FROM " . $table_name . ";";

のところを

$sql_delete = "TRUNCATE TABLE " . $table_name . ";";

としてもいいと思われます。

削除時になにを残して、何を消すのか、プラグインの性質をよく考えて
決めるのがよいと思います。

次回はプラグインのアンインストール時の動きを書きます。

前回の投稿ではプラグインのインストール時に特定のメソッドを呼ぶ方法を書きました。
それを踏まえて、今回はプラグインのインストール時にデータベース内に、簡単なテーブルを作成することをやってみます。

まずは、myplugin_activateメソッドが呼ばれた時に(インストールされた時に)、テーブル作成するSQL(DDL文)を呼びます。

function myplugin_activate()
{
	//テーブル作成などなど
	self::create_tables_sample_mst();
	
}

メソッド名に特に決まりはなく、任意の名前のものを書きます。

次に、呼ばれる側のメソッドを書きます。

function create_tables_sample_mst()
{
	//ここでテーブル作成を行う
	
	
}

これで、インストール時にcreate_tables_sample_mstメソッドが呼ばれて
テーブル作成などができるようになります。

では、実際に、create_tables_sample_mstメソッド内にテーブル作成のSQLを書いてみます。

function create_tables_sample_mst()
{
	global $wpdb;

	$charset_collate = "";

	//接頭辞の追加(socal_count_cache)
	$table_name = $wpdb->prefix . 'sample_mst';

	//charsetを指定する
	if (!empty($wpdb->charset)) {
		$charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset} ";
	}

	//照合順序を指定する(ある場合、通常デフォルトのutf8_general_ci)
	if (!empty($wpdb->collate)) {
		$charset_collate .= "COLLATE {$wpdb->collate}";
	}

	$sql = <<< EOL
CREATE TABLE {$table_name} (
id				INT NOT NULL AUTO_INCREMENT,
sample_name		VARCHAR(128),
create_date		DATETIME,
PRIMARY KEY(id)
) {$charset_collate};
EOL;

	//dbDeltaを実行する為に必要
	require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
	dbDelta($sql);
}

コードを一つづつ追うとわかるのですが、まず、

global $wpdb;

で、wordpress内のデータベースにアクセスする為のクラスを読み込んでいます。
$wpdbはSQLの実行時にいたるところで使うので、この読み込み方はよく覚えておくといいと思います。

作成するテーブル名称は「wp_sample_mst」となります。
wp_というのはwordpressに関するテーブルの接頭辞(接頭句?)になり、よほどのことがない限り、
この「wp_」という名称に従ったほうがよいと思います。

その次に、charset

//charsetを指定する
if (!empty($wpdb->charset)) {
	$charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset} ";
}

と照合順序を

//照合順序を指定する(ある場合、通常デフォルトのutf8_general_ci)
if (!empty($wpdb->collate)) {
	$charset_collate .= "COLLATE {$wpdb->collate}";
}

それぞれセットして、テーブル作成文を実行します。

実際にプラグインのインストールを試して、テーブルが作成されることを確認します。

最後に、少し長くなりますが、ソースコード全体を書いておきます。

<?php
/*
Plugin Name: SamplePlugin1
Plugin URI:  http://
Description: サンプルプラグイン。
Version:	 1.0
Author: 	 sample
Author URI:  http://
License:	 GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

register_activation_hook(__FILE__, array('SamplePlugin1', 'myplugin_activate'));
register_deactivation_hook(__FILE__, array('SamplePlugin1', 'myplugin_unactivate'));
register_uninstall_hook(__FILE__, array('SamplePlugin1', 'myplugin_uninstall'));

class SamplePlugin1
{

	function __construct()
	{
		add_action('admin_menu', array($this, 'add_pages'));
	}

	function add_pages()
	{
		add_menu_page(
			'SamplePlugin1 Plugin Settings',
			'SamplePlugin1',
			'manage_options',
			'SamplePluginMenu',
			array($this, 'sample_plugin')
		);
	}

	function myplugin_activate()
	{
		//テーブル作成などなど
		self::create_tables_sample_mst();
		
	}

	function myplugin_unactivate()
	{
		//テーブルデータ消去などなど
		
		
	}
	 
	function myplugin_uninstall()
	{
		//テーブル削除などなど
		
		
	}

	function create_tables_sample_mst()
	{
		global $wpdb;

		$charset_collate = "";

		//接頭辞の追加(socal_count_cache)
		$table_name = $wpdb->prefix . 'sample_mst';

		//charsetを指定する
		if (!empty($wpdb->charset)) {
			$charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset} ";
		}

		//照合順序を指定する(ある場合、通常デフォルトのutf8_general_ci)
		if (!empty($wpdb->collate)) {
			$charset_collate .= "COLLATE {$wpdb->collate}";
		}

		$sql = <<< EOL
CREATE TABLE {$table_name} (
id				INT NOT NULL AUTO_INCREMENT,
sample_name		VARCHAR(128),
create_date		DATETIME,
PRIMARY KEY(id)
) {$charset_collate};
EOL;

		//dbDeltaを実行する為に必要
		require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
		dbDelta($sql);
	}

}

new SamplePlugin1;

プラグインを「有効化」するタイミングで、そのプラグインを使うにあたっての初期設定をプログラムすることができます。

例えば、プラグイン内で使用するデータベース内のテーブルを作成する等といったことを記述します。

また、プラグインを停止したタイミング。プラグインをアンインストールしたタイミングでそれぞれの動作をプログラムすることができます。

試しに、プラグインを有効化したタイミングの挙動を書いてみます。

register_activation_hook(__FILE__, array('SamplePlugin1', 'myplugin_activate'));

まず、プラグイン内のクラスの外に、上記の命令を記述します。

次にクラス内に以下のメソッドを追加します。

function myplugin_activate()
{
	//テーブル作成などなど
	
	
}

ここでは、具体的なテーブル作成の命令は書いていませんが、このメソッド内に初期化の命令をまとめて記載しておくといいと思います。

これと同様に、停止時、アンインストール時には、次のように書きます。

register_deactivation_hook(__FILE__, array('SamplePlugin1', 'myplugin_unactivate'));
register_uninstall_hook(__FILE__, array('SamplePlugin1', 'myplugin_uninstall'));

同様にメソッドもクラス内に書いて用意しておきます。

function myplugin_unactivate()
{
	//テーブルデータ消去などなど
	
	
}

function myplugin_uninstall()
{
	//テーブル削除などなど
	
	
}

以上になります。
出来上がったプログラムの全体は、以下のようになります。

<?php
/*
Plugin Name: SamplePlugin1
Plugin URI:  http://
Description: サンプルプラグイン。
Version:	 1.0
Author: 	 sample
Author URI:  http://
License:	 GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

register_activation_hook(__FILE__, array('SamplePlugin1', 'myplugin_activate'));
register_deactivation_hook(__FILE__, array('SamplePlugin1', 'myplugin_unactivate'));
register_uninstall_hook(__FILE__, array('SamplePlugin1', 'myplugin_uninstall'));

class SamplePlugin1
{

	function __construct()
	{
		add_action('admin_menu', array($this, 'add_pages'));
	}

	function add_pages()
	{
		add_menu_page(
			'SamplePlugin1 Plugin Settings',
			'SamplePlugin1',
			'manage_options',
			'SamplePluginMenu',
			array($this, 'sample_plugin')
		);
	}

	function myplugin_activate()
	{
		//テーブル作成などなど
		 
		 
	}

	function myplugin_unactivate()
	{
		//テーブルデータ消去などなど
		 
		 
	}
	 
	function myplugin_uninstall()
	{
		//テーブル削除などなど
		 
		 
	}

}

new SamplePlugin1;

まだ、具体的な処理は入れていませんが、全体的な骨格が少しづつできてきました。

メニュー追加

前回作成したプラグインを、管理画面から有効にしてみます。

メニューのプラグインの中にアクセスすると、前回作ったプラグイン「SamplePlugin1」が見えるので、その下の有効化をクリックします。

20160624_001

ただ、この状態ではなにも変化は起きません。
プラグインの中になにもプログラムを書いていないので、変化しないのですね。

では、次にやることと言えば、プラグインを管理画面のメニューの表示させてみます。

<?php
/*
Plugin Name: SamplePlugin1
Plugin URI:  http://
Description: サンプルプラグイン。
Version:     1.0
Author:      sample
Author URI:  http://
License:     GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

class SamplePlugin1
{

	function __construct()
	{
		add_action('admin_menu', array($this, 'add_pages'));
	}

	function add_pages()
	{
		add_menu_page(
			'SamplePlugin1 Plugin Settings',
			'SamplePlugin1',
			'manage_options',
			'SamplePluginMenu',
			array($this, 'sample_plugin')
		);
	}

}

new SamplePlugin1;

上記のコードをプログラムの中に記載します。

まず、__constructというコンストラクタ内に

add_action('admin_menu', array($this, 'add_pages'));

と書きます。
これは同じクラス内のメソッド「add_pages」を呼ぶ為のアクションとなります。
アクションについてはwordpressに備わっている考え方で、フィルターフック、アクションフックという考え方があり、それぞれ何らかのきっかけがあった時に実行する命令。という意味で覚えておくといいと思います。

次にコンストラクタに書いた命令で「add_pages」という記載がありますが、これは同じクラス内のメソッド「function add_pages()」を呼ぶという意味になります。

なので、このメソッドを新しく作り、内部にメニューに表示する命令を書きます。

		add_menu_page(
			'SamplePlugin1 Plugin Settings',
			'SamplePlugin1',
			'manage_options',
			'SamplePluginMenu',
			array($this, 'sample_plugin')
		);

ここまで書いたらファイルを保存して、wordpressの管理画面内にアクセスしてみます。
すると「SamplePlugin1」というメニューが表示されます。
クリックはできるのですが、この状態だと、まだなにも表示はされません。

20160624_002

add_actionと、add_menu_pageの関数はwordpressに実装済みの関数なので、以下のページを参考にするといいかと思います。

■add_action
https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/add_action

■add_menu_page
https://wpdocs.osdn.jp/%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9/add_menu_page

前回までで、プラグイン開発の準備を進めていました。
ここからは具体的にプラグインを形にしていこうと思います。

まず、なんでもいいのでサンプル用のphpファイルを作成し、
前回に書いた、wordpressのプラグインフォルダ内にサンプルphpを設置します。

/wordpressのドキュメントルート/wp-content/plugins/SamplePlugin1/SamplePlugin1.php

次にSamplePlugin1.phpの中に以下のようにコードを書きます。
ここで注意が必要なのは、プラグインファイルの文字コードはUTF-8にしておかないと、
後々文字化け等の現象が出てくることがあるので、注意します。

<?php
/*
Plugin Name: SamplePlugin1
Plugin URI:  http://
Description: サンプルプラグイン。
Version:     1.0
Author:      sample
Author URI:  http://
License:     GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

class SamplePlugin1
{
	function __construct()
	{

	}

}

new SamplePlugin1; 

当然、このコードだけではプラグインとしての動作はなにもありません。
ですが、このファイルを設置するだけで、wordpressの管理画面の「プラグイン」の画面に
プラグイン名称が表示されます。

001

一旦、ここまで作ってみて、動作を確認し、次の段階にいきます。

早速プラグインを作っていきますが、まずはプラグインのファイルを
格納するディレクトリの確認からです。

pluginは、wordperssのドキュメントルート配下から、以下のようなパス配下に
ディレクトリを作ることで、認識されます。

/wordpressのドキュメントルート/wp-content/plugins/プラグイン名

注意が必要なのは、ここでいうプラグイン名というのは、世界中にある
プラグインの名前と重複しないこと。
重複しないプラグイン名(ローマ字)を考えて、ディレクトリを作ります。

その後は、「任意のプラグイン名.php」というphpファイルを作成し、
行頭からコメントを記載します。

<?php
Plugin Name: SamplePlugin1
Plugin URI: http://
Description: サンプルのプラグインです
Version: 1.0
Author: p
Author URI: http://
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

//ここからphpのプログラムを書き始める

こうすることで、wordpressの管理画面のプラグインメニューの中に
「SamplePlugin1」という名前のプラグインが表示されるようになります。

wordpressでplugin開発を始めます

前回の投稿から時間がたってしまいましたが、これからPHPの勉強を開始します。

PHPの勉強を兼ねて、wordpressのプラグイン開発メモを記述していきます。
まずは、プラグインの管理画面側の実装からします。

HashMapを使う

HashMapを使って次のようなプログラムを書きます。

package TestPackage;

import java.util.HashMap;
import java.util.Map;

public class LinkedListTest6 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Map<String, Integer> map = new HashMap<String, Integer>();

		//要素の追加
		map.put("test2", 100);
		map.put("qqq", 67);
		map.put("www", 34);
		map.put("eee", 81);

		System.out.println("map -> " + map);
                
                //ここに、コレクションの操作を記述する
	}

}

実行結果は次のようなります。

map -> {qqq=67, www=34, test2=100, eee=81}

「キー項目と値」のセットになっていることがわかります。
Hashmapはこのように、2つセットのものを、要素分、格納するコレクションと言えます。

次に、このコレクションを操作してみます。
「ここに、コレクションの操作を記述する」という箇所に次のようなプログラムを書いて見ます。

■キーと値を同時に取得する

		for (Map.Entry<String, Integer> entry : map.entrySet()){
			System.out.println(entry.getKey() + "=>" + entry.getValue());
		}

実行結果は次のようになります。

qqq=>67
www=>34
test2=>100
eee=>81

■キーを取得する

		for (String name : map.keySet()){
			System.out.println(name);
		}

実行結果は次のようになります。

qqq
www
test2
eee

■値を取得する

		for (Integer name : map.values()) {
			System.out.println(name);
		}

実行結果は次のようになります。

67
34
100
81

■キーと値を取得する

		for (String name : map.keySet()) {
			System.out.println(name + "->" + map.get(name));
		}

実行結果は次のようになります。

qqq->67
www->34
test2->100
eee->81

■あるキーをもとに値を取得する

		System.out.println(map.get("qqq"));

実行結果は次のようになります。

67

これは2つ目の要素の値を取得してきていることがわかります。

コレクションの使い方は上記のほかにもまだまだ使い方があるので、別途追求しようと思います。

HashSetを使う

HashSetを使ってみます。

HashSetは要素の集合に、目的の要素が含まれているかを調べるときに使います。

含まれているかどうかを調べる時に、要素の順番は関係ありません。

また、nullを追加することも可能です。

package TestPackage;

import java.util.HashSet;
import java.util.Set;

public class LinkedListTest5 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Set<String> set = new HashSet<String>();

		//要素の追加
		set.add("test2");
		set.add("qqq");
		set.add("www");
		set.add("eee");

		System.out.println("set -> " + set);

		if (set.contains("www")){
			System.out.println("要素に含まれている");
		} else {
			System.out.println("要素に含まれていない");
		}

	}

}

実行結果は次のようになります。

set -> [qqq, www, test2, eee]
要素に含まれている

LinkedListをキューとして使う

LinkedListをキューとして使ってみます。
プログラムは次のように記述します。

package TestPackage;

import java.util.LinkedList;
import java.util.Queue;

public class LinkedListTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Queue<String> queue = new LinkedList<String>();

		//要素の追加
		queue.offer("test2");
		queue.offer("ttt");
		queue.offer("mmm");
		queue.offer("ppp");

		//要素の中身を出力する
		System.out.println(queue);

		//先頭要素を取り出す
		queue.poll();

		//要素の中身を出力する
		System.out.println(queue);

		//要素の先頭を「参照」して変数へ代入する
		String peekStr = queue.peek();

		System.out.println(peekStr);

		//要素の中身を出力する
		System.out.println(queue);
}

}

実行結果は次のようになります。

[test2, ttt, mmm, ppp]
[ttt, mmm, ppp]
ttt
[ttt, mmm, ppp]

注意する点は、データ構造がキューを意味している点と、pollメソッドを使って要素を取り出す時には先に挿入したものから取り出されることです。(末尾から操作するメソッドも用意されています)

また、「参照」を行っている箇所は値そのものを取り出さず、値を見ているだけの動きになっています。(一旦、変数に代入してprint文をわかりやすくしているだけです)

LinkedListを使用したコレクションを書いて見ます。
LinkedListは要素の挿入と削除を高速に行います。

一見、ArrayListに似た記述になりますが、先頭に「rrr」を挿入する際のメソッドや宣言の仕方などが微妙に違います(詳しい使い方は別途調べて書いていくことにします)。

package TestPackage;

import java.util.LinkedList;

public class LinkedListTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		LinkedList<String> list = new LinkedList<String>();

		//要素の追加
		list.add("test2");
		list.add("ttt");
		list.add("mmm");
		list.add("ppp");

		//要素の中身を出力する
		System.out.println(list);

		//先頭に「rrr」を挿入
		list.addFirst("rrr");

		//要素の中身を出力する
		System.out.println(list);

	}

}


上記を実行した結果は次のように出力されます。

[test2, ttt, mmm, ppp]
[rrr, test2, ttt, mmm, ppp]

ArrayListを使用したコレクションを書いて見ます。

package TestPackage;

import java.util.ArrayList;
import java.util.List;

public class ArrayListTestX {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		List<String> list = new ArrayList<String>();

		//要素の追加
		list.add("test1");
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");

		//要素の中身を出力する
		System.out.println("最初->" + list.get(0));
		System.out.println("最後->" + list.get(list.size() - 1));

		//すべての要素を出力する
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}

		//bbbが格納されている要素の添え字を調べる
		//(要素は0から始まるので、出力結果は2と表示される)
		System.out.println(list.indexOf("bbb"));

	}

}

上記を実行した結果は次のように出力されます。

最初->test1
最後->ccc
test1
aaa
bbb
ccc
2

javaの引数パラメータに渡したパス(指定ディレクトリ)の中にあるファイルを一覧表示するプログラムを書きます。

実行した結果は、引数で指定したディレクトリ内のディレクトリとファイルが混在された形でコンソールに表示されます。

package TestPackage;

import java.io.File;

public class DirShow {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		if (args.length != 1){
			System.out.println("args not");
			System.exit(0);
		}

		String dirname = args[0];

		File dir = new File(dirname);

		String[] dirlist = dir.list();

		for (int i=0; i < dir.length(); i++)  {
			System.out.println(dirlist[i]);
		}

	}

}

ファイルの削除を行う

ファイルの削除を行ってみます。
ソースを下記のように記述し、実行をしてみます。

このとき、あらかじめD:\直下にa.txtというテキストファイルが存在するものとします。

package TestPackage;

import java.io.File;

public class DeleteFile {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		if (args.length != 1) {
			System.exit(0);
		}

		String filename = args[0];

		File file = new File(filename);

		if (file.delete()) {
			System.out.println("file delete ok");
		} else {
			System.out.println("file delete ok");
			System.exit(0);
		}

	}
}

実行した結果、a.txtファイルは消えたことを確認しました。

ファイルへの書き込みを行う

javaからファイルに書き込みを行うプログラムを書いてみました。

実行する時はeclipseの実行→実行構成で引数を2つ指定する必要があるので注意です。
下記画像は引数を設定した例をキャプチャしたものです(ご参考までに)
001

package TestPackage;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class WriteFile {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		/*
		if (args.length != 1) {
			System.out.println("len 1 !");
			System.exit(0);
		} else {
			System.out.println("len 2 !");
		}
		*/

		String inputData = args[0];
		String filename = args[1];

		try{
			//BufferedReader reader = new BufferedReader(new FileReader(filename));
			//BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
			BufferedReader reader = new BufferedReader(new FileReader(inputData));

			PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)));

			String line;

			//ファイルの中を開いて1行ずつに出力用変数に代入します
			while ((line = reader.readLine()) != null){
				writer.println(line);
			}

			//reader削除します
			reader.close();
			writer.close();

		} catch(FileNotFoundException e){
			System.out.println("filename -> なし");
		} catch(IOException e){
			System.out.println(e);
		}

	}
}

ファイルの読み込みを書いてみます。

package TestPackage;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ShowFile {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		if (args.length != 1) {
			System.out.println("len 1 !");
			System.exit(0);
		} else {
			System.out.println("len 2 !");
		}

		String filename = args[0];

		try{
			BufferedReader reader = new BufferedReader(new FileReader(filename));
			String line;
			//ファイルの中を開いて1行ずつコンソールに出力する
			while ((line = reader.readLine()) != null){
				System.out.println(line);
			}
		} catch(FileNotFoundException e){
			System.out.println("filename -> なし");
		} catch(IOException e){
			System.out.println(e);
		}

	}

}

上記のソースを書いてプログラムを実行するのですが、ひとつ気をつけなければならないことは

String filename = args[0];

の部分で、プログラム実行時の引数に値が渡ってくる。という概念があります。

eclipseの場合は、メニューの「実行」→「実行構成」から別ウィンドウを開き、その中でJAVAの目的のソースコード名を選択して、「引数」タブ内に引数情報を入力することでプログラムの動作を確認しました。

下記のようなソースを書きます。

package ThreadPackage;

public class JoinTest extends Thread {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		JoinTest th = new JoinTest();

		System.out.println("main start");

		th.start();

		System.out.println("main wait");

		try{
			th.join();
		} catch(InterruptedException e){
			System.out.println(e);
		}

		System.out.println("main end");
	}

	public void run(){
		System.out.println("run start");

		try {
			Thread.sleep(3000);
		}catch(InterruptedException e){
			System.out.println(e);
		}
		System.out.println("run end");
	}

}

実行すると次のようなメッセージがコンソールに表示されます

main start
main wait
run start
run end
main end

いったい何が起きているのかというと、

JoinTest th = new JoinTest();

でいったんスタートしたthというスレッドのインスタンスが実行している間に、

th.join();

というメソッドが起動したタイミングで

public void run(){

のメソッドで定義した別スレッドが起動され、その終了をthというスレッドが待っている状態になっています。

他のスレッドの終了を待ってからメインだったスレッドが再開する。という動作ができます。

注意として、joinというメソッドの使い方には3つの方法があります。

//タイムアウトなし、スレッドの終了を待つ
void join() throws InterruptedException

//タイムアウトあり、スレッドの終了を待つ(ミリ秒)
void join(long msec) throws InterruptedException

//タイムアウトあり、スレッドの終了を待つ(ミリ秒+ナノ秒)
void join(long msec, int nsec) throws InterruptedException

スレッド動作を指定秒ストップさせることができます。

次のようなプログラムを作成して実行してみます。

package ThreadPackage;

public class Ts {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		//スレッドの動作がsleep秒ごとにどんどん遅くなっていく
		for(int i=0; i<10; i++){
			int tm = i  * 1000;
			System.out.println("Start tm -> " + tm);
			try {
				Thread.sleep(tm);
			}catch(InterruptedException e){

			}
		}

	}

}

実行結果は以下のようになり、下に行けば行くほど表示する時間間隔が長くなっていきます。

Start tm -> 0
Start tm -> 1000
Start tm -> 2000
Start tm -> 3000
Start tm -> 4000
Start tm -> 5000
Start tm -> 6000
Start tm -> 7000
Start tm -> 8000
Start tm -> 9000

単純にとあるループ処理を10回繰り返すだけですが、ループ中に

Thread.sleep(tm);

と記載し、tm秒ごとに処理を一時停止しています。

スレッドを止める場合

既に実行しているスレッドを止める場合には以下のよう書きます。

public void stopRunning() {
    running = false;
}

使い方については上記のメソッドを起動するタイミング(操作)を既存のスレッドを利用しているプログラムに適用するように使います。
(実際の例は別途追記)

1 / 41234