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

Laravel中如何通过user_id与week匹配更新Challange表数据?

问题分析与解决方案

背景

我正在开发一款减重挑战应用,用户可发起挑战比拼减重成果。现有一张challange表,包含id、user_id(外键)、weight和week字段。为在前端按周展示数据,我构建了对应数组,UserController代码如下:

public function index() {
    $users = User::all();
    $challanges = Challange::orderBy('week', 'DESC')->get();
    $weightArray = array();
    $startweek = 1;
    $endweek = Challange::max('week');
    for ($i = $startweek; $i <= $endweek; $i++) {
        foreach ($users as $user) {
            $challenge = $user->challanges()->where('week', '=', $i)->get();
            if (count($challenge) > 0) {
                $weightArray[$i][$user->id] = $challenge[0]->weight;
            } else {
                $weightArray[$i][$user->id] = 0;
            }
        }
    }
    return view('dashboard', compact('users', 'weightArray'));
}

用户登录后点击更新,表单传入Auth::user()->id、week及自行输入的weight,但不知如何通过这两个字段匹配到需更新的记录。当前ChallangeController代码:

public function update(Request $request) {
    $week = $request->week;
    $challange = Challange::find($request->user_id)->where('week', $week);
    dd($challange);
}

执行报错:Error Call to a member function where() on null。请教是提前在数组加id,还是叠加where条件?


问题根源

你踩了一个Laravel查询的常见坑:find()方法是根据模型的**主键字段(默认是id)**查询记录的,而你传入的是user_id——这显然不是challange表的主键,所以这个查询大概率返回null,之后调用where()自然就触发了"Call to a member function where() on null"的错误。

正确解决方案

你需要通过叠加where条件来匹配user_id和week这两个字段,找到对应的挑战记录后再进行更新。这里有两种实用的处理方式:

1. 手动查询+更新/创建

先通过双条件找到记录,存在则更新,不存在可选择创建新记录或提示用户:

public function update(Request $request) {
    // 先做参数验证,确保数据合法
    $request->validate([
        'user_id' => 'required|exists:users,id',
        'week' => 'required|integer|min:1',
        'weight' => 'required|numeric|min:0'
    ]);

    // 同时匹配user_id和week字段
    $challange = Challange::where('user_id', $request->user_id)
                          ->where('week', $request->week)
                          ->first();

    if ($challange) {
        // 找到记录,更新体重
        $challange->update(['weight' => $request->weight]);
        return redirect()->back()->with('success', '体重更新成功');
    } else {
        // 没找到记录,自动创建新的周记录
        Challange::create([
            'user_id' => $request->user_id,
            'week' => $request->week,
            'weight' => $request->weight
        ]);
        return redirect()->back()->with('success', '该周记录不存在,已为你创建并更新');
    }
}

2. 用updateOrCreate简化逻辑

如果你需要实现"有则更新,无则创建"的逻辑,Laravel提供的updateOrCreate方法可以一步搞定,代码更简洁:

public function update(Request $request) {
    $request->validate([
        'user_id' => 'required|exists:users,id',
        'week' => 'required|integer|min:1',
        'weight' => 'required|numeric|min:0'
    ]);

    // 第一个数组是查询条件,第二个数组是要更新/创建的字段
    Challange::updateOrCreate(
        ['user_id' => $request->user_id, 'week' => $request->week],
        ['weight' => $request->weight]
    );

    return redirect()->back()->with('success', '体重记录更新成功');
}

这个方法会自动查找匹配user_id和week的记录:存在则更新weight,不存在则创建新记录,非常高效。

额外优化建议

你的UserController中构建weightArray的循环存在N+1查询问题,可以通过预加载优化:

public function index() {
    // 预加载所有用户的挑战记录,减少数据库查询次数
    $users = User::with(['challanges' => function($query) {
        $query->orderBy('week');
    }])->get();
    
    $endweek = Challange::max('week') ?? 1;
    $weightArray = [];

    for ($i = 1; $i <= $endweek; $i++) {
        foreach ($users as $user) {
            // 从预加载的记录中快速找到对应周的体重
            $challenge = $user->challanges->where('week', $i)->first();
            $weightArray[$i][$user->id] = $challenge ? $challenge->weight : 0;
        }
    }

    return view('dashboard', compact('users', 'weightArray'));
}

这样能大幅减少数据库请求,提升页面加载效率。


内容的提问来源于stack exchange,提问作者Jeffrey van der Kruit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 23:32:37