1

I'm trying to keep the original data in a temporary variable called $copyItems, but whenever I change the value of the original variable $items it also changes in the copied variable.

Take the following example:

$items = \App\Models\Customers::with('Account')->get();
$copyItems = $items; // Should hold all the original $items values no matter the changes

// Prints "Stackoverflow"
echo $copyItems[0]->Account->name;

$items[0]->Account->name = 'John';

// Prints "John" and should print "Stackoverflow"
echo $copyItems[0]->Account->name;

How d'hell is this happening? I'm not using variable references &$items why is my temporary variable being changed?

With the clone $items approach the result is the same.

Linesofcode
  • 5,327
  • 13
  • 62
  • 116
  • https://stackoverflow.com/questions/23895126/clone-an-eloquent-object-including-all-relationships This might help you out – Sandro Vardiashvili Sep 22 '20 at 13:33
  • @SandroVardiashvili `->replicate()` creates a new row in the database and I don't want it. – Linesofcode Sep 22 '20 at 13:37
  • you are not "copying" an object when you assign it to another variable ... both variable point to the same object ... and calling `clone` would only clone the Collection, not clone every object in it, so even the new Collection would contain the same exact objects as the other one – lagbox Sep 22 '20 at 13:41
  • in that case you could use Laravel collection for `$copyItems` and just do `$copyItems = collect($items);` – Sandro Vardiashvili Sep 22 '20 at 13:44
  • @SandroVardiashvili `collect($items)` works fine in my case, that way I don't need to do another Database Query. Set it as an answer and I'll accept it. – Linesofcode Sep 22 '20 at 13:51
  • @Linesofcode will do, thanks – Sandro Vardiashvili Sep 22 '20 at 15:38

3 Answers3

2

You are not copying an object when you assign it to another variable. $copyItems and $items are pointing to the same exact object.

Cloning won't help here, as clone will copy the Collection instance. Both of these collections would have items that actually point to the same exact objects, because clone is not cloning every object inside the collection, it is only cloning the Collection object itself.

Basically the variable isn't holding the object, it contains an identifier. This is why you hear/see people say that objects are always passed by reference, which is good enough for understanding but not completely true. The PHP manual goes into a little bit of an explanation about this.

"As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object."

PHP.net Manual - OOP - Objects and References

lagbox
  • 48,571
  • 8
  • 72
  • 83
0

No, it should print stackoverflow. If you look at it from another perspective.

$items = \App\Models\Customers::with('Account')->get();
$copyItems = $items;

The above means:

$items = \App\Models\Customers::with('Account')->get();
$copyItems = \App\Models\Customers::with('Account')->get();

Then it runs:

echo $copyItems[0]->Account->name;

So it prints out stackoverflow. Then it runs:

$items[0]->Account->name = 'John';

The above command basically means:

$copyItems[0]->Account->name = 'John';

Which is why your next command outputs John:

echo $copyItems[0]->Account->name;
  • 1
    No it should not be printing 'stackoverflow' for the second echo, the output they are seeing is correct ... objects are not copied when assigned ... both of those variable point to the same exact object in their example – lagbox Sep 22 '20 at 13:41
0

You could use Laravel Collections for $copyItems.

$copyItems = collect($items);

Sandro Vardiashvili
  • 189
  • 1
  • 4
  • 16