Description
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.