I have adverts (eloquent collection) with eager loaded advert attributes (details) and their values:
$adverts = Advert::where('category_id', 1312)->with(['attributesValues.attribute'])->get();
$adverts->map(function (Advert $advert) {
$details = new IlluminateDatabaseEloquentCollection();
foreach ($advert->attributesValues as $attributeValue) {
$detail = $attributeValue->attribute;
$detail->value = $attributeValue->value;
$details->push($detail);
}
$advert->details = $details;
echo ('<br>' . $advert->title . '<br>');
foreach ($advert->details as $detail) {
echo ('- ' . $detail->name . ': ' . $detail->value . '<br>');
}
return $advert;
});
echo ('<br><hr>');
foreach ($adverts as $advert) {
echo ('<br>' . $advert->title . '<br>');
foreach ($advert->details as $detail) {
echo ('- ' . $detail->name . ': ' . $detail->value . '<br>');
}
}
die;
My Models are:
class Advert extends Model
{
// @property int $id
// @property string $title
public function attributesValues()
{
return $this->hasMany(AttributeValue::class, 'advert_id', 'id');
}
}
class Attribute extends Model
{
// @property int $id
// @property string $name
}
class AttributeValue extends Model
{
// @property int $advert_id
// @property int $attribute_id
// @property string $value
public function attribute()
{
return $this->belongsTo(Attribute::class, 'attribute_id', 'id');
}
}
Issue
If I try to dump advert and its details just before return inside the map(), I get correct data:
Product 1
- Model: Eva
- Size: L
- Color: blue
Product 2
- Model: Maya
- Size: S
- Color: green
Product 3
- Model: Royal
- Size: M
- Color: black
But if I dump after mapping, "details" attribute of all adverts become the same (details of the last advert), which is wrong:
Product 1
- Model: Royal
- Size: M
- Color: black
Product 2
- Model: Royal
- Size: M
- Color: black
Product 3
- Model: Royal
- Size: M
- Color: black
Questions
- Why it is going on?
- How to fix it?
Thanks in advance!
2
Answers
Not sure why you are getting those results and if you pasted the whole code, but for
map
instead of:you should use:
to assign result of map to variable
I was able to replicate your issue but haven’t figured out why it happens. The answer is almost certain to involve references.
While the
$details
object is different for each element of$adverts
, the elements of the$details
collection are the same for all 3. Not just the same values, but the same object (you can confirm this by usingdump()
and looking at object IDs.)The easy answer here is to use a sensible loop in your view, rather than mangling models with a bunch of dynamic properties.
If you insist on making things uneccessarily complicated, assign the return from
map()
as Marcin says in the other answer, and also convert$detail
to an array and back again before pushing it onto the collection.See this live example for a demo of both solutions working.