PHPackages                             yannelli/schematic - 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. [Templating &amp; Views](/categories/templating)
4. /
5. yannelli/schematic

ActiveLibrary[Templating &amp; Views](/categories/templating)

yannelli/schematic
==================

Database-driven templating language with JSON Schema generation for LLM structured outputs

v0.2.1(3mo ago)1440↑70%MITPHPPHP ^8.3CI passing

Since Mar 19Pushed 3mo agoCompare

[ Source](https://github.com/yannelli/schematic)[ Packagist](https://packagist.org/packages/yannelli/schematic)[ RSS](/packages/yannelli-schematic/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (4)Dependencies (4)Versions (5)Used By (0)

[![GitHub branch check runs](https://camo.githubusercontent.com/59061d6a7a0d52191f4ec10d67460637419f46e27e2a21df862b6350c282b363/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636865636b2d72756e732f79616e6e656c6c692f736368656d617469632f6d61696e3f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/59061d6a7a0d52191f4ec10d67460637419f46e27e2a21df862b6350c282b363/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636865636b2d72756e732f79616e6e656c6c692f736368656d617469632f6d61696e3f7374796c653d666c61742d737175617265) [![Packagist Version](https://camo.githubusercontent.com/9a9b5352d2eb623f5c237799f798573cca3956aa60c645fde89c7c7af0f28f46/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f79616e6e656c6c692f736368656d617469633f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/9a9b5352d2eb623f5c237799f798573cca3956aa60c645fde89c7c7af0f28f46/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f79616e6e656c6c692f736368656d617469633f7374796c653d666c61742d737175617265)

Schematic
=========

[](#schematic)

- [Introduction](#introduction)
- [Installation](#installation)
- [Configuration](#configuration)
    - [JSON Schema Defaults](#json-schema-defaults)
    - [Custom Models](#custom-models)
- [Creating Templates](#creating-templates)
    - [Adding Sections](#adding-sections)
    - [Defining Fields](#defining-fields)
    - [Array &amp; Object Fields](#array-and-object-fields)
- [Ephemeral Templates](#ephemeral-templates)
    - [Creating Ephemeral Templates](#creating-ephemeral-templates)
    - [Ephemeral Schema &amp; Rendering](#ephemeral-schema-and-rendering)
- [JSON Schema Generation](#json-schema-generation)
    - [Template Schemas](#template-schemas)
    - [Section Schemas](#section-schemas)
    - [Using With Anthropic](#using-with-anthropic)
    - [Using With OpenAI](#using-with-openai)
- [Managing Sections](#managing-sections)
    - [Enabling &amp; Disabling Sections](#enabling-and-disabling-sections)
    - [Reordering Sections](#reordering-sections)
    - [Iterating Sections](#iterating-sections)
    - [Adding &amp; Removing Fields](#adding-and-removing-fields)
- [Rendering Templates](#rendering-templates)
    - [Full Template Rendering](#full-template-rendering)
    - [Previewing With Examples](#previewing-with-examples)
- [Template Syntax](#template-syntax)
- [Custom Macros](#custom-macros)
- [Extending Models](#extending-models)
- [License](#license)

Introduction
------------

[](#introduction)

Schematic is a templating engine for Laravel that generates JSON Schema definitions from your templates. It is designed for use with LLM structured output APIs such as those provided by OpenAI and Anthropic, allowing you to define templates with typed fields and automatically produce valid JSON Schema for tool use and structured responses. Templates can be persisted to the database or created as [ephemeral (in-memory) templates](#ephemeral-templates) for on-the-fly use without any database overhead.

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

[](#installation)

Install Schematic via Composer:

```
composer require yannelli/schematic
```

After installing, publish and run the migrations:

```
php artisan vendor:publish --tag=schematic-migrations
php artisan migrate
```

You may optionally publish the configuration file:

```
php artisan vendor:publish --tag=schematic-config
```

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

[](#configuration)

The Schematic configuration file is located at `config/schematic.php`. Each configuration option is documented below.

### JSON Schema Defaults

[](#json-schema-defaults)

The `schema` options control the defaults used when generating JSON Schema output:

OptionEnvironment VariableDefaultDescription`schema.draft`—`https://json-schema.org/draft/2020-12/schema`The JSON Schema draft URI included in schema documents.`schema.strict`—`true`When enabled, all generated schemas include `additionalProperties: false`.

### Custom Models

[](#custom-models)

If you need to extend the base Schematic models, you may specify your custom model classes in the `models` configuration array. See [Extending Models](#extending-models) for details.

Creating Templates
------------------

[](#creating-templates)

To create a new template, use the `Schematic` facade's `create` method:

```
use Yannelli\Schematic\Facades\Schematic;

$template = Schematic::create(
    slug: 'psychiatric-evaluation',
    name: 'Psychiatric Evaluation Note',
    description: 'Standard psychiatric evaluation template for initial patient encounters',
);
```

### Adding Sections

[](#adding-sections)

Once a template has been created, you may add sections to it using the `addSection` method. Each section defines a portion of the template with its own content, fields, and optional example data:

```
$template->addSection(
    slug: 'chief-complaint',
    name: 'Chief Complaint',
    description: 'The primary reason the patient is seeking treatment',
    content: 'Chief Complaint: {{ complaint }}',
    fields: [
        [
            'name' => 'complaint',
            'type' => 'string',
            'description' => 'The patient\'s primary complaint in their own words',
            'required' => true,
            'nullable' => false,
        ],
    ],
    examples: [
        'complaint' => 'Patient reports increasing anxiety over the past 3 months',
    ],
);
```

### Defining Fields

[](#defining-fields)

Each field in a section requires a `name`, `type`, and `description`. You may also specify whether the field is `required` or `nullable`:

```
$template->addSection(
    slug: 'mental-status-exam',
    name: 'Mental Status Exam',
    description: 'Structured mental status examination findings',
    content:  'appearance', 'type' => 'string', 'description' => 'General appearance and grooming'],
        ['name' => 'mood', 'type' => 'string', 'description' => 'Patient\'s self-reported mood'],
        ['name' => 'affect', 'type' => 'enum', 'description' => 'Observed affect', 'enum' => ['flat', 'blunted', 'constricted', 'full', 'labile']],
        ['name' => 'thought_process', 'type' => 'string', 'description' => 'Organization and flow of thoughts'],
        ['name' => 'suicidal_ideation', 'type' => 'string', 'description' => 'Details of suicidal ideation if present', 'required' => false, 'nullable' => true],
    ],
    examples: [
        'appearance' => 'Well-groomed, appropriately dressed, good hygiene',
        'mood' => 'Anxious',
        'affect' => 'constricted',
        'thought_process' => 'Linear and goal-directed',
        'suicidal_ideation' => null,
    ],
);
```

When a field's `type` is set to `enum`, you should provide an `enum` array containing the allowed values. Fields are required by default; set `required` to `false` and `nullable` to `true` for optional fields.

### Array &amp; Object Fields

[](#array--object-fields)

For more complex data structures, you may define fields with `array` and `object` types. Array fields require an `items` key describing the structure of each element:

```
$template->addSection(
    slug: 'diagnoses',
    name: 'Diagnoses',
    content:  'diagnoses',
            'type' => 'array',
            'description' => 'List of ICD-10 diagnoses',
            'items' => [
                'type' => 'object',
                'properties' => [
                    'code' => ['type' => 'string', 'description' => 'ICD-10 code'],
                    'description' => ['type' => 'string', 'description' => 'Diagnosis description'],
                ],
                'required' => ['code', 'description'],
            ],
        ],
    ],
    examples: [
        'diagnoses' => [
            ['code' => 'F41.1', 'description' => 'Generalized anxiety disorder'],
            ['code' => 'F32.1', 'description' => 'Major depressive disorder, single episode, moderate'],
        ],
    ],
);
```

Ephemeral Templates
-------------------

[](#ephemeral-templates)

Ephemeral templates are in-memory templates that are **not persisted to the database**. They are useful for one-off or dynamic templates that you build at runtime — no migrations or database queries required.

Ephemeral templates support the same core features as database-backed templates: sections, fields, JSON Schema generation, rendering, and previewing.

### Creating Ephemeral Templates

[](#creating-ephemeral-templates)

Use the `ephemeral` method on the `Schematic` facade to create an in-memory template:

```
use Yannelli\Schematic\Facades\Schematic;

$template = Schematic::ephemeral(
    slug: 'intake-form',
    name: 'Patient Intake Form',
    description: 'A quick intake form built on the fly',
);

$template->addSection(
    slug: 'demographics',
    name: 'Demographics',
    content: '{{ patient_name }}, Age: {{ age }}',
    fields: [
        ['name' => 'patient_name', 'type' => 'string', 'description' => 'Full name'],
        ['name' => 'age', 'type' => 'integer', 'description' => 'Patient age'],
    ],
    examples: ['patient_name' => 'Jane Doe', 'age' => 34],
);
```

You may also create ephemeral templates directly via the `EphemeralTemplate` class:

```
use Yannelli\Schematic\Ephemeral\EphemeralTemplate;

$template = EphemeralTemplate::make('quick-note', 'Quick Note');
$section = $template->addSection('body', 'Body', content: '{{ note }}');
$section->addField('note', 'string', 'The note content');
```

Sections on ephemeral templates support the same fluent methods as database-backed sections, including `addField`, `removeField`, `enable`, `disable`, and `setExamples`. All mutations happen in memory.

### Ephemeral Schema &amp; Rendering

[](#ephemeral-schema--rendering)

Ephemeral templates generate JSON Schema and render content exactly like their database-backed counterparts:

```
// JSON Schema generation
$schema = $template->toJsonSchema();
$doc = $template->toJsonSchemaDocument();
$sectionSchema = $template->sectionSchema('demographics');

// Rendering with data
$output = $template->render([
    'demographics' => ['patient_name' => 'Alice Smith', 'age' => 28],
]);

// Preview using example data
$preview = $template->preview();
```

Section management works identically — you can iterate, reorder, enable, and disable sections:

```
$template->section('demographics')->disable();
$template->reorderSections(['body', 'demographics']);

foreach ($template->iterateSections() as $section) {
    // Only enabled sections
}
```

JSON Schema Generation
----------------------

[](#json-schema-generation)

Schematic generates JSON Schema definitions from your templates, ready for use with LLM structured output APIs.

### Template Schemas

[](#template-schemas)

To generate a JSON Schema for an entire template, use the `toJsonSchema` method on a template instance. For a full schema document including the `$schema` header, use `toJsonSchemaDocument`:

```
use Yannelli\Schematic\Facades\Schematic;

// Schema object
$schema = $template->toJsonSchema();

// Full document with $schema header
$doc = $template->toJsonSchemaDocument();

// Via facade
$schema = Schematic::schema('psychiatric-evaluation');
$doc = Schematic::schemaDocument('psychiatric-evaluation');
```

### Section Schemas

[](#section-schemas)

You may also generate a schema for a single section:

```
$mseSchema = $template->sectionSchema('mental-status-exam');

// Via facade
$sectionSchema = Schematic::sectionSchema('psychiatric-evaluation', 'chief-complaint');
```

The generated schema for the `mental-status-exam` section would look like the following:

```
{
  "type": "object",
  "properties": {
    "appearance": {
      "type": "string",
      "description": "General appearance and grooming"
    },
    "mood": {
      "type": "string",
      "description": "Patient's self-reported mood"
    },
    "affect": {
      "type": "string",
      "enum": ["flat", "blunted", "constricted", "full", "labile"],
      "description": "Observed affect"
    },
    "thought_process": {
      "type": "string",
      "description": "Organization and flow of thoughts"
    },
    "suicidal_ideation": {
      "type": ["string", "null"],
      "description": "Details of suicidal ideation if present"
    }
  },
  "required": ["appearance", "mood", "affect", "thought_process", "suicidal_ideation"],
  "description": "Structured mental status examination findings",
  "additionalProperties": false
}
```

### Using With Anthropic

[](#using-with-anthropic)

To use a Schematic template with the Anthropic API, pass the generated schema as a tool's `input_schema`:

```
use Anthropic\Anthropic;
use Yannelli\Schematic\Facades\Schematic;

$schema = Schematic::schema('psychiatric-evaluation');

$response = Anthropic::messages()->create([
    'model' => 'claude-sonnet-4-20250514',
    'max_tokens' => 4096,
    'messages' => [
        ['role' => 'user', 'content' => $transcriptText],
    ],
    'tools' => [
        [
            'name' => 'generate_note',
            'description' => 'Generate a structured psychiatric evaluation note',
            'input_schema' => $schema,
        ],
    ],
    'tool_choice' => ['type' => 'tool', 'name' => 'generate_note'],
]);
```

### Using With OpenAI

[](#using-with-openai)

When using OpenAI's structured output, pass the schema document to the `response_format` parameter:

```
use OpenAI\Laravel\Facades\OpenAI;
use Yannelli\Schematic\Facades\Schematic;

$schema = Schematic::schemaDocument('psychiatric-evaluation');

$response = OpenAI::chat()->create([
    'model' => 'gpt-4o',
    'messages' => [
        ['role' => 'user', 'content' => $transcriptText],
    ],
    'response_format' => [
        'type' => 'json_schema',
        'json_schema' => [
            'name' => 'psychiatric_evaluation',
            'strict' => true,
            'schema' => $schema,
        ],
    ],
]);
```

Note

OpenAI's structured output requires the full schema document (via `schemaDocument`), while Anthropic's tool use expects the schema object (via `schema`).

Managing Sections
-----------------

[](#managing-sections)

### Enabling &amp; Disabling Sections

[](#enabling--disabling-sections)

You may enable or disable individual sections on a template. Disabled sections are excluded from both schema generation and rendering:

```
$template->section('diagnoses')->disable();

// Only enabled sections are included
$schema = $template->toJsonSchema();
$output = $template->render($data);

$template->section('diagnoses')->enable();
```

### Reordering Sections

[](#reordering-sections)

To change the order in which sections appear, pass an array of section slugs to the `reorderSections` method:

```
$template->reorderSections([
    'chief-complaint',
    'diagnoses',
    'mental-status-exam',
]);
```

### Iterating Sections

[](#iterating-sections)

To iterate over a template's sections, use the `iterateSections` method. By default, only enabled sections are returned in their defined order:

```
foreach ($template->iterateSections() as $section) {
    echo "{$section->name}: " . ($section->is_enabled ? 'ON' : 'OFF') . "\n";
    echo json_encode($section->toJsonSchema(), JSON_PRETTY_PRINT) . "\n\n";
}
```

To include disabled sections, use `iterateAllSections`:

```
foreach ($template->iterateAllSections() as $section) {
    // ...
}
```

### Adding &amp; Removing Fields

[](#adding--removing-fields)

You may add or remove fields from an existing section:

```
$section = $template->section('chief-complaint');

$section->addField(
    name: 'onset',
    type: 'string',
    description: 'When symptoms first appeared',
    required: false,
);

$section->removeField('onset');
```

Rendering Templates
-------------------

[](#rendering-templates)

### Full Template Rendering

[](#full-template-rendering)

To render a template with data, pass an associative array keyed by section slug to the `render` method:

```
$data = [
    'chief-complaint' => [
        'complaint' => 'Increasing anxiety and panic attacks',
    ],
    'mental-status-exam' => [
        'appearance' => 'Casually dressed, fidgeting',
        'mood' => 'Anxious',
        'affect' => 'constricted',
        'thought_process' => 'Circumstantial at times',
    ],
    'diagnoses' => [
        'diagnoses' => [
            ['code' => 'F41.0', 'description' => 'Panic disorder'],
        ],
    ],
];

echo $template->render($data);

// Via facade
echo Schematic::render('psychiatric-evaluation', $data);
```

### Previewing With Examples

[](#previewing-with-examples)

When you have defined example data on your sections, you may preview the rendered output without providing data manually. To set example data on a section, use the `setExamples` method:

```
$template->section('chief-complaint')->setExamples([
    'complaint' => 'Patient reports difficulty sleeping for the past 2 weeks',
]);
```

To preview a single section or the entire template using its example data:

```
// Preview a single section
echo $template->section('chief-complaint')->preview();

// Preview the entire template
echo $template->preview();

// Via facade
echo Schematic::preview('psychiatric-evaluation');
```

Template Syntax
---------------

[](#template-syntax)

Schematic provides a lightweight template syntax for defining section content:

SyntaxDescription`{{ variable }}`Variable substitution.`{{ nested.key }}`Dot-notation access for nested values.`@if(var) ... @endif`Conditional block; renders content only when `var` is truthy.`@if(var) ... @else ... @endif`Conditional with an else branch.`@foreach(items as item) ... @endforeach`Iterate over an array.`@macroName("arg1", "arg2")`Invoke a registered custom macro.

Custom Macros
-------------

[](#custom-macros)

You may register custom macros to extend the template syntax. Macros should be registered in a service provider's `boot` method:

```
use Yannelli\Schematic\Facades\Schematic;

public function boot(): void
{
    Schematic::macro('component', fn (string $name) => view("components.{$name}")->render());
    Schematic::macro('timestamp', fn () => now()->toDateTimeString());
    Schematic::macro('badge', fn (string $label, string $color) => "{$label}");
}
```

Once registered, macros may be used in any template content:

```
@component("vital-signs")
Generated at: @timestamp()
Status: @badge("Active", "green")

```

Extending Models
----------------

[](#extending-models)

If you need to add custom behavior to the Schematic models, you may extend the base `Template` and `Section` classes and register them in the configuration:

```
// config/schematic.php
'models' => [
    'template' => App\Models\CustomTemplate::class,
    'section' => App\Models\CustomSection::class,
],
```

Your custom models should extend the corresponding base classes:

```
use Yannelli\Schematic\Models\Template;

class CustomTemplate extends Template
{
    // Add your custom logic
}
```

Credits
-------

[](#credits)

- [Ryan Yannelli](https://ryanyannelli.com)
    - [Website](https://ryanyannelli.com)
    - [GitHub](https://github.com/yannelli)
- [Nextvisit AI](https://nextvisit.ai)
- [All Contributors](../../contributors)

License
-------

[](#license)

Schematic is open-sourced software licensed under the [MIT license](LICENSE).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance82

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

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 ~0 days

Total

4

Last Release

96d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/53b64331d4de8c9cef47bc20707ed728c0e900cd2bda4ed7d95359ced8b9adf1?d=identicon)[yannelli](/maintainers/yannelli)

---

Top Contributors

[![yannelli](https://avatars.githubusercontent.com/u/59575788?v=4)](https://github.com/yannelli "yannelli (39 commits)")

---

Tags

ailaravellaravel-packagestructured-outputstemplating-enginelaraveljson-schematemplateopenaianthropicstructured output

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/yannelli-schematic/health.svg)

```
[![Health](https://phpackages.com/badges/yannelli-schematic/health.svg)](https://phpackages.com/packages/yannelli-schematic)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9742.3M121](/packages/roots-acorn)[spatie/laravel-health

Monitor the health of a Laravel application

87411.3M152](/packages/spatie-laravel-health)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8723.1M23](/packages/yajra-laravel-oci8)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45344.0k1](/packages/pressbooks-pressbooks)[api-platform/laravel

API Platform support for Laravel

59156.3k11](/packages/api-platform-laravel)

PHPackages © 2026

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