使用 eloquent 从多个连接中检索数据

Posted

技术标签:

【中文标题】使用 eloquent 从多个连接中检索数据【英文标题】:Retrieve data from multiple joins using eloquent 【发布时间】:2020-04-30 10:55:55 【问题描述】:

我正在开发一个项目来跟踪货物的交付。 我的想法是一次送货可以去不同的地方,所有这些地方都通过单程连接。

这是我的 Eloquent 架构:

class Delivery extends Model

    public function places()
    
        return $this->hasMany(Place::CLASS, 'delivery_id');
    

    public function trips()
    
        // what should I do here?
    

class Place extends Model

    public function delivery()
    
        return $this->belongsTo(Delivery::CLASS, 'delivery_id');
    

class Trip extends Model

    public function departurePlace()
    
        return $this->belongsTo(Place::CLASS, 'departure_place_id');
    

    public function arrivalPlace()
    
        return $this->belongsTo(Place::CLASS, 'arrival_place_id');
    


基本上,我要做的是获取与一次交付相关的所有行程。或者,换一种说法,连接一次送货必须经过的所有地方的所有行程。 这是实现我想要的结果的 SQL 查询:

select distinct trip.*
from delivery
    join place on (place.delivery_id = delivery.id)
    join trip on (place.id = trip.arrival_place_id or place.id = trip.departure_place_id)

我想在我的 Delivery 模型上有一个 trips() 方法,它返回该结果。 但是,我对如何使用 Eloquent 实现这一点感到非常困惑。

【问题讨论】:

从我上面看到的查询来看,送货和旅行之间似乎没有直接联系。如果我正确理解了查询,那将返回曾经在交货地点进行的所有旅行。 是的,完全正确。送货和旅行之间没有联系。需要首先通过场所连接,如 SQL 查询所示。抱歉,我刚刚注意到我的问题中有一个错误。 您的意思是获取所有通过地点的送货行程吗? @Saengdaet 是的,我就是这个意思。 【参考方案1】:

不幸的是,我们本可以简单地使用union 方法来实现这一点,但它似乎不适用于hasManyThroughrelations。

无论如何,我认为Eloquent 关系并不意味着用于实现这样的特定查询。

相反,您可以使用Eloquent scopes 来实现此目的。

所以根据@Saengdaet 的回答,我们可以写两个关系,然后将它们与scope 结合起来:

(顺便说一句:我不知道你为什么说他的代码出错了......)

class Delivery extends Model

    public function places()
    
        return $this->hasMany(Place::class);
    

    public function outboundTrips()
    
        return $this->hasManyThrough(
            Trip::class, 
            Place::class,
            "delivery_id", // Foreign key on places table
            "departure_place_id", // Foreign key on trips table
        );
    

    public function inboundTrips()
    
        return $this->hasManyThrough(
            Trip::class, 
            Place::class,
            "delivery_id", // Foreign key on places table
            "arrival_place_id", // Foreign key on trips table
        );
    

    public function scopeTrips($query)
    
        $deliveriesWithTrips = $query->with(['outboundTrips', 'inboundTrips'])->get();

        $trips = [];
        $deliveriesWithTrips->each(function ($elt) use (&$trips) 
            $trips[] = $elt->inboundTrips->merge($elt->outboundTrips);
        );

        return $trips;
    

现在要检索给定交货的所有行程,您只需编写:

 Delivery::where('id', $id)->trips();

【讨论】:

【参考方案2】:

如果您想使用Delivery 模型获取所有行程,您可以使用“hasManyThrough”关系,它提供了通过中间关系访问远距离关系的便捷快捷方式。

但您必须选择您将使用 Trip 模型上的哪个 FK 来关联它

可以参考"has-many-through" relationship

class Delivery extends Model

    public function places()
    
        return $this->hasMany(Place::CLASS, 'delivery_id');
    

    public function trips()
    
        return $this->hasManyThrough(
                       Trip::Class, 
                       Place::Class,
                       "delivery_id", // Foreign key on places table
                       "departure_place_id", // Foreign key on trips table
                       "id", // Local key on deliveries table
                       "id" // Local key on places table
        );
    

【讨论】:

您的代码给了我一个错误。我将其更改为 return $this->hasManyThrough(Trip::Class, Place::Class, 'travel_id', 'departure_place_id');> 它可以工作,但显然这并没有返回我想要的所有结果,因为它缺少 arrival_place_id 的额外条件。虽然hasManyThrough 看起来是一个有趣的功能(我不知道),但也许它不是实现我想要的结果的正确方法? 啊我明白了,我只是给你举个例子,因为我实际上并不知道你的 FK 和你的 PK 是什么。 也许你可以在 hasManyThrough 之后添加更多 where 条件来过滤它们

以上是关于使用 eloquent 从多个连接中检索数据的主要内容,如果未能解决你的问题,请参考以下文章

Vue 2 Laravel 5.3 Eloquent 无法从对象中检索数据

使用 eloquent 和 vuejs 时如何检索数据库列

从 table1 中检索数据,但在 table2 中进行比较 - Laravel eloquent

在 Eloquent 中将数据透视表与多个表连接起来

如何使用 eloquent 在 laravel 中编写多个连接

如何使用 Laravel Eloquent 在不同服务器上的多个数据库之间执行连接查询?