つながるnet

カスタム投稿タイプとカスタムフィールドでカスタマイズ

お客様にWordpressを納品する場合、物件登録や施工例などのように特定の投稿に特化した 専用の投稿画面 が必要になることがほとんどです。最近実装する機会がありましたので参考に。必要に応じて追加削除してください。

実現するために必要な要素は:

カスタム投稿タイプで投稿画面を独立させる

カスタム投稿タイプを定義する関数は、register_post_type です。
以下の例は codex のサンプルをベースにラベル類を「物件」に置き換えた例です。
functions.phpに記述します。

add_action('init', 'my_estate_init');
function my_estate_init() 
{
  $labels = array(
    'name' => '登録物件',
    'singular_name' => '物件',
    'add_new' => '物件の追加',
    'add_new_item' => '物件を追加する',
    'edit_item' => '物件を編集する',
    'new_item' => '新しい物件',
    'view_item' => '物件表示',
    'search_items' => '物件検索',
    'not_found' =>  '検索物件が見つかりません',
    'not_found_in_trash' => 'ゴミ箱に物件はありません', 
    'parent_item_colon' => ''
  );
  $args = array(
    'labels' => $labels,
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true, 
    'query_var' => true,
    'rewrite' => true,
    'capability_type' => 'post',
    'hierarchical' => false,
    'menu_position' => 5,
//  'supports' => array('title','editor','author','thumbnail','excerpt','comments')
    'supports' => array('title','editor')
  ); 
  register_post_type('estate',$args);
}

各行の詳しい解説は割愛しますが、
編集画面で必要ないフィールドは28行目のように ‘supports’ の配列から除外すれば非表示にできます。

まず ‘rewrite’ => true によって投稿を表示するためのURLは http://ドメイン/?estate=投稿タイトル のデフォルトURLから  http://ドメイン/estate/投稿タイトル にリライトしてくれます。しかしこれだけではリライトされたURLにアクセスしても、404になって記事を表示することはできません。そこで必要なのが、一度、[管理画面/設定/パーマリンク設定]を開くだけでリライトルールを更新してくれます。

これで「物件登録」という投稿メニューを独立させることができました。
次にカスタムフィールドで入力しやすいように必要なフィールドを追加していきます。

カスタムフィールドで入力支援

秀逸なプラグイン、Custom Field GUI UtilityAdvanced Custom Fields などを使えばかなり便利です。
しかし、どうしても間延びした投稿画面となってしまいます。ここではプラグインを使用せずに使いやすい投稿画面を実装してみることにします。

行なっていることは:

  • カスタムフィールドの項目定義
  • カスタムフィールドコンテンツの作り込み
  • 投稿画面にカスタムフィールドのセクションを追加(add_meta_box)
  • カスタムフィールドの入力値の保存

functions.phpに以下のように記述します。

/*** カスタムフィールド項目定義 ***
 *
 * $meta_arr[$id] = array($name,$array,$option);
 *   $id:     キー
 *   $name:   項目名
 *   $array:  保存データ形式('array':配列、'single':テキスト)
 *   $option: オプション項目
 *   checkboxの場合は配列として登録される
 */
$meta_arr['esArea'     ] = array('エリア',         'array', array('ビジネス街', '住宅街', '商店街', '歓楽街'));
$meta_arr['esCatchcopy'] = array('キャッチコピー', 'single');
$meta_arr['esProfeel'  ] = array('物件紹介文',     'single');
$meta_arr['esRental'   ] = array('賃料',           'single');
$meta_arr['esNotes'    ] = array('備考',           'single');

このようにメタ項目を配列化しておくと、入力フォームでも、保存時にも、そして、ページ表示時にも、使い回すことができますので、とても便利です。必要に応じて、配列の項目を増やすことができることでしょう。

