daycry / schemas
Database schema management, for CodeIgniter 4
Requires
- php: ^8.1
Requires (Dev)
This package is auto-updated.
Last update: 2025-09-30 16:48:36 UTC
README
Minimal core edition: focused only on drafting (database / model / directory), reading, and archiving (cache). Async, plugins, events, layered environments, complex validation, advanced relation flags, and export/import formats were removed for a lean runtime.
Quick Start
- Install with Composer:
composer require daycry/schemas
- Generate and cache a schema (database + models) via spark:
php spark schemas -draft database,model -archive cache
- Fetch it (try cache first, draft if missing):
$schemas = service('schemas'); $schema = $schemas->load()->get(); // load() attempts cache (if archived previously) if (! $schema) { $schemas->draft()->archive('cache'); $schema = $schemas->get(); }
Core Feature Summary
- Database structure introspection (tables, fields, indexes, foreign keys)
- Draft sources: database, models, directory (user-provided PHP schema files)
- Archive + read from cache (file/redis/etc via CI4 cache handlers)
- Lightweight structure objects: Schema, Table, Field, Index, ForeignKey, Relation
- Simple automation flags (draft / archive / read)
- Explicit, extensible handler lists (swap or extend by config)
Removed Legacy Subsystems
Removed Feature | Why | Replacement / Path |
---|---|---|
Async background drafting | Complexity > benefit | Run synchronously (fast) |
Plugin manager | Indirection overhead | Create small wrapper packages |
Event bus | Rare real use | Add domain events externally if needed |
Export/Import (json/php/yaml) | Serialization bloat | Future addon (see Extensions) |
Snapshot/runtime overrides | Hidden mutable state | Explicit service instances |
Layered environment profiles | Hard to reason | Single flat config class |
Validation engine | Redundant vs tests | Rely on types + PHPUnit |
Logging / metrics collector | Unnecessary core weight | Use app/logger directly |
Advanced relation flags | Too many toggles | Single boolean $relationships |
If you relied on something removed, create a thin external package that composes over this core.
Installation
Composer (recommended):
composer require daycry/schemas
Manual: clone/download and add the src
namespace to your app/Config/Autoload.php
.
Configuration
Publish (optional) configuration to your app (if a publisher command is present) or copy the distributed template to app/Config/Schemas.php
.
Key options (see src/Config/Schemas.php
):
public string $defaultGroup = 'default'; // Database group public array $ignoredTables = ['migrations']; // Skip these tables public array $includedTables = []; // If non-empty: only these tables public string $tablePrefix = ''; // Add/remove prefix handling public bool $silent = true; // Best-effort mode public array $cache = [ // Archive/read cache config 'enabled' => false, 'handler' => 'file', 'ttl' => 3600, 'prefix' => 'schemas_', ]; public bool $relationships = true; // Auto relation detection public array $automate = [ // Auto actions when needed 'draft' => true, 'archive' => true, 'read' => true, ]; public array $draftHandlers = [ // Order-sensitive 'database' => DatabaseHandler::class, 'model' => ModelHandler::class, 'directory' => DirectoryHandler::class, ]; public array $archiveHandlers = [ // Archive modes 'cache' => CacheHandler::class, ]; public array $readHandlers = [ // Reader sources 'cache' => \Daycry\Schemas\Reader\Handlers\CacheHandler::class, 'directory' => \Daycry\Schemas\Reader\Handlers\DirectoryHandler::class, 'php' => \Daycry\Schemas\Reader\Handlers\PhpHandler::class, 'json' => \Daycry\Schemas\Reader\Handlers\JsonHandler::class, ];
Edit handler lists to extend or swap implementations.
Public API Surface
The intent is a small, explicit contract. Everything not listed here should be considered internal and subject to change between minor versions.
Service: Daycry\Schemas\Schemas
Workflow (chainable) methods:
draft(array|string|null $handlers = null): self
– Merge drafted structures from one or more handler keys or class names. Null = all configured draft handlers in order.archive(string|array $mode = 'cache'): self
– Persist the current schema using configured archive handler(s) for a mode, or pass an array of archiver instances.read(string|array $path): self
– Load schema data from cache/directory/php/json sources and merge.
State helpers:
get(): ?Schema
– Current in-memory schema (ornull
). Does NOT perform I/O.load(): ?Schema
– Attempt to load from cache if not already loaded; returns the schema ornull
.setSchema(Schema $schema): self
– Replace current schema.reset(): self
– Clear schema & errors.getErrors(): string[]
– Retrieve & clear collected errors.
Structures (Daycry\Schemas\Structures
)
Plain data objects (all final except Mergeable
base):
Schema
,Table
,Field
,Index
,ForeignKey
,Relation
, plus additional DB objects if present (e.g., Procedure, Trigger, View).
Draft Handlers (Daycry\Schemas\Drafter\Handlers
)
DatabaseHandler
– Introspects DB schema via configured database group.ModelHandler
– Parses application Models for table/field hints.DirectoryHandler
(+ sub-handlers likeDirectoryHandlers\PhpHandler
) – Loads user-provided schema PHP files.
Archive Handlers (Daycry\Schemas\Archiver\Handlers
)
CacheHandler
– Stores schema in CodeIgniter Cache.
Reader Handlers (Daycry\Schemas\Reader\Handlers
)
CacheHandler
,DirectoryHandler
,PhpHandler
,JsonHandler
– Rehydrate schema from respective sources.
Extensibility Points
- Add a new draft handler: implement
DrafterInterface
, register in$draftHandlers
. - Add an archive handler: implement
ArchiverInterface
, add to$archiveHandlers
list or new mode key. - Add a reader: implement
ReaderInterface
, map extension/key in$readHandlers
. - Provide custom static schema slices: place PHP schema files in your configured
schemasDirectory
.
Usage
Basic automated workflow (all automate flags true):
$schemas = service('schemas'); $schema = $schemas->get(); // Will auto draft/read/archive on first call depending on flags
Manual workflow control:
$schemas = service('schemas'); // Draft database + models then archive $schemas->draft(['database','model'])->archive(); // Later, read from cache, add directory schemas, and fetch $schema = $schemas->read('cache')->draft('directory')->get();
Custom handler instance:
$db = db_connect('alternate'); $schemas = service('schemas'); $databaseHandler = new \Daycry\Schemas\Drafter\Handlers\DatabaseHandler(config('Schemas'), $db); $schema = $schemas->draft([$databaseHandler])->get();
Command
Spark command to draft & archive or print schemas:
php spark schemas -draft database,model -archive cache
php spark schemas -draft database,model,directory -print
Flags:
-draft handler1,handler2
(optional; default = all configured)-archive cache
(or omit to skip archiving)-print
output current schema to console (bypasses archive)
Automation
$automate
can be used (if enabled) to mimic legacy behavior (auto draft/read/archive). In the minimal core you are encouraged to call the methods you need explicitly. The provided load()
method offers a lightweight manual cache check pattern. Disable flags you do not want executed implicitly.
Recommended explicit flow:
$schemas = service('schemas'); if (! $schemas->load()) { // null => nothing in cache $schemas->draft()->archive(); // build & store } $schema = $schemas->get(); // schema now present
Relationships, Foreign Keys & Lazy Tables
Drafting from the database collects tables, fields, indexes and foreign keys. Relationships are inferred (when $relationships = true
) using:
- Real foreign keys (preferred)
- Pivot table heuristics: table with only two foreign key columns referencing distinct tables → many-to-many
- Column naming pattern:
{other_table}_id
→ belongsTo/hasMany guess (fallback)
Lazy Table Placeholders (Cache)
When archiving to cache, the CacheHandler
stores a scaffold: each table slot becomes tableName => true
and the full Table
object is saved under a separate cache key. This minimizes the size of the main schema entry and defers hydration.
How to materialize:
$schemas->load(); // scaffold only (tables => true) $schema = $schemas->get(); // Access one table (lazy load via reader magic) $users = $schema->tables->users; // now users is a Table object // Or fetch all tables eagerly if reader supports it if (is_object($schema->tables) && method_exists($schema->tables, 'fetchAll')) { $schema->tables->fetchAll(); }
Foreign Keys of a Table
$posts = $schema->tables->posts; // lazy hydrate foreach ($posts->foreignKeys as $name => $fk) { echo $fk->column_name, ' -> ', $fk->foreign_table_name, '.', $fk->foreign_column_name, PHP_EOL; }
If a table still shows as true
, call:
$schema->tables->fetch('posts');
Intervention / Manual Adjustments
- Config/Schemas: Use
$ignoredTables
to skip noisy tables. - app/Schemas/*.php: Provide overrides or custom additions (DirectoryHandler). Example: tests/_support/Schemas/Good/Products.php.
Supported Draft / Archive / Read
Draft: database
, model
, directory
Archive: cache
Read: cache
, directory
, php
, json
Database Support
All CodeIgniter 4 database drivers work but due to some differences in index handling they may not all report the same results. Example: see skipped tests for SQLite3.
Extensions & Addons
You can prototype external addons without modifying core by creating packages that:
- Provide new handler classes (implement the appropriate interface)
- Recommend a config snippet for users to append handlers
- (Optionally) add a spark command for export or diagnostics
Example add‑ons (future packages):
daycry/schemas-export-json
– export/import JSONdaycry/schemas-events
– lightweight event dispatcher wrapper
Roadmap (Minimal Core Perspective)
Potential future (only if demanded by real-world use):
- External export/import addon(s)
- Optional tiny event hook layer
- Migration diff generation as standalone tool
PRs welcome – keep the core surface area minimal.