我正在为现有数据库(名为Epicor的ERP系统)构建Laravel前端,以期在单独的(新)数据库中扩展该功能。目前,我正在尝试显示状态为“运送给客户”的“设备”,并包括“零件”表中的信息。数据库关系就在那里,我可以使用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
这一切都可以正常工作,我可以得到所有状态为已发货给该客户的设备清单。我现在想做的是在设备列表中,包括零件表中相关的字段(ClassID和PartDescription)。
我尝试了一些尝试,但是觉得我抓着稻草,所有尝试都失败了。我已经设法在Equipment show.blade.php上显示零件信息,因此我相信模型设置正确。
预先感谢
理查德
答案 0 :(得分:2)
首先,由于要匹配多个实体,因此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
答案 1 :(得分:1)
我认为您正在寻找的是with()
。
在我理解之前,您实际上在那里遇到的问题比看起来更大。 Matei Mihai实际上涉及到这一点。
当您拥有$Customer->Equipment
之类的东西时,您实际上是在利用Eloquent的“动态属性”。这意味着在其中某处有一个魔术__get()
,它指示如果目标模型上不存在所需的属性,请检查该名称是否具有关联方法。如果是这样的话,如果尚未通过with()
或load()
急切加载,请延迟加载它。
因此,当您执行$Customer->Equipment
时,它基本上是$Customer->Equipment()->get()
的快捷方式。
接下来要考虑的是get()
的结果是Eloquent\Collection,它是Support\Collections的子类。并且Support \ Collections有自己的where()
方法版本。
总而言之,$Customer->Equipment->where('SNStatus', 'SHIPPED')
不会 导致运行如下查询:
SELECT * FROM Equipment WHERE customerID = ? AND SNStatus = 'SHIPPED'
您正在做的是运行此命令:
SELECT * FROM Equipment WHERE customerID = ?
然后要求Collection类通过SNStatus='SHIPPED'
之后 过滤结果集。根据这些表的大小,这可能会严重影响性能,甚至使服务器的RAM最大化。我认为您真正想要的是:
$Customer->Equipment()->where('SNStatus', 'SHIPPED')->get()
通过调用实际的Equipment()
方法而不是dynamic属性,是在告诉Eloquent您尚未准备好执行查询,因为您仍在附加条件。
(另外,请注意,您的命名约定对我的OCD有点伤害,方法应始终为“ camelCased”。只有类名的首字母大写。)
所以...回到您实际提出的问题,并包括对Model::where()
和Collection::where()
之间区别的理解,我们所拥有的是这样的:
$resutls = $Customer->Equipment()->with(['Part'])->where('SNStatus', 'SHIPPED')->get();
由于您要在零件表中指定您真正关心的几个字段,因此可以使用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
对象,其中包含Equipment
结果中每个Eloquent\Collection
模型元素上您关心的字段。
关于如何在刀片文件中处理这些结果,我将与Matei Mihai有所不同,我认为答案相当不错。