方法调用之间的差异$model->relation();和$model>关系;

2022-08-30 11:27:41

这里有一些基本的理解/理论,我错过了。我不明白这些函数调用之间的区别:

$distributors = $store->distributors();
$distributors = $store->distributors;
$distributors = $store->distributors()->get();
$distributors = $store->distributors->get();

我试图在这里完成的是获得一个商店的分销商列表(多对多关系),他们将每个分销商的啤酒列表放入一个巨大的列表中。

foreach ($distributors as $distributor) 
{
    $available_beers = array_merge($distributor->beers(), $available_beers);
}

我不知道这是否是最好的方法,我无法让它发挥作用。与第一个方法列表类似,我不知道我是否需要或->$beers->$beers()

更新

感谢所有回答的人!这将是我前进的良好参考。我最大的教训是取回集合与取回查询生成器/关系对象之间的区别。为了将来参考那些发现这个问题的人,这是我在控制器中设置的内容:

$store = $this->store->find($id)->first();
$distributors = $store->distributors;
$beers = [];
foreach ($distributors as $distributor){
    $beers = array_merge($distributor->beers->lists('name', 'id'), $beers);
}

答案 1

简短的回答

$model->relation()返回关系对象

$model->relation返回关系的结果


长答案

$model->relation()可以解释得很简单。您正在调用定义关系的实际函数。你的可能看起来有点像这样:distributor

public function distributors(){
    return $this->hasMany('Distributor');
}

因此,在调用时,您只需获得其返回值的实例$store->distributors()$this->hasMany('Distributor')Illuminate\Database\Eloquent\Relations\HasMany

你什么时候使用它?

如果要在运行查询之前进一步指定查询,则通常会调用关系函数。例如,添加一个 where 语句:

$distributors = $store->distributors()->where('priority', '>', 4)->get();

当然,您也可以这样做:但这与 具有相同的结果。$store->distributors()->get()$store->distributors


这让我想到了动态关系属性的解释。

Laravel在引擎盖下做了一些事情,让你直接访问关系的结果作为财产。喜欢:。$model->relation

以下是Illuminate\Database\Eloquent\Model

1) 属性实际上并不存在。因此,如果您访问该调用将被代理为$store->distributors__get()

2) 此方法然后使用属性名称进行调用getAttributegetAttribute('distributors')

public function __get($key)
{
    return $this->getAttribute($key);
}

3) 在它检查关系是否已加载 (存在于 中)。如果不是,并且存在关系方法,它将加载关系 (getAttributerelationsgetRelationshipFromMethod)

public function getAttribute($key)
{
    // code omitted for brevity

    if (array_key_exists($key, $this->relations))
    {
        return $this->relations[$key];
    }

    $camelKey = camel_case($key);

    if (method_exists($this, $camelKey))
    {
        return $this->getRelationshipFromMethod($key, $camelKey);
    }
}

4)最后,Laravel调用关系,然后导致查询构建器实例上的一个。(这给出了与 相同的结果。getResults()get()$model->relation()->get()


答案 2

直接回答您的问题:

  • $store->distributors()将返回实际的关系对象(\Illuminate\Database\Eloquent\Relations\BelongsToMany)。
  • $store->distributors将是一个包含关系查询结果的集合(\Illuminate\Database\Eloquent\Collection)。
  • $store->distributors()->get()将是一个包含关系查询结果的集合(\Illuminate\Database\Eloquent\Collection)。
  • $store->distributors->get()应返回错误,因为您正在调用 Collection 对象,并且第一个参数不是可选的。如果不是错误,则至少应返回 null。get()

更多信息:

给定以下模型:

class Store extends Eloquent {
    public function distributors() {
        return $this->belongsToMany('Distributor');
    }
}

调用关系方法 () 将返回关系 (\Illuminate\Database\Eloquent\Relations\BelongsToMany) 对象。这基本上是一个查询对象,您可以继续修改,但您仍然需要调用某种类型的方法来获得结果(例如,,等)。$store->distributors()get()first()

但是,访问关系属性 () 将返回一个集合 (\Illuminate\Database\Eloquent\Collection) 对象,其中包含执行关系查询的结果。$store->distributors

默认情况下,在首次访问关系属性时,将创建关系属性并为其分配一个值(称为“延迟加载”)。因此,第一次访问时,它会在后台执行关系查询,将结果存储在属性中,然后返回这些结果。但是,它只执行一次此操作。下次访问 时,该属性已包含数据,因此这就是您正在访问的内容。$store->distributors$store->distributors$store->distributors

为了说明这一点:

// the following two statements will run the query twice
$r1 = $store->distributors()->get();
$r2 = $store->distributors()->get();

// the following two statements will run the query once.
// the first statement runs the query, populates $store->distributors, and assigns the variable
// the second statement just accesses the data now stored in $store->distributors
$r3 = $store->distributors;
$r4 = $store->distributors;

// at the end, $r1 == $r2 == $r3 == $r4

还可以使用查询上的方法“预先”加载关系。这样做是为了缓解延迟加载可能需要的所有额外查询(称为 n+1 问题)。您可以在此处阅读更多相关信息。with()