/*** カスタムフィールドコンテンツの作り込み ***/
function my_meta_boxes() {

    global $post, $meta_arr;

    //metaの現在の登録値を取得(可変変数)
    foreach($meta_arr as $meta => $meta_val) {
        $true = ( $meta_val[1] == 'single' )? true: false;
        $val = $meta.'Val';
        $nam = $meta.'Nam';
        $$nam = $meta_val[0];
        $$val = get_post_meta( $post->ID, $meta, $true );
    }

?>
<div class="postbox postbox_estate">
    <h4>物件プロフィール</h4>
    <dl>
        <dt><?php echo $esAreaNam ?>:</dt>
        <dd>
        <?php foreach ($meta_arr['esArea'][2] as $optn): ?>
                <label><input type="checkbox" name="esArea[]" value="<?php echo $optn ?>"<?php if ( is_array($esAreaVal[0]) ) {if ( in_array($optn, $esAreaVal[0]) ) echo ' checked="checked"';} ?> /> <?php echo $optn ?></label>
        <?php endforeach ?></dd>
        <dt><?php echo $esCatchcopyNam ?>:</dt>
        <dd><textarea name="esCatchcopy" type="textfield" rows="2" style="width:90%"><?php echo $esCatchcopyVal ?></textarea><br />
            見出しの下に表示されます。一行に収まる長さでご入力ください。</dd>
        <dt><?php echo $esProfeelNam ?>:</dt>
        <dd><textarea name="esProfeel" type="textfield" rows="4" style="width:90%"><?php echo $esProfeelVal ?></textarea></dd>
    </dl>
</div>
<div class="postbox postbox_estate">
    <h4>物件詳細データ</h4>
    <dl>
        <dt><?php echo $esRentalNam ?>:</dt>
        <dd><input name="esRental" type="text" value="<?php echo $esRentalVal ?>" style="width:60%"><br />
        ○○円/月</dd>
        <dt><?php echo $esNotesNam ?>:</dt>
        <dd><textarea name="esNotes" type="textfield" rows="4" style="width:90%"><?php echo $esNotesVal ?></textarea></dd>
    </dl>
</div>
<?
}

細かなことは解説しませんが、フォームエントリの生成前に既存値を可変変数で取得していること、そして、配列として取得したチェックボックスの値の処理に注目して解析してみてください。

次に、これらエントリを含むフォームセクションを投稿画面に挿入します。

/*** 投稿画面にカスタムフィールドのセクションを追加 ***/
function create_meta_box() {

    if ( function_exists('add_meta_box') )
//  add_meta_box('id', 'title', 'callback', 'page', 'context', 'priority');
        add_meta_box( 'my-meta-boxes', '物件登録カスタムフィールド', 'my_meta_boxes', 'estate', 'normal', 'high' );
}

次に登録値をデータベースに保存します。

/*** カスタムフィールド入力値の保存 ***/
function save_postdata( $post_id ) {

    global $post, $meta_arr;
    foreach($meta_arr as $meta => $arr) {
        $true = ( $arr == 'single' )? true: false;

        $meta_cur = get_post_meta($post_id, $meta, $true);
        $meta_new = $_POST[$meta];

        if( $meta_cur == "" && $meta_new != "") {
            add_post_meta($post_id, $meta, $meta_new, true);
        } elseif ( $meta_cur != $meta_new ) {
            update_post_meta($post_id, $meta, $meta_new);
        } elseif ( $meta_new == "" ) {
            delete_post_meta($post_id, $meta, get_post_meta($post_id, $meta_cur, true));
        }
    }
}
add_action('admin_menu', 'create_meta_box');
add_action('save_post', 'save_postdata');

今回は管理画面内で登録が完結しますのでセキュリティ面は特に処理は必要ないものと思います。これらのロジックをフロントエンドに実装する場合には、投稿権限のあるユーザーか、登録フォームからの登録かの判定等を実装する必要があります。

投稿画面をCSSで体裁もカスタマイズ

「登録物件」の編集時のみ、管理画面の head にフックします。直接スタイル定義してもよいのですが、ここではcssファイルを読み込ませています。これもfunctions.phpに記述します。

/*** 投稿画面用CSSの追加 ***/
function admin_estate_head() {
    global $post_type;
    if ($post_type == 'estate') {
        ?>
        <link type="text/css" href="<?php echo get_template_directory_uri() ?>/admin_estate.css" rel="stylesheet" />
        <?php
    }
}
add_action('admin_head', 'admin_estate_head');

cssファイルでスタイル定義して体裁をかっこよくします。以下のサンプルはカスタムフィール部分の体裁定義に加えて
ビジュアルエディタはいらないけど「メディアを追加」は必要というケースに対応しています。

@charset "utf-8";

.postbox_estate {
    padding:10px;
    line-height:1.2;
}
.postbox_estate h4 {
    margin:0 0 8px 0;
    padding-bottom:10px;
    border-bottom:solid 1px #dfdfdf;
}
.postbox_estate p {
    margin:0 0 8px 0;
}
.postbox_estate dl dt {
    width:100px;
    float:left;
}
.postbox_estate dl dd {
    margin:0;
    padding-left:100px;
    padding-bottom:8px;
    margin-bottom:8px;
    border-bottom:solid 1px #dfdfdf;
}
.postbox_estate dl dd label {
    display:inline-block;
    margin-right:3px;
}

