简体   繁体   中英

Laravel One to Many of One to Many relationship

Currently I'm doing a project with:

  • Invoice->hasMany Items
  • Item->hasMany SubItems

My code is:

$invoice = Invoice::find($id);
foreach($invoice->items as $item) {
    $item->subItems;
}
return $invoice;

This works well until an Invoice has too many Items and each Items has too many SubItems and PHP return with a timeout. Does anybody have any other better solution?

The relations are lazy loaded, so for each item a new database connection is opened. This takes some time.

You can eager load the relations with: Invoice::with('subitems')->find($id) . More about this at: https://laravel.com/docs/5.7/eloquent-relationships#eager-loading .

First, I think you have a mistake in your code. Instead of $invoice->$items , you probably meant $invoice->items .

The answer to your question could be to do eager load. Instead of:

$invoice = Invoice::find($id);

try:

$invoice = Invoice::with([
'items' => function ($q) {
    $q->with('subItems');
    }
])->find($id);

This way everything will be loaded in one query. You are currently doing count($invoice->items) + 1 queries. This is called the N + 1 problem and it is very common and important to be aware of.

$invoice = Invoice::with(array('items','items.subItems')->where('id',$id)->get();

Update the time allowed with set_time_limit for the thread as each item is iterated over:

$invoice = Invoice::find($id);
$allowed_seconds_per_row = 3;
foreach($invoice->$items as $item){
    set_time_limit($allowed_seconds_per_row);
    $item->subItems();
}
return $invoice;

This still means a request will be taking a large amount of time, but it will only take $allowed_seconds_per_row seconds per row iterated over. If one row takes longer than expected it will sill time out.

Other options are to move the process to cli and adjust the max execution time for that to account for the worst case.

try this:

$invoice = Invoice::with('items.subItems')->find($id);

and in Invoice model:

public function items() { 
    return $this->hasMany(Item::class, 'foreign_id', 'id')->orderBy('rank');
}

in Items model:

public function subItems() {
    return $this->hasMany(SubItem::class, 'foreign_id', 'id')->where('rank' ,$this->rank);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM