1

I have a collection based on a property of a that collection i would like to make two extra collections and push it to response collection.

For example

$user = collect([
            "ID" => 4944,
            "reason" => "Friend Referral",
            "created_at" => "2016-08-29 18:23:53",
            "updated_at" => "2016-08-29 18:23:53",
            "type" => "credit",
            "amount" => "100",
            "usageCount" => 1
        ]);

    $credits = collect([]);
    $credits->push($user);

    if ($user->get('usageCount')) {
        $user->put('type','debit');
        $credits->push($user);
    }

I get the credits collection as the following

[
{
"ID": 4944,
"reason": "Friend Referral",
"created_at": "2016-08-29 18:23:53",
"updated_at": "2016-08-29 18:23:53",
"type": "debit",
"amount": "100",
"usageCount": 1
},
{
"ID": 4944,
"reason": "Friend Referral",
"created_at": "2016-08-29 18:23:53",
"updated_at": "2016-08-29 18:23:53",
"type": "debit",
"amount": "100",
"usageCount": 1
}
]

The type should be credit and debit, but somehow i am pushing the debit type make the first array as type debit as well.

I don't understand.

f_i
  • 3,084
  • 28
  • 31
  • 2
    `$credits->push($user)` adds a *reference* to `$user` to the `$credits` collection. You need to [make a copy of `$user`](https://stackoverflow.com/a/27746958/5051310). Not sure the best way to make a copy with Laravel but that page has some useful discussion on the subject. –  Sep 13 '18 at 11:20

2 Answers2

3

This is because objects are passed by reference. So when you modify the original object that was pushed into the collection, it will modify the item in the collection. You can resolve this by using the clone feature in PHP. Here is an example.

$user = collect([
        "ID" => 4944,
        "reason" => "Friend Referral",
        "created_at" => "2016-08-29 18:23:53",
        "updated_at" => "2016-08-29 18:23:53",
        "type" => "credit",
        "amount" => "100",
        "usageCount" => 1
    ]);

$credits = collect([]);
$credits->push($user);

$user = clone $user;

if ($user->get('usageCount')) {
    $user->put('type','debit');
    $credits->push($user);
}
George Hanson
  • 2,940
  • 1
  • 6
  • 18
  • @OP Don't forget about doing something with the `ID` –  Sep 13 '18 at 11:26
  • 1
    The "ID" within the collection doesn't matter. It doesn't affect the behaviour experienced. – George Hanson Sep 13 '18 at 11:28
  • I meant question OP just btw. I know this answer technically answers the question. Just want to be sure question OP is keeping an eye out for behavior he might not expect. –  Sep 13 '18 at 11:45
  • Thanks this is helpful. I pushed $user->toArray() for now but this is good insight. – f_i Sep 13 '18 at 11:57
  • @Terminus you mean using the ID instead of id can bite me sometime in terms of sql? i am not sure what you meant there. – f_i Sep 13 '18 at 11:58
2

To quote the PHP documentation

One of the key-points of PHP 5 OOP that is often mentioned is that "objects are passed by references by default". This is not completely true. This section rectifies that general thought using some examples.

A PHP reference is an alias, which allows two different variables to write to the same value. 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.

In your example, the $user variable is a reference (or identifier) that points to the object you've created. So when you've added the $user object to you collection, it will then contain that reference too. So when you change your $user variable, it updates the very same object that both places refer to, which is why you're seeing what you're seeing.

You could do this and see similar behaviour:

$user = collect([
    "ID" => 4944,
    "reason" => "Friend Referral",
    "created_at" => "2016-08-29 18:23:53",
    "updated_at" => "2016-08-29 18:23:53",
    "type" => "credit",
    "amount" => "100",
    "usageCount" => 1
]);

// $second and $user now contain the same "reference" to your object
$second = $user;

// $second->type = 'debit' changes the object both $user and $second point to
$second->type = 'debit';

echo $user->type; // Outputs 'debit'

To get around your problem, you can simply make a copy of your user object which creates a new object and a new reference, and modify that one before passing it into your collection:

$second = clone $user;
if ($second->get('usageCount')) {
    $second->put('type','debit');
    $credits->push($second);
}
Community
  • 1
  • 1
Jonathon
  • 15,873
  • 11
  • 73
  • 92