Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making copies of Laravel collections

Tags:

php

laravel

I'm trying to give a copy of a collection of users to an eloquent model jobs. So I'd effectively have:

jobs : [
    1 : {
        users : {
            1: {}
            2: {}
            3: {}
        }
    }
    2 : {
        users : {
            1: {}
            2: {}
            3: {}
        }
    }
]

Once I get this, I'm going to sum some numbers from another query to essentially give myself a total for each user on each job, so the above may end up looking like this:

jobs : [
    1 : {
        users : {
            1: {
                total: 120
            }
            2: {
                total: 45
            }
            3: {
                total: 12
            }
        }
    }
    2 : {
        users : {
            1: {
                total: 32
            }
            2: {
                total: 4
            }
            3: {
                total: 17
            }
        }
    }
]

Except I can't seem to clone this users list, and I'm effectively ending up with all the totals being the same as each other:

{  
   1:{  
      id:1,
      users:{  
         1:{  
            id:1,
            total:807
         },
         2:{  
            id:2,
            total:9
         }
      }
   },
   2:{  
      id:2,
      users:{  
         1:{  
            id:1,
            total:807
         },
         2:{  
            id:2,
            total:9
         }
      }
   },
   3:{  
      id:3,
      users:{  
         1:{  
            id:1,
            total:807
         },
         2:{  
            id:2,
            total:9
         }
      }
   }
}

Here is what I am doing:

public function summary()
{
    $jobs = Job::all()->keyBy('id');
    $users = User::with('closed_time_chunks')->get();

    $users_list = User::all(['id'])->keyBy('id');

    // I think this is the problem statement:
    foreach ($jobs as $job):
        $job->users = clone $users_list;
    endforeach;

    Log::info('Starting');

    
    foreach ($users as $user):
        foreach ($user->closed_time_chunks as $chunk):

            Log::info('Adding ' . ($chunk->stop_time - $chunk->start_time) . ' to job: ' . $chunk->job_id);
            $jobs[$chunk->job_id]->users[$chunk->user_id]['total'] += $chunk->stop_time - $chunk->start_time;

        endforeach;
    endforeach;
}

My guess is that I am actually just creating a reference to the same thing and any addition is in fact just adding to the 'master' collection. How can I successfully clone the users so that the totals will be unique across jobs?

Edit

Using an array (as Matheos recommends) results in a really bizarre error:

ErrorException (E_NOTICE)

Indirect modification of overloaded property Job::$users has no effect

like image 420
Djave Avatar asked Apr 20 '15 07:04

Djave


1 Answers

Your problem is that you are cloning your $users_list, but that is a Collection of User objects. In PHP, when you clone an object, any of it's properties that are references to objects remain references to those objects, in other words those child objects do not get cloned themselves. See __clone

Being that your code is dynamically adding a 'total' property to every User instance in the Collection, it is effectively altering that total value of all instances of that particular User, because they are all references to themselves. What you would need to do is clone every child member (User) of your Collection, along with the Collection itself.

foreach ($jobs as $job):
    $job->users = clone $users_list;
    $job->users->transform(function($user) { return clone $user; });
endforeach;

There are probably better ways to do what you're trying to do, but this should get you going and hopefully answer your question of why too.

like image 52
Jeff Berry Avatar answered Nov 13 '22 19:11

Jeff Berry