今回が「WP REST APIで投稿を非同期で全件取得した話」の最終話になります。
前回までの記事は下記からご覧ください。


最後はタクソノミーフィルターをついけていきます。
チェックボックスにチェックを入れたら、そのタームに属する記事だけ表示してくれるという機能になります。
今回も前回同様、子コンポーネントを作成して、最初に作成したarchive.vueと連携を取る形で進めます。
それではやっていきましょう。
# この記事で解決すること
- WP REST APIで取得してきた一覧に対してタクソノミーフィルターをつけることができる
手順
- 事前準備
- フォルダ・ファイル構成の確認
- 構造とデータの確認
- 初期処理
- archive.vue
- taxFilter.vue
- page-test.php
- タームの全件取得
- タームの取得
- タクソノミー初期情報の取得
- タームの全件取得
- templateに反映
- タクソノミーフィルター
- タクソノミー名の取得
- フィルター作成
- 親コンポーネントでのデータ受取準備
- ターム変更時の設定(発火設定)
- フィルター表示時の挙動調整
- 挙動確認
フォルダ・ファイル構成の確認
今回追加するファイルはtaxFilter.vue、
変更するファイルはarchive.vueとpage-test.phpとなります。
- theme folder
- src
- js
- components
- archive.vue(edit)
- taxFilter.vue(add)
- pagination.vue
- app.js
- components
- js
- page-test.php(edit)
- src
構造とデータの確認
前回の続きとして作業を行いますので、データなども前回から引き続き使用するものがあります。
また、今回はタクソノミーとタームが少しややこしいので下記の画像を確認してください。
そちらを踏まえた上で確認していきます。
今回はCacooで作成しました!こちらの方がラクです。
やっていることとしては、
フロントのチェックボックスにチェックが入ったら、
その内容から全投稿をフィルターして、
チェックが入ったタクソノミーに属する投稿のみを表示させるというものになります。
新しく追加するものを青としており、連携させるデータ自体は割りと少ないです。
taxName
はprops
に入れても良かったのですが、
taxFetchURl
から正規表現で取得できそうだったので、
あえてcomputed
で取得するようにしています。
では、前回から引き続き使用するデータと新たに追加するデータの一覧です。
(タクソノミーフィルターで使用するもののみ記載)
引き続き使用するデータ
name | remark |
---|---|
pagenationShow | ページネーションの表示 |
dataOrigin | 投稿の全データ |
新たに追加するデータ:props
name | type | remark |
---|---|---|
taxFetchUrl | String | REST APIで取得してくるタクソノミーのURL default: ‘/wp-json/wp/v2/categories’ |
taxFilterSearch | String | 検索方法(or, and) default: ‘or’ |
taxQuery | Object | REST APIに送るパラメータ default: () => ({ ‘exclude’: 1, // 未分類の非取得 }) |
taxFilterShow | Boolean | タクソノミーフィルターを表示するか否か default: true |
新たに追加するデータ:data
name | type | remark |
---|---|---|
terms | Array | チェックが入ったタームを格納 |
dataTerms | Array | ターム一覧(全件) |
totalTaxPages | Number | タクソノミーのpage数 (タームの全件取得に必要) |
初期処理
前項のデータを入れ込むと各ファイルの初期状態は下記の内容となります。
※説明と関係ない部分は省いています。
archive.vue
<template>
<div class="my-post-archive">
<taxFilter
v-if="taxFilterShow"
:taxFetchUrl="taxFetchUrl"
:taxFilterSearch="taxFilterSearch"
:taxQuery="taxQuery"
:dataOrigin="dataOrigin"
:paginationShow="paginationShow"
/>
<article class="archive" v-if="datas">...</article>
<pagination ... />
</div>
</template>
<style lang="scss" scoped>...</style>
<script>
import axios from 'axios'
import pagination from './pagination.vue'
import taxFilter from './taxFilter.vue'
export default {
props: {
postFetchUrl: {...},
postQuery: {...},
perPage: {...},
/* pagination */
pagerVisible: {...},
pagerScroll: {...},
paginationShow: {...},
/* taxFilter ------------------------------------------------- */
taxFetchUrl: {
type: String,
default: '/wp-json/wp/v2/categories',
},
taxFilterShow: {
type: Boolean,
default: true,
},
taxFilterSearch: {
type: String,
default: 'or', // 'or' or 'and'
},
taxQuery: {
type: Object,
default: () => ({
'exclude': 1, // 未分類の非取得
})
},
},
components: {
pagination,
taxFilter
},
...
4行目
taxFilterShow
でタクソノミーフィルターを表示させるか否か決めたいので、
v-if
で判定しています。
21行目, 36行目
taxFilter.vueの読み込み
33~50行目
propsの追加
taxFilter.vue
<template>
<div class="filter">
<div class="filter__label">
<input type="checkbox" id="term_id_0">
<label for="term_id_0" v-text="ターム0">
</div>
<div class="filter__label">
<input type="checkbox" id="term_id_1">
<label for="term_id_1" v-text="ターム1">
</div>
</div>
</template>
<style lang="scss" scoped>
.filter {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: .5rem 1rem;
border-bottom: 2px solid #eee;
margin-bottom: 1rem;
padding-bottom: 1rem;
}
</style>
<script>
import axios from 'axios'
export default {
props: {
taxFetchUrl: {
type: String,
},
taxFilterSearch: {
type: String,
},
taxQuery: {
type: Object,
},
dataOrigin: {
type: Array,
},
paginationShow: {
type: Boolean,
},
},
data() {
return {
terms: [],
dataTerms: [],
totalTaxPages: 0,
}
},
}
</script>
1~24行目
一旦適当なhtmlをあてています。scssもサンプルなので適当です。
27行目
今回はタクソノミーのREST APIにアクセスして取得するので、axiosを読み込んでおきます。
30行目~
props
は親コンポーネントであるarchive.vueからのデータの引き渡しであり、
defaultやrequireは親コンポーネントに設定していますので、type
のみ設定しておきます。
page-test.php
<div id="archive">
<archive
:post-fetch-url="'/wp-json/wp/v2/posts'"
:per-page="10"
:post-query="{
'_fields': 'id,content,title,categories,_links,_embedded',
'_embed': '',
}"
:pager-visible="5"
:pager-scroll="1"
:pagination-show="true"
:tax-fetch-url="'/wp-json/wp/v2/categories'"
:tax-filter-show="true"
:tax-filter-search="'or'"
:tax-query="{
'_fields': 'id,name,slug,count',
'exclude': 1,
}"
#post="{ data }"
>
<span v-text="data.id"></span>
</archive>
</div>
12~18行目
大方default
と同様の内容にしています。
tax-query
だけ取得するフィールドを制限しておきたいので追記します。
上記以外は前回同様となります。
タームの全件取得
フィルターする前に、フィルターさせるためのタームチェックボックスの一覧をフロントに表示させる必要があります。
一覧データの全件取得の構造については投稿全件取得編で説明しています。
今回はリクエストURLのwp/v2/post_type/の部分がwp/v2/taxonomy/に変わっただけで、
全件取得に使用するパラメーターは投稿もタクソノミーも対して変わりませんので、ざっくりいきます。
タームの取得
taxFilter.vue: methods
async getTermData(num, taxQuery = this.taxQuery) {
// パラメーターを追加できるようにしておく
const params = { page: num }
const insertParams = await {...taxQuery, ...params}
await axios.get(this.taxFetchUrl, {params: insertParams})
.then((res) => {
// タームの情報をdataTermsに結合
this.dataTerms = this.dataTerms.concat(res.data)
})
.catch(error => console.log(error))
},
タクソノミー初期情報の取得
taxFilter.vue: methods
async getTaxInfo() {
await axios.get(this.taxFetchUrl)
.then((res) => {
this.totalTaxPages = Number(res.headers['x-wp-totalpages']) // 合計ページ数
})
.catch(error => console.log(error))
},
投稿全件取得時には合計投稿数も取得してきましたが、今回は特に必要ありませんので、合計ページ数のみ変数に代入しておきます。
タームの全件取得
taxFilter.vue: methods
async getTermDatas() {
// タクソノミー情報の取得
await this.getTaxInfo()
// タームの全件取得
const numTaxAry = [...Array(this.totalTaxPages).keys()].map( i => ++i )
for (let num of numTaxAry) {
await this.getTermData(num)
}
},
mountedに反映
一旦mounted
にgetTaxDatas
を入れておきましょう。
taxFilter.vue: mounted
mounted() {
this.getTaxDatas()
},
templateに反映
これで、dataTax
に対してターム一覧が入りますので、template
に反映します。
taxFilter.vue: template
...
<div class="filter__label" v-for="term in dataTerms" :key="term.id">
<input type="checkbox" :id="`term_${term.id}`" v-model="terms" :value="term.id">
<label :for="`term_${term.id}`" v-text="`${term.name}(${term.count})`" />
</div>
...
id
とfor
でテキストを押したときにもチェックが入る仕様にしています。
また、チェックが入った場合にterms
にterm.id
が格納されていく流れにしたいのでv-model
を使用しています。
term.count
はそのタームに属する記事が何記事あるか表示させているだけですので、必須ではありません。
これで、チェックボックスの付いたターム一覧が表示されているはずです。
タクソノミーフィルター
フィルターを行うには、投稿のデータとterms
に格納されているタームIDの値が一致するか否かを確認する必要があります。
それを確認するためには、投稿データのどこにタクソノミーの情報が入っているかを把握しなければなりません。
実際に、タクソノミーの情報は下記画像の箇所にあります。
左側が通常のタクソノミー(カテゴリー)、右側がカスタムタクソノミーの場合のREST APIの構造になります。
keyに対してタクソノミー名があり、その配下にタームIDを確認することができます。
タクソノミー名の取得
タクソノミーはカテゴリーだけではなく、カスタムタクソノミーを指定した場合には、そのタクソノミー名のkeyをたどる必要があります。
そのためタクソノミー名を予め取得しておきます。
taxFilter.vue: computed
taxName() {
return this.taxFetchUrl.match(/.*\/([\w|~]+).*$/u)[1]
},
正規表現で、「wp-json/wp/v2/taxonomy_name」から taxonomy_name だけを抽出しています。
フィルター作成
まず前提として投稿データは3つあります。
dataOrigin | 投稿の全データ |
dataBase | フィルターされたデータ |
data | 表示するデータ |
流れとしては、下記の通りです。
- dataOriginからデータをフィルターする
- フィルターされたデータを親コンポーネントに送る
- 親コンポーネントで表示するデータを差し替える
また、今回は「or検索」と「and検索」を選べるようにしています。
それを踏まえてご覧ください。
async postTaxFilter() {
// 送信データの初期化
let datas = []
// タームが指定されていない場合(チェックを外した場合など)
if(!this.terms.length){
// 全データをdataBaseに格納
datas = await this.dataOrigin
// タームが指定されている場合
} else {
// or検索の場合
if(this.taxFilterSearch == 'or') {
// フィルターで該当タームに属する投稿をdataBaseに格納
datas = await this.dataOrigin.filter((obj)=>{
return this.terms.some(id => obj[this.taxName].includes(id))
})
// and検索の場合(or以外の場合)
} else {
// フィルターで該当タームに属する投稿をdataBaseに格納
datas = await this.dataOrigin.filter((obj)=>{
return this.terms.every(id => obj[this.taxName].includes(id))
})
}
}
// dataBaseの更新
this.$emit('changeData', datas)
// totalPostsの更新
this.$emit('changeTotal', datas.length)
},
16~18行目
or検索(チェックが入ったタームに1つでも属していれば表示)の場合の処理です。
array.filter
はarrayに対してループ処理を行います。
returnがtrue判定だった場合、その配列や要素を返します。
例えば、obj
がタームID 1,5に属した投稿だったとしましょう。
そして、this.terms=[3,5]
と仮定します。
その場合、下記のようになります。
[3,5].some(id => [1,5].includes(id))
// (3 => [1,5].includes(3)) // false
// (5 => [1,5].includes(5)) // true
includes
は引数が対象に含まれているかをtrue
false
で返します。
some
は1つでもtrueであった場合、tureを返します。
23~25行目
and検索(チェックが入ったターム全てに属していれば表示)の場合の処理です。
every
は1つでもfalseがあった場合はfalseを返します。
29, 31行目
「フィルターを通した投稿」と「フィルターを通した投稿の合計数」を親コンポーネントに送ります。
ただ、今のままでは親が受け取れませんし、定義されていないemitを書いているのでエラーになります。
親コンポーネントでのデータ受取
では、親で「フィルターを通した投稿」と「フィルターを通した投稿の合計数」を受け取ります。
まずは、methodsで受け取るための関数を定義します。
archive.vue: methods
// dataBaseの受け取り
changeDataBase(datas) {
this.dataBase = datas
},
// totalPostsの受け取り
changeTotalPosts(num) {
this.totalPosts = num
},
そして、templateでemitに定義します。
archive.vue: template
<taxFilter
v-if="taxFilterShow"
...
@changeData="changeDataBase"
@changeTotal="changeTotalPosts"
/>
親子を見比べるとこんな感じです。
// 親
changeDataBase(datas) // methods
@changeData="changeDataBase" // template
// 子
this.$emit('changeData', datas) // methods
子は親のコンポーネントを直接使うことができないので、emitに定義して呼び出している形になります。
タクソノミー変更時の設定(発火設定)
このままでは、チェックボックにチェックが入っても、特に何も起こりません。
ですので、チェックボックにチェックが入った時=termsが更新された時にフィルターを発火させます。
taxFilter.vue: watch
async terms() {
// フィルターかける
await this.postTaxFilter()
// paginationを表示しない場合はスルー
if(this.paginationShow) this.$parent.$refs.pagination.pagerDefault()
},
3行目
フィルターの処理が重めなので、awaitで完了まで待つよう設定しておきます。
5行目
ページネーションがある場合は、フィルターされた投稿数に応じたページネーションに変更し、
pagerを1に戻してほしいので、前記事で書いたpagerDefault
を呼び出します。
$parent
= archive.vue
$refs
は定義する必要があるのですが、前記事で下記のように定義していますので、
$parent.$refs.pagination
がたどれるわけです。
archive.vue: template
...
<pagination
v-if="paginationShow"
ref="pagination"
...
フィルター表示時の挙動調整
フィルター表示時に投稿を全件取得するようにします。
というのも、全件取得しなかった場合、本来10記事あるのに3記事しか表示されないタームが出てくるからです。
archive.vue: methods: getPostDatas
async getPostDatas() {
// paginationもしくはtaxFilterを表示する場合
if(this.paginationShow === true || this.taxFilterShow === true) {
await this.getPostInfo()
// paginationを表示する場合
if(this.paginationShow) this.$refs.pagination.pagerDefault()
const numPostAry = [...Array(this.totalPages).keys()].map( i => ++i )
for (let num of numPostAry) {
await this.getPostData(num)
}
} else {
// paginationもしくはtaxFilterを表示しない場合
await this.getPostData(false)
}
},
3行目
タクソノミーフィルターがtrueの場合は全件取得
挙動確認
or検索時
チェックを増やすとページャーが増えているのがわかると思います。
and検索時
チェックを増やすとページャーが減っているのがわかると思います。
双方問題なく動いていますね。
最後に
3部にわたってお送りしてきたWP REST APIの投稿取得ですが、いかがでしたでしょうか?
私なりのポイントをまとめると
# point
async
await
はやっぱり便利- header情報を一番簡単に取得するには
axios
が有力解 - コンポーネント化して使い回せるコードが正義
- 毎度変わる可能性があるものは
props
とslot
で対応 filter
+some
every
は使い所多め
総合して言えるのは、async
await
はやっぱり便利だということです。
それはつまり、async
await
を理解しない限りsetTimeOut
などの関数を使用して理解しにくい長い構文を書かないといけないということです。
また、今後記事にしていきたいのは下記の内容です。
- infinit scrollでの追加投稿表示
- ボタンによる追加投稿表示
- タクソノミーの階層化(再帰関数)
- ローディングの実装
どれも普段使いそうなものばかりなので、紹介していければと思います!
それでは、よいWPライフを!
あわせて読みたい記事

