skip to Main Content

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

  1. Why it is going on?
  2. How to fix it?

Thanks in advance!

2

Answers


  1. Not sure why you are getting those results and if you pasted the whole code, but for map instead of:

    
    $adverts->map(function (Advert $advert) {
    

    you should use:

    
    $adverts = $adverts->map(function (Advert $advert) {
    

    to assign result of map to variable

    Login or Signup to reply.
  2. 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 using dump() 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.

    $adverts = Advert::where('category_id', 1312)->with(['attributesValues.attribute'])->get();
    
    @foreach ($adverts as $advert)
    <p style="color:green">
      {{ $advert->title }}<br/>
      @foreach ($advert->attributeValues as $val)
        - {{ $val->attribute->name }}: {{ $val->value }}<br/>
      @endforeach
    </p>
    @endforeach
    

    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.

    $detail = (object)$detail->toArray();
    

    See this live example for a demo of both solutions working.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search