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

Pbjc Laravel Package

gdbots/pbjc

gdbots/pbjc is a compiler for PBJ schemas that generates PHP classes and supporting code, helping teams define typed messages and keep generated models in sync with schema changes. It streamlines schema-driven development for PHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

gdbots/pbjc is a build-time code generator, not a runtime library. It reads PBJ schema files (YAML) describing messages and emits PHP classes, JSON Schema, and JS. In a Laravel app you install it as a dev dependency and treat its output like compiled assets.

composer require --dev gdbots/pbjc
composer require gdbots/pbj  # runtime the generated PHP depends on

Then look at, in this order:

  1. vendor/bin/pbjc (the console entry point) — run --help first; this is the fastest way to learn the real command names and flags, since docs are thin.
  2. vendor/gdbots/pbjc/src/ — the Compiler class and the generators (PhpGenerator, JsonSchemaGenerator, …). Reading the generator for PHP tells you exactly what namespaces/files it writes.
  3. vendor/gdbots/pbj/schemas/ and any *.pbj.yml in the vendor tree — use these as canonical examples of the schema format rather than guessing at keys.

First use case: define one message, compile it, use it.

# schemas/acme:blog:post.pbj.yml
types:
  - acme:blog:post
fields:
  title: {type: string, required: true}
  body:  {type: text}
vendor/bin/pbjc compile --path=schemas --out=app/Messages
composer dump-autoload

Now App\Messages\Acme\Blog\Post is a plain immutable message object you can construct from a request payload and serialize to JSON for storage. That round-trip — schema → DTO → JSON → DB — is the whole point of the package; everything else is packaging.

Implementation Patterns

Schema-first, generate-on-build. Keep schemas/ in the repo, generate into a PSR-4 directory (app/Messages/), and commit the output or generate in CI. Pick one and enforce it — mixing generated and hand-edited files in the same directory causes painful merge conflicts. A composer.json script is the lowest-friction hook:

"scripts": {
  "pbj:compile": "pbjc compile --path=schemas --out=app/Messages",
  "post-autoload-dump": ["@pbj:compile"]
}

Wrap it in an Artisan command so the team has one invocation and you can add logging, path resolution, and a --watch mode for local dev:

class CompileSchemas extends Command
{
    protected $signature = 'pbj:compile {--watch}';

    public function handle()
    {
        // either Process::run(base_path('vendor/bin/pbjc ...'))
        // or instantiate Gdbots\Pbjc\Compiler directly
    }
}

Shelling out is usually safer: the binary already handles config resolution and exit codes, and you avoid coupling to internal constructor signatures that may change between patch releases.

Multi-target from one schema. Compile the same schemas to JSON Schema for a second consumer. Serve the JSON Schema from a controller (or bake it into an OpenAPI spec) so your Vue/React front end validates identically to the back end, instead of hand-maintaining two validation rulesets.

Use the JSON Schema for request validation. Rather than rewriting rules in FormRequest, validate the decoded payload against the emitted schema with justinrainbow/json-schema or opis/json-schema. This keeps validation authoritative to the schema and avoids drift. Wrap it in a custom rule or a base FormRequest::rules() that returns the schema-derived constraints.

Persistence. Generated messages are value objects, not Eloquent models. Store them in a json column and cast:

protected $casts = ['payload' => 'array'];

public function post(): Post
{
    return Post::fromArray($this->payload); // generated fromArray/fromJson
}

Keep a thin mapper class per aggregate; don't scatter fromArray calls through controllers.

Schema versioning. Namespaced type names (acme:blog:post) are your migration boundary. Add fields as optional, never rename in place; emit a new type (acme:blog:post_v2) and map at the edges. Generated classes are cheap, so a parallel class tree costs little.

Gotchas and Tips

  • Maintenance risk is the headline. Last release 2019, zero stars, zero dependents. Assume no PHP 8.x fixes, no security patches, and no answers on the issue tracker. Before adopting, run it against your target PHP version in a throwaway branch; each(), create_function, or untyped property reflection will bite. If it fails on your runtime, pin PHP for the build step (a separate CI container running 7.2/7.3) and keep the runtime off the package entirely.
  • Compile in dev/CI, never per-request. Generation is filesystem-heavy and non-idempotent in cost. Guard your Artisan command with app()->isProduction() and fail loudly if generated classes are missing rather than silently compiling at runtime.
  • Autoloading after generation. New classes won't resolve until composer dump-autoload runs. If you generate into app/, PSR-4 covers it; if you generate into build/ or generated/, add an explicit classmap entry and re-dump. This is the #1 "it worked locally, 500s in CI" cause.
  • Diff noise. Generators often stamp headers or reorder output. Check whether the emitted files churn on every run — if so, either commit them and accept review noise, or gitignore them and generate in CI. A --check / dry-run mode, if the CLI supports it, makes a good CI gate.
  • Naming mismatch. PBJ type names use : separators and typically camelCase fields, while Laravel/DB conventions are snake_case. Decide the mapping once (in your mappers, not in the schema) — mixing produces payloads that fail schema validation on round-trip.
  • JSON Schema draft version. Old generators commonly emit draft-04. Modern validators (opis, ajv v8) default to draft-07/2020-12. Explicitly declare the draft in your validator config or you'll get silently ignored keywords like const and if/then.
  • Mixins and inheritance in PBJ schemas compose fields in non-obvious order. When a field "disappears," dump the generated PHP class and read the property list — the schema's mixins are the usual culprit.
  • Debugging compile failures. Errors point at YAML lines but rarely explain the meta-schema rule violated. Validate the schema file against the PBJ meta-schema shipped in gdbots/pbj before blaming the compiler; most failures are a missing types entry or a field referencing an undeclared type.
  • Extension points live in the generator layer. If you need a custom output (TypeScript interfaces, Eloquent casts, OpenAPI fragments), look for a generator interface to implement rather than post-processing the PHP output with regex — and consider whether string-templating the JSON Schema output is a cheaper path than extending a dead library.
  • Don't let it into the request path. No facade, no service provider bindings to compiler internals. Treat pbjc as you would webpack or protoc: a tool that runs before your app boots, with the generated artifacts being the only contract.
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.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr