【Wordpress】カスタム投稿を使って作品紹介用の登録ページを作る


自分で作ったアニメーションやスクリプトを、作品というカテゴリで記事として投稿していました。しかし、カテゴリやタグをつける際に困りました。作品の中でJavaScriptやJava。備忘録の中でのJavaScriptやJava。と同じカテゴリ名で区分けしないと後で整理するときに面倒になります。そこで今回はカスタム投稿を使い、作品を記事からカスタム投稿へ移行しました。その際にやったことを記録しておきます。

### カスタム投稿/タクソノミーの関数を使った追加方法

プラグインを使用する前に仕組みを理解します。

* [[WordPress]3大カスタム要素、カスタム投稿タイプ・タクソノミー・フィールドを使いこなす | バシャログ。](http://bashalog.c-brains.jp/13/06/18-182559.php)
* [WordPressをカスタマイズするなら絶対覚えておきたい条件分岐やカスタム投稿タイプなどいろいろ](http://webdesignrecipes.com/wordpress-conditional-tags-and-custom-post-type/)

カスタムタクソノミーはタグかカテゴリとして設定できますが、タグを入力する分類を2つ以上用意するのは思いつきませでした。

> カスタム分類（カスタムタクソノミー）機能を使って、「ルームタイプ」と「家具の種類」という分類も付けています。  
> <cite><a href="http://webdesignrecipes.com/wordpress-corporate-website/" target="_blank">WordPressでブログじゃないWebサイトを作るときのいろいろ（サンプル付き）</a></cite>

### プラグインでカスタム投稿/タクソノミーを管理する

function.phpに記述しなくてもダッシュボードからカスタム投稿・カスタムタクソノミーは追加できるようになるプラグイン。どうやらregister_post_typeやregister_taxonomyはプラグインに記述しても有効になるらしいです。ようは1つのサイトならfunciton.phpに。複数テーマで使いまわすならプラグインにと、使い分けられています。

使用方法は下記を参考。

* [Custom Post Type UIの使い方[WordPress]](http://kotori-blog.com/wordpress/custom_post_type_ui/)
* [「Advanced Custom Fields」](https://www.eyemovic.com/blog_it/4684.php)

**注意点**

* function.phpに自動で記述されるわけではない
* 登録されたカスタム投稿slugを変更すると、修正ではなく新規で登録されることになる。以前のものはそのままになっている。

### カスタムフィールドの入力値に文字列以外のフォーマットを追加

カスタムフィールドは名前と値の組み合わせなのですが、この値が文字列しか入れれないというのは不便です。フォーマットやバリデートをかけたいですよね。このカスタムフィールドを柔軟に拡張できるすごいプラグインがありました。

[Advanced Custom Fieldsの使い方](http://kotori-blog.com/wordpress/advanced-custom-fields/)

無料でもすごいのですが、有料版があるとのこと。合わせて出力方法まで別記事で丁寧に解説してくれいますが、解説がなくても直感的に使えるような関数が用意されていて便利だなと感じました。 おそらくフィールド名で値をゲットするために、DBにはidなどの数値ではなく文字列で登録することが多いのかな。

[Advanced Custom Fieldsの出力](http://kotori-blog.com/wordpress/acf_output/)

### Advanced Custom Fieldsの有料アドオンを購入。

Repeater Fieldという有料アドオンを公式サイトから購入しました。初めてwordpressの有料プラグインを購入しましたが、方法がとても簡単でした。

**購入・導入手順**

1.  公式サイトにて購入をクリック
2.  新規登録するアカウント情報を入力
3.  paypalでログイン
4.  オンラインレシートを受け取る
5.  オンラインレシートに記述してあるリンクからプラグインであるzipファイルをDL
6.  wordpressのダッシュボードからzipファイルを新規プラグインとしてアップロード
7.  プラグインを有効化

**注意点**

* オンラインレシートにあるライセンスキーを入力する必要はなかった。
* Pro版だとライセンスが1サイトのみと無制限がある。
* 個別アドオンだと複数サイトにしようできるライセンスとなる

このアドオンによってカスタムフィールドを親子階層にすることができます。また作成したフィールドは1つの記事に対していくつも追加して値を登録することができます。

![setting_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/26a19df8-ef90-fe1a-1727-c4c3ddb6f77c.jpeg "setting_150618.jpg")


今回は作品使用した素材を1つずつカスタムフィールドで登録するために使用しました。このアドオンのお陰で、素材というカスタムフィールドを1列追加すると、素材名とURLを入力できる2つの欄が表示されます。

Tableレイアウトの投稿画面
![post_input_filed_layout_table_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/340c4b8a-7547-6e02-55c1-502f2553eee0.jpeg "post_input_filed_layout_table_150618.jpg")



この見た目はレイアウト設定で変更することができます。

![setting2_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/a8bedbda-eded-b27e-fd73-e83d07876d2b.jpeg "setting2_150618.jpg")

**Rowレイアウトの投稿画面**
![Rowレイアウトの投稿画面](https://qiita-image-store.s3.amazonaws.com/0/62881/1b6c5734-f055-3856-2224-cab0f5d630cd.jpeg "post_input_filed_layout_row_150618.jpg")


![post_input_filed_layout_table_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/24fd0c95-e6d8-1397-d7c7-b1772a13ffa7.jpeg "post_input_filed_layout_table_150618.jpg")


出力すると下記のようになります。

![post_view_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/7582401a-79cb-3272-4bc8-e589651d31c0.jpeg "post_view_150618.jpg")

### Advanced Custom Fieldsのカスタムフィールドはどのように登録されているのか

Advanced Custom Fieldsで登録したカスタムフィールドがどのようにDBに登録されているか確認したところ、meta_keyは親フィールド名_フィールド順序_子フィールド名となっていました。区切りに_（アンダーバー）が使われていたため**単語区切りに_（アンダーバー）は使用せずにキャメルケースで記述した方が良い**かと思われます。

meta_key                  | meta_value              
------------------------- | ------------------------
material_0_material_name  | グーグル                    
_material_0_material_name | field_5581556e5ed0c     
material_0_material_url   | http://www.google.co.jp/
_material_0_material_url  | field_558155895ed0d     

**訂正**

ここからカスタムフィールドの値を変更した際に、既存レコードを修正ではなく新規レコードとして登録されてしまう現象について記述していましたが原因がわかりました。ラベルではなくフィールド名を日本語にしていたため発生していたようです。英字に変更したところ、既存フィールドを更新するようになりました。日本語をkeyとするとこういうところでバグを生むのですね。念のため文章は残しておきます。

ちなみに、投稿画面で親子階層を持ったカスタムフィールドの順序を変えても新規レコードが追加されることはなく、既存レコードの値の順番の数値を書き換えてくれていました。

下記のサイトにも詳しい解説があります。カスタムフィールドは検索としては使用しない方がいいみたいです。  
[Advanced Custom Fieldsプラグインを使う際の注意点など | notnil creation weblog](http://notnil-creative.com/blog/archives/1752)

---

「グーグル」という文字を「グーグル1」にダッシュボードから変更したところ、同フィールドグループの値すべてが新しいレコードとして登録されていました。_material~のfiledより後ろの連番は以前と違うものが登録されています。

**追加されたレコード**

* グーグル1
* http://www.google.co.jp/
* ヤフー
* http://www.yahoo.co.jp/

また、入力したフィールド値だけでなく、下記のように毎回いくつのレコードを追加したかというデータ（レコード）まで一緒に登録されます。余計なデータが増えていく気がします。私には必要性がわかりませんでした。

meta_key       | meta_value         
-------------- | -------------------
materiall_name | 2                  
_material      | field_558155675ed0b

ただしどのレコード、つまりカスタムフィールドの値を変更せずに記事を更新した際は、重複したレコードが登録されることはありませんでした。

カスタムフィールドで親子階層を登録となると、別テーブルや親のレコードIDを保持するのかなと思いました。しかし、親も子もただのカスタムフィールドであり、既存テーブルであるwp_postmetaに登録されるので階層は文字列で表記されていました。

### 既存の記事をカスタム投稿に移行する

下記を参考に、プラグインで特定カテゴリの記事を一括で移行しました。カテゴリ単位でしか細かく指定できないので少し不便です。移行後にカスタムタクソノミーは手動でつけました。移行してもテーブルwp_postsに新規レコードが追加されるわけでなく、既存レコードのpost_typeを書き換えることを確認しました。

[Convert Post Types：記事とカスタム投稿記事との変換プラグイン - ログブック｜CONSA DE CONSA](http://www.consadeconsa.net/20131006-200645/)

### カスタムフィールドの値を表示する

取得したいフィールド名さえわかれば簡単に値を習得することができます。

```php
the_field('material');
// =>Array, Array

var_dump(the_field('material'));
// =>Array, ArrayNULL
// Arrayという文字列が返ってくるので構造を表示できない？
```

親子階層のカスタムフィールドは配列になるので注意が必要です。

```php
echo("<pre>");
var_dump(get_field('material'));
echo("</pre>");
// =>
// array(2) {
//   [0]=>
//   array(2) {
//     ["material_name"]=>
//     string(24) "りんごのイラスト"
//     ["material_url"]=>
//     string(11) "http:~apple"
//   }
//   [1]=>
//   array(2) {
//     ["material_name"]=>
//     string(3) "BGN"
//     ["material_url"]=>
//     string(12) "http:~sound1"
//   }
// }
```

親子階層のカスタムフィールドはforeachを入れ子にすることによって取得できます。

```php
echo("<pre>");
$materials = get_field('material');
var_dump($materials[0]);
foreach ($materials as $material) {
    foreach ($material as $key => $value) {
        echo("フィールド名 = ${key}\n");
        echo("フィールド値 = ${value}\n");
    }
    echo("\n");
}
echo("</pre>");
// =>
// フィールド名 = material_name
// フィールド値 = りんごのイラスト
// フィールド名 = material_url
// フィールド値 = http:~apple
// 
// フィールド名 = material_name
// フィールド値 = BGN
// フィールド名 = material_url
// フィールド値 = http:~sound1
```

素材の種類も選択できるようにしてみました。

![post_input_filed_add_type_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/f113be6b-fa94-bd5f-7c10-02ba2bce5677.jpeg "post_input_filed_add_type_150618.jpg")


さらに今回は下記のように装飾しました。

![post_view2_150618.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/ddc4e702-d429-52dd-47ca-ff77871c8baf.jpeg "post_view2_150618.jpg")


```php
// content.php
<div class="entry-content">
    <?php
        /* translators: %s: Name of current post */
        the_content(
            // 省略
        ) );
    ?>

    <!-- 追加コード -->
    <div class="work-materials">
        <strong>使用した素材</strong>
        <ul>
        <?php
            $materials = get_field('material');
            foreach ($materials as $material) {
                foreach ($material as $key => $value) {
                    //echo "$kye , $value \n";
                    if ($key == 'material_name') $material_name = $value;
                    if ($key == 'material_url') $material_url = $value;
                    if ($key == 'material_type') $material_type = $value;
                }
                echo '<li class="'.${material_type}.'"><a href="'.${material_url}.'" target="_blank" >'.${material_name}.'</a></li>';
            }
        ?>
        </ul>
    </div>

    <?php
        wp_link_pages( array(
        // 省略
        ) );

    ?>
</div><!-- .entry-content -->
```

CSSは下記のジェネレータを使用しました。  
[好きな色でCSS見出しが作れます | スタイルシート見出しメーカー](http://midashi-maker.v-colors.com/)

```css
/* style.css */
/* 作品の素材集 */
div.work-materials > strong {
    margin-bottom: -3%;
    font-size: 90%;
    margin:0 0 10px 0;
    padding:3px 8px;
    border-width:0 0 0 5px;
    border-color: #8d6d6d;
    border-style:solid;
    background:#5c3739;
    color:#d6c7c7;
    line-height:140%;
    font-weight:bold;
}

div.work-materials > ul {
    margin-top: -2%;
}

div.work-materials > ul > li {
    margin-bottom: -5%;
    margin-left: 7%;
}

div.work-materials > ul > li.image {
    font-size: 200%;
    list-style-image: url('img/icon/image.svg');
} 

div.work-materials > ul > li.audio {
    font-size: 200%;
    list-style-image: url('img/icon/audio.svg');
} 

div.work-materials > ul > li.video {
    font-size: 200%;
    list-style-image: url('img/icon/video.svg');
}

div.work-materials > ul > li > a {
    font-size: 40%;
}
```

アイコンの画像は下記のものを使用しています。

* [音符のフラットアイコン素材 | FLAT ICON DESIGN -フラットアイコンデザイン-](http://flat-icon-design.com/?p=122)
* [写真画像イメージのアイコン素材 | FLAT ICON DESIGN -フラットアイコンデザイン-](http://flat-icon-design.com/?p=272)
* [動画、movieのアイコン素材 | FLAT ICON DESIGN -フラットアイコンデザイン-](http://flat-icon-design.com/?p=282)

### カスタム投稿の一覧ページを作る

今回は3つある方法の内、アーカイブテンプレートを用意する方法にしました。これなら固定ページを作る必要はありません。どれが主流なのかはわかりませんでした。

[カスタム投稿タイプの一覧ページの作成 | Web Design Leaves](http://www.webdesignleaves.com/wp/wordpress/965/)

**プラグインCPT UIでカスタム投稿を登録しているのですが、アーカイブページを作成する際はなぜかhas_archiveをtrueではなくfalseにしないといけません。**trueの時にアーカイブページに繊維しようとすると、そのカスタム投稿の特定ページに飛ばされてしまいました。

**例**

1.  カスタム投稿タイプでworkというスラッグを使用。
2.  カスタム投稿の1つの記事にwork1というスラッグを使用。
3.  localhost:8888/work/をクリックすると、localhost:8888/work/work1/に飛ばされる。

#### カスタムメニュー

固定ページを作るにしろ、作らないにしろ、カスタム投稿一覧ページへのリンクを置くためにカスタムメニューを使います。

**注意点**

* ウィジェットにカスタムメニューを追加。
* タイトルを設定しないと何も表示されない。デフォルトはなし。
* メニューを選択しないと、タイトルも出力されない。
* 固定ページのslugにハイフンを入れても、page-スラッグ名.phpののファイル名は正常に認識される。
* 固定ページのテンプレート優先順位

    1.  ~.php（固定ページのページ属性でテンプレートを指定）
    2.  page-固定ページのスラッグ名.php
    3.  page.php

#### アクションフック

一覧ページを作る上で合わせてアクションフックについて調べました。  
[WordPressをカスタマイズするなら覚えておきたいアクションフックとフィルターフック](http://webdesignrecipes.com/wordpress-action-hook-and-filter-hook/)

参考サイトを読む際に、調べた関数は下記の通り。リンク先はリファレンスとなっております。

**関数リファレンス**

* [get header](http://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/get_header)
* [have_posts](http://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/have_posts)
* [the post](http://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/the_post)  
     ループを次の投稿へ進めます。 次の投稿を取得して、それを「現在の投稿」としてセットアップし、ループの 'in the loop' プロパティを true にします。
* [in_the_loop](http://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/in_the_loop)  
     in_the_loop() がループの中で呼び出されたかどうか判定します（つまり、現在ループの中であるかどうか）。
* [get_query_var](http://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/get_query_var)  
     グローバル $wp_query オブジェクトの_ _WP_Query クラス内の public query variable /en を取得する。
* [is_admin](http://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/is_admin)  
     ダッシュボードまたは管理パネルが表示されているかどうかをチェックします。
* [add_action](http://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_filter() のエイリアスです。
* [locate_template](http://elearn.jp/wpman/function/locate_template.html)  
     テンプレートファイルが存在するか確認する
* [wp_reset_postdata](http://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/wp_reset_postdata)  
     new WP_Query を使って二番目のクエリを実行した後に、メインクエリの $post グローバル変数を復元するために使用します。つまり $post がメインクエリの現在の投稿になります。

### webサービスを作るならwordpressではなく、他のフレームワークを使おう

wordpressでブログを構築する際に、カテゴリやタグの分類に迷ったら、とにかく細かくやっておくと後から修正が効きやすいと思います。その分記事作成の労力は増えますので、そこでカスタム投稿の出番だと思いました。なるべく速い段階からカスタム投稿は使用したほうがいいと思います。

思ったより簡単に使えるものでしたので、ほかのサイトを構築する際にはどんどん使いたいと思います。ただし、カスタム投稿はあくまで特定記事のコンテンツを出力する際に整形させやすくするものだと思います。これを検索機能として活用するのは無理があるかと。なので、検索機能を持たせたい場合はカテゴリやタグをカスタムタクソノミーでいくつも用意しておくとよいでしょう。

Webサービスを構築する際に、wordpressで頑張るのもいいですが、あくまでもwordpressはブログやサイトを作るためのcmsなのだと思いました。