Phase 1 Implementation

04 β€” Phase 1 Implementation Plan (MVP)

Back to README | Prev: Database Design | Next: Roadmap


Goal

A user can open LaraSchema, design tables visually on a canvas, define relationships, preview and export working Laravel migration files.


Build Order β€” 8 Steps

Each step builds on the previous one. The project should be runnable and testable after each step.


Step 1: Project Scaffolding

What: Create the Laravel app and package skeleton.

Tasks:

  1. Create a Laravel 12 app (with Pest) in a temp folder, then move into LaraSchema/
  2. Install Livewire 4: composer require livewire/livewire
  3. Verify Tailwind CSS 4 + Vite setup (Laravel 12 ships with it)
  4. Add @source "../../packages/laraschema/resources/views"; to resources/css/app.css so Tailwind scans package views
  5. Create the package directory structure under packages/laraschema/
  6. Create packages/laraschema/composer.json with:
    • PSR-4 autoload (LaraSchema\\ maps to src/)
    • Laravel auto-discovery for the service provider
    • Dependencies: illuminate/support, livewire/livewire
  7. Add path repository to root composer.json and require laraschema/laraschema:@dev
  8. Run composer update to symlink the package

Files created/modified:

  • packages/laraschema/composer.json (new)
  • Root composer.json (modified β€” path repo + require)
  • resources/css/app.css (modified β€” Tailwind source path)

Verify: composer update completes without errors. Package is symlinked in vendor/laraschema/.


Step 2: Package Foundation

What: Service provider, config, routes, migrations, models, enums, and layout.

Service Provider β€” src/LaraSchemaServiceProvider.php

  • Merges config from config/laraschema.php
  • Loads routes from routes/web.php (with configurable prefix + middleware)
  • Loads views namespaced as laraschema::
  • Publishes migrations to host app
  • Registers Livewire components via Livewire::addNamespace('laraschema', ...)

Config β€” config/laraschema.php

  • route_prefix (default: laraschema)
  • middleware (default: ['web'])
  • default_canvas settings (zoom, pan, grid_size, snap_to_grid)
  • default_table settings (width, color)

Routes β€” routes/web.php

Method URI Action
GET /laraschema ProjectManager Livewire component
GET /laraschema/project/{project} Canvas designer view
GET /laraschema/project/{project}/export ZIP download

Migrations (5 files)

All prefixed laraschema_ β€” see Database Design for full schemas.

Migration Table
2026_01_01_000001_create_laraschema_projects_table.php laraschema_projects
2026_01_01_000002_create_laraschema_tables_table.php laraschema_tables
2026_01_01_000003_create_laraschema_columns_table.php laraschema_columns
2026_01_01_000004_create_laraschema_relationships_table.php laraschema_relationships
2026_01_01_000005_create_laraschema_indexes_table.php laraschema_indexes

Models (5 files in src/Models/)

Model Key Relationships Casts
Project hasMany tables, hasMany relationships canvas_settings β†’ array, SoftDeletes
Table belongsTo project, hasMany columns, hasMany indexes booleans, decimals
Column belongsTo table enum_values β†’ array, booleans
Relationship belongsTo project, belongsTo sourceTable/targetTable line_points β†’ array
Index belongsTo table column_ids β†’ array

Enums (2 files in src/Enums/)

Enum Values Helper Methods
ColumnType All 30+ Laravel column types requiresLength(), requiresPrecision(), requiresValues()
RelationshipType hasOne, hasMany, belongsTo, belongsToMany, morphOne, morphMany, etc. β€”

Layout β€” resources/views/layouts/app.blade.php

  • Uses @vite() from host app
  • Includes @livewireStyles / @livewireScripts
  • Simple nav bar with "LaraSchema" branding
  • Renders Livewire component dynamically

Files created:

packages/laraschema/src/LaraSchemaServiceProvider.php
packages/laraschema/config/laraschema.php
packages/laraschema/routes/web.php
packages/laraschema/database/migrations/ (5 files)
packages/laraschema/src/Models/ (5 files β€” Project, Table, Column, Relationship, Index)
packages/laraschema/src/Enums/ (2 files β€” ColumnType, RelationshipType)
packages/laraschema/resources/views/layouts/app.blade.php

