From aaf3a6a3e03a041311b1194b32e2e76a38427d4b Mon Sep 17 00:00:00 2001 From: Ronald A Richardson Date: Fri, 27 Feb 2026 19:56:45 -0500 Subject: [PATCH 1/3] fix: Resolve vehicle from uuid, public_id, or object when creating/updating driver - Add ResolvableVehicle validation rule that accepts a vehicle public_id string, UUID string, or an array/object with id/public_id/uuid key - Update CreateDriverRequest to use ResolvableVehicle rule instead of the previous string-only validation which threw a TypeError when the frontend sent the full vehicle object - Normalize vehicle input in DriverController createRecord and updateRecord before validation so array payloads are reduced to a string identifier Fixes: TypeError: str_starts_with(): Argument #1 ($haystack) must be of type string, array given --- .../Internal/v1/DriverController.php | 24 ++++- .../src/Http/Requests/CreateDriverRequest.php | 3 +- server/src/Rules/ResolvableVehicle.php | 99 +++++++++++++++++++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 server/src/Rules/ResolvableVehicle.php diff --git a/server/src/Http/Controllers/Internal/v1/DriverController.php b/server/src/Http/Controllers/Internal/v1/DriverController.php index 1e966f61..a8c8ba2c 100644 --- a/server/src/Http/Controllers/Internal/v1/DriverController.php +++ b/server/src/Http/Controllers/Internal/v1/DriverController.php @@ -45,6 +45,17 @@ public function createRecord(Request $request) { $input = $request->input('driver'); + // Normalize vehicle field - the frontend may send the full vehicle object, + // a UUID string, or a public_id string. Normalize to a single identifier + // so the ResolvableVehicle rule can validate it correctly. + if (isset($input['vehicle']) && is_array($input['vehicle'])) { + $input['vehicle'] = data_get($input['vehicle'], 'id') + ?? data_get($input['vehicle'], 'public_id') + ?? data_get($input['vehicle'], 'uuid') + ?? null; + $request->merge(['driver' => $input]); + } + // create validation request $createDriverRequest = CreateDriverRequest::createFrom($request); $rules = $createDriverRequest->rules(); @@ -262,8 +273,19 @@ public function updateRecord(Request $request, string $id) // get input data $input = $request->input('driver'); + // Normalize vehicle field - the frontend may send the full vehicle object, + // a UUID string, or a public_id string. Normalize to a single identifier + // so the ResolvableVehicle rule can validate it correctly. + if (isset($input['vehicle']) && is_array($input['vehicle'])) { + $input['vehicle'] = data_get($input['vehicle'], 'id') + ?? data_get($input['vehicle'], 'public_id') + ?? data_get($input['vehicle'], 'uuid') + ?? null; + $request->merge(['driver' => $input]); + } + // create validation request - $updateDriverRequest = UpdateDriverRequest::createFrom($request); + $updateDriverRequest = UpdateDriverRequest::createFrom($request);; $rules = $updateDriverRequest->rules(); // manually validate request diff --git a/server/src/Http/Requests/CreateDriverRequest.php b/server/src/Http/Requests/CreateDriverRequest.php index 774982c8..9548d7b6 100644 --- a/server/src/Http/Requests/CreateDriverRequest.php +++ b/server/src/Http/Requests/CreateDriverRequest.php @@ -3,6 +3,7 @@ namespace Fleetbase\FleetOps\Http\Requests; use Fleetbase\FleetOps\Rules\ResolvablePoint; +use Fleetbase\FleetOps\Rules\ResolvableVehicle; use Fleetbase\Http\Requests\FleetbaseRequest; use Illuminate\Validation\Rule; @@ -34,7 +35,7 @@ public function rules() 'password' => 'nullable|string', 'country' => 'nullable|size:2', 'city' => 'nullable|string', - 'vehicle' => 'nullable|string|starts_with:vehicle_|exists:vehicles,public_id', + 'vehicle' => ['nullable', new ResolvableVehicle()], 'status' => 'nullable|string|in:active,inactive', 'vendor' => 'nullable|exists:vendors,public_id', 'job' => 'nullable|exists:orders,public_id', diff --git a/server/src/Rules/ResolvableVehicle.php b/server/src/Rules/ResolvableVehicle.php new file mode 100644 index 00000000..3c4f66a4 --- /dev/null +++ b/server/src/Rules/ResolvableVehicle.php @@ -0,0 +1,99 @@ +extractIdentifier($value); + + if (empty($identifier)) { + return true; // nullable — let the nullable rule handle empty values + } + + if (Str::isUuid($identifier)) { + $this->resolved = Vehicle::where('uuid', $identifier)->first(); + } else { + $this->resolved = Vehicle::where('public_id', $identifier)->first(); + } + + return $this->resolved !== null; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The :attribute must be a valid vehicle public ID, UUID, or vehicle object.'; + } + + /** + * Extract a string identifier from the given value. + * + * Handles a plain string, an associative array, or a stdClass object. + * + * @param mixed $value + * + * @return string|null + */ + protected function extractIdentifier($value): ?string + { + if (is_string($value)) { + return $value; + } + + if (is_array($value)) { + return data_get($value, 'id') + ?? data_get($value, 'public_id') + ?? data_get($value, 'uuid') + ?? null; + } + + if (is_object($value)) { + return data_get($value, 'id') + ?? data_get($value, 'public_id') + ?? data_get($value, 'uuid') + ?? null; + } + + return null; + } + + /** + * Get the resolved Vehicle model instance after validation passes. + * + * @return Vehicle|null + */ + public function getResolved(): ?Vehicle + { + return $this->resolved; + } +} From 437e60584928dd72e7ae39bf311cf76dc4ca3883 Mon Sep 17 00:00:00 2001 From: Ronald A Richardson Date: Fri, 27 Feb 2026 20:03:12 -0500 Subject: [PATCH 2/3] fix: Apply ResolvableVehicle to Internal request only, revert API request The API CreateDriverRequest (consumable API) must always require a vehicle public_id string - this is intentional and correct. The Internal CreateDriverRequest is what the console frontend uses, and it sends the full vehicle object from Ember Data. Apply ResolvableVehicle rule only to the internal request so it accepts uuid, public_id string, or object. --- server/src/Http/Requests/CreateDriverRequest.php | 3 +-- server/src/Http/Requests/Internal/CreateDriverRequest.php | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/Http/Requests/CreateDriverRequest.php b/server/src/Http/Requests/CreateDriverRequest.php index 9548d7b6..774982c8 100644 --- a/server/src/Http/Requests/CreateDriverRequest.php +++ b/server/src/Http/Requests/CreateDriverRequest.php @@ -3,7 +3,6 @@ namespace Fleetbase\FleetOps\Http\Requests; use Fleetbase\FleetOps\Rules\ResolvablePoint; -use Fleetbase\FleetOps\Rules\ResolvableVehicle; use Fleetbase\Http\Requests\FleetbaseRequest; use Illuminate\Validation\Rule; @@ -35,7 +34,7 @@ public function rules() 'password' => 'nullable|string', 'country' => 'nullable|size:2', 'city' => 'nullable|string', - 'vehicle' => ['nullable', new ResolvableVehicle()], + 'vehicle' => 'nullable|string|starts_with:vehicle_|exists:vehicles,public_id', 'status' => 'nullable|string|in:active,inactive', 'vendor' => 'nullable|exists:vendors,public_id', 'job' => 'nullable|exists:orders,public_id', diff --git a/server/src/Http/Requests/Internal/CreateDriverRequest.php b/server/src/Http/Requests/Internal/CreateDriverRequest.php index 59deb2ad..8dac9771 100644 --- a/server/src/Http/Requests/Internal/CreateDriverRequest.php +++ b/server/src/Http/Requests/Internal/CreateDriverRequest.php @@ -4,6 +4,7 @@ use Fleetbase\FleetOps\Http\Requests\CreateDriverRequest as CreateDriverApiRequest; use Fleetbase\FleetOps\Rules\ResolvablePoint; +use Fleetbase\FleetOps\Rules\ResolvableVehicle; use Fleetbase\Support\Auth; use Illuminate\Validation\Rule; @@ -49,7 +50,7 @@ public function rules() 'internal_id' => 'nullable|string|max:255', 'country' => 'nullable|string|size:2', 'city' => 'nullable|string|max:255', - 'vehicle' => 'nullable|string|starts_with:vehicle_|exists:vehicles,public_id', + 'vehicle' => ['nullable', new ResolvableVehicle()], 'status' => 'nullable|string|in:active,inactive', 'vendor' => 'nullable|exists:vendors,public_id', 'job' => 'nullable|exists:orders,public_id', From b50d622dff31afaeaa85267706ef61a17d629e70 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Sat, 28 Feb 2026 09:43:04 +0800 Subject: [PATCH 3/3] bumped version to v0.6.37 --- composer.json | 2 +- extension.json | 2 +- package.json | 2 +- .../Http/Controllers/Internal/v1/DriverController.php | 2 +- server/src/Rules/ResolvableVehicle.php | 9 --------- 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 00e138af..be0effb4 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/fleetops-api", - "version": "0.6.36", + "version": "0.6.37", "description": "Fleet & Transport Management Extension for Fleetbase", "keywords": [ "fleetbase-extension", diff --git a/extension.json b/extension.json index 5136cfa8..6dad2d34 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "Fleet-Ops", - "version": "0.6.36", + "version": "0.6.37", "description": "Fleet & Transport Management Extension for Fleetbase", "repository": "https://github.com/fleetbase/fleetops", "license": "AGPL-3.0-or-later", diff --git a/package.json b/package.json index e0b4b294..c33e2082 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/fleetops-engine", - "version": "0.6.36", + "version": "0.6.37", "description": "Fleet & Transport Management Extension for Fleetbase", "fleetbase": { "route": "fleet-ops" diff --git a/server/src/Http/Controllers/Internal/v1/DriverController.php b/server/src/Http/Controllers/Internal/v1/DriverController.php index a8c8ba2c..93e6452b 100644 --- a/server/src/Http/Controllers/Internal/v1/DriverController.php +++ b/server/src/Http/Controllers/Internal/v1/DriverController.php @@ -285,7 +285,7 @@ public function updateRecord(Request $request, string $id) } // create validation request - $updateDriverRequest = UpdateDriverRequest::createFrom($request);; + $updateDriverRequest = UpdateDriverRequest::createFrom($request); $rules = $updateDriverRequest->rules(); // manually validate request diff --git a/server/src/Rules/ResolvableVehicle.php b/server/src/Rules/ResolvableVehicle.php index 3c4f66a4..42aea235 100644 --- a/server/src/Rules/ResolvableVehicle.php +++ b/server/src/Rules/ResolvableVehicle.php @@ -10,8 +10,6 @@ class ResolvableVehicle implements Rule { /** * The resolved vehicle instance, if found. - * - * @var Vehicle|null */ protected ?Vehicle $resolved = null; @@ -24,7 +22,6 @@ class ResolvableVehicle implements Rule * - An array/object containing an "id", "public_id", or "uuid" key * * @param string $attribute - * @param mixed $value * * @return bool */ @@ -59,10 +56,6 @@ public function message() * Extract a string identifier from the given value. * * Handles a plain string, an associative array, or a stdClass object. - * - * @param mixed $value - * - * @return string|null */ protected function extractIdentifier($value): ?string { @@ -89,8 +82,6 @@ protected function extractIdentifier($value): ?string /** * Get the resolved Vehicle model instance after validation passes. - * - * @return Vehicle|null */ public function getResolved(): ?Vehicle {