📦 Marketplace⭐ GitHub
📦

monkeyslegion-resources

v1.0.0MITApi

by Jorge Peraza

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 =&gt; $this-&gt;entity-&gt;name;
}

#[Expose]
#[Groups(['admin'])]
public string $role {
    get =&gt; $this-&gt;entity-&gt;role;
}

#[Computed]
public string $displayName {
    get =&gt; "{$this-&gt;entity-&gt;name} &lt;{$this-&gt;entity-&gt;email}&gt;";
}

#[Expose]
#[When('isAdmin')]
public string $secretField {
    get =&gt; $this-&gt;entity-&gt;secret;
}

#[Expose]
#[WhenLoaded('orders')]
public int $orderCount {
    get =&gt; count($this-&gt;entity-&gt;orders);
}

#[Hidden]
public string $password {
    get =&gt; $this-&gt;entity-&gt;password;
}

protected function isAdmin(): bool
{
    return $this-&gt;entity-&gt;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'      =&gt; $entity-&gt;email,
        'name'       =&gt; $entity-&gt;name,
        'created_at' =&gt; $entity-&gt;createdAt-&gt;format('c'),
    ];
}

protected function toRelationships(object $entity): array
{
    return [
        'roles'  =&gt; RoleResource::collection($entity-&gt;roles),
        'orders' =&gt; fn() =&gt; OrderResource::collection($entity-&gt;orders),
    ];
}

protected function toLinks(object $entity): array
{
    return ['self' =&gt; "/api/v2/users/{$entity-&gt;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

Related Packages