カテゴリー
Linux

【Laravel 5.5】WordPress の DB を使ってお勉強。投稿に紐づくカテゴリーを取得して表示する

学んだこと

WordPress のカテゴリー (タグ) についての調査

  • テーブルのリレーション構造: wp_posts 1—n wp_term_relationships n—1 wp_term_taxonomy n—1 wp_terms WordPress データベーステーブルの、カテゴリー (タグ) 周りのリレーション図
  • カテゴリーは wp_term_taxonomy.taxonomy の値が category のもの。
  • カテゴリーのデータ構造はとても複雑
    • wp_terms と wp_term_taxonomy で名前、スラッグ、分類 (カテゴリーかタグか) などを定義する。
    • wp_term_relationships で、カテゴリー (タグ) と投稿との紐付けを行う。
  • wp_terms hasMany wp_term_taxonomy の関係である。ただ、実際には、 wp_terms hasOne wp_term_taxonomy で使用すると思われる。

Laravel でカテゴリーを作る上で考えたこと

  • wp_term_taxonomy を主とする。 wp_terms は隠す。 name や slug が欲しい場合は、 wp_term_taxonomy の属性として取得する。
  • App\Models\Wp\Taxonomy\Category, App\Models\Wp\Taxonomy\PostTag というクラスを作り、 wp_term_taxonomy を継承する。
  • ポリモーフィックリレーションは該当しなさそう。 wp_term_taxonomy.taxonomy の値で切り替えるようなイメージを持っていた。しかし、 ポリモーフィックでは親のモデルのタイプを指定する のであるから、 post や page が wp_term_taxonomy.taxonomy の値となるのであれば使えた。しかし実際は、 category や post_tag が wp_term_taxonomy.taxonomy の値となり、これは 自分がどのようなタイプであるのか を示している。

コード

カテゴリーのモデルクラスのコード

  • TermTaxonomy クラスを継承して Category クラスとした。 TermTaxonomy クラスはテーブルそのもの、 Category クラスはアプリで使うカテゴリーを意識した。
  • テーブルのリレーションとしては、 wp_term_taxonomy n—1 wp_terms となる。 アプリからはこのひも付きを意識しないで済むように、 Category クラスの中にこの関係を閉じ込めた。具体的には、 name と slug 属性を Category から得ようとすれば、自動的に紐づく wp_terms の name と slug から値を取得するようにした。

app/Models/Wp/Taxonomy/Category.php

<?php

namespace App\Models\Wp\Taxonomy;

use App\Models\Wp\TermTaxonomy;
use Illuminate\Database\Eloquent\Builder;

class Category extends TermTaxonomy
{
    /**
     * モデルの「初期起動」メソッド
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('TaxonomyCategory', function (Builder $builder) {
            $builder->where('taxonomy', 'category');
        });
    }

    /**
     * name を取得します。
     *
     * @return string
     */
    public function getNameAttribute(): string
    {
        return $this->term->name;
    }

    /**
     * slug を取得します。
     *
     * @return string
     */
    public function getSlugAttribute(): string
    {
        return $this->term->slug;
    }
}

app/Models/Wp/TermTaxonomy.php

  • xxx_id を持つテーブルモデルから xxx テーブルのモデルを取得するには、 belongsTo メソッドでリレーションメソッドを定義する。今回、 term_id を持つ wp_term_taxonomy テーブルから、 wp_terms テーブルの Term モデルを取得したい。
... 略 ...

use Illuminate\Database\Eloquent\Relations\BelongsTo;

... 略 ...

    /**
     * この TermTaxonomy を所有する Term を取得します。
     *
     * @return BelongsTo
     */
    public function term(): BelongsTo
    {
        return $this->belongsTo('App\Models\Wp\Term', 'term_id', 'term_id');
    }

... 略 ...

投稿モデルからカテゴリーモデルを扱うコード

app/Models/Wp/PostType/Post.php

... 略 ...

use Illuminate\Database\Eloquent\Relations\BelongsToMany;

... 略 ...

    /**
     * この投稿に所属するカテゴリーを取得します。
     *
     * @return BelongsToMany
     */
    public function categories(): BelongsToMany
    {
        return $this->belongsToMany(
            'App\Models\Wp\Taxonomy\Category',
            'wp_term_relationships',
            'object_id',
            'term_taxonomy_id',
            'ID',
            'term_taxonomy_id'
        );
    }

... 略 ...

投稿と、投稿に紐づくカテゴリーを取得するコード

  • コントローラの with メソッドで指定することにより、 Eager ロードする。
  • categories.terms というように、カテゴリーモデルに存在するリレーションを指定しなければならない。せっかくカテゴリーモデルを定義したことで、カテゴリー内部のリレーションを気にしなくてよくしたのに、 SQL の発行回数を抑えるためにはカテゴリー内部のリレーションを意識しなければならなくなるのが悔しい。
diff --git a/app/Http/Controllers/Admin/PostController.php b/app/Http/Controllers/Admin/PostController.php
index 0549a61..ba5e09d 100644
--- a/app/Http/Controllers/Admin/PostController.php
+++ b/app/Http/Controllers/Admin/PostController.php
@@ -16,8 +16,17 @@ class PostController extends Controller
      */
     public function index()
     {
+        $posts = PostTypePost
+            ::with([
+                'user',
+                'categories',
+                'categories.term',
+            ])
+            ->postStatusAll()
+            ->orderByPostDateDesc()
+            ->get();
+
         return view('admin.posts.index', [
-            'posts' => PostTypePost::with('user')->postStatusAll()->orderByPostDateDesc()->get(),
+            'posts' => $posts,
         ]);
     }

ビューで投稿に紐づくカテゴリーをカンマ区切りで表示するコード

  • $post->categories()->get()->implode('name', ', ') としてはいけない。 Eager ロードされなくなってしまう。 Eager ロードをさせるには $post->categories->implode('name', ', ') と動的プロパティを使って取得する。
  • implode() コレクション 5.8 Laravel
  • カテゴリー名をカンマで連結するのをビューで行なった。しかし、モデル等で行う方が筋が良いように感じる。そのためにはカテゴリーのコレクションクラスを定義するのが良いと思う。ただ良い組み立てが思いつかない。
diff --git a/resources/views/admin/posts/index.blade.php b/resources/views/admin/posts/index.blade.php
index 06159c9..6de9736 100644
--- a/resources/views/admin/posts/index.blade.php
+++ b/resources/views/admin/posts/index.blade.php
@@ -28,7 +28,7 @@
           <th scope="row">{{ $post->ID }}</th>
           <td>{{ $post->post_title }} - {{ $post->post_status_description }}</td>
           <td>{{ $post->user->display_name }}</td>
-          <td>categories</td>
+          <td>{{ $post->categories()->get()->implode('name', ', ') }}</td>
           <td>tags</td>
           <td>{{ $post->comment_count }}</td>
           <td>{{ $post->post_date }}</td>

おわりに

次は、同様に投稿に紐づくタグを表示したいと思います。ただ、これはタグのクラスを定義して、あとはカテゴリーとほぼ同様になるのではないかと予想しています。

以上です。

コメントを残す