From d652c7aa80c6c94438b8d15320b634945cabee7c Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Tue, 15 Oct 2024 16:43:37 +0800 Subject: [PATCH] new utility methods, added georgian lari currency, add update exception handler in model behaviour trait --- composer.json | 2 +- src/Support/Utils.php | 70 +++++++++++++++++++++++++ src/Traits/HasApiControllerBehavior.php | 38 ++++++++++++-- src/Traits/HasApiModelBehavior.php | 9 +++- src/Types/Currency.php | 9 ++++ 5 files changed, 122 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 8edf86d..2154314 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/core-api", - "version": "1.5.13", + "version": "1.5.14", "description": "Core Framework and Resources for Fleetbase API", "keywords": [ "fleetbase", diff --git a/src/Support/Utils.php b/src/Support/Utils.php index 7ba4dec..57041ef 100644 --- a/src/Support/Utils.php +++ b/src/Support/Utils.php @@ -2492,4 +2492,74 @@ public static function clearCacheByPattern(string $pattern): void } } } + + /** + * Converts the given input into an array. + * + * This method handles different types of inputs: + * - **Array:** Returns the array as is. + * - **String:** Splits the string into an array using common delimiters (comma or pipe). + * - **Object:** Converts the object into an associative array. If the object is traversable, + * it uses the iterator; otherwise, it extracts public properties. + * + * @param array|string|object $target the input to convert to an array + * + * @return array the converted array + * + * @example + * // Example with an array: + * $result = arrayFrom([1, 2, 3]); + * // Result: [1, 2, 3] + * @example + * // Example with a string containing delimiters: + * $result = arrayFrom('apple, banana, cherry'); + * // Result: ['apple', 'banana', 'cherry'] + * @example + * // Example with a traversable object: + * $iterator = new ArrayIterator(['x' => 1, 'y' => 2]); + * $result = arrayFrom($iterator); + * // Result: ['x' => 1, 'y' => 2] + * @example + * // Example with a regular object: + * class Person { + * public $name = 'John'; + * public $age = 30; + * } + * $person = new Person(); + * $result = arrayFrom($person); + * // Result: ['name' => 'John', 'age' => 30] + */ + public static function arrayFrom(array|string|object $target): array + { + if (is_array($target)) { + // If the target is already an array, return it as is. + return $target; + } + + if (is_string($target)) { + // Define possible delimiters. + foreach ([',', '|'] as $delim) { + if (strpos($target, $delim) !== false) { + // Split the string by the delimiter and trim each element. + return array_map('trim', explode($delim, $target)); + } + } + + // If no delimiter is found, return the string as a single-element array. + return [$target]; + } + + if (is_object($target)) { + if ($target instanceof \Traversable) { + // If the object is traversable (like an iterator), convert it to an array. + return iterator_to_array($target); + } else { + // Get an associative array of the object's public properties. + return get_object_vars($target); + } + } + + // If $target is none of the above types, return it as a single-element array. + return [$target]; + } } diff --git a/src/Traits/HasApiControllerBehavior.php b/src/Traits/HasApiControllerBehavior.php index 2263f5b..78112d6 100644 --- a/src/Traits/HasApiControllerBehavior.php +++ b/src/Traits/HasApiControllerBehavior.php @@ -2,6 +2,7 @@ namespace Fleetbase\Traits; +use Closure; use Fleetbase\Exceptions\FleetbaseRequestValidationException; use Fleetbase\Http\Requests\Internal\BulkDeleteRequest; use Fleetbase\Support\Http; @@ -303,8 +304,9 @@ public function validateRequest(Request $request) */ public function queryRecord(Request $request) { - $single = $request->boolean('single'); - $data = $this->model->queryFromRequest($request); + $single = $request->boolean('single'); + $queryCallback = $this->getControllerCallback('onQueryRecord'); + $data = $this->model->queryFromRequest($request, $queryCallback); if ($single) { $data = Arr::first($data); @@ -391,8 +393,11 @@ public function findRecord(Request $request, $id) public function createRecord(Request $request) { try { + $onBeforeCallback = $this->getControllerCallback('onBeforeCreate'); + $onAfterCallback = $this->getControllerCallback('onAfterCreate'); + $this->validateRequest($request); - $record = $this->model->createRecordFromRequest($request); + $record = $this->model->createRecordFromRequest($request, $onBeforeCallback, $onAfterCallback); if (Http::isInternalRequest($request)) { $this->resource::wrap($this->resourceSingularlName); @@ -441,8 +446,11 @@ public function createRecord(Request $request) public function updateRecord(Request $request, string $id) { try { + $onBeforeCallback = $this->getControllerCallback('onBeforeUpdate'); + $onAfterCallback = $this->getControllerCallback('onAfterUpdate'); + $this->validateRequest($request); - $record = $this->model->updateRecordFromRequest($request, $id); + $record = $this->model->updateRecordFromRequest($request, $id, $onBeforeCallback, $onAfterCallback); if (Http::isInternalRequest($request)) { $this->resource::wrap($this->resourceSingularlName); @@ -611,4 +619,26 @@ public function count(Request $request) return response()->json(['count' => $results]); } + + /** + * Retrieves a Closure for a specified method of the controller if it exists. + * + * This method checks if a method with the given name exists in the current controller instance. + * If the method exists, it returns a Closure that, when invoked, will call the specified method + * with any provided arguments. This allows for dynamic method invocation while ensuring the method's existence. + * + * @param string $name the name of the controller method to retrieve as a Closure + * + * @return \Closure|null a Closure that calls the specified method, or null if the method does not exist + */ + private function getControllerCallback(string $name): ?\Closure + { + if (method_exists($this, $name)) { + return function (...$args) use ($name) { + return $this->{$name}(...$args); + }; + } + + return null; + } } diff --git a/src/Traits/HasApiModelBehavior.php b/src/Traits/HasApiModelBehavior.php index 4289085..7a19bd2 100644 --- a/src/Traits/HasApiModelBehavior.php +++ b/src/Traits/HasApiModelBehavior.php @@ -256,7 +256,14 @@ public function updateRecordFromRequest(Request $request, $id, ?callable $onBefo } } - $record->update($input); + // Remove ID's and timestamps from input + $input = Arr::except($input, ['uuid', 'public_id', 'deleted_at', 'updated_at', 'created_at']); + + try { + $record->update($input); + } catch (\Exception $e) { + throw new \Exception('Failed to update ' . $this->getApiHumanReadableName()); + } if (isset($options['return_object']) && $options['return_object'] === true) { return $record; diff --git a/src/Types/Currency.php b/src/Types/Currency.php index f51b39b..b496e20 100644 --- a/src/Types/Currency.php +++ b/src/Types/Currency.php @@ -353,6 +353,15 @@ class Currency implements \JsonSerializable 'decimalSeparator' => ',', 'symbolPlacement' => 'before', ], + 'GEL' => [ + 'code' => 'GEL', + 'title' => 'Georgian lari', + 'symbol' => '₾', + 'precision' => 2, + 'thousandSeparator' => ',', + 'decimalSeparator' => '.', + 'symbolPlacement' => 'before', + ], 'GHC' => [ 'code' => 'GHC', 'title' => 'Ghana, Cedi',