monkeyscloud / monkeyslegion-resources
API Resources & Transformers — PHP 8.4 property hooks, JSON:API, attribute-driven field control, pagination, and OpenAPI schema generation
Package info
github.com/MonkeysCloud/MonkeysLegion-Resources
pkg:composer/monkeyscloud/monkeyslegion-resources
1.0.0
2026-05-27 02:17 UTC
Requires
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-05-27 02:18:03 UTC
README
API Resources & Transformers for MonkeysLegion — PHP 8.4 property hooks, JSON:API spec, attribute-driven field control, pagination, and OpenAPI schema generation.
Installation
composer require monkeyscloud/monkeyslegion-resources
Features
| Feature | Description |
|---|---|
| Dual-mode resources | PHP 8.4 property hooks and classic method-based |
| JSON:API compliance | Full {type, id, attributes, relationships, links, meta} envelope |
| Attribute-driven | #[Expose], #[Hidden], #[Groups], #[Computed], #[When], #[WhenLoaded] |
| Sparse fieldsets | ?fields[users]=name,email |
| Includes | ?include=roles,orders |
| Pagination | Integrates with monkeyslegion-pagination + offset/cursor/simple |
| OpenAPI generation | #[ApiField] → OpenAPI 3.1 schemas via monkeyslegion-openapi |
| Serializer bridge | Optional delegation to monkeyslegion-serializer |
| CLI generator | php ml make:resource UserResource --json-api |
Quick Start
Property-Hook Style (PHP 8.4)
<?php declare(strict_types=1); namespace App\Resource; use MonkeysLegion\Resources\JsonResource; use MonkeysLegion\Resources\Attributes\{Expose, Hidden, Computed, Groups, When, WhenLoaded, ApiField}; final class UserResource extends JsonResource { #[Expose] #[ApiField(type: 'string', format: 'email')] public string $email { get => $this->entity->email; } #[Expose] public string $name { get => $this->entity->name; } #[Expose] #[Groups(['admin'])] public string $role { get => $this->entity->role; } #[Computed] public string $displayName { get => "{$this->entity->name} <{$this->entity->email}>"; } #[Expose] #[When('isAdmin')] public string $secretField { get => $this->entity->secret; } #[Expose] #[WhenLoaded('orders')] public int $orderCount { get => count($this->entity->orders); } #[Hidden] public string $password { get => $this->entity->password; } protected function isAdmin(): bool { return $this->entity->role === 'admin'; } }
Method-Based Style (Classic)
final class UserResource extends JsonResource { protected function toFields(): array { return [ 'email' => $this->entity->email, 'name' => $this->entity->name, 'display_name' => "{$this->entity->name} <{$this->entity->email}>", ]; } }
JSON:API Resource
final class UserResource extends JsonApiResource { protected string $type = 'users'; protected function toAttributes(object $entity): array { return [ 'email' => $entity->email, 'name' => $entity->name, 'created_at' => $entity->createdAt->format('c'), ]; } protected function toRelationships(object $entity): array { return [ 'roles' => RoleResource::collection($entity->roles), 'orders' => fn() => OrderResource::collection($entity->orders), ]; } protected function toLinks(object $entity): array { return ['self' => "/api/v2/users/{$entity->id}"]; } }
Controller Usage
// Single resource return UserResource::make($user)->toResponse(); // With status return UserResource::make($user)->toResponse(status: 201); // With message envelope return UserResource::make($user) ->withMessage('User created') ->toResponse(status: 201); // Collection return UserResource::collection($users)->toResponse(); // Collection with pagination return UserResource::collection($users) ->paginate(total: 150, page: 3, perPage: 25) ->toResponse(); // Collection with groups, message, and meta return UserResource::collection($users) ->withGroups(['admin']) ->withMessage('Admin listing') ->withMeta(['source' => 'api_v2']) ->toResponse(); // Serialization groups return UserResource::make($user) ->withGroups(['admin']) ->toResponse(); // JSON:API sparse fieldsets return UserResource::make($user) ->withSparseFields(['name', 'email']) ->toResponse(); // JSON:API includes filter return UserResource::make($user) ->withIncludes(['roles']) ->toResponse(); // Custom wrapper key return UserResource::collection($users) ->withWrap('items') ->toResponse();
OpenAPI Schema Generation
use MonkeysLegion\Resources\OpenApi\ResourceSchemaGenerator; use MonkeysLegion\OpenApi\OpenApiGenerator; // Standalone (no injected generator) $generator = new ResourceSchemaGenerator(); $schema = $generator->generate(UserResource::class); // With OpenApiGenerator (reads base spec) $generator = new ResourceSchemaGenerator($openApiGenerator); // Merge resource schemas into a full OpenAPI spec $spec = $generator->mergeIntoSpec([ UserResource::class, OrderResource::class, ]); // Returns a full OpenAPI 3.1 array with components.schemas populated
Serializer Bridge
use MonkeysLegion\Resources\Bridge\SerializerBridge; use MonkeysLegion\Serializer\Serializer; $bridge = new SerializerBridge(Serializer::create()); // Normalize an entity using the serializer $data = $bridge->normalize($user); $data = $bridge->normalizeCollection($users, groups: ['public']); $json = $bridge->toJson($user, groups: ['api']);
CLI Generator
# Basic resource (property-hook style) php ml make:resource UserResource # With entity auto-introspection php ml make:resource UserResource --entity=User # JSON:API variant php ml make:resource UserResource --json-api # With collection class php ml make:resource UserResource --collection
Attributes Reference
| Attribute | Target | Description |
|---|---|---|
#[Expose(name?)] |
Property | Include field in output, optionally rename |
#[Hidden] |
Property | Exclude field from output |
#[Groups(['...'])] |
Property / Class | Serialization groups |
#[Computed] |
Property | Virtual field (no backing entity prop) |
#[When(condition)] |
Property | Include only if method returns true |
#[WhenLoaded(relation)] |
Property | Include only if relation is loaded on entity |
#[ApiField(...)] |
Property | OpenAPI type, format, description, example |
Collection Interface
Both ResourceCollection and JsonApiCollection implement ResourceCollectionInterface:
interface ResourceCollectionInterface { public function toArray(): array; public function paginate(int $total, int $page, int $perPage): static; public function withMeta(array $meta): static; public function withWrap(?string $wrap): static; public function withGroups(array $groups): static; public function withMessage(string $message): static; public function count(): int; public function toResponse(int $status = 200): ResponseInterface; }
Dependencies
| Package | Required | Purpose |
|---|---|---|
monkeyscloud/monkeyslegion-http |
✅ | PSR-7 JsonResponse |
monkeyscloud/monkeyslegion-di |
✅ | Dependency injection |
monkeyscloud/monkeyslegion-pagination |
✅ | Pagination integration |
monkeyscloud/monkeyslegion-openapi |
✅ | OpenAPI schema generation |
monkeyscloud/monkeyslegion-serializer |
💡 Suggested | Advanced normalization via SerializerBridge |
monkeyscloud/monkeyslegion-cli |
💡 Suggested | make:resource CLI command |
License
MIT © MonkeysCloud Team