From 53b7750b05f4da909e234ed7136cc9e8f6e7d95a Mon Sep 17 00:00:00 2001 From: Antonio Zhu Date: Tue, 16 Sep 2025 20:50:48 +0200 Subject: [PATCH 1/4] Update TokenEventSubscriber.php grants guest rights to every registered token and also grants user + customer rights to every customer and admin token --- src/Listeners/TokenEventSubscriber.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Listeners/TokenEventSubscriber.php b/src/Listeners/TokenEventSubscriber.php index fdae194..88b7baa 100644 --- a/src/Listeners/TokenEventSubscriber.php +++ b/src/Listeners/TokenEventSubscriber.php @@ -49,12 +49,13 @@ public function subscribe($events): void protected function checkGroup(mixed $group, mixed $token): bool { if ($group == 'guest') { - return false; + return true; } return match ($group) { 'admin' => $token->isForAdmin(), 'customer' => $token->isForCustomer(), + 'users' => $token->isForAdmin() || $token->isForCustomer(), default => false, }; } From 74f0498117351277cfae282fcd6b29ed2ea20c8b Mon Sep 17 00:00:00 2001 From: Antonio Zhu Date: Sun, 12 Oct 2025 12:46:30 +0200 Subject: [PATCH 2/4] Added status users endpoints --- .../Repositories/StatusRepository.php | 13 +++++++++ .../Repositories/UserRepository.php | 14 ++++++++++ src/ApiResources/Status.php | 27 ++++++++++++++++++ src/ApiResources/Users.php | 28 +++++++++++++++++++ src/Extension.php | 22 +++++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 src/ApiResources/Repositories/StatusRepository.php create mode 100644 src/ApiResources/Repositories/UserRepository.php create mode 100644 src/ApiResources/Status.php create mode 100644 src/ApiResources/Users.php diff --git a/src/ApiResources/Repositories/StatusRepository.php b/src/ApiResources/Repositories/StatusRepository.php new file mode 100644 index 0000000..824556f --- /dev/null +++ b/src/ApiResources/Repositories/StatusRepository.php @@ -0,0 +1,13 @@ + [ + 'index' => [ + 'pageLimit' => 20, + ], + 'store' => [], + 'show' => [], + 'update' => [], + 'destroy' => [], + ], + 'request' => \Admin\Requests\Status::class, + 'repository' => Repositories\StatusRepository::class, + 'transformer' => Transformers\StatusTransformer::class, + ]; + + protected string|array $requiredAbilities = ['status:*']; +} \ No newline at end of file diff --git a/src/ApiResources/Users.php b/src/ApiResources/Users.php new file mode 100644 index 0000000..663d638 --- /dev/null +++ b/src/ApiResources/Users.php @@ -0,0 +1,28 @@ + [ + 'index' => [ + 'pageLimit' => 20, + ], + 'store' => [], + 'show' => [], + 'update' => [], + 'destroy' => [], + ], + 'request' => UserRequest::class, + 'repository' => Repositories\UserRepository::class, + 'transformer' => Transformers\UserTransformer::class, + ]; + + protected string|array $requiredAbilities = ['staff:*']; +} \ No newline at end of file diff --git a/src/Extension.php b/src/Extension.php index 4116674..89c1b7c 100755 --- a/src/Extension.php +++ b/src/Extension.php @@ -15,6 +15,8 @@ use Igniter\Api\ApiResources\Orders; use Igniter\Api\ApiResources\Reservations; use Igniter\Api\ApiResources\Reviews; +use Igniter\Api\ApiResources\Status; +use Igniter\Api\ApiResources\Users; use Igniter\Api\Classes\ApiManager; use Igniter\Api\Console\IssueApiToken; use Igniter\Api\Exceptions\ErrorHandler; @@ -211,6 +213,26 @@ public function registerApiResources(): array 'destroy:admin', ], ], + 'status' => [ + 'controller' => Status::class, + 'name' => 'Status', + 'description' => 'An API resource for status', + 'actions' => [ + 'index:admin', 'show:admin', + 'store:admin', 'update:admin', + 'destroy:admin', + ], + ], + 'users' => [ + 'controller' => Users::class, + 'name' => 'Staff', + 'description' => 'An API resource for staff', + 'actions' => [ + 'index:admin', 'show:admin', + 'store:admin', 'update:admin', + 'destroy:admin', + ], + ], ]; } From 090a714bada7dbdc9ce267d9c91996634e547847 Mon Sep 17 00:00:00 2001 From: Antonio Zhu Date: Wed, 15 Oct 2025 00:00:29 +0200 Subject: [PATCH 3/4] Added UserTransformer --- .../Transformers/UserRoleTransformer.php | 19 ++++++++++ .../Transformers/UserTransformer.php | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/ApiResources/Transformers/UserRoleTransformer.php diff --git a/src/ApiResources/Transformers/UserRoleTransformer.php b/src/ApiResources/Transformers/UserRoleTransformer.php new file mode 100644 index 0000000..034d545 --- /dev/null +++ b/src/ApiResources/Transformers/UserRoleTransformer.php @@ -0,0 +1,19 @@ +mergesIdAttribute($userRole); + } +} diff --git a/src/ApiResources/Transformers/UserTransformer.php b/src/ApiResources/Transformers/UserTransformer.php index 2dca312..0aef961 100644 --- a/src/ApiResources/Transformers/UserTransformer.php +++ b/src/ApiResources/Transformers/UserTransformer.php @@ -7,13 +7,49 @@ use Igniter\Api\Traits\MergesIdAttribute; use Igniter\User\Models\User; use League\Fractal\TransformerAbstract; +use League\Fractal\Resource\Collection; +use League\Fractal\Resource\Item; class UserTransformer extends TransformerAbstract { use MergesIdAttribute; + protected array $availableIncludes = [ + 'groups', + 'locations', + 'role', + 'language', + ]; + public function transform(User $user): array { return $this->mergesIdAttribute($user); } + + public function includeGroups(User $user): ?Collection + { + return $this->collection( + $user->groups, + new UserGroupTransformer, + 'groups' + ); + } + + public function includeLocations(User $user): ?Collection + { + return $this->collection( + $user->locations, + new LocationTransformer, + 'locations' + ); + } + + public function includeRole(User $user): ?Item + { + return $this->item( + $user->role, + new UserRoleTransformer, + 'role' + ); + } } From 4fb982f3e43e3cba9f081f54fe34bc21ce3d4802 Mon Sep 17 00:00:00 2001 From: Antonio Zhu Date: Tue, 30 Dec 2025 03:06:07 +0100 Subject: [PATCH 4/4] Implement plural nested relations and location->settings --- .../Transformers/LocationTransformer.php | 3 +- src/Classes/AbstractRepository.php | 72 +++++++++++++++++-- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/ApiResources/Transformers/LocationTransformer.php b/src/ApiResources/Transformers/LocationTransformer.php index 35e531d..b44a997 100644 --- a/src/ApiResources/Transformers/LocationTransformer.php +++ b/src/ApiResources/Transformers/LocationTransformer.php @@ -24,7 +24,8 @@ class LocationTransformer extends TransformerAbstract public function transform(Location $location): array { - return $this->mergesIdAttribute($location); + return $this->mergesIdAttribute($location, ['settings' => $location->settings]); + // return $this->mergesIdAttribute($location); } public function includeMedia(Location $location): ?Item diff --git a/src/Classes/AbstractRepository.php b/src/Classes/AbstractRepository.php index 3592713..e8bb56f 100644 --- a/src/Classes/AbstractRepository.php +++ b/src/Classes/AbstractRepository.php @@ -9,6 +9,7 @@ use Igniter\Flame\Database\Model; use Igniter\Flame\Exception\SystemException; use Igniter\Flame\Traits\EventEmitter; +use Igniter\Local\Models\Concerns\Locationable; use Igniter\User\Models\Customer; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model as IlluminateModel; @@ -233,6 +234,7 @@ protected function setModelAttributes($model, $saveData) $this->modelsToSave[] = $model; $singularTypes = ['belongsTo', 'hasOne', 'morphOne']; + $pluralTypes = ['hasMany', 'morphMany']; foreach ($saveData as $attribute => $value) { if ($attribute === $model->getKeyName() || !$model->isFillable($attribute)) { continue; @@ -242,14 +244,74 @@ protected function setModelAttributes($model, $saveData) continue; } - $isNested = ($attribute == 'pivot' || ( + $isSingularNested = ($attribute == 'pivot' || ( $model->hasRelation($attribute) && in_array($model->getRelationType($attribute), $singularTypes) )); - if ($isNested && is_array($value) && $model->{$attribute}) { + // Handle singular nested relations (belongsTo, hasOne, morphOne) + if ($isSingularNested && is_array($value) && $model->{$attribute}) { $this->setModelAttributes($model->{$attribute}, $value); - } elseif (!starts_with($attribute, '_')) { + continue; + } + + // Handle plural nested relations (hasMany, morphMany) + if ($model->hasRelation($attribute) && in_array($model->getRelationType($attribute), $pluralTypes) && is_array($value)) { + try { + $relation = $model->{$attribute}(); + $relatedModel = method_exists($relation, 'getModel') ? $relation->getModel() : (method_exists($relation, 'getRelated') ? $relation->getRelated() : null); + if ($relatedModel === null) { + continue; + } + + $relatedKey = $relatedModel->getKeyName(); + $fkName = $model->getForeignKey(); + + $existing = $relation->get()->keyBy($relatedKey)->all(); + + foreach ($value as $attrs) { + if (!is_array($attrs)) { + continue; + } + + if (!empty($attrs[$relatedKey]) && isset($existing[$attrs[$relatedKey]])) { + $rel = $existing[$attrs[$relatedKey]]; + $rel->fill($attrs); + } else { + $rel = new ($relatedModel::class)($attrs); + } + + // Queue wrapper that will set parent's FK and save related model after parent saved + $this->modelsToSave[] = new class($rel, $model, $fkName) { + protected $rel; + protected $parent; + protected $fk; + + public function __construct($rel, $parent, $fk) + { + $this->rel = $rel; + $this->parent = $parent; + $this->fk = $fk; + } + + public function save() + { + if (empty($this->rel->{$this->fk}) && $this->parent->getKey()) { + $this->rel->{$this->fk} = $this->parent->getKey(); + } + + $this->rel->save(); + } + }; + } + } catch (\Throwable $ex) { + // If relation introspection fails, skip gracefully + } + + continue; + } + + if (!starts_with($attribute, '_')) { $model->{$attribute} = $value; } } @@ -261,11 +323,11 @@ protected function applyLocationAwareScope($query) return; } - if (!in_array(\Igniter\Local\Models\Concerns\Locationable::class, class_uses($query->getModel()))) { + if (!in_array(Locationable::class, class_uses($query->getModel()))) { return; } - $ids = request()->user()?->locations->where('location_status', true)->pluck('location_id')->all(); + $ids = request()->user()?->locations?->where('location_status', true)->pluck('location_id')->all(); if (empty($ids)) { return; }