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 UserResourceWith 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
Related Packages
First-class Stripe integration package for the MonkeysLegion PHP framework, providing PSR-compliant HTTP clients and service container integration.
Code-first GraphQL server for the MonkeysLegion framework — PHP 8.4 attributes, PSR-15, DataLoader, subscriptions, and security out of the box.