grazulex/laravel-modelschema

Generate and manage Laravel model schemas with automatic validation, documentation, and migration helpers for better data structure management.

v0.0.1 2025-08-03 18:31 UTC

This package is auto-updated.

Last update: 2025-08-03 18:37:29 UTC


README

Laravel ModelSchema

A foundational Laravel package for schema-driven development. Parse YAML schemas, generate insertable fragments for models, migrations, requests, resources, factories, and seeders. Built to power Laravel TurboMaker, Arc, and other schema-based packages.

[![Latest Version](https://img.shi### Field Types & Extensions

Overview

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.

๐Ÿš€ Key Features

  • ๐Ÿ” 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
  • ๏ฟฝ 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

๏ฟฝ Installation

composer require grazulex/laravel-modelschema

๐Ÿ—๏ธ Architecture

Core Services

  • SchemaService - Main API for parsing, validation, and core/extension separation
  • GenerationService - Coordinates all generators to produce insertable fragments
  • YamlOptimizationService - Advanced YAML parsing with lazy loading, streaming, and intelligent caching
  • SchemaDiffService - Advanced schema comparison and difference analysis
  • SchemaOptimizationService - Performance analysis and optimization recommendations
  • 8 Specialized Generators - Model, Migration, Request, Resource, Factory, Seeder, Controller, Test, Policy
  • FieldTypePluginManager - Manages extensible field type plugins for custom functionality

Schema Structure

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']

๐Ÿš€ Quick Start

1. Basic Schema Parsing

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);

2. Complete Integration Workflow

// 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);

3. Fragment-Based Generation

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: {...}']
// ]

๐Ÿ”ง API Reference

SchemaService

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

GenerationService

Method Description Returns
generateAll() Generate all fragments for schema array
generateSingle() Generate single generator fragment array
getAvailableGenerators() List available generators array

๐Ÿ”Œ Trait-Based Field Type Plugin System

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.

Plugin Manager

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();

Creating Custom Plugins with Traits

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'];
    }
}

Trait-Based Custom Attributes System

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

Advanced Trait Examples

// 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.

๐Ÿ“ Example Schema Files

Basic User Schema

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

Blog Post Schema with Relations

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

๏ฟฝ Integration with Parent Applications

This package is designed to be consumed by larger Laravel packages like TurboMaker and Arc. Here's the typical integration pattern:

Parent Application Workflow

// 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

Fragment Structure

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.

๐Ÿงช Testing

# Run all tests
composer test

# Run with coverage
composer test-coverage

# Run specific test file
./vendor/bin/pest tests/Unit/SchemaServiceTest.php

๐Ÿ”ง Requirements

  • PHP: ^8.3
  • Laravel: ^12.19 (optional, used in service provider)
  • Symfony YAML: ^7.3 (for YAML parsing)

๐Ÿ“š Documentation

Core Documentation

Field Types & Extensions

Advanced Features

Integration Examples

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guide for details.

๐Ÿ”’ Security

Please review our Security Policy for reporting vulnerabilities.

๐Ÿ“„ License

Laravel ModelSchema is open-sourced software licensed under the MIT license.

Made with โค๏ธ by Jean-Marc Strauven (https://github.com/Grazulex)