A foundational Laravel package for schema-driven development. Parse YAML schemas, generate insertable fragments for models, migrations, requests, resources, factories, seeders, controllers, tests, policies, observers, services, actions, and validation rules. Built to power Laravel TurboMaker, Arc, and other schema-based packages.
[
- π Migration Guide - Upgrading from previous versions
- π Fragment Examples - Understanding generated fragmentsocs/FIELD_TYPES.md)** - Complete field types reference
- π Field Type Plugins - Creating custom field type plugins
- β¨ Custom Attributes Examples - Practical examples of custom attributes usage
- β
Custom Field Validation - Validating custom field types.io/packagist/v/grazulex/laravel-modelschema.svg?style=flat-square)](https://packagist.org/packages/grazulex/laravel-modelschema)
Laravel ModelSchema provides schema parsing, validation, and fragment generation for Laravel applications. Instead of generating complete files, it produces insertable JSON/YAML fragments that parent applications can integrate into their own generation workflows.
π― Core Purpose: Enable schema-driven development with clean separation between core schema logic and application-specific generation.
- π Schema Parsing & Validation - Parse YAML schemas with core/extension separation
- π§© Fragment Generation - Generate insertable JSON/YAML fragments for Laravel artifacts
- ποΈ Clean Architecture - Separate core schema responsibilities from app-specific generation
- π Multi-Generator Support - Models, Migrations, Requests, Resources, Factories, Seeders, Controllers, Tests, Policies, Observers, Services, Actions, Rules
- π Schema Analysis - Advanced schema comparison, optimization, and performance analysis
- π Plugin System - Extensible field type plugins for custom functionality
- π Integration API - Complete workflow for external packages (TurboMaker, Arc, etc.)
- β¨ Extensible Design - Custom field types, generators, and validation rules
composer require grazulex/laravel-modelschema
SchemaService
- Main API for parsing, validation, and core/extension separationGenerationService
- Coordinates all generators to produce insertable fragmentsYamlOptimizationService
- Advanced YAML parsing with lazy loading, streaming, and intelligent cachingSchemaDiffService
- Advanced schema comparison and difference analysisSchemaOptimizationService
- Performance analysis and optimization recommendations- 13 Specialized Generators - Model, Migration, Request, Resource, Factory, Seeder, Controller, Test, Policy, Observer, Service, Action, Rule
FieldTypePluginManager
- Manages extensible field type plugins for custom functionality
The package uses a "core" structure to clearly separate core schema data from application extensions:
core:
model: User
table: users
fields:
name:
type: string
nullable: false
email:
type: string
unique: true
relations:
posts:
type: hasMany
model: App\Models\Post
options:
timestamps: true
soft_deletes: false
# Extensions added by parent applications
turbomaker:
views: ['index', 'create', 'edit']
routes: ['api', 'web']
arc:
permissions: ['view', 'create', 'edit', 'delete']
use Grazulex\LaravelModelschema\Services\SchemaService;
$schemaService = new SchemaService();
// Parse and separate core from extensions
$result = $schemaService->parseAndSeparateSchema($yamlContent);
// Returns: ['core' => [...], 'extensions' => [...]]
// Validate only the core schema
$errors = $schemaService->validateCoreSchema($yamlContent);
// Extract structured data for generation
$data = $schemaService->extractCoreContentForGeneration($yamlContent);
// 1. Generate complete YAML from stub + app data
$completeYaml = $schemaService->generateCompleteYamlFromStub(
'user.schema.stub',
['MODEL_NAME' => 'User', 'TABLE_NAME' => 'users'],
$appExtensionData
);
// 2. Validate the complete YAML (focuses on core section)
$errors = $schemaService->validateFromCompleteAppYaml($completeYaml);
// 3. Extract all generation data as insertable fragments
$generationData = $schemaService->getGenerationDataFromCompleteYaml($completeYaml);
// 4. Use fragments in your application
$modelFragment = json_decode($generationData['generation_data']['model']['json'], true);
$migrationFragment = json_decode($generationData['generation_data']['migration']['json'], true);
use Grazulex\LaravelModelschema\Services\GenerationService;
$generationService = new GenerationService();
// Generate all fragments for a schema
$fragments = $generationService->generateAll($schema);
// Result structure:
// [
// 'model' => ['json' => '{"model": {...}}', 'yaml' => 'model: {...}'],
// 'migration' => ['json' => '{"migration": {...}}', 'yaml' => 'migration: {...}'],
// 'requests' => ['json' => '{"requests": {...}}', 'yaml' => 'requests: {...}'],
// 'resources' => ['json' => '{"resources": {...}}', 'yaml' => 'resources: {...}'],
// 'factory' => ['json' => '{"factory": {...}}', 'yaml' => 'factory: {...}'],
// 'seeder' => ['json' => '{"seeder": {...}}', 'yaml' => 'seeder: {...}'],
// 'controllers' => ['json' => '{"controllers": {...}}', 'yaml' => 'controllers: {...}'],
// 'tests' => ['json' => '{"tests": {...}}', 'yaml' => 'tests: {...}'],
// 'policies' => ['json' => '{"policies": {...}}', 'yaml' => 'policies: {...}'],
// 'observers' => ['json' => '{"observers": {...}}', 'yaml' => 'observers: {...}'],
// 'services' => ['json' => '{"services": {...}}', 'yaml' => 'services: {...}'],
// 'actions' => ['json' => '{"actions": {...}}', 'yaml' => 'actions: {...}'],
// 'rules' => ['json' => '{"rules": {...}}', 'yaml' => 'rules: {...}']
// ]
Laravel ModelSchema provides 13 specialized generators, each producing insertable JSON/YAML fragments:
Generator | Description | Output Fragment |
---|---|---|
Model | Eloquent model with relationships, casts, and configurations | model: {class_name, table, fields, relations, casts, ...} |
Migration | Database migration with fields, indexes, and foreign keys | migration: {table, fields, indexes, foreign_keys, ...} |
Request | Form Request classes for validation (Store/Update) | requests: {store: {...}, update: {...}} |
Resource | API Resource classes for data transformation | resources: {main_resource: {...}, collection_resource: {...}} |
Factory | Model Factory for testing and seeding | factory: {class_name, definition, states, ...} |
Seeder | Database Seeder for data population | seeder: {class_name, model, count, relationships, ...} |
Generator | Description | Output Fragment |
---|---|---|
Controller | API and Web Controllers with CRUD operations | controllers: {api_controller: {...}, web_controller: {...}} |
Test | PHPUnit test classes (Feature and Unit) | tests: {feature_tests: [...], unit_tests: [...]} |
Policy | Authorization Policy classes | policies: {class_name, methods, gates, ...} |
Generator | Description | Output Fragment |
---|---|---|
Observer | Eloquent Observer with model event handlers | observers: {class_name, events, methods, ...} |
Service | Business logic Service classes with CRUD operations | services: {class_name, methods, dependencies, ...} |
Action | Single-responsibility Action classes | actions: {crud_actions: [...], business_actions: [...]} |
Rule | Custom Validation Rule classes | rules: {business_rules: [...], foreign_key_rules: [...]} |
// Generate specific components
$observerFragment = $generationService->generateObservers($schema);
$serviceFragment = $generationService->generateServices($schema);
$actionFragment = $generationService->generateActions($schema);
$ruleFragment = $generationService->generateRules($schema);
// Generate multiple new components
$fragments = $generationService->generateMultiple($schema, [
'observers', 'services', 'actions', 'rules'
]);
// Generate everything including new components
$allFragments = $generationService->generateAll($schema, [
'model' => true,
'migration' => true,
'requests' => true,
'resources' => true,
'factory' => true,
'seeder' => true,
'controllers' => true,
'tests' => true,
'policies' => true,
'observers' => true, // New
'services' => true, // New
'actions' => true, // New
'rules' => true, // New
]);
Method | Description | Returns |
---|---|---|
parseAndSeparateSchema() |
Parse YAML and separate core/extensions | ['core' => array, 'extensions' => array] |
validateCoreSchema() |
Validate only core schema section | array (errors) |
extractCoreContentForGeneration() |
Extract structured core data | array |
generateCompleteYamlFromStub() |
Generate complete YAML from stub | string |
getGenerationDataFromCompleteYaml() |
Extract all generation fragments | array |
Method | Description | Returns |
---|---|---|
generateAll() |
Generate all fragments for schema | array |
generateSingle() |
Generate single generator fragment | array |
getAvailableGenerators() |
List available generators | array |
Laravel ModelSchema features an extensible plugin system using a trait-based architecture for custom field types. This modern approach provides powerful customization through traits and configuration objects.
use Grazulex\LaravelModelschema\Support\FieldTypePluginManager;
$manager = new FieldTypePluginManager();
// Register a custom plugin
$manager->registerPlugin(new CustomFieldTypePlugin());
// Auto-discover plugins in specific paths
$manager->discoverPlugins([
'App\\FieldTypes\\*Plugin',
'Custom\\Packages\\*FieldTypePlugin'
]);
// Get all registered plugins
$plugins = $manager->getAllPlugins();
The new trait-based approach allows you to define field options through configuration arrays rather than hardcoded properties:
use Grazulex\LaravelModelschema\Support\FieldTypePlugin;
class UrlFieldTypePlugin extends FieldTypePlugin
{
protected string $version = '1.0.0';
protected string $author = 'Your Name';
protected string $description = 'Advanced URL field with validation traits';
public function __construct()
{
// Define custom attributes using trait-based configuration
$this->customAttributes = [
'schemes', 'verify_ssl', 'timeout', 'domain_whitelist', 'max_redirects'
];
// Configure each attribute with validation traits
$this->customAttributeConfig = [
'schemes' => [
'type' => 'array',
'default' => ['http', 'https'],
'enum' => ['http', 'https', 'ftp', 'ftps'],
'description' => 'Allowed URL schemes for validation'
],
'verify_ssl' => [
'type' => 'boolean',
'default' => true,
'description' => 'Enable SSL certificate verification'
],
'timeout' => [
'type' => 'integer',
'min' => 1,
'max' => 300,
'default' => 30,
'description' => 'Connection timeout in seconds'
],
'domain_whitelist' => [
'type' => 'array',
'required' => false,
'validator' => function ($value): array {
// Custom validation trait for domain lists
if (!is_array($value)) return ['must be an array'];
foreach ($value as $domain) {
if (!filter_var("http://{$domain}", FILTER_VALIDATE_URL)) {
return ["Invalid domain: {$domain}"];
}
}
return [];
}
]
];
}
public function getType(): string
{
return 'url';
}
public function getAliases(): array
{
return ['website', 'link', 'uri'];
}
}
The trait-based plugin system supports sophisticated custom attributes through configuration objects:
- Type validation traits:
string
,int
,boolean
,array
, etc. - Constraint traits:
min
,max
,required
,enum
values - Default value traits: Automatically applied if not provided
- Custom validator traits: Callback functions for complex validation logic
- Transformation traits: Custom value transformation before storage
- Integration traits: Seamlessly merged with Laravel's standard attributes
// Numeric validation traits
'timeout' => [
'type' => 'integer',
'min' => 1,
'max' => 300,
'default' => 30,
'transform' => fn($value) => (int) $value // Type transformation trait
],
// Array validation traits with enum constraints
'schemes' => [
'type' => 'array',
'enum' => ['http', 'https', 'ftp', 'ftps'],
'default' => ['http', 'https'],
'validator' => function($schemes): array {
// Custom validation trait
return array_filter($schemes, fn($s) => in_array($s, ['http', 'https']));
}
],
// Complex custom validator traits
'domain_pattern' => [
'type' => 'string',
'validator' => function($pattern): array {
if (!preg_match('/^\/.*\/[gimxs]*$/', $pattern)) {
return ['Domain pattern must be a valid regex'];
}
return [];
}
]
π See Field Type Plugins Documentation for complete trait-based implementation guide.
core:
model: User
table: users
fields:
name:
type: string
nullable: false
rules: ['required', 'string', 'max:255']
email:
type: string
unique: true
rules: ['required', 'email', 'unique:users']
email_verified_at:
type: timestamp
nullable: true
password:
type: string
rules: ['required', 'string', 'min:8']
options:
timestamps: true
soft_deletes: false
core:
model: Post
table: posts
fields:
title:
type: string
rules: ['required', 'string', 'max:255']
slug:
type: string
unique: true
rules: ['required', 'string', 'unique:posts']
content:
type: text
rules: ['required']
published_at:
type: timestamp
nullable: true
user_id:
type: foreignId
rules: ['required', 'exists:users,id']
relations:
user:
type: belongsTo
model: App\Models\User
comments:
type: hasMany
model: App\Models\Comment
tags:
type: belongsToMany
model: App\Models\Tag
pivot_table: post_tags
options:
timestamps: true
soft_deletes: true
This package is designed to be consumed by larger Laravel packages like TurboMaker and Arc. Here's the typical integration pattern:
// 1. Parent app generates complete YAML
$yaml = $schemaService->generateCompleteYamlFromStub('user.schema.stub', [
'MODEL_NAME' => 'User',
'TABLE_NAME' => 'users'
], $parentAppData);
// 2. Parent app validates the schema
$errors = $schemaService->validateFromCompleteAppYaml($yaml);
if (!empty($errors)) {
throw new ValidationException($errors);
}
// 3. Parent app extracts generation fragments
$data = $schemaService->getGenerationDataFromCompleteYaml($yaml);
// 4. Parent app integrates fragments into its own files
$parentAppGenerator->generateModelFile($data['generation_data']['model']['json']);
$parentAppGenerator->generateMigrationFile($data['generation_data']['migration']['json']);
// ... etc for requests, resources, factory, seeder
Each generator produces insertable fragments with this structure:
{
"model": {
"class_name": "User",
"table": "users",
"fields": [...],
"relations": [...],
"casts": {...},
"options": {...}
}
}
The parent application receives these fragments and inserts them into its own generation templates.
# Run all tests
composer test
# Run with coverage
composer test-coverage
# Run specific test file
./vendor/bin/pest tests/Unit/SchemaServiceTest.php
- PHP: ^8.3
- Laravel: ^12.19 (optional, used in service provider)
- Symfony YAML: ^7.3 (for YAML parsing)
- ποΈ Architecture Guide - Understanding the package structure and design
- οΏ½ Migration Guide - Upgrading from previous versions
- π Fragment Examples - Understanding generated fragments
- οΏ½ Field Types Guide - Complete field types reference
- π Field Type Plugins - Creating custom field type plugins
- β Custom Field Validation - Validating custom field types
- π Logging System - Comprehensive logging and debugging
- β‘ Enhanced Features - Advanced capabilities overview
- οΏ½ YAML Optimization - High-performance YAML parsing with intelligent caching and streaming
- οΏ½π Schema Optimization - Schema analysis and optimization tools
- π Security Features - Comprehensive security validation and protection
- π Integration Example - Complete integration workflow
- β¨ New Generators Example - Observer, Service, Action, Rule generators demo
- π οΈ Schema Service API - API usage examples
- π API Extensions - Extended API implementations
- π YAML Optimization Examples - Performance optimization usage and examples
- β‘ Schema Optimization Usage - Advanced schema analysis examples
- π Security Usage Examples - Security validation and protection examples
We welcome contributions! Please see our Contributing Guide for details.
Please review our Security Policy for reporting vulnerabilities.
Laravel ModelSchema is open-sourced software licensed under the MIT license.
Made with β€οΈ by Jean-Marc Strauven (https://github.com/Grazulex)