※WordPressのバージョンは3.4を使用します。

postsテーブルに関連付けされたデータベース

完全に独立したテーブルを作成するのはちょっと大変なので、投稿データ(postsテーブル)に関連付けされたテーブルを作成します。
WordPressにはカスタムフィールドという機能がありますが、あれはpostmetaというテーブルに保存されるので、これをオリジナルのテーブルに保存するイメージです。

カスタムフィールドをオリジナルのテーブルに保存するメリット

カスタムフィールドを作成すると「price」とか「pdf」などのキーを設定しますが、これは「postmeta」テーブルの「meta_key」カラムにすべて保存されます。
これはこれで柔軟性があっていいのですが、検索性がいまいちだったりします。
これをオリジナルのテーブルにしてキーをオリジナルのカラムに設定すればなんとなく気分がいいです。

カラムに別けられたカスタムフィールド

プラグインを有効にしたときデータベースを作成する

直接データベースを編集してもいいですが、配布を目的としているならプラグインから自動で作成できた方が便利です。WordPressにはその辺りの便利な機能があります。

「custom-meta-table」という名前でプラグインを作成します。
プラグインフォルダにフォルダとファイルを作成しましょう。
「plugins」→「custom-meta-table」→「custom-meta-table.php」

プラグインのひな形とコンストラクタを作成します。

custom-meta-table.php

	<?php
	/*
	Plugin Name: Custom Meta Table
	Plugin URI: https://www.webopixel.net/
	Description: カスタムフィールドの値をオリジナルのテーブル(DB)に保存する
	Author: MyName
	Version: 0.1
	Author URI: https://www.webopixel.net/
	*/
	
	class CustomMetaTable {
		//プラグインのテーブル名
		var $table_name;
		public function __construct()
		{
			global $wpdb;
			// 接頭辞(wp_)を付けてテーブル名を設定
			$this->table_name = $wpdb->prefix . 'ex_meta';
			// プラグイン有効かしたとき実行
			register_activation_hook (__FILE__, array($this, 'cmt_activate'));
		}
	}
	$exmeta = new CustomMetaTable;
	

constructではテーブル名の指定と、「register_activation_hook」フックを書きました。
「register_activation_hook」はプラグインを有効にしたときだけ実行されるフックです。
引数に「cmt_activate」を指定しているので、このメソッドでテーブルを作成する処理を書けばいいわけです。

以下のコードはすべてこのクラス内に記述します。

custom-meta-table.php

	function cmt_activate() {
		global $wpdb;
		//DBのバージョン
		$cmt_db_version = '1.0';
		//現在のDBバージョン取得
		$installed_ver = get_option( 'cmt_meta_version' );
   		// DBバージョンが違ったら作成
   		if( $installed_ver != $cmt_db_version ) {
   			$sql = "CREATE TABLE " . $this->table_name . " (
				  meta_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
				  post_id bigint(20) UNSIGNED DEFAULT '0' NOT NULL,
				  item_name text,
				  price int(11),
				  UNIQUE KEY meta_id (meta_id)
				)
				CHARACTER SET 'utf8';";
			require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
			dbDelta($sql);
			//オプションにDBバージョン保存
			update_option('cmt_meta_version', $cmt_db_version);
   		}
	}
	

あとあとのことも考えてデータベースのバージョンを「options」テーブルに保存しています。
「$cmt_db_version」とこの「options」のバージョンが違ったらデータベースを作成&更新します。
9行目からが実際にテーブルを作成するためのSQL文です。
ここでは自身のIDを保存する「meta_id」とpostテーブルと関連付けるための「post_id」、あとはカスタムフィールドの値である「 item_name」「price」というカラムを設定しました。

18行目の「dbDelta」を使用していますが、これはデータベースの差分を比較して必要に応じてテーブルを追加・修正をしてくれます。

この状態でプラグインを有効化すると、設定した「wp_ex_meta」という名前で新しいテーブルが追加されます。

Creating Tables with Plugins – WordPress Codex 日本語版

カスタムフィールドの入力欄の作成

次に記事投稿にカスタムフィールドのフォームを作成します。

custom-meta-table.php

	public function __construct()
	{
		・・・・・
		// カスタムフィールドの作成
		add_action( 'add_meta_boxes', array($this, 'ex_metabox'));
	}
	
	function ex_metabox( $post ) {
		add_meta_box( 
			'exmeta_sectionid',
			'その他の項目',
			array($this, 'ex_meta_html'),
			'post'
		);
	}
	function ex_meta_html () {
		wp_nonce_field( plugin_basename( __FILE__ ), $this->table_name );
		global $post;
		global $wpdb;

		$get_meta = $wpdb->get_results(
			$wpdb->prepare( "SELECT * FROM
				".$this->table_name. " WHERE
				post_id = %d", $post->ID
			)
		);
		$get_meta = isset($get_meta[0]) ? $get_meta[0] : null;
		$item_name = isset($get_meta->item_name) ? $get_meta->item_name : null;
		$price = isset($get_meta->price) ? $get_meta->price : null;
		?>
		<div>
		<table>
			<tr>
				<th>商品名</th>
				<td><input name="item_name" value="<?php echo $item_name ?>" /></td>
			</tr>
			<tr>
				<th>価格</th>
				<td><input name="price" value="<?php echo $price ?>" /></td>
			</tr>
		</table>
		</div>
		<?php
	}
	

