I'm using a creating Eloquent model observer to automatically generate a slug before inserting a new product into the database.
Here’s the code from my ProductObserver:
public function creating(Product $product): void
{
if (empty($product->slug)) {
$product->slug = ProductSlugManager::handle($product);
\Log::info('Observer:creating', $product->only(['id', 'slug']));
}
}
The slug is generated correctly — I can confirm it via the logs:
Observer:creating Product ID=null slug=some-generated-slug
But the problem is that the final INSERT query (captured via DB::listen()) does not include the slug field:
insert into "products" ("name", "description", "created_at", "updated_at") values (?, ?, ?, ?) returning "id"
So the slug is never saved to the database — it appears on the returned model, but not in the DB itself.
Notes: The slug does show up in the returned model after save, but it’s not in the actual SQL statement. Looks like Laravel builds the INSERT query before the observer executes? DB is PostgreSQL if it matters.
How can I ensure that the slug generated in a creating observer is actually included in the INSERT SQL query?
Is this behavior expected when using Octane or model observers? Should I move slug generation elsewhere (e.g., overriding performInsert() or using model boot())? What’s the recommended way to safely mutate attributes just before creation?