#post-body-content {
    margin-bottom:0;
}
#wp-content-editor-container,
#post-status-info,
#content-html,
#content-tmce {
    display:none;
}
.media-toolbar-primary {
    display:none;
}

RSS発信に追加

RSS登録してくださっている閲覧ユーザーの方には、カスタム投稿タイプからの投稿はRSSで知らされません。RSS発信に追加するには:

/*** RSS発信に追加 ***/
function estate_post_rss_set( $query ) {
    if ( is_feed() ) {
        $post_type = $query->get( 'post_type' );
        if ( empty( $post_type ) ) {
            $query->set( 'post_type', array( 'post', 'estate' ) );
        }
        return $query;
    }
}
add_filter( 'pre_get_posts', 'estate_post_rss_set' );

モンキーレンチさんのページで紹介されていたコードです。

プラグイン化する?(おまけ)

この例では、cssファイルの記述以外のすべて、functions.phpに記述しました。
しかし、これらの記述を別のphpファイルに記述して、プラグインヘッダを追加すれば、独立したプラグインとして動作します。複数のお客様で流用するようなことがあればプラグインにしたほうが便利かもしれません。

しかし、ほとんど場合、まったく同じということはなく、お客様ごとに作り込まなければならないことでしょう。プラグイン化してしまうと、別のフォルダで管理しなければなりませんので、ちょっと面倒な点も生じるかもしれません。プラグイン化しないまでも、テーマ内で別ファイルとして管理すれば便利と思われます。

今回の例の場合は、admin_estate.phpというファイルにコードを独立させて、functions.phpにincludeすればOKです。記述は一行:

include_once (TEMPLATEPATH . '/admin_estate.php');

何かおかしな点に気付かれましたら、どうぞお気軽にご指摘ください。

修正履歴

修正(2013/09/10)
post_type名としてworkとしてしまっているところが何か所かあり、estateと混在してしまっていたようです。
estateに修正して統一いたしました。

直接メールしてみる

お名前 (必須)
メールアドレス (必須)
件名
ご相談内容
画像文字をご入力ください
captcha

コメントをどうぞ

コメント:6 件

  1. 匿名

    LPテンプレートクリエイティブパック「Colorful(カラフル)」の古いバージョンをダウンロードしたら
    Fatal error: Cannot redeclare class Acf in~~~~
    と出てきてワードプレスのHP自体が開けなくなりました

    返信
  2. 初歩的な質問で本当にすみませんが、教えてください。

    複数のカスタム投稿タイプを設置したく、上記を参考に
    include_once (TEMPLATEPATH . ‘/admin_estate1.php’);
    include_once (TEMPLATEPATH . ‘/admin_estate2.php’);
    といった具合にfunction.phpに書き込んだのですが、
    2つ目以降のphpファイル(↑の場合admin_estate2.php)にエラーが出てしまい表示されなくなってしまいました。
    (1と2を入れ替えると、同じ内容のエラーですが、2つ目に記述したほうのエラーになります。)
    具体的には

    …..

    <?
    }
    の「<?」の箇所だと言われました。
    どうしたら動作するようになるのでしょうか?

    返信
    1. yusuke

      木さん、お問い合わせくださりありがとうございます。
      直接メールでやり取りをさせていただき、解決したということです。

      今後のご訪問者のためにちょっとまとめますので参考になさってください。

      —————
      表示されていたエラーメッセージは:
      Fatal error: Cannot redeclare [ファンクション名] (previously declared in …..
      直訳すると、
      致命的なエラー: [ファンクション名]を再定義できません(以前に….で定義されています)

      種類の違うカスタムフィールドを複数設けるためにファイルを二つに分けておられ、そのカスタムフィールドを定義するファンクションの名前が重複してしまっているのが原因でした。

      重複しないように異なるファンクション名にすることにより問題が解決したということです。

      返信
  3. yahss

    匿名さん、ご指摘ありがとうございました。
    本当ですね。post_type名としてworkとestateが何か所かで混在してしまっていたようです。
    修正いたしました。

    返信
  4. 匿名

    if ($post_type == ‘work)
          ↓↓
    if ($post_type == ‘estate’)

    もです。

    返信
  5. 匿名

    有用な情報ありがとうございます。

    register_post_type(‘work’,$args);
    の部分が
    register_post_type(‘estate’,$args);
    でないとそのままでは動かないようです。

    今後見る方のために修正をされてはどうでしょうか?

    返信

コメントを残す