【WordPress】プラグインなしでカスタム投稿タイプにカスタムフィールド（テキスト＆画像）を追加する方法

プラグインを使わずに、カスタム投稿タイプにカスタムフィールド（テキスト＆画像）を追加する方法を書いていこうと思います。

## まずはじめに

カスタム投稿タイプなどに限らずですが、基本はWordPressに何か機能を追加しようする場合などは、アクションフックやフィルターフックを利用して追加していきます。**いろいろなフックを知ることで、好きなタイミングで任意の処理などを追加できるようになります**ので、WordPress標準のアクションフック・フィルターフックは覚えておくといいと思います。今回は、`init`と`admin_menu`と`save_post`と`post_edit_form_tag`というフックを利用していきます。

## カスタム投稿タイプを追加

まずは、管理画面にカスタム投稿タイプを追加していきます。
`init`をフックして`register_post_type関数`を使ってカスタム投稿タイプを登録します。ちなみに、`init`の実行タイミングは**WordPressのコアファイルの読み込みが終わり、HTTPのヘッダーを出力する前**となります。

```php
function create_post_type(){
  //カスタム投稿タイプがダッシュボードの編集画面で使用する項目を配列で用意
  $supports = array(
    'title',
    'editor',
    'author',
    'thumbnail',
    'revisions'
  );
  //カスタム投稿タイプを追加するための関数
  //第一引数は任意のカスタム投稿タイプ名
  register_post_type('hoge_custom_post',
    array(
      'label' => 'サンプルカスタム投稿',
      'public' => true, //フロントエンド上で公開する場合true
      'has_archive' => true, //アーカイブページを表示したい場合true
      'menu_position' => 3, //メニューを表示させる場所
      'supports' => $supports //ダッシュボードの編集画面で使用する項目
    )
  );
}
add_action('init','create_post_type');
```

## 作成したカスタム投稿タイプにカスタムフィールドを追加

次に、作成したカスタム投稿タイプにカスタムフィールドを追加していきます。
`admin_menu`をフックして`add_meta_box関数`でメタボックスを追加します。`admin_menu`の実行タイミングは **管理画面が読み込まれた時** です。

```php
function add_custom_fields(){
    add_meta_box(
        'custom_setting', // 編集画面セクションのHTML ID
        'カスタム情報', // 編集画面セクションのタイトル、画面上に表示される
        'insert_custom_fields', // 編集画面セクションにHTML出力する関数
        'hoge_custom_post', // 投稿タイプ名
        'normal' // 編集画面セクションが表示される部分
    );
}
add_action('admin_menu', 'add_custom_fields');

function custom_metabox_edit_form_tag(){
  echo ' enctype="multipart/form-data"';
}
// 画像をアップする場合は、multipart/form-dataの設定が必要なので、post_edit_form_tagをフックしてformタグに追加
add_action('post_edit_form_tag', 'custom_metabox_edit_form_tag');

// カスタムフィールドの入力エリア
function insert_custom_fields(){
    global $post;
    // get_post_meta関数を使ってpostmeta情報を取得
    $hoge_name = get_post_meta(
                     $post->ID, // 投稿ID
                     'hoge_name', // キー名
                     true // 戻り値を文字列にする場合true(falseの場合は配列)
                 );
    $hoge_thumbnail = get_post_meta($post->ID,'hoge_thumbnail',true);
    echo '名前： <input type="text" name="hoge_name" value="'.$hoge_name.'" /><br>';
    echo '画像： <input type="file" name="hoge_thumbnail" accept="image/*" /><br>';
    if(isset($hoge_thumbnail) && strlen($hoge_thumbnail) > 0){
        // hoge_thumbnailキーのpostmeta情報がある場合は、画像を表示
        // $hoge_thumbnailには、後述するattach_idが格納されているので、wp_get_attachment_url関数にそのattach_idを渡して画像のURLを取得する
        echo '<img style="width: 200px;display: block;margin: 1em;" src="'.wp_get_attachment_url($hoge_thumbnail).'">';
    }
}
```

## カスタムフィールドの保存処理

最後に、カスタムフィールドの値を保存する処理を書いていきます。
`save_post`をフックして`update_post_meta関数`を使って`wp_postmetaテーブル`に値を保存していきます。`save_post`の実行タイミングは**投稿を保存した時**です。また、画像の場合は`wp_postsテーブル`に画像情報を格納して、そのレコードのIDを`wp_postmetaテーブル`に保持させる形になっています。
 **※ 画像ファイル自体は、uploadsディレクトリ配下に置くので、パーミッションがない場合は権限を与えるようにしてください** 

```php
function save_custom_fields( $post_id ) {
    if(isset($_POST['hoge_name'])){
        //hoge_nameキーで、$_POST['hoge_name']を保存
        update_post_meta($post_id, 'hoge_name', $_POST['hoge_name']);
    }else{
        //hoge_nameキーの情報を削除
        delete_post_meta($post_id, 'hoge_name');
    }
	
    if(isset($_FILES['hoge_thumbnail']) && $_FILES["hoge_thumbnail"]["size"] !== 0){
        $file_name = basename($_FILES['hoge_thumbnail']['name']);
        $file_name = trim($file_name);
        $file_name = str_replace(" ", "-", $file_name);

        $wp_upload_dir = wp_upload_dir(); //現在のuploadディクレトリのパスとURLを入れた配列
        $upload_file = $_FILES['hoge_thumbnail']['tmp_name'];
        $upload_path = $wp_upload_dir['path'].'/'.$file_name; //uploadsディレクトリ以下などに配置する場合は$wp_upload_dir['basedir']を利用する
        //画像ファイルをuploadディクレトリに移動させる
        move_uploaded_file($upload_file,$upload_path);

        $file_type = $_FILES['hoge_thumbnail']['type'];
        //正規表現で拡張子なしのスラッグ名を生成
        $slug_name = preg_replace('/\.[^.]+$/', '', basename($upload_path));

        if(file_exists($upload_path)){
            //保存に成功してファイルが存在する場合は、wp_postsテーブルなどに情報を追加
            $attachment = array(
                'guid'           => $wp_upload_dir['url'].'/'.basename($file_name), 
                'post_mime_type' => $file_type, 
                'post_title' => $slug_name, 
                'post_content' => '', 
                'post_status' => 'inherit'
            );
            //添付ファイルを追加
            $attach_id = wp_insert_attachment($attachment,$upload_path,$post_id);
            if(!function_exists('wp_generate_attachment_metadata')){
                require_once(ABSPATH . "wp-admin" . '/includes/image.php');
            }
            //添付ファイルのメタデータを生成し、wp_postsテーブルに情報を保存
            $attach_data = wp_generate_attachment_metadata($attach_id,$upload_path);
            wp_update_attachment_metadata($attach_id,$attach_data);
            //wp_postmetaテーブルに画像のattachment_id(wp_postsテーブルのレコードのID)を保存
            update_post_meta($post_id, 'hoge_thumbnail',$attach_id);
        }else{
            //保存失敗
            echo '画像保存に失敗しました';
            exit;
        }
    }
}
add_action('save_post', 'save_custom_fields');
```

## 最後に

以上の手順で、カスタム投稿タイプにカスタムフィールド（テキスト&画像）を追加することができます。あとは、画像のinput要素などはlabel要素を使ってリッチにしたり、画像がすでに登録されている場合はinput要素を非表示にするなどの処理を入れれば、いい感じのカスタムフィールドが作れると思います。