Eloquent 循环中的关系
Eloquent Relationship in Loop
我正在为现有数据库(名为 Epicor 的 ERP 系统)构建 Laravel 前端,以期在单独的(新)数据库中扩展该功能。目前,我正在尝试显示 "equipment" 的状态为正在运送给客户的部分,并包含来自部分 table 的信息。数据库关系都在那里,我可以使用 SSMS 获得我需要的所有信息 - 所以我相信我在使用 Eloquent 时一定是出错了。我有以下型号:
设备 - 这是系统中的序列号,因此实际上是零件的实例:
<?php
class Equipment extends Model
{
protected $table = 'ERP.SerialNo';
public $timestamps = false;
protected $primaryKey = 'SerialNumber';
protected $keyType = 'string';
protected $fillable = [
'SerialNumber',
'SNStatus',
'PartNum',
'TerritoryID',
'JobNum',
'PackNum',
'PackLine',
'RMANum',
'CustNum',
'SNStatus'
];
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Customer()
{
return $this->belongsTo(Customer::class,'CustNum', 'CustNum');
}
}
部分
class Part extends Model
{
protected $table = 'ERP.Part';
public $timestamps = false;
protected $primaryKey = 'PartNum';
protected $keyType = 'string';
protected $fillable = [
'PartNum',
'SearchWord',
'Innactive',
'PartDescription',
'ClassID',
'CommodityCode',
'NetWeight'
];
public function ShipmentLine()
{
return $this->hasMany(Shipment::class, 'PartNum', 'PartNum');
}
public function Equipment()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
}
客户控制员
public function show($CustID)
{
$Customer = Customer::find($CustID);
$Shipments = $Customer->Shipment->where('Voided', '0');
$Equipments = $Customer->Equipment->where('SNStatus', 'SHIPPED');
return view('Customer.show', compact('Equipments', 'Customer','Shipments', 'Parts'));
}
show.blade.php(在客户下)
<?php
@foreach($Equipments as $Equipment)
<tr>
<td>ClassID</td>
<td><a href="{{ route('Part.show',$Equipment->PartNum)}}">{{$Equipment->PartNum}}</a></td>
<td><a href="{{ route('Equipment.show',$Equipment->SerialNumber)}}">{{$Equipment->SerialNumber}}</a></td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
@endforeach
一切正常,我得到了一份状态为已运送给该客户的所有设备的清单。我现在想做的是,在设备列表中,包括 Part table 中相关的字段(ClassID 和 PartDescription)。
我已经尝试了一些方法,但感觉我在抓住救命稻草,所有尝试都失败了。我已经设法在设备 show.blade.php 上显示部件信息,所以我相信模型设置正常。
提前致谢,
理查德
首先,Part 模型(以及 Customer 模型)中的关系方法必须写成复数形式,因为您要匹配多个实体:
public function ShipmentLines()
{
return $this->hasMany(Shipment::class, 'PartNum', 'PartNum');
}
public function Equipments()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
其次,您可以使用关系加载控制器中的设备,而不是使用延迟加载:
public function show($CustID)
{
$Customer = Customer::find($CustID);
$Shipments = $Customer->ShipmentLines()
->where('Voided', '0')
->get();
$Equipments = $Customer->Equipments()
->with('Part') // load the Part too in a single query
->where('SNStatus', 'SHIPPED')
->get();
return view('Customer.show', compact('Equipments', 'Customer', 'Shipments'));
}
最后,在刀刃模板中,可以很方便的使用装备零件:
@foreach ($Equipments as $Equipment)
<tr>
<td>{{$Equipment->Part->ClassID}}</td>
<td><a href="{{ route('Part.show',$Equipment->PartNum)}}">{{$Equipment->PartNum}}</a></td>
<td><a href="{{ route('Equipment.show',$Equipment->SerialNumber)}}">{{$Equipment->SerialNumber}}</a></td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
@endforeach
此外,我建议使用 @forelse
而不是 @foreach
来涵盖没有设备存在的情况:
@forelse ($Equipments as $Equipment)
<tr>
<td>{{$Equipment->Part->ClassID}}</td>
<td><a href="{{ route('Part.show',$Equipment->PartNum)}}">{{$Equipment->PartNum}}</a></td>
<td><a href="{{ route('Equipment.show',$Equipment->SerialNumber)}}">{{$Equipment->SerialNumber}}</a></td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
@empty
<tr>
<td colspan="4">There is no existing equipment!</td>
</tr>
@endforelse
我想你要找的是with()
。
不过,在我开始之前,您实际上遇到了一个比看起来更严重的问题。马太·米海其实也提到了这一点。
当你有类似 $Customer->Equipment
的东西时,你实际上是在利用 Eloquent 的 "dynamic properties"。这意味着,某处有一个神奇的 __get()
,如果目标模型上不存在所需的 属性,请检查它是否具有该名称的关系方法。如果是这样,如果尚未通过 with()
或 load()
.
预先加载它,请延迟加载它
因此,当您执行 $Customer->Equipment
时,它基本上是 $Customer->Equipment()->get()
的快捷方式。
接下来要考虑的是 get()
的结果是一个 Eloquent\Collection, which is a child-class to Support\Collections. And Support\Collections have their own version of the where()
方法。
综上所述,$Customer->Equipment->where('SNStatus', 'SHIPPED')
不会 导致 运行 查询如下所示:
SELECT * FROM Equipment WHERE customerID = ? AND SNStatus = 'SHIPPED'
你正在做的是 运行 这个:
SELECT * FROM Equipment WHERE customerID = ?
然后要求集合 class 按 SNStatus='SHIPPED'
之后 过滤结果集。这可能是一个巨大的性能损失,甚至会根据 table 的大小来最大化您的服务器 RAM。我想你真正要找的是这个:
$Customer->Equipment()->where('SNStatus', 'SHIPPED')->get()
通过调用实际的 Equipment()
方法而不是动态的 属性,您是在告诉 Eloquent 您还没有完全准备好执行查询,因为您仍在为其附加条件。
(顺便提一句,您的命名约定对我的强迫症造成了一点伤害,方法应始终为 "camelCased"。只有 class 个名称的首字母大写。)
所以...回到你实际提出的问题,包括对 Model::where()
和 Collection::where()
之间区别的理解,我们有这样的东西:
$resutls = $Customer->Equipment()->with(['Part'])->where('SNStatus', 'SHIPPED')->get();
由于您想在您真正关心的部分 table 中指定几个字段,您可以使用 constrained eager-load
$resutls = $Customer->Equipment()->with(['Part' => function (Illuminate\Database\Eloquent\Builder $query) {
$query->select([
'PartNum', //Per Equipment::Part(), This needs to be there for the relation to be mated with its parent
'ClassID',
'PartDescription'
]);
// Since PHP always handles objects by-reference, you don't actually need to return $query after having altered it here.
}])->where('SNStatus', 'SHIPPED')->get();
这将为您提供一个嵌套的 Part
对象,其中仅包含您在 Eloquent\Collection
结果中的每个 Equipment
模型元素上关心的字段。
至于如何在你的 blade 文件中处理这些结果,我会与 Matei Mihai 不同,我认为这个答案很好。
我正在为现有数据库(名为 Epicor 的 ERP 系统)构建 Laravel 前端,以期在单独的(新)数据库中扩展该功能。目前,我正在尝试显示 "equipment" 的状态为正在运送给客户的部分,并包含来自部分 table 的信息。数据库关系都在那里,我可以使用 SSMS 获得我需要的所有信息 - 所以我相信我在使用 Eloquent 时一定是出错了。我有以下型号:
设备 - 这是系统中的序列号,因此实际上是零件的实例:
<?php
class Equipment extends Model
{
protected $table = 'ERP.SerialNo';
public $timestamps = false;
protected $primaryKey = 'SerialNumber';
protected $keyType = 'string';
protected $fillable = [
'SerialNumber',
'SNStatus',
'PartNum',
'TerritoryID',
'JobNum',
'PackNum',
'PackLine',
'RMANum',
'CustNum',
'SNStatus'
];
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Customer()
{
return $this->belongsTo(Customer::class,'CustNum', 'CustNum');
}
}
部分
class Part extends Model
{
protected $table = 'ERP.Part';
public $timestamps = false;
protected $primaryKey = 'PartNum';
protected $keyType = 'string';
protected $fillable = [
'PartNum',
'SearchWord',
'Innactive',
'PartDescription',
'ClassID',
'CommodityCode',
'NetWeight'
];
public function ShipmentLine()
{
return $this->hasMany(Shipment::class, 'PartNum', 'PartNum');
}
public function Equipment()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
}
客户控制员
public function show($CustID)
{
$Customer = Customer::find($CustID);
$Shipments = $Customer->Shipment->where('Voided', '0');
$Equipments = $Customer->Equipment->where('SNStatus', 'SHIPPED');
return view('Customer.show', compact('Equipments', 'Customer','Shipments', 'Parts'));
}
show.blade.php(在客户下)
<?php
@foreach($Equipments as $Equipment)
<tr>
<td>ClassID</td>
<td><a href="{{ route('Part.show',$Equipment->PartNum)}}">{{$Equipment->PartNum}}</a></td>
<td><a href="{{ route('Equipment.show',$Equipment->SerialNumber)}}">{{$Equipment->SerialNumber}}</a></td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
@endforeach
一切正常,我得到了一份状态为已运送给该客户的所有设备的清单。我现在想做的是,在设备列表中,包括 Part table 中相关的字段(ClassID 和 PartDescription)。
我已经尝试了一些方法,但感觉我在抓住救命稻草,所有尝试都失败了。我已经设法在设备 show.blade.php 上显示部件信息,所以我相信模型设置正常。
提前致谢,
理查德
首先,Part 模型(以及 Customer 模型)中的关系方法必须写成复数形式,因为您要匹配多个实体:
public function ShipmentLines()
{
return $this->hasMany(Shipment::class, 'PartNum', 'PartNum');
}
public function Equipments()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
其次,您可以使用关系加载控制器中的设备,而不是使用延迟加载:
public function show($CustID)
{
$Customer = Customer::find($CustID);
$Shipments = $Customer->ShipmentLines()
->where('Voided', '0')
->get();
$Equipments = $Customer->Equipments()
->with('Part') // load the Part too in a single query
->where('SNStatus', 'SHIPPED')
->get();
return view('Customer.show', compact('Equipments', 'Customer', 'Shipments'));
}
最后,在刀刃模板中,可以很方便的使用装备零件:
@foreach ($Equipments as $Equipment)
<tr>
<td>{{$Equipment->Part->ClassID}}</td>
<td><a href="{{ route('Part.show',$Equipment->PartNum)}}">{{$Equipment->PartNum}}</a></td>
<td><a href="{{ route('Equipment.show',$Equipment->SerialNumber)}}">{{$Equipment->SerialNumber}}</a></td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
@endforeach
此外,我建议使用 @forelse
而不是 @foreach
来涵盖没有设备存在的情况:
@forelse ($Equipments as $Equipment)
<tr>
<td>{{$Equipment->Part->ClassID}}</td>
<td><a href="{{ route('Part.show',$Equipment->PartNum)}}">{{$Equipment->PartNum}}</a></td>
<td><a href="{{ route('Equipment.show',$Equipment->SerialNumber)}}">{{$Equipment->SerialNumber}}</a></td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
@empty
<tr>
<td colspan="4">There is no existing equipment!</td>
</tr>
@endforelse
我想你要找的是with()
。
不过,在我开始之前,您实际上遇到了一个比看起来更严重的问题。马太·米海其实也提到了这一点。
当你有类似 $Customer->Equipment
的东西时,你实际上是在利用 Eloquent 的 "dynamic properties"。这意味着,某处有一个神奇的 __get()
,如果目标模型上不存在所需的 属性,请检查它是否具有该名称的关系方法。如果是这样,如果尚未通过 with()
或 load()
.
因此,当您执行 $Customer->Equipment
时,它基本上是 $Customer->Equipment()->get()
的快捷方式。
接下来要考虑的是 get()
的结果是一个 Eloquent\Collection, which is a child-class to Support\Collections. And Support\Collections have their own version of the where()
方法。
综上所述,$Customer->Equipment->where('SNStatus', 'SHIPPED')
不会 导致 运行 查询如下所示:
SELECT * FROM Equipment WHERE customerID = ? AND SNStatus = 'SHIPPED'
你正在做的是 运行 这个:
SELECT * FROM Equipment WHERE customerID = ?
然后要求集合 class 按 SNStatus='SHIPPED'
之后 过滤结果集。这可能是一个巨大的性能损失,甚至会根据 table 的大小来最大化您的服务器 RAM。我想你真正要找的是这个:
$Customer->Equipment()->where('SNStatus', 'SHIPPED')->get()
通过调用实际的 Equipment()
方法而不是动态的 属性,您是在告诉 Eloquent 您还没有完全准备好执行查询,因为您仍在为其附加条件。
(顺便提一句,您的命名约定对我的强迫症造成了一点伤害,方法应始终为 "camelCased"。只有 class 个名称的首字母大写。)
所以...回到你实际提出的问题,包括对 Model::where()
和 Collection::where()
之间区别的理解,我们有这样的东西:
$resutls = $Customer->Equipment()->with(['Part'])->where('SNStatus', 'SHIPPED')->get();
由于您想在您真正关心的部分 table 中指定几个字段,您可以使用 constrained eager-load
$resutls = $Customer->Equipment()->with(['Part' => function (Illuminate\Database\Eloquent\Builder $query) {
$query->select([
'PartNum', //Per Equipment::Part(), This needs to be there for the relation to be mated with its parent
'ClassID',
'PartDescription'
]);
// Since PHP always handles objects by-reference, you don't actually need to return $query after having altered it here.
}])->where('SNStatus', 'SHIPPED')->get();
这将为您提供一个嵌套的 Part
对象,其中仅包含您在 Eloquent\Collection
结果中的每个 Equipment
模型元素上关心的字段。
至于如何在你的 blade 文件中处理这些结果,我会与 Matei Mihai 不同,我认为这个答案很好。