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

Laravel中JSON数组字段的模型关联查询解决方案:Promotion表关联Games表的实现方法

解决Laravel中JSON数组字段的模型关联问题

首先,你当前的getAllGames方法用了belongsTo,这本身就不符合你的业务场景——belongsTo是一对一关联,而你的promotion表中game_id是存多个游戏ID的JSON数组,对应多条game记录,常规关联自然没法生效。下面给你几个简便可行的解决方案,按使用场景区分:

方案1:用访问器快速获取关联游戏(适合单条记录查询)

如果只是偶尔查单条Promotion记录并拿对应游戏数据,最简单的方式是给Promotion模型加个访问器:

class Promotion extends Model {
    use HasFactory;
    protected $guarded = [];
    protected $casts = [
        'game_id' => 'array'
    ];

    // 新增访问器
    public function getGamesAttribute()
    {
        // 直接通过数组里的ID查询游戏集合
        return Game::find($this->game_id);
    }
}

使用时直接调用:

$promotion = Promotion::findOrFail($id);
$games = $promotion->games; // 直接拿到对应的游戏集合

这个方法优点是实现简单,缺点是批量查询Promotion时会触发N+1查询问题(每条记录单独查一次游戏表),所以不适合批量场景。

方案2:自定义关联方法支持预加载(适合批量查询)

如果需要批量查Promotion并高效加载对应游戏,可以自定义关联方法,结合数据库的JSON函数实现关联:

class Promotion extends Model {
    use HasFactory;
    protected $guarded = [];
    protected $casts = [
        'game_id' => 'array'
    ];

    public function games()
    {
        return $this->belongsToMany(Game::class)
            ->whereRaw('JSON_CONTAINS(promotion.game_id, CAST(game.id AS JSON))');
    }
}

然后就能像常规关联一样用预加载:

// 批量查询并预加载游戏,全程只执行2次查询
$promotions = Promotion::with('games')->get();

foreach($promotions as $promotion) {
    echo $promotion->games; // 直接访问关联的游戏集合
}

注意:这个方法需要你的数据库支持JSON函数(比如MySQL 5.7+、PostgreSQL 9.4+),旧版本数据库可能用不了。

方案3:手动预加载(兼容所有数据库,性能最优)

如果你的数据库不支持JSON函数,或者想要极致的批量查询性能,可以手动处理预加载逻辑,避免N+1问题:

// 1. 先查询所有促销记录
$promotions = Promotion::all();

// 2. 提取所有关联的游戏ID并去重
$allGameIds = $promotions->flatMap(fn($promo) => $promo->game_id)->unique();

// 3. 一次性查询所有需要的游戏,并按ID分组
$games = Game::find($allGameIds)->keyBy('id');

// 4. 手动给每个促销记录绑定关联的游戏
$promotions->each(function ($promotion) use ($games) {
    $promotion->setRelation('games', $games->only($promotion->game_id));
});

这个方法全程只执行2次数据库查询,性能最优,还兼容所有数据库版本,唯一的小缺点是要手动写几行代码,但逻辑非常清晰。

额外提示

不建议长期用JSON数组存关联ID,虽然灵活,但会失去数据库关联的约束性和部分查询性能。如果业务允许,最好改成中间表(比如promotion_game),用标准的belongsToMany关联,这样Laravel的所有关联特性都能直接用,维护成本更低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:52:31