monkeyscloud/monkeyslegion-resources

API Resources & Transformers — PHP 8.4 property hooks, JSON:API, attribute-driven field control, pagination, and OpenAPI schema generation

Maintainers

Package info

github.com/MonkeysCloud/MonkeysLegion-Resources

pkg:composer/monkeyscloud/monkeyslegion-resources

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-05-27 02:17 UTC

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.

PHP 8.4+ License: MIT

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