【问题标题】:Laravel pivot table create association once, then updateLaravel 数据透视表创建一次关联,然后更新
【发布时间】:2014-04-11 17:43:27
【问题描述】:

我有以下表格:

用户:

userID
...

课程:

lessonID
...

Users_Lessons_Status(充当数据透视表并保存其他信息):

userID references User.userID
lessonID references Lessons.lessonID
latestSectionID
percentComplete

我想要做的是,对于每个用户,对于每节课,数据透视表中应该有一行显示用户在该课中完成了多少以及他们最新的部分 ID 是什么。也就是说,userIDlessonID(主键?)应该有一个唯一的对。

我已经这样设置了我的模型:

<?php

class User extends Eloquent implements UserInterface, RemindableInterface {

...


    public function lessonStatuses() 
    {
        return $this->belongsToMany('Lesson', 'users_lessons_status', 'lessonID', 'userID')->withPivot('latestSectionID', 'percentComplete');
    }

}

<?

class Lesson extends Eloquent {

protected $table = 'lessons';
protected $primaryKey = 'lessonID';

public function userStatuses()
{
    return $this->belongsToMany('User', 'users_lessons_status', 'userID', 'lessonID');
}


}

?>

我现在的路线是这样的:

Route::post('dbm/users/setLatestSectionID', function() {
    if(Auth::check()) {
        $user = User::find(Input::get('userID'));
        $lesson = Lesson::find(Input::get('lessonID'));
        $us = $user->lessonStatuses(); 
        $us->attach($lesson->lessonID, 
            ["latestSectionID" => Input::get('latestSectionID'), "percentComplete" => Input::get('percentComplete')] );
    }
});

这可行,但是,每次我为相同的userIDlessonID 更新它时,它都会创建一个新行,因此这对不再是唯一的。为此我应该使用哪些方法?我在文档中尝试了save()attach()push(),但我不确定在这里使用哪一个。

编辑:澄清一下,生成的表格应如下所示:

id|userID|lessonID|latestSectionID|percentComplete
1    1      1             X             Y
2    1      2        
3    1      3         
4    2      1          
5    3      1
6    3      2
....

编辑 2:修复了 User-&gt;belongsToMany() 方法并添加了 withPivot 调用。

【问题讨论】:

    标签: php mysql laravel eloquent


    【解决方案1】:

    这似乎是一个错误,但你可以这样做:

    ...->sync([$id], false); // detaching set to false, so it will only insert new rows, skip existing and won't detach anything
    

    编辑: 正如评论中所说 - 它对您不起作用,因为您想设置数据透视数据。 所以目前基本上没有办法做到这一点,但是应该这样做:

    // belongsToMany.php
    public function attachOrUpdate($id, array $attributes = array(), $touch = true)
    {
        if ($id instanceof Model) $id = $id->getKey();
    
        if ( ! $this->allRelatedIds()->contains($id)) // getRelatedIds() in prior to v5.4
        {
            return $this->attach($id, $attributes, $touch); 
        }
        else if ( ! empty($attributes))
        {
            return $this->updateExistingPivot($id, $attributes, $touch);
        }
    }
    

    我要测试一下,如果通过了,就向 4.1 发送拉取请求

    【讨论】:

    • 你的意思是这样的:$us-&gt;sync($lesson-&gt;lessonID, ["latestSectionID" =&gt; Input::get('latestSectionID'), "percentComplete" =&gt; Input::get('percentComplete')], false );?由于某种原因,它说参数 1 是一个字符串而不是一个数组。
    • 应该是$us-&gt;sync([$lesson-&gt;lessonID, ["latestSectionID" =&gt; ...
    • user2.. 不,同步不接受属性。 pg-robban 我没有注意到您正在更新(或创建)数据透视数据。更新了我的答案。
    • @decoz 手册上说有,我记得以前没有这个选项。
    • @SarahKemp 是的,谢谢,已编辑。这就是 Laravel 的诅咒——以一种几乎没有任何价值的方式更改公共接口,但却给消费者带来了很多 BC 破坏性问题
    【解决方案2】:

    我最近遇到了这个问题并以这种方式修复它:

    先使用updateExistingPivot查看结果,如果结果为1表示有相同的行userIDlessonID更新成功,否则如果结果为0表示userIDlessonID 没有行,因此您可以附加它以创建新行

        $update_result = $us->updateExistingPivot($lesson->lessonID, 
                ["latestSectionID" => Input::get('latestSectionID'), "percentComplete" => Input::get('percentComplete')] );
        if($update_result == 0) {
            $us->attach($lesson->lessonID, 
                ["latestSectionID" => Input::get('latestSectionID'), "percentComplete" => Input::get('percentComplete')] );
        }
    

    【讨论】:

      【解决方案3】:

      您应该使用updateExistingPivot()

      更新您的代码以使用

          $us->updateExistingPivot($lesson->lessonID, 
              ["latestSectionID" => Input::get('latestSectionID'), "percentComplete" => Input::get('percentComplete')], true );
      

      最后一个参数将更新所有相关模型的时间戳。如果没有,您可以删除或设置为 false。

      如果您只想在记录不存在时附加,您可以执行以下操作...

      Route::post('dbm/users/setLatestSectionID', function() {
          if(Auth::check()) {
              $user = User::find(Input::get('userID'));
      
              $lesson = [
                  "latestSectionID" => Input::get('latestSectionID'), 
                  "percentComplete" => Input::get('percentComplete')
              ];
      
              $num_lessons = $user->lessonStatuses()->where('id', Input::get('lessonID'))->count();
      
              if($num_lessons == 0) {
                  $user->attach($lesson->lessonID, $lesson);
              } else {
                  $user->updateExistingPivot($lesson->lessonID, $lesson);
      
              }
          }
      });
      

      【讨论】:

      • 这似乎不会在表中创建行。
      • 对不起,我以为这正是您想要的。你想创建一个,除非已经存在一个,在这种情况下你想更新?
      • 您对我的理解正确,但也许我需要澄清一下:我现在拥有的当前attach() 方法在它不存在时创建行。但是,在后续调用中,它会创建额外的行而不是更新现有的行。我想你的意思是我需要先创建关联,然后调用updateExistingPivot,对吧?
      • 我认为那会是更好的路线,因为一旦你到达dbm/users/setLatestSectionID 路线,我会假设已经为课程创建了一个部分。我更新了答案,但在决定attachupdateExistingPivot 之前会先检查课程。
      • 它给了我Trying to get property of non-object attach 行。我认为where 子句有点奇怪。这将检查id 列是否等于lessonID,但事实并非如此。我已更新问题以阐明表格的外观。
      猜你喜欢
      • 2021-04-03
      • 1970-01-01
      • 1970-01-01
      • 2016-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 2013-03-27
      相关资源
      最近更新 更多