PHPackages                             socialdept/atp-schema - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. socialdept/atp-schema

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

socialdept/atp-schema
=====================

ATProto Lexicon parser and validator with DTO generation for Laravel

v0.4.4(3mo ago)04133MITPHPPHP ^8.2CI failing

Since Nov 23Pushed 3mo agoCompare

[ Source](https://github.com/socialdept/atp-schema)[ Packagist](https://packagist.org/packages/socialdept/atp-schema)[ RSS](/packages/socialdept-atp-schema/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (8)Versions (17)Used By (3)

[![Schema Header](./header.png)](https://github.com/socialdept/atp-signals)

###  Parse, validate, and transform AT Protocol Lexicon schemas in Laravel.

[](#----parse-validate-and-transform-at-protocol-lexicon-schemas-in-laravel)

 [![](https://camo.githubusercontent.com/f2f87e69fb877be755f1662f70cc69e8239626b9f2dc1b3fa2bc689630cf6999/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f736f6369616c646570742f6174702d736368656d612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/socialdept/atp-schema "Latest Version on Packagist") [![](https://camo.githubusercontent.com/ae3bbfc8f7f7fe8523dbed25857bbda42efafff69d2f632f6a3676cc5a913592/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f736f6369616c646570742f6174702d736368656d612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/socialdept/atp-schema "Total Downloads") [![](https://camo.githubusercontent.com/4247c64debea778a83ca1d61e46df6d97bcac6e28cf24308209549fbea9f3528/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f736f6369616c646570742f6174702d736368656d613f7374796c653d666c61742d737175617265)](LICENSE "Software License")

---

What is Schema?
---------------

[](#what-is-schema)

**Schema** is a Laravel package for working with AT Protocol Lexicon schemas. It provides comprehensive tools for parsing schema definitions, validating data against those schemas, handling file uploads (blobs), and transforming between raw data and domain models.

Think of it as your complete toolkit for building AT Protocol applications that need strict data validation and schema compliance.

Why use Schema?
---------------

[](#why-use-schema)

- **Complete Lexicon support** - All types, formats, and constraints from the AT Protocol spec
- **Multiple validation modes** - STRICT, OPTIMISTIC, and LENIENT for different use cases
- **Laravel integration** - Works seamlessly with Laravel's validation and storage systems
- **Blob handling** - Built-in file upload validation and storage
- **Model transformation** - Convert between arrays and domain objects
- **Union types** - Full support for discriminated unions with `$type` fields
- **Extensible** - Macros and hooks let you customize behavior
- **Production ready** - 842 passing tests with comprehensive coverage
- **Pre-generated classes** - Includes type-safe PHP classes for all standard AT Protocol &amp; Bluesky lexicons

Pre-Generated Lexicon Classes
-----------------------------

[](#pre-generated-lexicon-classes)

Schema ships with pre-generated PHP classes for all standard AT Protocol and Bluesky lexicons, providing immediate type-safe access without any generation step:

```
use SocialDept\AtpSchema\Generated\App\Bsky\Feed\Post;
use SocialDept\AtpSchema\Generated\App\Bsky\Graph\Follow;
use SocialDept\AtpSchema\Generated\Com\Atproto\Repo\StrongRef;

// Create type-safe records
$post = new Post(
    text: 'Hello ATP!',
    createdAt: now()
);

// Create references
$ref = new StrongRef(
    uri: 'at://did:plc:example/app.bsky.feed.post/123',
    cid: 'bafyreic3...'
);

// Validate and use
Schema::validate('app.bsky.feed.post', $post); // true
```

### Available Pre-Generated Classes

[](#available-pre-generated-classes)

Schema includes **220+ pre-generated classes** covering all standard AT Protocol and Bluesky lexicons:

**Record Types (`SocialDept\AtpSchema\Generated\App\Bsky\*`)**

- `Feed\Post` - Social media posts
- `Feed\Like` - Like records
- `Feed\Repost` - Repost records
- `Graph\Follow` - Follow relationships
- `Graph\Block` - Block records
- `Graph\List` - User lists
- `Graph\Listitem` - List items
- `Labeler\Service` - Labeler service records

**Embed Types**

- `Embed\Images` - Image embeds
- `Embed\External` - External link embeds
- `Embed\Record` - Record embeds
- `Embed\RecordWithMedia` - Record with media embeds
- `Embed\Video` - Video embeds

**Feed &amp; Post Views (`App\Bsky\Feed\Defs\*`)**

- `PostView` - Post with engagement metrics
- `FeedViewPost` - Post in feed context
- `ThreadViewPost` - Post in thread context
- `ReplyRef` - Reply references
- `ViewerState` - User's interaction state

**Actor &amp; Profile Views (`App\Bsky\Actor\Defs\*`)**

- `ProfileView` - Full profile view
- `ProfileViewBasic` - Basic profile view
- `ProfileViewDetailed` - Detailed profile view
- `ViewerState` - Viewer's relationship to profile
- Plus 25+ preference and state classes

**Graph &amp; Social Views (`App\Bsky\Graph\Defs\*`)**

- `ListView` - List views
- `ListItemView` - List item views
- `Relationship` - User relationships
- `StarterPackView` - Starter pack views

**Rich Text (`App\Bsky\Richtext\Facet\*`)**

- `Facet` - Text annotations (mentions, URLs, hashtags)
- `Mention` - User mention facets
- `Link` - URL link facets
- `Tag` - Hashtag facets

**AT Protocol Core (`Com\Atproto\*`)**

- `Repo\StrongRef` - Content-addressed record references
- `Repo\Defs\CommitMeta` - Repository commit metadata
- `Label\Defs\Label` - Content labels
- `Admin\Defs\*` - Administrative tools (20+ classes)
- `Sync\SubscribeRepos\*` - Repository sync events (6+ classes)
- `Server\Defs\*` - Server definitions

**Moderation (`Tools\Ozone\*`)**

- `Moderation\Defs\*` - Moderation definitions
- `Communication\Defs\*` - Moderation communication

### Generated Enums

[](#generated-enums)

Schema also generates PHP 8.1+ enums for string types with known values:

**Moderation**

- `Com\Atproto\Moderation\ReasonType` - Report reason types (spam, violation, etc.)
- `Com\Atproto\Moderation\SubjectType` - Report subject types (account, record, chat)

**Labels &amp; Content**

- `Com\Atproto\Label\LabelValue` - Content label values

**Graph &amp; Social**

- `App\Bsky\Graph\ListPurpose` - List purpose types (modlist, curatelist)
- `App\Bsky\Actor\MutedWordTarget` - Muted word target types

**Moderation Tools**

- `Tools\Ozone\Moderation\SubjectReviewState` - Review state values

### Publishing Source Lexicons

[](#publishing-source-lexicons)

Developers can optionally publish the source JSON lexicons to their project for reference or custom generation:

```
php artisan vendor:publish --tag=atp-lexicons
```

This copies all lexicon JSON files to `resources/lexicons/`.

### Generating Custom DTOs

[](#generating-custom-dtos)

Generate PHP classes from any lexicon schema. By default, classes are output to `app/Lexicons` with a namespace derived from the path (`App\Lexicons`). You can change this in `config/atp-schema.php` under `generators.lexicon_path`:

```
# Generate a single lexicon
php artisan schema:generate app.bsky.feed.post

# Generate with all dependencies
php artisan schema:generate app.bsky.feed.post --with-dependencies

# Regenerate existing files
php artisan schema:generate app.bsky.feed.post --force

# Override output directory and namespace
php artisan schema:generate app.bsky.feed.post --output=app/DTOs --namespace="App\DTOs"
```

Generated classes include a `#[Generated]` attribute that controls regeneration behavior:

```
use SocialDept\AtpSchema\Attributes\Generated;

#[Generated(regenerate: true)]  // Will be overwritten on next --force
class Post extends Data
{
    // ...
}
```

**Protecting customized files:** If you modify a generated class and want to prevent it from being overwritten:

1. Change `regenerate: true` to `regenerate: false`, or
2. Remove the `#[Generated]` attribute entirely

Files without the attribute or with `regenerate: false` will be skipped when running `--force`, keeping your customizations safe.

Quick Example
-------------

[](#quick-example)

```
use SocialDept\AtpSchema\Data\LexiconDocument;
use SocialDept\AtpSchema\Validation\Validator;
use SocialDept\AtpSchema\Parser\SchemaLoader;

// Load a schema
$schema = LexiconDocument::fromArray([
    'lexicon' => 1,
    'id' => 'app.bsky.feed.post',
    'defs' => [
        'main' => [
            'type' => 'record',
            'record' => [
                'type' => 'object',
                'required' => ['text', 'createdAt'],
                'properties' => [
                    'text' => ['type' => 'string', 'maxLength' => 300],
                    'createdAt' => ['type' => 'string', 'format' => 'datetime'],
                ],
            ],
        ],
    ],
]);

// Validate data
$validator = new Validator(new SchemaLoader([]));

$data = [
    'text' => 'Hello, AT Protocol!',
    'createdAt' => '2024-01-01T12:00:00Z',
];

if ($validator->validate($data, $schema)) {
    // Data is valid!
}

// Get Laravel-formatted errors
$errors = $validator->validateWithErrors($invalidData, $schema);
// ['text' => ['The text field exceeds maximum length.']]
```

Installation
------------

[](#installation)

```
composer require socialdept/atp-schema
```

The package will auto-register with Laravel. Optionally publish the config:

```
php artisan vendor:publish --tag=atp-schema-config
```

Basic Usage
-----------

[](#basic-usage)

### Validation Modes

[](#validation-modes)

Choose the validation strictness that fits your use case:

```
use SocialDept\AtpSchema\Validation\Validator;

// STRICT - Rejects unknown fields
$validator->setMode(Validator::MODE_STRICT);

// OPTIMISTIC - Allows unknown fields (default)
$validator->setMode(Validator::MODE_OPTIMISTIC);

// LENIENT - Skips constraint validation
$validator->setMode(Validator::MODE_LENIENT);
```

### Handling Blobs

[](#handling-blobs)

Upload and validate files with built-in constraints:

```
use SocialDept\AtpSchema\Services\BlobHandler;

$blobHandler = new BlobHandler('local');

$blob = $blobHandler->store(request()->file('image'), [
    'accept' => ['image/*'],
    'maxSize' => 1024 * 1024 * 5, // 5MB
]);

// Use in validated data
$data = [
    'image' => $blob->toArray(),
];
```

### Model Transformation

[](#model-transformation)

Transform between raw arrays and domain objects:

```
use SocialDept\AtpSchema\Services\ModelMapper;
use SocialDept\AtpSchema\Contracts\Transformer;

class Post
{
    public function __construct(
        public string $text,
        public string $createdAt
    ) {}
}

class PostTransformer implements Transformer
{
    public function fromArray(array $data): Post
    {
        return new Post(
            text: $data['text'],
            createdAt: $data['createdAt']
        );
    }

    public function toArray(mixed $model): array
    {
        return [
            'text' => $model->text,
            'createdAt' => $model->createdAt,
        ];
    }

    public function supports(string $type): bool
    {
        return $type === 'app.bsky.feed.post';
    }
}

// Register and use
$mapper = new ModelMapper();
$mapper->register('app.bsky.feed.post', new PostTransformer());

$post = $mapper->fromArray('app.bsky.feed.post', $data);
$array = $mapper->toArray('app.bsky.feed.post', $post);
```

### Union Types

[](#union-types)

Work with discriminated unions using the `$type` field:

```
use SocialDept\AtpSchema\Services\UnionResolver;

$resolver = new UnionResolver();

$data = [
    '$type' => 'app.bsky.embed.images',
    'images' => [/* ... */],
];

$type = $resolver->extractType($data);
// "app.bsky.embed.images"

// Validate discriminated union
$refs = ['app.bsky.embed.images', 'app.bsky.embed.video'];
$resolver->validateDiscriminated($data, $refs);
```

Complete Workflow Example
-------------------------

[](#complete-workflow-example)

Here's how to validate a post with an image upload:

```
use SocialDept\AtpSchema\Data\LexiconDocument;
use SocialDept\AtpSchema\Validation\Validator;
use SocialDept\AtpSchema\Services\BlobHandler;

// Load schema
$schema = LexiconDocument::fromArray([/* ... */]);

// Handle image upload
$blobHandler = new BlobHandler('local');
$blob = $blobHandler->store(request()->file('image'), [
    'accept' => ['image/*'],
    'maxSize' => 1024 * 1024 * 5,
]);

// Create post data
$postData = [
    'text' => 'Check out this photo!',
    'createdAt' => now()->toIso8601String(),
    'embed' => [
        '$type' => 'app.bsky.embed.images',
        'images' => [
            [
                'image' => $blob->toArray(),
                'alt' => 'A beautiful sunset',
            ],
        ],
    ],
];

// Validate
$validator = new Validator(new SchemaLoader([]));

if ($validator->validate($postData, $schema)) {
    // Store post...
} else {
    $errors = $validator->validateWithErrors($postData, $schema);
    // Handle errors...
}
```

Supported Types
---------------

[](#supported-types)

Schema supports all AT Protocol Lexicon types:

- **Primitives** - `string`, `integer`, `boolean`, `bytes`
- **Objects** - Nested objects with properties
- **Arrays** - Sequential lists with item validation
- **Blobs** - File uploads with mime type and size constraints
- **Unions** - Discriminated unions with `$type` field
- **Unknown** - Accept any value

Supported Formats
-----------------

[](#supported-formats)

Built-in validators for AT Protocol formats:

- `datetime` - ISO 8601 timestamps
- `uri` - Valid URIs
- `at-uri` - AT Protocol URIs
- `did` - Decentralized identifiers
- `nsid` - Namespaced identifiers
- `cid` - Content identifiers

Advanced Features
-----------------

[](#advanced-features)

### Extension Hooks

[](#extension-hooks)

Add custom logic at key points in the validation lifecycle:

```
use SocialDept\AtpSchema\Support\ExtensionManager;

$extensions = new ExtensionManager();

$extensions->hook('before:validate', function ($data) {
    $data['text'] = trim($data['text']);
    return $data;
});

$transformed = $extensions->filter('before:validate', $data);
```

### Macros

[](#macros)

Extend core services with custom methods:

```
use SocialDept\AtpSchema\Services\ModelMapper;

ModelMapper::macro('validateAndTransform', function ($type, $data, $schema) {
    if (!$this->validator->validate($data, $schema)) {
        return null;
    }
    return $this->fromArray($type, $data);
});

$mapper = new ModelMapper();
$result = $mapper->validateAndTransform('app.bsky.feed.post', $data, $schema);
```

API Reference
-------------

[](#api-reference)

### Core Classes

[](#core-classes)

**LexiconDocument**

```
LexiconDocument::fromArray(array $data): self
LexiconDocument::fromJson(string $json): self
$document->getNsid(): string
$document->getVersion(): int
$document->getDefinition(string $name): ?array
$document->getMainDefinition(): ?array
```

**Validator**

```
$validator->validate(array $data, LexiconDocument $schema): bool
$validator->validateWithErrors(array $data, LexiconDocument $schema): array
$validator->setMode(string $mode): self
```

**BlobHandler**

```
$handler->store(UploadedFile $file, array $constraints = []): BlobReference
$handler->storeFromString(string $content, string $mimeType): BlobReference
$handler->get(string $ref): ?string
$handler->delete(string $ref): bool
$handler->exists(string $ref): bool
```

**ModelMapper**

```
$mapper->register(string $type, Transformer $transformer): self
$mapper->fromArray(string $type, array $data): mixed
$mapper->toArray(string $type, mixed $model): array
$mapper->fromArrayMany(string $type, array $items): array
```

**UnionResolver**

```
$resolver->extractType(array $data): ?string
$resolver->validateDiscriminated(mixed $data, array $refs): void
$resolver->getTypeDefinition(array $data, array $definition): ?LexiconDocument
```

Testing
-------

[](#testing)

Run the test suite:

```
vendor/bin/phpunit
```

Run specific test categories:

```
# Unit tests only
vendor/bin/phpunit --testsuite=unit

# Integration tests only
vendor/bin/phpunit --testsuite=integration
```

Configuration
-------------

[](#configuration)

Publish and customize `config/atp-schema.php`:

```
return [
    // Local lexicon directories (searched in order)
    'sources' => [
        resource_path('lexicons'),
    ],

    // Generator settings — namespace is derived from the path
    // e.g. 'app/Lexicons' → 'App\Lexicons'
    'generators' => [
        'lexicon_path' => 'app/Lexicons',
    ],

    // Bundled pre-generated classes (SocialDept\AtpSchema\Generated)
    'generated' => [
        'namespace' => 'SocialDept\\AtpSchema\\Generated',
        'enabled' => env('SCHEMA_USE_GENERATED', true),
    ],

    'validation' => [
        'mode' => env('SCHEMA_VALIDATION_MODE', 'strict'),
    ],

    'cache' => [
        'enabled' => env('SCHEMA_CACHE_ENABLED', true),
    ],

    'blobs' => [
        'disk' => env('SCHEMA_BLOB_DISK', 'local'),
        'path' => env('SCHEMA_BLOB_PATH', 'blobs'),
    ],
];
```

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11+

Resources
---------

[](#resources)

- [AT Protocol Documentation](https://atproto.com/)
- [Lexicon Specification](https://atproto.com/specs/lexicon)
- [Bluesky API Docs](https://docs.bsky.app/)

Support &amp; Contributing
--------------------------

[](#support--contributing)

Found a bug or have a feature request? [Open an issue](https://github.com/socialdept/atp-schema/issues).

Want to contribute? We'd love your help! Check out the [contribution guidelines](CONTRIBUTING.md).

Credits
-------

[](#credits)

- [Miguel Batres](https://batres.co) - founder &amp; lead maintainer
- [All contributors](https://github.com/socialdept/atp-schema/graphs/contributors)

License
-------

[](#license)

Schema is open-source software licensed under the [MIT license](LICENSE).

---

**Built for the Atmosphere** • By Social Dept.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance81

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Recently: every ~0 days

Total

15

Last Release

99d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ae252717302843968ef8adec35c3edb4a8fe70656083863691b6566b54ffeea9?d=identicon)[socialdept](/maintainers/socialdept)

---

Top Contributors

[![btrsco](https://avatars.githubusercontent.com/u/1373528?v=4)](https://github.com/btrsco "btrsco (105 commits)")

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/socialdept-atp-schema/health.svg)

```
[![Health](https://phpackages.com/badges/socialdept-atp-schema/health.svg)](https://phpackages.com/packages/socialdept-atp-schema)
```

###  Alternatives

[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[spatie/laravel-sitemap

Create and generate sitemaps with ease

2.6k14.6M107](/packages/spatie-laravel-sitemap)[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.2M51](/packages/spatie-laravel-responsecache)[laravel-zero/framework

The Laravel Zero Framework.

3371.4M369](/packages/laravel-zero-framework)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[torchlight/torchlight-laravel

A Laravel Client for Torchlight, the syntax highlighting API.

120452.8k11](/packages/torchlight-torchlight-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
