@@ -18,6 +18,20 @@ public function created(Order $order)
1818 $ this ->invalidateCache ($ order );
1919 }
2020
21+ /**
22+ * Handle the Order "updating" event.
23+ *
24+ * This event is fired before the order is persisted to the database.
25+ * It is used to mutate attributes as part of the same update operation
26+ * without triggering additional save cycles.
27+ *
28+ * @param Order $order The order being updated
29+ */
30+ public function updating (Order $ order ): void
31+ {
32+ $ this ->ensureOrderStarted ($ order );
33+ }
34+
2135 /**
2236 * Handle the Order "updated" event.
2337 *
@@ -62,4 +76,40 @@ protected function invalidateCache(?Order $order = null): void
6276 Cache::forget ("order: {$ order ->uuid }:tracker " );
6377 }
6478 }
79+
80+ /**
81+ * Detects when an order has just transitioned to the "started" status
82+ * and initializes start-related fields.
83+ *
84+ * This method should be called during the "updating" lifecycle event
85+ * to ensure that the changes are persisted as part of the same database
86+ * update and do not trigger additional observer events.
87+ *
88+ * An order is considered "started" when:
89+ * - The "status" attribute is being changed in the current update
90+ * - The previous status was not "started"
91+ * - The new status is "started"
92+ *
93+ * When these conditions are met, the order's start timestamp and
94+ * started flag are set if they have not already been initialized.
95+ *
96+ * @param Order $order The order being evaluated for a start transition
97+ */
98+ protected function ensureOrderStarted (Order $ order ): void
99+ {
100+ if (
101+ $ order ->isDirty ('status ' )
102+ && $ order ->getOriginal ('status ' ) === 'dispatched '
103+ && $ order ->status === 'started '
104+ ) {
105+ // Only set defaults if not explicitly provided
106+ if (is_null ($ order ->started_at )) {
107+ $ order ->started_at = now ();
108+ }
109+
110+ if (!$ order ->started ) {
111+ $ order ->started = true ;
112+ }
113+ }
114+ }
65115}
0 commit comments