I managed to find the reason. The problem is calling the query builder only from the same model class via a static method.
I have class MaterialCard with item morph. I do the following:
$card = MaterialCard::firstWhere('item_type', 'handbook');
$card->copy();
Inside the copy method I call the builder for model MaterialCard. When called in this way, the \Illuminate\Database\Eloquent\Builder::$model variable retains the model with item_type=handbook, which results in an incorrect query being generated.
Example of incorrect result with static call:
public function copy() {
MaterialCard::whereMorphedTo('item', Course::class)->dumpRawSql();
}
select * from `material_cards` where `handbook`.`item_type` = 'course' and `material_cards`.`archived_at` is null
Example of correct result with a call via the query method:
public function copy() {
MaterialCard::query()->whereMorphedTo('item', Course::class)->dumpRawSql();
}
select * from `material_cards` where `material_cards`.`item_type` = 'course' and `material_cards`.`archived_at` is null
Thus, calling via the MaterialCard::query() method forces the creation of a new instance of the MaterialCard class inside the query builder, while calling MaterialCard::whereMorphedTo uses the previously loaded model with data.