LaravelでWordPressの記事を取得して、Vueで表示する

この記事は [ここのえ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/99no_exit) Day 7 の記事です。

# Wordpressのブログ記事をトップページに乗せたい

Laravel + Vue環境のホームページをメインで置いているのですが、それとは別のところで動いているWordpressへの導線が欲しく、トップページに新着記事を載せてみました。

Wordpressから新着情報を取ること自体は、JSON形式で取得できるため、データ構造さえ分かっていれば必要に応じてデータ取得が可能です。

LPとかに新着情報を載せる……とかそういう需要も多いと思うので、必要に応じて試してみてください。

# 環境

今回サンプルに使うのはこんな感じの構成です。

- Laravel 10.x
- PHP 8.2
- Vue 3 (Composition API)
- Typescript

# 実装

前提の環境を構築済みとします。
また各ライブラリ・フレームワークの解説は省略します。

例として、今回のサイトのWordpressが動作しているアドレスは **https://example.com/blog** と仮定します。

今回の実装の目的としては、Vue側で

- 記事タイトル
- URL
- 投稿日時
- サムネイル画像

を表示することを目指します。

## バックエンド

### LaravelのController

Wordpress REST APIを叩いて、必要なデータを取得します。
エンドポイントは、`GET /wp/v2/posts` です。

```
https://example.com/blog/wp-json/wp/v2/posts
```

加えて`per_page`のクエリパラメータを付けることで取得数を制限することができます。
新着記事を表示するだけなら全体を取得する必要もないので、無駄がなくて良いです (デフォルトでper_page=10なので、速度的にはあまり変わらないかも) 。
```
https://example.com/blog/wp-json/wp/v2/posts?per_page=5
```

Controllerの実装は以下の通りです。

```php
public function index() {
    // 記事の一覧を取得
    $posts_raw = file_get_contents("https://example.com/blog/wp-json/wp/v2/posts?per_page=5");
    $posts = json_decode($posts_raw, true);

    // Viewに返すデータをまとめたArray
    $recent = [];

    foreach ($posts as $post) {
        // サムネイル画像の情報を取得
        $thumb_raw = file_get_contents($post["_links"]["wp:featuredmedia"][0]["href"]);
        $thumb_data = json_decode($thumb_raw, true);

        // 必要な情報を配列にまとめる
        $recent[] = [
            "title" => $post["title"]["rendered"],
            "url" => $post["link"],
            "date" => $post["date"],
            "thumbnail" => $thumb_data["guid"]["rendered"]
        ];
    }

    return view("index", ["recent" => $recent]);
}
```

サムネイルのみ、`GET /wp/v2/posts`で返ってくるJSONでは取得することができません。
JSON内の`_links -> wp:featuredmedia[0] -> href`のリンクに `GET /wp/v2/media` のエンドポイントへのリンクが入っているので、サムネイル画像の情報を取得するために `file_get_contents` を再度回しています。

APIの詳細については、公式のHandbookを参照してください。

https://developer.wordpress.org/rest-api/reference/posts/

https://developer.wordpress.org/rest-api/reference/media/

## フロントエンド

### Blade

Controllerで取得したデータがBladeに飛んで来たら、Vueに渡します。
`@js`ディレクティブを使って、Vueのpropsとして飛ばします。
```html
<div id="app">
    <index-page :recent="@js($recent)" />
</div>
```

### Vue (Typescript, Composition API)

propsを適宜storeに保存したり、使いやすいように取り扱ってください。サンプルでは最小構成にしています。
Typescriptで記述しているため型定義が多めですが、jsの場合は適宜読み替えてください。
`date`がstringになってしまうため、JSのDate型に変換したほうが取り扱い易いです。

```vue
<!-- IndexPage.vue -->

<script setup lang="ts">

// 記事のinterface
interface Post {
  title: string
  url: string
  date: Date
  thumbnail: string
}

// propsの定義
const props = defineProps<{
  recent: {
    title: string
    url: string
    date: string
    thumbnail: string
  }[]
}>()

// 記事の一覧を保持したref
const posts: ref<Post[]> = ref([])

// propsからの変換処理
onBeforeMount(() => {
  for(const p of props.recent) {
    posts.value.push({
      title: p.title,
      url: p.url,
      date: new Date(p.date),
      thumbnail: p.thumbnail,
    })
  }
})

// 日付をyyyy/mm/dd形式に変更
const getDateString = (date: Date): string => {
  return date.toLocaleDateString('ja-JP', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  })
}

</script>

<template>
  <a v-for="post of posts" :key="post.url" :href="post.url">
    <img :src="post.thumbnail" :alt="post.title" />
    <p>{{ post.title }}</p>
    <p>{{ getDateString(post.date) }}</p>
  </a>
</template>
```

# まとめ

PHPの方である程度整形してあげれば、フロント側で取り扱うのは容易です。
Wordpressで取得できるのは記事情報に限らないので、アイデア次第で色々できそうです。