雄辩的关系在循环

时间:2018-10-31 14:09:25

标签: php laravel laravel-blade laravel-5.7

我正在为现有数据库(名为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上显示零件信息,因此我相信模型设置正确。

预先感谢

理查德

2 个答案:

答案 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有所不同,我认为答案相当不错。