laravel eloquent 'has' 方法的行为出人意料

Posted

技术标签:

【中文标题】laravel eloquent \'has\' 方法的行为出人意料【英文标题】:laravel eloquent 'has' method behaves in an unexpected waylaravel eloquent 'has' 方法的行为出人意料 【发布时间】:2020-04-09 08:44:39 【问题描述】:

如果 Section 模型至少有一个 User,我想获取它的集合。从文档中,has() 方法可以做到这一点,太好了。检索到的集合中没有 users 关系。然而,当我遍历集合时,我可以获得users 的属性。为什么?

class Section extends Model

    protected $guarded = [];

    public function users()
    
        return $this->hasMany('App\User');
    

class User extends Authenticatable

    protected $guarded = [];

    public function section()
    
        return $this->belongsTo('App\Section');
    

我做的是这样的:

$section = Section::where('id' , 1)->has('users')->get();

收藏是这样的:

Illuminate\Database\Eloquent\Collection #3025
     all: [
       App\Section #3015
         id: 1,
         class_id: 1,
         section_name: "A",
         created_at: "2019-12-14 18:26:01",
         updated_at: "2019-12-14 18:26:01",
       ,
     ],
   

现在奇怪的是,当我执行以下操作时,即使集合中不存在 users 关系,它也会给出用户的属性。

为什么?

 @foreach ($section as $section)
   @foreach ($section->users as $student)
     <p>$student->name</p>
   @endforeach
@endforeach
solomon
uche
kene

【问题讨论】:

【参考方案1】:

好的,现在我明白你的问题了。

    首先

has 方法并不意味着它将包含User。这意味着将返回所有至少有一个用户的sections。我认为有Section id ===1 的用户。因此,无论在您的代码中是否使用has,都没有任何区别。

如果你想显式加载关系,你应该使用with

Section::where('id' , 1)-&gt;with('users')-&gt;get();。那么你应该在每个部分下都有用户集合。

    第二

    您仍然可以访问blade 文件中的用户属性的原因是lazy loading。即使它不包含在原始数据库查询和结果中,但是当您尝试访问它时,laravel 仍然会尝试为您获取它们。这可能会导致 N+1 问题。

【讨论】:

如果您查看该集合,您会注意到没有“用户”关系,我要说的是循环不应该将“用户”的属性作为“集合中不存在用户的关系。但确实如此,这就是为什么我很困惑,我想知道为什么会这样。 这大部分是正确的,但是您仍然可以(并且通常应该)将has()with() 结合使用; has() 将处理 Sectionwith() 的基于计数的过滤,并急切加载关系以避免您提到的 N+1 问题。在上面的代码中,你仍然可以得到没有任何Users 的Section 模型,这似乎违背了原问题的意图。 @TimLewis 我认为他对has 方法感到困惑,他认为has 意味着包含关系,事实并非如此。【参考方案2】:

看起来像你的第一个回声:

$section = Section::where('id' , 1)->has('users')->get();

只打印它有用户的部分,但你并没有特别说给我用户。

在下一个循环中,您将遍历视图中的每个部分,并特别说明循环关系。这可以通过这一行看到:@foreach ($section-&gt;users as $student)

我在这里阅读文档:https://laravel.com/docs/6.x/eloquent-relationships#querying-relations

在打印该部分时的第一个回显中,您可以像这样获得用户:echo $section-&gt; users()

【讨论】:

大部分是伪代码,因为我的新计算机上目前没有可用的 laravel 环境。 你是对的,现在刚刚浏览了文档,现在对我来说更清楚了..【参考方案3】:

这就是 Laravel 的工作原理。

访问$model-&gt;relationship,在你的例子中是$section-&gt;users,是一个神奇的函数,它检查你是否通过Section::with('users')之类的东西显式加载了关系,如果没有,就加载它。

运行dd($section) 时没有看到users 的原因是您没有显式加载关系,但这并不意味着它不可用。如果您在初始查询中包含 with('users'),您会看到以下内容:

$section = Section::where('id' , 1)->has('users')->with('users')->get();

App\Section #3015
   id: 1,
   class_id: 1,
   section_name: "A",
   created_at: "2019-12-14 18:26:01",
   updated_at: "2019-12-14 18:26:01",
   users: [
     0 => App\User #3016
       id: ...
       name: ...
   ]
 ,

// Or similar

基本上,您并没有加载关系,因此在使用 dd($section) 时无法看到它,但由于 Laravel 的魔法方法,它仍然可以在 php 中使用。

我还应该注意,为您的查询使用正确的变量命名和闭包(-&gt;get()-&gt;first() 等)。

$section 在使用-&gt;get() 时是一个糟糕的名称,因为您要从数据库中获取多条记录。要么使用$sections,要么将闭包更改为-&gt;first(),如果使用-&gt;first(),请不要使用foreach()

【讨论】:

以上是关于laravel eloquent 'has' 方法的行为出人意料的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Eloquent:计数项目模型关系

Eloquent Laravel 按相关模型排序分页查询

Laravel Eloquent:通过具有多个关系的枢轴将表绑定到另一个表

在 Laravel Eloquent 中获取 HasOne 关系中的特定列

php [Eloquent CheatSheet] Eloquent #laravel #eloquent的常见查询方法

使用 Eloquent 的 Laravel 块方法