Verify: php artisan migrate creates all 5 tables. Visit /laraschema renders the layout.


Step 3: Project Management (CRUD)

What: List, create, rename, and delete projects.

Livewire Component β€” ProjectManager.php

  • Lists all projects in a card grid with pagination
  • Create new project (name + description) via modal
  • Rename project inline
  • Delete project with confirmation
  • Navigate to canvas designer on click

View β€” project-manager.blade.php

  • Card grid showing: project name, description, table count, created date
  • "Create New Project" button opens Alpine.js modal
  • Action buttons per card: Open Designer, Rename, Delete

Files created:

packages/laraschema/src/Http/Livewire/ProjectManager.php
packages/laraschema/resources/views/livewire/project-manager.blade.php

Verify: Create, rename, delete projects via the UI. Database records update correctly.


Steps 4 & 5: Canvas + Table Nodes

What: The visual canvas with pan, zoom, drag, and table node rendering. This is the most technically complex step.

Canvas Livewire Component β€” Canvas.php

Method Purpose
mount($project) Loads project with tables, columns, relationships
addTable() Creates new table with offset position
updateTablePosition($tableId, $x, $y) Persists drag result (called from Alpine)
updateCanvasSettings($settings) Persists zoom/pan state
selectTable($tableId) Opens side panel for editing
deselectTable() Closes side panel
deleteTable($tableId) Removes table with cascading deletes

Canvas View β€” canvas.blade.php

Structure:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Toolbar: [Add Table] [Reset View] [Zoom: 100%]    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                             β”‚ Table β”‚
β”‚  SVG Canvas (full viewport)                 β”‚Editor β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚ Panel β”‚
β”‚  β”‚  users        β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚(when  β”‚
β”‚  β”‚  id           │────│  posts        β”‚      β”‚select β”‚
β”‚  β”‚  name         β”‚    β”‚  id           β”‚      β”‚ed)   β”‚
β”‚  β”‚  email        β”‚    β”‚  user_id (FK) β”‚      β”‚       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  title        β”‚      β”‚       β”‚
β”‚                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚       β”‚
β”‚                                             β”‚       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜
  • Full-viewport <div x-data="schemaCanvas(...)"> wrapping an SVG
  • SVG grid background using <pattern> (minor + major grid lines)
  • <g :transform="viewTransform"> applies pan/zoom to all content
  • Table nodes via <template x-for>: white card with colored header, column rows
  • wire:ignore on SVG container prevents Livewire DOM-diffing during drags

Alpine.js schemaCanvas Component (inline in canvas view)

Feature Implementation
Pan mousedown on background starts pan, mousemove updates panX/panY, mouseup persists
Zoom wheel event adjusts zoom (0.2–3.0), zooms toward mouse via getScreenCTM()
Drag tables mousedown on table captures offset, mousemove updates position (snap-to-grid optional), mouseup calls $wire.updateTablePosition()
Dynamic height Computed from column count + built-in fields (id, timestamps, softDeletes)
Livewire sync $wire.$on('table-added', ...) pushes updates into Alpine state

Files created:

packages/laraschema/src/Http/Livewire/Canvas.php
packages/laraschema/resources/views/livewire/canvas.blade.php

Verify: Add tables, drag to reposition, refresh page β€” positions persist. Zoom/pan works smoothly at 60fps.


Step 6: Table Editor Panel

What: Side panel for editing table settings and columns.

Livewire Component β€” TableEditor.php

Feature Details
Table name Edit with snake_case validation
Toggle switches use_id, use_timestamps, use_soft_deletes
Color picker Table header color
Add column Name, type (dropdown from ColumnType enum), nullable, default, unique, index
Edit columns Inline editing with wire:model.blur for performance
Remove columns With confirmation
Reorder columns Via sort_order
Delete table With confirmation
Event dispatch table-updated after every change

