Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The AsCollection::class cast does not behave the same as "collection" cast when mutating the collection object. #49995

Closed
Waghabond opened this issue Feb 7, 2024 · 4 comments

Comments

@Waghabond
Copy link
Contributor

Waghabond commented Feb 7, 2024

Laravel Version

10.43.0

PHP Version

8.3.2

Database Driver & Version

Postgres 16

Description

TLDR: the "collection" cast is not able to handle mutations but AsCollection:class handles mutations correctly. These casts should both behave the same as how the AsCollection::class cast behaves.

Consider the following models.

// testAttr is a non-nullable json column with default = "[]"
class MyAsCollectionModel extends Model
{
    protected $casts = ["testAttr" => AsCollection::class];

    protected $attributes = ["testAttr" => "[]"];
}

class MyCollectionModel extends Model
{
    protected $casts = ["testAttr" => "collection"];

    protected $attributes = ["testAttr" => "[]"];
}

The following are examples of the behaviour of the casted attribute for the two models respectively (in the tinker interactive console).

$x = MyAsCollectionModel::first();
// App\Models\MyAsCollectionModel {#6219
//     id: 1,
//     testAttr: "[]",
//   }

$x->testAttr;
// Illuminate\Support\Collection {#6220
//     all: [],
// }

$x->testAttr[] = 'hi taylor';
// "hi taylor"

$x->testAttr;
// Illuminate\Support\Collection {#6220
//     all: [
//         "hi taylor",
//     ],
// }

$x;
// App\Models\MyAsCollectionModel {#6219
//     id: 1,
//     testAttr: "["hi taylor"]",
//   }

$x->testAttr = collect(["I'm a new collection"]);
// Illuminate\Support\Collection {#6221
//     all: [
//         "I'm a new collection",
//     ],
// }

$x->testAttr;
// Illuminate\Support\Collection {#6221
//     all: [
//         "I'm a new collection",
//     ],
// }

$x;
// App\Models\MyAsCollectionModel {#6219
//     id: 1,
//     testAttr: "["I'm a new collection"]",
//   }

vs

$x = MyCollectionModel::first();
// App\Models\MyCollectionModel {#6232
//     id: 1,
//     testAttr: "[]",
//   }

$x->testAttr;
// Illuminate\Support\Collection {#6233
//     all: [],
// }

$x->testAttr[] = 'hi taylor';
// "hi taylor"

$x->testAttr;
// Illuminate\Support\Collection {#6234
//     all: [],
// }

$x;
// App\Models\MyCollectionModel {#6232
//     id: 1,
//     testAttr: "[]",
//   }

$x->testAttr = collect(["I'm a new collection"]);
// Illuminate\Support\Collection {#6235
//     all: [
//         "I'm a new collection",
//     ],
// }

$x->testAttr;
// Illuminate\Support\Collection {#6236
//     all: [
//         "I'm a new collection",
//     ],
// }

$x;
// App\Models\MyCollectionModel {#6232
//     id: 1,
//     testAttr: "["I'm a new collection"]",
//   }

As we can see, appending items to the collection doesn't behave the same for these casts. This seems to be because the "collection" cast is instantiating a new collection every time by JSON decoding the model's data, where as AsCollection doesn't do the same and is able to handle mutations correctly.

I believe that both of these casts should be behaving how AsCollection behaves.

I can provide a minimum recreation of this issue if required and might also endeavour to try creating a PR providing a fix.

Steps To Reproduce

Create a model with a json column that is getting cast to "collection", and an identical model which is casting the column to AsCollection::class. These casts will not behave the same as demonstrated in the description.

Copy link

github-actions bot commented Feb 7, 2024

Thank you for reporting this issue!

As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub.

If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team.

Thank you!

@rojtjo
Copy link
Contributor

rojtjo commented Feb 7, 2024

I think this is the intended behavior. The AsArrayObject and AsCollection casts where added later to support mutating the array/collection directly.

See:
https://laravel.com/docs/10.x/eloquent-mutators#array-object-and-collection-casting
#36245

@Waghabond
Copy link
Contributor Author

Waghabond commented Feb 7, 2024

Ooh, that's interesting. And of course, this behaviour makes a lot of sense to me now given the history. I think that perhaps this nuance with the difference in mutability should be mentioned in the documentation. Should I open a PR against laravel/docs?

@crynobone
Copy link
Member

Feel free to submit PR to the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants