@fleetbase/fleetops-engine 0.6.16 → 0.6.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +1 -1
- package/server/migrations/2025_08_11_170800_add_company_index_to_routes_table.php +21 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +31 -8
- package/server/src/Models/Entity.php +25 -50
- package/server/src/Models/Waypoint.php +2 -0
- package/server/src/Traits/HasTrackingNumber.php +64 -10
- package/server/src/Traits/PayloadAccessors.php +126 -0
package/composer.json
CHANGED
package/extension.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration {
|
|
8
|
+
public function up(): void
|
|
9
|
+
{
|
|
10
|
+
Schema::table('routes', function (Blueprint $table) {
|
|
11
|
+
$table->index(['company_uuid', 'id'], 'idx_routes_company_id');
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public function down(): void
|
|
16
|
+
{
|
|
17
|
+
Schema::table('routes', function (Blueprint $table) {
|
|
18
|
+
$table->dropIndex('idx_routes_company_id');
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -30,6 +30,7 @@ use Fleetbase\Models\Company;
|
|
|
30
30
|
use Fleetbase\Models\File;
|
|
31
31
|
use Fleetbase\Models\Setting;
|
|
32
32
|
use Fleetbase\Support\Auth;
|
|
33
|
+
use Fleetbase\Support\TemplateString;
|
|
33
34
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
34
35
|
use Illuminate\Http\Request;
|
|
35
36
|
use Illuminate\Http\UploadedFile;
|
|
@@ -461,6 +462,14 @@ class OrderController extends Controller
|
|
|
461
462
|
]);
|
|
462
463
|
}
|
|
463
464
|
|
|
465
|
+
// vehicle assignment
|
|
466
|
+
if ($request->has('vehicle')) {
|
|
467
|
+
$input['vehicle_assigned_uuid'] = Utils::getUuid('vehicles', [
|
|
468
|
+
'public_id' => $request->input('vehicle'),
|
|
469
|
+
'company_uuid' => session('company'),
|
|
470
|
+
]);
|
|
471
|
+
}
|
|
472
|
+
|
|
464
473
|
// facilitator assignment
|
|
465
474
|
if ($request->has('facilitator')) {
|
|
466
475
|
$facilitator = Utils::getUuid(
|
|
@@ -1091,6 +1100,10 @@ class OrderController extends Controller
|
|
|
1091
1100
|
$activity->set('pod_method', $order->pod_method);
|
|
1092
1101
|
}
|
|
1093
1102
|
|
|
1103
|
+
// resolved status and details
|
|
1104
|
+
$activity->set('_resolved_status', TemplateString::resolve($activity->get('status', ''), $order));
|
|
1105
|
+
$activity->set('_resolved_details', TemplateString::resolve($activity->get('details', ''), $order));
|
|
1106
|
+
|
|
1094
1107
|
return $activity;
|
|
1095
1108
|
});
|
|
1096
1109
|
|
|
@@ -32,6 +32,7 @@ use Fleetbase\Http\Requests\Internal\BulkDeleteRequest;
|
|
|
32
32
|
use Fleetbase\Models\CustomFieldValue;
|
|
33
33
|
use Fleetbase\Models\File;
|
|
34
34
|
use Fleetbase\Models\Type;
|
|
35
|
+
use Fleetbase\Support\TemplateString;
|
|
35
36
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
36
37
|
use Illuminate\Http\Request;
|
|
37
38
|
use Illuminate\Support\Facades\DB;
|
|
@@ -722,20 +723,42 @@ class OrderController extends FleetOpsController
|
|
|
722
723
|
*
|
|
723
724
|
* @return \Illuminate\Http\Response
|
|
724
725
|
*/
|
|
725
|
-
public function nextActivity(string $id)
|
|
726
|
+
public function nextActivity(string $id, Request $request)
|
|
726
727
|
{
|
|
727
|
-
$
|
|
728
|
-
->where('uuid', $id)
|
|
729
|
-
->orWhere('public_id', $id)
|
|
730
|
-
->first();
|
|
728
|
+
$waypointId = $request->input('waypoint');
|
|
731
729
|
|
|
732
|
-
|
|
730
|
+
try {
|
|
731
|
+
$order = Order::findRecordOrFail($id, ['payload']);
|
|
732
|
+
} catch (ModelNotFoundException $exception) {
|
|
733
733
|
return response()->error('No order found.');
|
|
734
734
|
}
|
|
735
735
|
|
|
736
|
-
|
|
736
|
+
// Get waypoint record if available
|
|
737
|
+
$waypoint = null;
|
|
738
|
+
if ($waypointId) {
|
|
739
|
+
$waypoint = Waypoint::where('payload_uuid', $order->payload_uuid)->whereHas('place', function ($query) use ($waypointId) {
|
|
740
|
+
$query->where('public_id', $waypointId);
|
|
741
|
+
})->first();
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
$activities = $order->config()->nextActivity($waypoint);
|
|
745
|
+
|
|
746
|
+
// If activity is to complete order add proof of delivery properties if required
|
|
747
|
+
// This is a temporary fix until activity is updated to handle POD on it's own
|
|
748
|
+
$activities = $activities->map(function ($activity) use ($order) {
|
|
749
|
+
if ($activity->completesOrder() && $order->pod_required) {
|
|
750
|
+
$activity->set('require_pod', true);
|
|
751
|
+
$activity->set('pod_method', $order->pod_method);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// resolved status and details
|
|
755
|
+
$activity->set('_resolved_status', TemplateString::resolve($activity->get('status', ''), $order));
|
|
756
|
+
$activity->set('_resolved_details', TemplateString::resolve($activity->get('details', ''), $order));
|
|
757
|
+
|
|
758
|
+
return $activity;
|
|
759
|
+
});
|
|
737
760
|
|
|
738
|
-
return response()->json($
|
|
761
|
+
return response()->json($activities);
|
|
739
762
|
}
|
|
740
763
|
|
|
741
764
|
/**
|
|
@@ -7,6 +7,7 @@ use Fleetbase\Casts\Json;
|
|
|
7
7
|
use Fleetbase\Casts\PolymorphicType;
|
|
8
8
|
use Fleetbase\FleetOps\Support\Utils;
|
|
9
9
|
use Fleetbase\FleetOps\Traits\HasTrackingNumber;
|
|
10
|
+
use Fleetbase\FleetOps\Traits\PayloadAccessors;
|
|
10
11
|
use Fleetbase\Models\Model;
|
|
11
12
|
use Fleetbase\Traits\HasApiModelBehavior;
|
|
12
13
|
use Fleetbase\Traits\HasInternalId;
|
|
@@ -15,6 +16,9 @@ use Fleetbase\Traits\HasPublicId;
|
|
|
15
16
|
use Fleetbase\Traits\HasUuid;
|
|
16
17
|
use Fleetbase\Traits\SendsWebhooks;
|
|
17
18
|
use Fleetbase\Traits\TracksApiCredential;
|
|
19
|
+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
20
|
+
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
21
|
+
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|
18
22
|
use Illuminate\Support\Carbon;
|
|
19
23
|
use Illuminate\Support\Str;
|
|
20
24
|
use Milon\Barcode\Facades\DNS2DFacade as DNS2D;
|
|
@@ -31,6 +35,7 @@ class Entity extends Model
|
|
|
31
35
|
use HasTrackingNumber;
|
|
32
36
|
use HasApiModelBehavior;
|
|
33
37
|
use HasMetaAttributes;
|
|
38
|
+
use PayloadAccessors;
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
41
|
* The database table used by the model.
|
|
@@ -162,81 +167,81 @@ class Entity extends Model
|
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
/**
|
|
165
|
-
* @var
|
|
170
|
+
* @var BelongsTo
|
|
166
171
|
*/
|
|
167
|
-
public function photo()
|
|
172
|
+
public function photo(): BelongsTo
|
|
168
173
|
{
|
|
169
174
|
return $this->belongsTo(\Fleetbase\Models\File::class);
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
/**
|
|
173
|
-
* @var
|
|
178
|
+
* @var HasMany
|
|
174
179
|
*/
|
|
175
|
-
public function files()
|
|
180
|
+
public function files(): HasMany
|
|
176
181
|
{
|
|
177
182
|
return $this->hasMany(\Fleetbase\Models\File::class);
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
/**
|
|
181
|
-
* @var
|
|
186
|
+
* @var HasMany
|
|
182
187
|
*/
|
|
183
|
-
public function proofs()
|
|
188
|
+
public function proofs(): HasMany
|
|
184
189
|
{
|
|
185
190
|
return $this->hasMany(Proof::class, 'subject_uuid');
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
/**
|
|
189
|
-
* @var
|
|
194
|
+
* @var BelongsTo
|
|
190
195
|
*/
|
|
191
|
-
public function destination()
|
|
196
|
+
public function destination(): BelongsTo
|
|
192
197
|
{
|
|
193
198
|
return $this->belongsTo(Place::class);
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
/**
|
|
197
|
-
* @var
|
|
202
|
+
* @var BelongsTo
|
|
198
203
|
*/
|
|
199
|
-
public function payload()
|
|
204
|
+
public function payload(): BelongsTo
|
|
200
205
|
{
|
|
201
206
|
return $this->belongsTo(Payload::class)->without(['entities']);
|
|
202
207
|
}
|
|
203
208
|
|
|
204
209
|
/**
|
|
205
|
-
* @var
|
|
210
|
+
* @var BelongsTo
|
|
206
211
|
*/
|
|
207
|
-
public function supplier()
|
|
212
|
+
public function supplier(): BelongsTo
|
|
208
213
|
{
|
|
209
214
|
return $this->belongsTo(Vendor::class, 'supplier_uuid', 'uuid');
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
/**
|
|
213
|
-
* @var
|
|
218
|
+
* @var BelongsTo
|
|
214
219
|
*/
|
|
215
|
-
public function driver()
|
|
220
|
+
public function driver(): BelongsTo
|
|
216
221
|
{
|
|
217
222
|
return $this->belongsTo(Driver::class, 'driver_assigned_uuid');
|
|
218
223
|
}
|
|
219
224
|
|
|
220
225
|
/**
|
|
221
|
-
* @var
|
|
226
|
+
* @var BelongsTo
|
|
222
227
|
*/
|
|
223
|
-
public function company()
|
|
228
|
+
public function company(): BelongsTo
|
|
224
229
|
{
|
|
225
230
|
return $this->belongsTo(\Fleetbase\Models\Company::class);
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
/**
|
|
229
|
-
* @var
|
|
234
|
+
* @var BelongsTo
|
|
230
235
|
*/
|
|
231
|
-
public function trackingNumber()
|
|
236
|
+
public function trackingNumber(): BelongsTo
|
|
232
237
|
{
|
|
233
238
|
return $this->belongsTo(TrackingNumber::class);
|
|
234
239
|
}
|
|
235
240
|
|
|
236
241
|
/**
|
|
237
|
-
* @var
|
|
242
|
+
* @var MorphTo
|
|
238
243
|
*/
|
|
239
|
-
public function customer()
|
|
244
|
+
public function customer(): MorphTo
|
|
240
245
|
{
|
|
241
246
|
return $this->morphTo(__FUNCTION__, 'customer_type', 'customer_uuid')->withoutGlobalScopes();
|
|
242
247
|
}
|
|
@@ -475,34 +480,4 @@ class Entity extends Model
|
|
|
475
480
|
$this->customer_uuid = $model->uuid;
|
|
476
481
|
$this->customer_type = Utils::getMutationType($model);
|
|
477
482
|
}
|
|
478
|
-
|
|
479
|
-
public function getPayload(): ?Payload
|
|
480
|
-
{
|
|
481
|
-
$this->load('payload');
|
|
482
|
-
|
|
483
|
-
if ($this->payload instanceof Payload) {
|
|
484
|
-
return $this->payload;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
if (Str::isUuid($this->payload_uuid)) {
|
|
488
|
-
return Payload::where('uuid', $this->payload_uuid)->first();
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return null;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
public function getTrashedPayload(): ?Payload
|
|
495
|
-
{
|
|
496
|
-
$payload = $this->payload()->withoutGlobalScopes()->first();
|
|
497
|
-
|
|
498
|
-
if ($payload instanceof Payload) {
|
|
499
|
-
return $payload;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (Str::isUuid($this->payload_uuid)) {
|
|
503
|
-
return Payload::where('uuid', $this->payload_uuid)->withoutGlobalScopes()->first();
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return null;
|
|
507
|
-
}
|
|
508
483
|
}
|
|
@@ -6,6 +6,7 @@ use Barryvdh\DomPDF\Facade\Pdf;
|
|
|
6
6
|
use Fleetbase\Casts\PolymorphicType;
|
|
7
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
8
8
|
use Fleetbase\FleetOps\Traits\HasTrackingNumber;
|
|
9
|
+
use Fleetbase\FleetOps\Traits\PayloadAccessors;
|
|
9
10
|
use Fleetbase\Models\Model;
|
|
10
11
|
use Fleetbase\Traits\HasPublicId;
|
|
11
12
|
use Fleetbase\Traits\HasUuid;
|
|
@@ -21,6 +22,7 @@ class Waypoint extends Model
|
|
|
21
22
|
use HasPublicId;
|
|
22
23
|
use TracksApiCredential;
|
|
23
24
|
use HasTrackingNumber;
|
|
25
|
+
use PayloadAccessors;
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* The database table used by the model.
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Traits;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
6
7
|
use Fleetbase\FleetOps\Models\Proof;
|
|
7
8
|
use Fleetbase\FleetOps\Models\TrackingNumber;
|
|
8
9
|
use Fleetbase\FleetOps\Models\TrackingStatus;
|
|
9
10
|
use Fleetbase\FleetOps\Support\Utils;
|
|
10
11
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
12
|
+
use Fleetbase\Support\TemplateString;
|
|
11
13
|
use Illuminate\Database\Eloquent\Model;
|
|
12
14
|
use Illuminate\Support\Facades\DB;
|
|
15
|
+
use Illuminate\Support\Facades\Log;
|
|
13
16
|
|
|
14
17
|
trait HasTrackingNumber
|
|
15
18
|
{
|
|
@@ -55,11 +58,11 @@ trait HasTrackingNumber
|
|
|
55
58
|
*/
|
|
56
59
|
public function createActivity(Activity $activity, $location = [], $proof = null): TrackingStatus
|
|
57
60
|
{
|
|
58
|
-
$status
|
|
59
|
-
$details
|
|
60
|
-
$code
|
|
61
|
-
$proof
|
|
62
|
-
$activity
|
|
61
|
+
$status = $this->resolveActivityTemplateString($activity->get('status', ''));
|
|
62
|
+
$details = $this->resolveActivityTemplateString($activity->get('details', ''));
|
|
63
|
+
$code = $activity->get('code');
|
|
64
|
+
$proof = static::resolveProof($proof);
|
|
65
|
+
$activity = TrackingStatus::create([
|
|
63
66
|
'company_uuid' => data_get($this, 'company_uuid', session('company')),
|
|
64
67
|
'tracking_number_uuid' => $this->tracking_number_uuid,
|
|
65
68
|
'proof_uuid' => data_get($proof, 'uuid'),
|
|
@@ -89,11 +92,11 @@ trait HasTrackingNumber
|
|
|
89
92
|
*/
|
|
90
93
|
public function insertActivity(Activity $activity, $location = [], $proof = null): string
|
|
91
94
|
{
|
|
92
|
-
$status
|
|
93
|
-
$details
|
|
94
|
-
$code
|
|
95
|
-
$proof
|
|
96
|
-
$activityId
|
|
95
|
+
$status = $this->resolveActivityTemplateString($activity->get('status', ''));
|
|
96
|
+
$details = $this->resolveActivityTemplateString($activity->get('details', ''));
|
|
97
|
+
$code = $activity->get('code');
|
|
98
|
+
$proof = static::resolveProof($proof);
|
|
99
|
+
$activityId = TrackingStatus::insertGetUuid([
|
|
97
100
|
'company_uuid' => data_get($this, 'company_uuid', session('company')),
|
|
98
101
|
'tracking_number_uuid' => $this->tracking_number_uuid,
|
|
99
102
|
'proof_uuid' => data_get($proof, 'uuid'),
|
|
@@ -184,4 +187,55 @@ trait HasTrackingNumber
|
|
|
184
187
|
|
|
185
188
|
return null;
|
|
186
189
|
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Resolve {placeholders} inside an activity template string.
|
|
193
|
+
*
|
|
194
|
+
* Behavior:
|
|
195
|
+
* - If called on an Order, resolves placeholders against that Order.
|
|
196
|
+
* - Otherwise, if the model exposes getOrder(), resolves against the returned Order.
|
|
197
|
+
* - If no suitable target model can be determined, returns the original template unchanged.
|
|
198
|
+
* - Uses App\Support\TemplateString::resolve() to process placeholders and modifiers
|
|
199
|
+
* (e.g., {waypoint.type}, {capitalize waypoint.type}, {order.number | snake | uppercase}).
|
|
200
|
+
*
|
|
201
|
+
* Robustness:
|
|
202
|
+
* - Fast path: if there are no braces, returns early.
|
|
203
|
+
* - Catches unexpected resolver failures and logs a warning rather than throwing inside UI flows.
|
|
204
|
+
*
|
|
205
|
+
* @param string $template the template containing {placeholders}
|
|
206
|
+
*
|
|
207
|
+
* @return string the resolved template string, or the original on fallback
|
|
208
|
+
*/
|
|
209
|
+
private function resolveActivityTemplateString(string $template): string
|
|
210
|
+
{
|
|
211
|
+
// Fast path: no placeholders to resolve.
|
|
212
|
+
if ($template === '' || strpos($template, '{') === false) {
|
|
213
|
+
return $template;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Determine which model should resolve dynamic properties.
|
|
217
|
+
// Prefer $this when it's already an Order; otherwise try a getOrder() accessor.
|
|
218
|
+
$target = $this instanceof Order
|
|
219
|
+
? $this
|
|
220
|
+
: (method_exists($this, 'getOrder') ? $this->getOrder() : null);
|
|
221
|
+
|
|
222
|
+
// If we couldn't locate a suitable Eloquent model, leave template unchanged.
|
|
223
|
+
if (!$target instanceof Model) {
|
|
224
|
+
return $template;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
// Uses default resolver name 'resolveDynamicProperty' on the target model.
|
|
229
|
+
return TemplateString::resolve($template, $target, 'resolveDynamicProperty');
|
|
230
|
+
} catch (\Throwable $e) {
|
|
231
|
+
// Don't break user flows on template issues; log and return original.
|
|
232
|
+
Log::warning('Activity template resolution failed.', [
|
|
233
|
+
'template' => $template,
|
|
234
|
+
'target' => get_class($target),
|
|
235
|
+
'message' => $e->getMessage(),
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
return $template;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
187
241
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Traits;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Payload;
|
|
7
|
+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
8
|
+
use Illuminate\Support\Str;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* PayloadAccessors.
|
|
12
|
+
*
|
|
13
|
+
* Drop this trait into the model that has the `payload()` relationship and a `payload_uuid` FK.
|
|
14
|
+
* It provides:
|
|
15
|
+
* - getPayload(): returns the related Payload if present (with global scopes applied)
|
|
16
|
+
* - getTrashedPayload(): returns the related Payload without global scopes (e.g., soft-deleted)
|
|
17
|
+
* - getOrder(): returns the Order associated through Payload (or null)
|
|
18
|
+
*
|
|
19
|
+
* Design notes / best practices:
|
|
20
|
+
* - Avoids unconditional eager loading; checks relation cache first via relationLoaded().
|
|
21
|
+
* - Uses the relationship query as the primary source of truth, falling back to a direct lookup by UUID.
|
|
22
|
+
* - When resolving via direct lookup, caches the relation with setRelation() to prevent repeat queries.
|
|
23
|
+
* - Keeps the “ignore global scopes” behavior in a single private helper.
|
|
24
|
+
*
|
|
25
|
+
* Assumptions:
|
|
26
|
+
* - The host model defines `public function payload(): BelongsTo`.
|
|
27
|
+
* - `payload_uuid` stores a UUID, and `Payload`’s primary key is configured for UUIDs (so `find()` works).
|
|
28
|
+
*/
|
|
29
|
+
trait PayloadAccessors
|
|
30
|
+
{
|
|
31
|
+
/**
|
|
32
|
+
* Get the associated Payload with global scopes applied.
|
|
33
|
+
*
|
|
34
|
+
* @return \App\Models\Payload|null
|
|
35
|
+
*/
|
|
36
|
+
public function getPayload(): ?Payload
|
|
37
|
+
{
|
|
38
|
+
return $this->resolvePayload(ignoreGlobalScopes: false);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get the associated Payload without global scopes (e.g., include soft-deleted).
|
|
43
|
+
*
|
|
44
|
+
* @return \App\Models\Payload|null
|
|
45
|
+
*/
|
|
46
|
+
public function getTrashedPayload(): ?Payload
|
|
47
|
+
{
|
|
48
|
+
return $this->resolvePayload(ignoreGlobalScopes: true);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Convenience accessor: get the Order via the associated Payload.
|
|
53
|
+
*
|
|
54
|
+
* @return \App\Models\Order|null
|
|
55
|
+
*/
|
|
56
|
+
public function getOrder(): ?Order
|
|
57
|
+
{
|
|
58
|
+
// Leverage PHP nullsafe operator; no extra branching needed.
|
|
59
|
+
return $this->getPayload()?->order;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Core resolver for the Payload relation.
|
|
64
|
+
*
|
|
65
|
+
* Strategy:
|
|
66
|
+
* 1) If the relation is already loaded and we are NOT ignoring global scopes, return it.
|
|
67
|
+
* 2) Otherwise, query via the relationship, optionally disabling global scopes.
|
|
68
|
+
* 3) If still not found and payload_uuid looks like a UUID, do a direct lookup on Payload,
|
|
69
|
+
* optionally disabling global scopes. If found, cache back into the relation.
|
|
70
|
+
*
|
|
71
|
+
* @param bool $ignoreGlobalScopes when true, fetch via `withoutGlobalScopes()`
|
|
72
|
+
*
|
|
73
|
+
* @return \App\Models\Payload|null
|
|
74
|
+
*/
|
|
75
|
+
protected function resolvePayload(bool $ignoreGlobalScopes = false): ?Payload
|
|
76
|
+
{
|
|
77
|
+
// (1) If relation is already loaded and we accept scoped data, return it.
|
|
78
|
+
if (!$ignoreGlobalScopes && $this->relationLoaded('payload')) {
|
|
79
|
+
$related = $this->getRelation('payload');
|
|
80
|
+
if ($related instanceof Payload) {
|
|
81
|
+
return $related;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// (2) Query through the relationship (preferred, keeps FK semantics consistent).
|
|
86
|
+
$relation = $this->payload();
|
|
87
|
+
$query = $ignoreGlobalScopes ? $relation->withoutGlobalScopes() : $relation;
|
|
88
|
+
|
|
89
|
+
/** @var \App\Models\Payload|null $payload */
|
|
90
|
+
$payload = $query->first();
|
|
91
|
+
|
|
92
|
+
if ($payload instanceof Payload) {
|
|
93
|
+
// Cache the resolved relation for subsequent access on this model instance.
|
|
94
|
+
$this->setRelation('payload', $payload);
|
|
95
|
+
|
|
96
|
+
return $payload;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// (3) Fallback: direct lookup by UUID (useful if relation is not properly hydrated).
|
|
100
|
+
$uuid = $this->payload_uuid ?? null;
|
|
101
|
+
if ($uuid && Str::isUuid($uuid)) {
|
|
102
|
+
$payloadQuery = Payload::query();
|
|
103
|
+
if ($ignoreGlobalScopes) {
|
|
104
|
+
$payloadQuery->withoutGlobalScopes();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
$payload = $payloadQuery->find($uuid); // relies on Payload PK being 'uuid'
|
|
108
|
+
|
|
109
|
+
if ($payload instanceof Payload) {
|
|
110
|
+
// Keep the relation cache consistent for this instance.
|
|
111
|
+
$this->setRelation('payload', $payload);
|
|
112
|
+
|
|
113
|
+
return $payload;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Relationship stub (documentational).
|
|
122
|
+
* Ensure your model actually defines this in the host model—not strictly required here,
|
|
123
|
+
* but included as a guide for expected signature.
|
|
124
|
+
*/
|
|
125
|
+
abstract public function payload(): BelongsTo;
|
|
126
|
+
}
|