View β€” table-editor.blade.php

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Table Editor ──────────────┐
β”‚  Table: [users          ]                  β”‚
β”‚  Color: [β–  #3B82F6]                       β”‚
β”‚                                            β”‚
β”‚  β˜‘ id()  β˜‘ timestamps()  ☐ softDeletes() β”‚
β”‚                                            β”‚
β”‚  ── Columns ──────────────────────────     β”‚
β”‚  name    β”‚ string  β”‚ ☐N ☐U ☐I β”‚ [Γ—]      β”‚
β”‚  email   β”‚ string  β”‚ β˜‘N ☐U β˜‘I β”‚ [Γ—]      β”‚
β”‚  bio     β”‚ text    β”‚ β˜‘N ☐U ☐I β”‚ [Γ—]      β”‚
β”‚                                            β”‚
β”‚  ── Add Column ───────────────────────     β”‚
β”‚  Name:  [____________]                     β”‚
β”‚  Type:  [string     β–Ό]                     β”‚
β”‚  [☐ Nullable] [☐ Unique] [☐ Index]       β”‚
β”‚  [+ Add Column]                            β”‚
β”‚                                            β”‚
β”‚  [πŸ—‘ Delete Table]                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Right-side sliding panel (w-96, full height)
  • Table settings section at top
  • Scrollable column list in middle
  • "Add Column" form at bottom

Files created:

packages/laraschema/src/Http/Livewire/TableEditor.php
packages/laraschema/resources/views/livewire/table-editor.blade.php

Verify: Click table to open editor. Add/edit/remove columns. Canvas table node updates to show changes.


Step 7: Relationships

What: Draw relationships between tables with auto FK creation and SVG lines.

User Flow

  1. Click "Draw Relationship" button in toolbar (enters relationship mode)
  2. Click source table β€” highlight appears
  3. Click target table β€” modal opens for type selection
  4. Select type (hasOne, hasMany, belongsTo, belongsToMany) + on_delete action
  5. System auto-creates FK column on the correct table
  6. SVG line appears between the two tables with cardinality indicators

RelationshipManager Component β€” RelationshipManager.php

Method Purpose
createRelationship($sourceId, $targetId, $type) Creates relationship + auto-creates FK column
deleteRelationship($id) Removes relationship and optionally the FK column
List/edit Manage existing relationships

FK Column Placement Logic:

Relationship Type FK Goes On Example
belongsTo Source table posts.user_id
hasOne / hasMany Target table phones.user_id
belongsToMany Pivot table No FK on either table directly

SVG Relationship Lines (in canvas view)

  • Lines rendered as <line> or <path> between table nodes
  • Coordinates computed from table positions (midpoints of nearest edges)
  • SVG <marker> definitions for cardinality:
    • "1" side β†’ vertical line (|)
    • "Many" side β†’ crow's foot (three fanning lines)

Files created:

packages/laraschema/src/Http/Livewire/RelationshipManager.php
packages/laraschema/resources/views/livewire/relationship-manager.blade.php

Verify: Draw relationships. Lines appear on canvas. FK columns auto-created. Cardinality indicators visible.


Step 8: Code Generation + Export

What: Generate valid Laravel migrations and export as ZIP.

MigrationGenerator β€” src/Generators/MigrationGenerator.php

Pure PHP class with no HTTP/Livewire dependency.

Method Purpose
generateForProject(Project $project): array Returns ['filename.php' => 'content']

Generates:

  • All 30+ column types with correct parameters (length, precision, enum values)
  • All modifiers: nullable, default, unique, index, unsigned, comment
  • Foreign key constraints with cascadeOnDelete
  • id(), timestamps(), softDeletes() shortcuts
  • Pivot table migrations for belongsToMany relationships
  • Proper down() methods with Schema::dropIfExists()

CodePreview Component β€” src/Http/Livewire/CodePreview.php

  • Shows generated migration code in a tabbed viewer
  • File tabs on left, syntax-highlighted code on right
  • Uses MigrationGenerator to produce output

SchemaExportService β€” src/Services/SchemaExportService.php

  • Creates a ZIP file containing database/migrations/ folder
  • Uses PHP's ZipArchive extension

ExportController β€” src/Http/Controllers/ExportController.php

  • download($project) β€” generates ZIP, returns download response, deletes temp file

Files created:

packages/laraschema/src/Generators/MigrationGenerator.php
packages/laraschema/src/Services/SchemaExportService.php
packages/laraschema/src/Http/Controllers/ExportController.php
packages/laraschema/src/Http/Livewire/CodePreview.php
packages/laraschema/resources/views/livewire/code-preview.blade.php

Verify: Click "Preview Code" β€” see generated migrations. Click "Export" β€” download ZIP. Unzip contains valid Laravel migration syntax.


Complete File Manifest (30 files)

packages/laraschema/
β”œβ”€β”€ composer.json
β”œβ”€β”€ config/
β”‚   └── laraschema.php
β”œβ”€β”€ database/migrations/
β”‚   β”œβ”€β”€ 2026_01_01_000001_create_laraschema_projects_table.php
β”‚   β”œβ”€β”€ 2026_01_01_000002_create_laraschema_tables_table.php
β”‚   β”œβ”€β”€ 2026_01_01_000003_create_laraschema_columns_table.php
β”‚   β”œβ”€β”€ 2026_01_01_000004_create_laraschema_relationships_table.php
β”‚   └── 2026_01_01_000005_create_laraschema_indexes_table.php
β”œβ”€β”€ resources/views/
β”‚   β”œβ”€β”€ layouts/
β”‚   β”‚   └── app.blade.php
β”‚   └── livewire/
β”‚       β”œβ”€β”€ canvas.blade.php
β”‚       β”œβ”€β”€ code-preview.blade.php
β”‚       β”œβ”€β”€ project-manager.blade.php
β”‚       β”œβ”€β”€ relationship-manager.blade.php
β”‚       └── table-editor.blade.php
β”œβ”€β”€ routes/
β”‚   └── web.php
└── src/
    β”œβ”€β”€ LaraSchemaServiceProvider.php
    β”œβ”€β”€ Enums/
    β”‚   β”œβ”€β”€ ColumnType.php
    β”‚   └── RelationshipType.php
    β”œβ”€β”€ Generators/
    β”‚   └── MigrationGenerator.php
    β”œβ”€β”€ Http/
    β”‚   β”œβ”€β”€ Controllers/
    β”‚   β”‚   └── ExportController.php
    β”‚   └── Livewire/
    β”‚       β”œβ”€β”€ Canvas.php
    β”‚       β”œβ”€β”€ CodePreview.php
    β”‚       β”œβ”€β”€ ProjectManager.php
    β”‚       β”œβ”€β”€ RelationshipManager.php
    β”‚       └── TableEditor.php
    β”œβ”€β”€ Models/
    β”‚   β”œβ”€β”€ Column.php
    β”‚   β”œβ”€β”€ Index.php
    β”‚   β”œβ”€β”€ Project.php
    β”‚   β”œβ”€β”€ Relationship.php
    β”‚   └── Table.php
    └── Services/
        └── SchemaExportService.php

Verification Checklist

Run these checks after completing each step:

After Step Test
1 composer update β€” package symlinks without errors
2 php artisan migrate β€” 5 tables created. /laraschema renders layout
3 Create, rename, delete projects via UI. Database records correct
4-5 Add tables, drag to reposition, refresh β€” positions persist. Zoom/pan smooth
6 Click table β†’ editor opens. Add/edit/remove columns. Canvas updates
7 Draw relationships. Lines appear. FK columns auto-created. Cardinality visible
8 Preview code β€” valid migrations. Export ZIP β€” contains migration files

End-to-End Test

Design a small schema: users, posts, comments, tags (with pivot). Export migrations, paste into a fresh Laravel app, run php artisan migrate β€” all tables created correctly with foreign keys.


Next: Roadmap

Reading the docs because someone has to build this?

If the schema is done and the application around it is the part you would rather hand over, an experienced Laravel team can take it from here.

Talk to a Laravel team
Loading...