Красноречивые отношения в цикле - PullRequest
0 голосов
/ 31 октября 2018

Я создаю интерфейс Laravel для существующей базы данных (ERP-система под названием Epicor) с целью расширения этой функциональности в отдельной (новой) базе данных. В данный момент я пытаюсь отобразить части «оборудования», которые находятся в состоянии доставки клиенту, и включить информацию из таблицы деталей. Связи с БД все есть, и я могу получить всю информацию, которая мне нужна, используя 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).

Я попробовал несколько вещей, но чувствую, что цепляюсь за соломинку, и все мои попытки терпят неудачу. Мне удалось отобразить на оборудовании show.blade.php информацию о деталях, поэтому я считаю, что модели настроены нормально.

Заранее спасибо,

Ричард

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Я думаю, что вы ищете with().

Прежде чем я до этого доберусь, у вас на самом деле есть большая проблема, чем кажется. Матей Михай на самом деле коснулся этого.

Когда у вас есть что-то вроде $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' , а затем . Это может привести к огромному снижению производительности и даже к максимальному увеличению ОЗУ серверов в зависимости от размера этих таблиц. Я думаю, что вы действительно ищете, это:

$Customer->Equipment()->where('SNStatus', 'SHIPPED')->get()

Вызывая реальный метод Equipment(), а не динамическое свойство, вы говорите Eloquent, что вы еще не совсем готовы к выполнению запроса, поскольку вы все еще добавляете к нему условия.

(Также как примечание, ваше соглашение об именах немного вредит моему OCD, методы всегда должны быть "camelCased". Только имена классов имеют свою первую букву в верхнем регистре.)


Итак ... вернемся к вопросу, который вы на самом деле задали, и включая понимание разницы между Model::where() и Collection::where(), то, что мы имеем, выглядит примерно так:

$resutls = $Customer->Equipment()->with(['Part'])->where('SNStatus', 'SHIPPED')->get();

Поскольку вы хотели указать пару полей в таблице деталей, которые вам действительно нужны, вы можете использовать ограниченную готовую нагрузку

$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.

Что касается того, как обрабатывать эти результаты в вашем блейд-файле, я расскажу об этом Матею Михаю, я думаю, что ответ довольно хороший.

0 голосов
/ 31 октября 2018

Прежде всего, методы отношений внутри модели 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...