Search code examples
phplaravellaravel-5array-pushlaravel-collection

Laravel collection issue


I have to get the quantity of supplies for each supply by a certain supplier. I know that sounds weird but let me explain:

I have a Supplier model which has many supplies and each supply has many stocks which have a quantity. So now I'm building a view in which I want to represent each Supply with their quantity on a certain supplier.

So that's what I came up with in my controller:

foreach ($suppliers as $supplier) {

        foreach($supplier->supplies as $supply){

            if(!$comp_supplies->contains('name', $supply->name)){

                $comp_supplies->push(['name'=>$supply->name, 'supplier'=>[['name'=> $supplier->name, 'quantity' => $supply->stocks->first()->quantity]]]);

            }elseif($comp_supplies->contains('name', $supply->name)){

                $array = (['name'=> $supplier->name, 'quantity' => $supply->stocks->first()->quantity]);
                $array2 = $comp_supplies->where('name', $supply->name)->first()['supplier'];

                array_push($array2, $array);

                //dd($array2);
                $comp_supplies->where('name', $supply->name)->first()['supplier'] = $array2;
                dd($comp_supplies->where('name', $supply->name)->first()['supplier']);
            }
        }
        
    }

So I'm iterating over my suppliers and iterate again over the supplies from each of them. Now I want to fill the collection I want as a result.

If this collection not contains a supply with the name of "$supply->name", I push an array with the supply-name and create an array "suppliers" in which I also set the first entry with the current supplier information.

Now we are getting close to my problem.

If comp_supply already contains a supply with the current supply-name, if have to push the new supplier into the already existing "supplier" array we created in the first "if".

Therefore I created $array, which holds the new supplier-informations, and $array2, which holds the supplier-array ($comp_supplies->where('name', $supply->name)->first()['supplier']) we already made.

Now, if I push $array onto $array2 and dd(array2) everything works as i want it to. But if I now set

$comp_supplies->where('name', $supply->name)->first()['supplier'] = $array2 

and then

dd($comp_supplies->where('name', $supply->name)->first()['supplier']);

it didn't change.

I'm stuck on this Problem for nearly 2h and getting really frustrated.

Please if anyone has an idea what I could do to solve this, or knows where I can look next, let me know.

Here are also the migrations:

Supplier:

public function up()
{
    Schema::create('suppliers', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name')->unique();
        $table->unsignedInteger('coordinates_id')->index()->nullable();
        $table->timestamps();
    });
}

Supplies:

public function up()
{
    Schema::create('supplies', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->unsignedInteger('supplier_id');
        $table->foreign('supplier_id')
              ->references('id')
              ->on('suppliers')
              ->onDelete('cascade');
        $table->timestamps();
    });
}

Stocks:

    public function up()
{
    Schema::create('stocks', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('supplies_id');
        $table->foreign('supplies_id')
              ->references('id')
              ->on('supplies')
              ->onDelete('cascade');
        $table->integer('quantity');
        $table->timestamps();
    });
}

Solution

  • Your problem is related to how PHP deals with arrays. Arrays are passed by value, not by reference, so when you call $comp_supplies->where('name', $supply->name)->first(), you actually get a copy of the array, and therefore modifying the value at the index 'supplier' modifies only the value of the copy, not the value of the original array.

    You can verify this behavior with this example code:

    class TestList
    {
        protected $values;
    
        public function push($value)
        {
            $this->values[] = $value;
        }
    
        public function get($index)
        {
            return $this->values[$index];
        }
    }
    
    $list = new TestList();
    $list->push(['test' => 1]);
    
    var_dump($list->get(0)['test']); // int(1)
    
    $list->get(0)['test'] = 2;
    var_dump($list->get(0)['test']); // still int(1)... 
    

    You can overcome this issue by using objects instead of arrays, cause objects are passed by reference (notice the (object) cast):

    $list = new TestList();
    $list->push((object)['test' => 1]);
    
    var_dump($list->get(0)->test); // int(1)
    
    $list->get(0)->test = 2;
    var_dump($list->get(0)->test); // int(2)!