【问题标题】:Laravel 5 issue with wherePivotLaravel 5 与 wherePivot 的问题
【发布时间】:2015-04-10 00:35:37
【问题描述】:

我正在使用 Laravel 5,但在让 ->wherePivot() 处理多对多关系时遇到问题。当我dd() SQL 时,看起来 Eloquent 正在使用 `pose_state` 查找数据透视表中的记录。`pose_id` 为 null`。

我希望这是一个简单的错误,而不是错误。任何想法都表示赞赏。

数据库结构

姿势

id
name
type

状态

id
name
machine_name

姿势状态

pose_id
state_id
status

型号

姿势

<?php namespace App;
use DB;
use App\State;
use Illuminate\Database\Eloquent\Model;

class Pose extends Model {

  public function states()
  {
      return $this->belongsToMany('App\State')
                  ->withPivot('status_id')
                  ->withTimestamps();
  }
  public function scopeWithPendingReviews()
  {
      return $this->states()
                  ->wherePivot('status_id',10);
  }
}

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class State extends Model {

    public function poses()
    {
      return $this->belongsToMany('Pose')
                  ->withPivot('status_id')
                  ->withTimestamps();
    }

}

PosesController 函数

public function listPosesForReview(){
    $poses = Pose::withPendingReviews()->get();
    dd($poses->toArray() );
}

SQL

select 
  `states`.*, `pose_state`.`pose_id` as `pivot_pose_id`,
  `pose_state`.`state_id` as `pivot_state_id`, 
  `pose_state`.`status_id` as `pivot_status_id`, 
  `pose_state`.`created_at` as `pivot_created_at`, 
  `pose_state`.`updated_at` as `pivot_updated_at` 
from 
  `states` inner join `pose_state` on `states`.`id` = `pose_state`.`state_id` 
where 
  `pose_state`.`pose_id` is null and `pose_state`.`status_id` = ?

编辑

当我更新我的代码以删除它工作的范围时。感谢@Deefour 让我走上了正确的道路!也许范围还有其他我想念的东西。

public function pendingReviews()
{
  return $this->states()
              ->wherePivot('status_id','=', 10);
}

又一次编辑

我终于让它工作了。上面的解决方案是给我重复的条目。不知道为什么会这样,但确实如此,所以我会坚持下去。

public function scopeWithStatusCode($query, $tag)
{
  $query->with(['states' => function($q) use ($tag)
            {
              $q->wherePivot('status_id','=', $tag);
            }])
        ->whereHas('states',function($q) use ($tag)
            {
              $q->where('status_id', $tag);
            });
}

【问题讨论】:

    标签: php laravel eloquent laravel-5


    【解决方案1】:

    我认为您对scopeWithPendingReviews() 的实现是对作用域预期用途的滥用。

    除此之外,我相信您没有正确使用wherePivot()。根据the source,方法签名为

    public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
    

    但你把它当作

    public function wherePivot($column, $value = null, $boolean = 'and')
    

    这意味着

    ->wherePivot('status_id',10)
    

    应该是

    ->wherePivot('status_id', '=', 10)
    

    回复评论

    范围应该被认为是一组可重用的条件,以附加到现有查询,即使该查询只是

    SomeModel::newQuery()
    

    这个想法是预先存在的查询将通过范围方法中的条件进一步细化(阅读:'scoped'),而不是生成新的查询,并且绝对不会根据关联的查询生成新的查询型号。

    默认情况下,传递给范围方法的第一个也是唯一的参数是查询构建器实例本身。

    您在Pose 模型上的作用域实现实际上是对states 表的查询

    $this->states()
    

    这就是您的 SQL 如此显示的原因。这也是您滥用范围的明确指标。范围可能看起来像这样

    public function scopeWithPendingReviews($query) {
      $query->join('pose_state', 'poses.id', '=', 'pose_state.pose.id')
            ->where('status_id', 10);
    }
    

    与返回基于State 模型的查询的新pendingReviews() 方法不同,此范围将优化Pose 模型上的查询。

    现在您可以按照最初的意图使用示波器。

    $poses = Pose::withPendingReviews();
    

    这可以翻译成更详细的

    $poses = Pose::newQuery()->withPendingReviews();
    

    还要注意,上面的范围不返回值。它接受现有的查询构建器对象并添加到它上面。

    这个问题的另一个答案充满了错误信息。

    1. 您不能按原样使用wherePivot()
    2. 您对withTimestamps()的使用与您的问题完全无关
    3. 您无需执行任何“自定义工作”即可使时间戳正常工作。只需像您一样添加withTimestamps() 调用即可。只需确保您的联接表中有 created_atupdated_at 列即可。

    【讨论】:

    • 感谢您的回复。我认为这样的事情是你会使用范围的吗?你会在这里用什么代替?我根据您的建议更改了代码,但遇到了同样的错误。
    • @Deefour - 不幸的是,您对wherePivot 用法的回答不正确。在您链接的源代码的 sn-p 中,它显示 wherePivot 几乎只是 Illuminate\Database\Query\Builder::where 方法的代理,它允许 OP 正在尝试的语法。参考source here
    • @Scopey ... func_num_args() 在这种情况下将返回 4,而不是 2,因为 FOUR 参数被传递到where().
    • 原来 scopeWithPendingReviews() 是问题所在。我不知道为什么,所以如果你有任何想法,很高兴听到你的想法。
    • 我不知道你为什么要攻击我。非常欢迎您编辑我的答案以提出更正建议。您可能想更深入地阅读您链接的页面,了解数据透视表时间戳,特别是这一点:如果您希望数据透视表自动维护 created_at 和 updated_at 时间戳,请在关系定义上使用 withTimestamps 方法
    【解决方案2】:

    我认为您的范围实现很好,我看到的问题只是一个错字。您的架构显示该字段名为 status,但您的 where 条件指的是 status_id

    试试:

    ->wherePivot('status', 10);
    

    另外,withTimestamps() 方法也会导致问题。您的架构中没有用于枢轴的时间戳(如我所见),因此您不应该将它们放在关系定义中,因为它试图获取与创建/更新关系时相关的时间戳。如果您将数据透视表架构设置为具有时间戳字段,则可以执行此操作,但我认为您必须做一些自定义工作才能正确保存时间戳。

    【讨论】:

      【解决方案3】:

      这对我有用(Laravel 5.3):

      $task = App\Models\PricingTask::find(1);
      $task->products()->wherePivot('taggable_type', 'product')->get();
      

      【讨论】:

        【解决方案4】:

        如果您在wherePivot 中使用的列尚未添加到withPivot,您也可能遇到此问题(不返回结果)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-28
          • 1970-01-01
          • 1970-01-01
          • 2016-06-30
          • 2016-04-24
          • 2015-05-29
          相关资源
          最近更新 更多