Adam Lambert
Adam Lambert

Reputation: 1421

Laravel Eloquent - Merging two eloquent model results (not the collections)

I have a table of stock movements, which shows a history of a product (receiving, moving location etc.)

I have two queries which calculate (via aggregate sums):

  1. The original received qty.
  2. The current live qty (after things have been moved).

Both of these queries return a collection of the same models. They will always have the same number of results in each, and the product_id's will always be the same. The only part that is different is the received/live qty and I want to somehow merge the individual records into a new collection. i.e.

Single record from query 1:

0 => StockMovement {#1602 ▼
  #original: array:2 [▼
    "live_qty" => "8"
    "product_id" => "2606598e-4150-461a-9e91-10d67cce8daa"
  ]
}

Single record from query 2;

0 => StockMovement {#1602 ▼
  #original: array:2 [▼
    "received_qty" => "15"
    "product_id" => "2606598e-4150-461a-9e91-10d67cce8daa"
  ]
}

I am trying to end up with a merged result which looks like the following:

0 => StockMovement {#1602 ▼
  #original: array:3 [▼
    "received_qty" => "15"
    "live_qty" => "8"
    "product_id" => "2606598e-4150-461a-9e91-10d67cce8daa"
  ]
}

I want to do this in a way that does not convert the Product object to an array, as I need the relationships that are embedded within it.

Currently, I have hacked this as follows:

$live = $this->liveQtyCollection();
$merged = $this->receivedQtyCollection()->map(function(StockMovement $received) use($live){
    $line = $live->where('product_id', $received->product_id)->first();
    return arrayToObject(array_merge($received->toArray(), $line->toArray()));
});
return $merged;

The arrayToObject function, recursively converts the array back into an object so I can use the arrow selector in the same way as when it was its original collection, but I am sure there must be a better way!

I have tried:

My end goal is to be able to use the resulting collection as follows:

@foreach($stockMovements as $stockMovement)
    {{ $stockMovement->id }}
    {{ $stockMovement->live_qty }}
    {{ $stockMovement->received_qty }}
@endforeach

Upvotes: 6

Views: 12819

Answers (3)

Grant
Grant

Reputation: 6309

I know you mentioned in your question that you did not mean collections and also that you had already tried merging, but for the sake of discussion I thought I'd throw my answer in the mix, as this worked precisely for what I needed; merging the responses of two model calls.

$quote = \App\AllQuotes::withoutGlobalScopes()->find($quote_id);
$settings = \App\Settings::findOrFail(1);
$settings = collect($settings);
$quote = collect($quote);
$merged = $quote->merge($settings);

return $merged;

You have to first encapsulate the model responses as collections with the collect helper, then you can merge them together. The output then treats it as a logical join, maintaining the data from both models in a singular output.

Upvotes: 1

Tim van Uum
Tim van Uum

Reputation: 1903

You could do something like this:

$live = $this->liveQtyCollection()->keyBy('product_id')
$merged = $this->receivedQtyCollection()->map(function(StockMovement $received) use($live){
    $received->live_qty = $live->get($received->product_id)->live_qty ?? null;
    return $received;
});

Upvotes: 0

Jafar Akhondali
Jafar Akhondali

Reputation: 1537

You can use this code:

$collection = collect();
$cars = Car::all();
$bikes = Bike::all();

foreach ($cars as $car)
    $collection->push($car);

foreach ($bikes as $bike)
    $collection->push($bike);

Source

Upvotes: 8

Related Questions