コンストラクタに「add_meta_boxes」というアクションフックを追加します。
「ex_metabox」ではカスタムフィールドのセクション名や、表示する投稿タイプを指定します。
「ex_meta_html」には実際表示するフォームを記述します。
この部分は新規投稿以外にも編集画面にも同じものを表示しますので、データベースにデータがある場合は、データを読み込んで表示するという処理が入っています。

レコードの保存

custom-meta-table.php

	public function __construct()
	{
		・・・・・
		add_action ('save_post', array($this, 'save_meta'));
	}
	function save_meta($post_id) {
		if (!isset($_POST[$this->table_name])) return; 

		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )  return;
  		if ( !wp_verify_nonce( $_POST[$this->table_name], plugin_basename( __FILE__ ) ) )  return;

		global $wpdb;
		global $post;

		//リビジョンを残さない
		if ($post->ID != $post_id) return;

		$temp_item_name = isset($_POST['item_name']) ? $_POST['item_name'] : null;
		$temp_price = isset($_POST['price']) ? $_POST['price'] : null;

		//保存するために配列にする
		$set_arr = array(
			'item_name' => $temp_item_name,
			'price' => $temp_price
			);

		$get_id = $wpdb->get_var(
		            $wpdb->prepare( "SELECT post_id FROM
		            	". $this->table_name ." WHERE 
		            	post_id = %d", $post_id)
		);
		//レコードがなかったら新規追加あったら更新
		if ($get_id) {
			$wpdb->update( $this->table_name, $set_arr, array('post_id' => $post_id));
		} else {
			$set_arr['post_id'] = $post_id;
			$wpdb->insert( $this->table_name, $set_arr);
		}
		$wpdb->show_errors();
	}
	

保存処理は「save_post」というアクションに引っかけます。
データの更新には「$wpdb->update」を使用して、新規保存するには「$wpdb->insert」と別のメソッドを使用しますので、その前の行でデータベースのレコードがあるか確認しています。
16行目はリビジョンの場合はリターンして残さないようにしています。

レコードの削除

投稿を削除したときにカスタムフィールドの値も削除しないといけませんね。

custom-meta-table.php

	public function __construct()
	{
		・・・・・
		add_action ('delete_post', array($this, 'dalete_meta'));
	}
	function dalete_meta($post_id) {
		global $wpdb;
		$wpdb->query( $wpdb->prepare( "DELETE FROM $this->table_name WHERE post_id = %d", $post_id) );
	}
	

保存処理は「delete_post」というアクションに引っかけます。
保存のときとは専用のメソッドがありましたが、削除はたぶんないのでMySQLのDELETEコマンドを使用します。

テンプレートに表示

これで「作成」「更新」「削除」という管理画面の部分はできましたので、あとはテンプレートに表示するためのメソッドを作成しましょう。

custom-meta-table.php

	function get_meta($post_id) {
		if (!is_numeric($post_id)) return;
		global $wpdb;
		$get_meta = $wpdb->get_results(
			$wpdb->prepare( "SELECT * FROM
				".$this->table_name. " WHERE
				post_id = %d", $post_id
			)
		);
		return isset($get_meta[0]) ? $get_meta[0] : null;
	}
	

これは「$post_id」を引数で渡すと、カスタムフィールドの値をオブジェクトで返します。
たとえば「single.php」で表示したい場合は次のようになります。

single.php

	<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
	<?php the_content(); ?>
	<h4>カスタムフィールド</h4>
	<?php
	$get_meta = $exmeta->get_meta($post->ID);
	$item_name = isset($get_meta->item_name) ? $get_meta->item_name : null;
	$price = isset($get_meta->price) ? $get_meta->price : null;
	echo esc_html($item_name );
	echo esc_html($price);
	?>
	<?php endwhile; else: ?>
	    <p>記事はまだありません。</p>
	<?php endif; ?>
	

もっと簡単にできるとこととか、逆に足りない部分があるかと思いますが、全体的な流れはこんな感じかなと思います。 全体的なコードはGistで公開しています。

WordPressでカスタムフィールドの値をオリジナルのテーブル(DB)に保存する — Gist