学んだこと
- xxx_id を持つテーブルモデルから xxx テーブルのモデルを取得するには、 belongsTo メソッドでリレーションメソッドを定義する。 -> 1対多 (Inverse) Eloquent:リレーション 5.8 Laravel
- リレーションメソッドよりも動的プロパティを使う。 -> リレーションメソッド 対 動的プロパティ Eloquent:リレーション 5.8 Laravel
- コレクション要素を指定した文字列を末尾に付けて連結する -> implode() コレクション 5.8 Laravel
WordPress のカテゴリー (タグ) についての調査
- テーブルのリレーション構造: wp_posts 1—n wp_term_relationships n—1 wp_term_taxonomy n—1 wp_terms
- カテゴリーは wp_term_taxonomy.taxonomy の値が category のもの。
- 他に、値が post_tag ならタグ。
- リンクカテゴリーというものも、以前なら存在したが、今回は気にしない。 -> 管理画面/リンク/リンクカテゴリー – WordPress Codex 日本語版
- カテゴリーのデータ構造はとても複雑
- 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');
}
... 略 ...
投稿モデルからカテゴリーモデルを扱うコード
- Laravel 5.8 で多対多構造を belongsToMany の引数をほぼ使い尽くしてなんとしてでもリレーションを実現する方法 – oki2a24
- テーブルのリレーションとしては、 wp_posts 1—n wp_term_relationships n—1 wp_term_taxonomy n—1 wp_terms となる。 アプリ的には Post (wp_posts) n—n Category (wp_term_taxonomy 1—1 wp_terms) となる。中間テーブルの wp_term_relationships は意識しなくて良いし、 wp_term_taxonomy と wp_terms の関係も意識しなくて良くなるようにした。
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>
おわりに
次は、同様に投稿に紐づくタグを表示したいと思います。ただ、これはタグのクラスを定義して、あとはカテゴリーとほぼ同様になるのではないかと予想しています。
以上です。