Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Model Laravel Package

psx/model

PSX\Model is a lightweight PHP model and entity library from the PSX framework. It offers annotated model classes and DTOs, plus schema generation, hydration, and serialization helpers, letting you define typed, self-documenting data structures without a full ORM.

View on GitHub
Deep Wiki
Context7

Getting Started

psx/model is a library of generated POPOs (Plain Old PHP Objects) — one class tree per open specification. You are not installing a framework feature; you are installing a vocabulary of ready-made DTOs for schema.org/ActivityStreams (ActivityPub), OpenAPI/Swagger, OAuth2, JSON-RPC, and TypeAPI (Api), plus a small Common set (Error, Message, Collection, Metadata) and the POPO contract that ties them together.

Minimal steps:

composer require psx/model

There is nothing to configure — no service provider, no config file, no facade, no migrations. It is a plain PSR-4 library under the PSX\Model\ namespace. In a Laravel app you just use the classes.

Where to look first, in this order:

  1. src/POPO/Proxy.php — the interface every model implements. Read this before anything else; once you understand get() / set() / getAll() / setAll() / jsonSerialize(), every other class in the package is obvious.
  2. src/POPO/Popo.php — the trait providing the generic accessors and JSON serialization.
  3. The directory listing of src/ — each subfolder is one specification. Pick the folder matching the payload you actually need to model.
  4. Any single concrete class, e.g. src/OAuth2/Token.php or src/Api/Operation.php, to see the naming convention for setters.

First use case — emit a typed response instead of a hand-rolled array:

use PSX\Model\OAuth2\Token;

$token = new Token();
$token->setAccessToken($jwt);
$token->setTokenType('bearer');
$token->setExpiresIn(3600);
$token->setScope('openid profile');

return response()->json($token); // models are JsonSerializable

The second use case, and the one that actually pays off: describing your own API. Instantiating Api / Swagger models lets you build a spec document programmatically and hand it to psx/schema or TypeAPI to generate OpenAPI, JSON Schema, or client SDKs — instead of maintaining YAML by hand.

Be clear-eyed about what you are getting: 9 stars, 0 dependents outside the apioo ecosystem (Fusio, TypeAPI). It is a first-party building block of one vendor's stack, published for reuse. Treat it as vendor code you will read, not a community library with a support surface.

Implementation Patterns

Hydrate from arrays, don't hand-write twenty setters. The generic proxy accessors are the day-to-day workhorse:

use PSX\Model\Api\Operation;

$operation = new Operation();
$operation->setAll($row);        // DB row / decoded JSON / request payload
$payload   = $operation->getAll(); // plain array for caching, diffing, or tests

getAll() recursively returns the underlying property map, which makes it the natural bridge to array_diff_assoc, test fixtures, and json_encode.

Use it as the response contract in your Laravel controllers. The strongest integration point is not Laravel-specific at all: return a model from a controller and let JsonSerializable do the work. This buys you IDE completion, a single place where the shape of a payload lives, and a class name you can assert on in tests:

public function show(int $id): JsonResponse
{
    $error = new \PSX\Model\Common\Error();
    $error->setSuccess(false);
    $error->setTitle('Not Found');
    $error->setMessage("Record {$id} does not exist");

    return response()->json($error, 404);
}

Pair with psx/schema for anything beyond transport. The models are deliberately dumb; validation, JSON Schema generation, and OpenAPI/TypeAPI emission live in psx/schema, which reads POPO classes via reflection. In practice: define/extend the model, register the class, and you get a schema plus a validator for free. If you are building an API, model → schema → validate → respond is the intended pipeline. Using the models without the schema layer gets you about 30% of the value.

Compose small models rather than flattening. Spec-derived classes reference each other (an operation has responses, a response has schemas). Follow that nesting; it keeps getAll() output structurally identical to the spec, which is what makes round-tripping to JSON and back lossless.

For ActivityPub / federation work, the ActivityPub tree is the most valuable part of the package — it saves you from transcribing the ActivityStreams vocabulary by hand. Build actor and activity objects as models, serialize, and serve with the correct content type. This is the one area where the package gives you something genuinely tedious to replicate.

Never hand-edit generated classes. These are produced from the upstream specification repos. If a field is missing from Swagger\Operation, the fix is upstream (or a local subclass), not a patch to vendor/. Local extension pattern:

class MyOperation extends \PSX\Model\Api\Operation
{
    private ?string $tenantId = null;
    // ... getter/setter, and include it in getAll() semantics
}

Keep the Proxy contract intact — getAll() / setAll() must round-trip your extra property, or schema generation and serialization will silently drop it.

Testing. Assert on getAll() arrays or on decoded JSON, not on object identity. Fixture-in / fixture-out is the pattern these classes were designed for.

Gotchas and Tips

  • No validation, no coercion, no persistence. Setters assign whatever you give them. setExpiresIn("abc") will not complain. All type safety is at the schema layer — if you skip psx/schema, you own the validation.
  • JSON key naming is the #1 surprise. PHP setters are camelCase; the wire format follows the specification, which is sometimes snake_case, sometimes camelCase, sometimes a @-prefixed or hyphenated key. Always dump getAll() and json_encode() for a new class before wiring it into an API response — a mismatch here produces a silently wrong payload, not an exception.
  • Null vs. absent. Most properties default to null and serialize as null rather than being omitted. OpenAPI and ActivityStreams consumers often care about the difference. Filter explicitly, or use a spec-aware serializer, rather than assuming json_encode matches the spec's required semantics.
  • Open objects lose data. Typed models cannot represent additionalProperties: true / free-form maps. If the spec allows arbitrary keys, those are dropped on hydrate. Keep a raw array alongside the model for those nodes.
  • Pin the version. Generated-from-spec code has a large surface area and no meaningful BC promise across minor bumps. composer.lock is your friend here; ^ ranges can hand you renamed setters.
  • Verify the generic accessors exist on the class you're using. Some classes implement Proxy explicitly rather than via the Popo trait. A quick src/POPO/Popo.php read plus one method_exists($model, 'setAll') check saves a confusing BadMethodCallException.
  • No Laravel glue, by design. No service provider, no config, no bindings. If you want these classes injectable or resolvable, write the trivial binding yourself — and honestly, don't; they are values, not services.
  • Extension point worth knowing: PSX\Model\POPO\Proxy. Implement it on your own DTOs and psx/schema tooling, hydrators, and serializers will treat your classes identically to the shipped ones. That is the package's real API — far more durable than any individual model class.
  • Documentation is the source. With a project this small, src/ is the documentation. Budget an hour to read POPO/ and skim the spec folder you need; you will come out ahead of any tutorial, including this one.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource