You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel:如何加载含对应Category标签的所有Post?

解决Laravel中通过共享标签加载关联Post的问题

嘿,我来帮你搞定这个需求!首先咱们得先确认三个模型的多态关联配置是正确的,这是后续查询的基础,然后再分场景给出具体的实现方案。

第一步:确认模型关联配置

先确保你的Tag、Post、Category模型的多态关联是这么写的:

Tag 模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Tag extends Model
{
    protected $fillable = ['name'];

    public function taggable(): MorphTo
    {
        return $this->morphTo();
    }
}

Post 模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Post extends Model
{
    protected $fillable = ['title', 'content'];

    public function tags(): MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

Category 模型

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Category extends Model
{
    protected $fillable = ['name'];

    public function tags(): MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

第二步:根据需求实现查询

下面分几种常见的场景来写查询逻辑,你可以根据自己的实际需求选择:

场景1:获取某个特定Category的标签关联的所有Post

比如你想拿到ID为1的Category下所有标签对应的Post(避免重复结果),可以这么写:

$category = Category::find(1);

// 方式1:先拿到该Category的所有标签ID,再筛选关联这些标签的Post
$tagIds = $category->tags()->pluck('tags.id');
$posts = Post::whereHas('tags', function ($query) use ($tagIds) {
    $query->whereIn('tags.id', $tagIds);
})->distinct()->get();

// 方式2:更简洁的链式写法,不用提前拉取标签ID
$posts = Post::whereHas('tags', function ($query) use ($category) {
    $query->whereHas('taggable', function ($q) use ($category) {
        $q->where('taggable_type', Category::class)
          ->where('taggable_id', $category->id);
    });
})->distinct()->get();

场景2:获取Post并同时加载对应的关联Category(通过共享标签)

如果需要同时拿到Post和它们通过共享标签关联的Category,可以用嵌套预加载:

$posts = Post::with(['tags.taggable' => function ($query) {
    $query->where('taggable_type', Category::class);
}])->get();

// 遍历Post时,记得去重Category(一个Post可能多个标签关联同一个Category)
foreach ($posts as $post) {
    $relatedCategories = $post->tags->pluck('taggable')
        ->filter() // 过滤掉空值(如果标签没有关联Category的话)
        ->unique('id');
}

场景3:高效查询(避免N+1问题)

如果数据量比较大,用join的方式会更高效:

// 获取所有和Category共享标签的Post(可指定特定Category)
$posts = Post::select('posts.*')
    ->join('taggables as post_taggables', 'posts.id', '=', 'post_taggables.taggable_id')
    ->where('post_taggables.taggable_type', Post::class)
    ->join('taggables as category_taggables', 'post_taggables.tag_id', '=', 'category_taggables.tag_id')
    ->where('category_taggables.taggable_type', Category::class)
    // 如果要指定某个Category,取消下面这行注释
    // ->where('category_taggables.taggable_id', $categoryId)
    ->distinct()
    ->get();

额外补充

  • 为什么要加distinct()?因为一个Post可能关联多个属于同一个Category的标签,会导致查询结果重复,所以去重是必须的。
  • 如果你需要的是Post必须关联该Category的所有标签(而不是至少一个),可以用下面的写法:
$category = Category::find(1);
$totalTags = $category->tags()->count();

$posts = Post::whereHas('tags', function ($query) use ($category) {
    $query->whereHas('taggable', function ($q) use ($category) {
        $q->where('taggable_type', Category::class)
          ->where('taggable_id', $category->id);
    });
}, '=', $totalTags)->get();

内容的提问来源于stack exchange,提问作者slickness

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:09:39