← CLI

CLI FEATURE

ORM Console Toolkit

Framework credibility also lives in operations. The ORM CLI should tell you what will change before it changes anything.

Feature Guide

A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.

What this does

The ORM includes a practical console toolkit for schema inspection, diffing, syncing, and seeding with safe defaults.

How it works

orm:status reports server capabilities and sync state, orm:diff shows the delta, orm:sync can dry-run or export the SQL plan, and orm:seed applies defaults() upserts for seedable resources.

Why it matters

A framework should not stop at attributes and repositories. Real teams need an operational surface that explains what will happen before it changes production state.

Key concepts

orm:status
Reports database/server capabilities and whether the schema is currently in sync.
orm:diff
Shows structural differences between the code schema and the live database.
--output
Exports the computed SQL plan to a file for audit, review, or deployment pipelines.

Operational Surface

CLI that explains the plan before touching the database

The ORM console commands are part of the product story. They let you inspect status, review diffs, export SQL plans, and seed data without improvising custom scripts.

Command Purpose Operational value
bin/semitexa orm:status Show DB/server capabilities and whether schema is in sync. Gives fast operational context before any change.
bin/semitexa orm:diff List code-vs-database differences. Lets reviewers see pending table, column, index, and FK changes.
bin/semitexa orm:sync --dry-run Build the execution plan without executing it. Safe default for CI, review, and local inspection.
bin/semitexa orm:sync --output plan.sql Export the SQL plan to a file. Useful for audit trails and DevOps handoff.
bin/semitexa orm:seed Run defaults() upserts for seedable resources. Makes local/demo environments reproducible quickly.

Inspect current sync state

bin/semitexa orm:status
bin/semitexa orm:diff

Review SQL before execution

bin/semitexa orm:sync --dry-run -vv
bin/semitexa orm:sync --dry-run --output var/migrations/history/review.sql

Apply safe changes, then seed

bin/semitexa orm:sync
bin/semitexa orm:seed
orm:status orm:diff orm:sync orm:seed --output

Verified against Semitexa Ultimate 2026.09.19.1020

ORM Console Toolkit

Framework credibility also lives in operations. The ORM CLI should tell you what will change before it changes anything.

How it works

orm:status shows database capabilities and whether the schema is in sync. orm:diff lists code-versus-database differences without applying them. orm:sync --dry-run builds the execution plan as reviewable output, and --output exports it to a SQL file for audit trails. orm:seed runs defaults() upserts for seedable resources to make local and demo environments reproducible.

Why this matters

Teams that skip review tooling end up applying schema changes they did not fully understand. Treating orm:sync --dry-run as the normal review path — rather than an exotic flag — shortens incident recovery and makes schema changes safe to delegate.

© Edsger W. Dijkstra: "Simplicity is prerequisite for reliability."

orm:status Command Implementation slice
<?phpdeclare(strict_types=1);namespace Semitexa\Orm\Application\Console\Command;use Semitexa\Core\Attribute\AsCommand;use Semitexa\Core\Attribute\InjectAsReadonly;use Semitexa\Core\Console\BaseCommand;use Semitexa\Orm\Adapter\ServerCapability;use Semitexa\Orm\Application\Service\Connection\ConnectionRegistry;use Symfony\Component\Console\Command\Command;use Symfony\Component\Console\Input\InputInterface;use Symfony\Component\Console\Input\InputOption;use Symfony\Component\Console\Output\OutputInterface;use Symfony\Component\Console\Style\SymfonyStyle;#[AsCommand(name: 'orm:status', description: 'Show ORM status: database info, server capabilities, schema summary')]class OrmStatusCommand extends BaseCommand{    #[InjectAsReadonly]    protected ConnectionRegistry $connections;    protected function configure(): void    {        $this->setName('orm:status')            ->setDescription('Show ORM status: database info, server capabilities, schema summary')            ->addOption('connection', 'c', InputOption::VALUE_REQUIRED, 'Connection name to inspect', 'default');    }    protected function execute(InputInterface $input, OutputInterface $output): int    {        $io = new SymfonyStyle($input, $output);        $connectionOption = $input->getOption('connection');        $connection = is_string($connectionOption) && $connectionOption !== '' ? $connectionOption : 'default';        try {            $orm = $this->connections->manager($connection);            $adapter = $orm->getAdapter();            $isSqlite = str_contains(strtolower($adapter::class), 'sqlite');            // Server info            $io->section('Database Server');            $serverInfo = [                ['Server Version' => $adapter->getServerVersion()],                ['Database' => $orm->getDatabaseName()],            ];            if (!$isSqlite) {                $pool = $orm->getPool();                $serverInfo[] = ['Pool Size' => (string) $pool->getSize()];                if ($pool instanceof \Semitexa\Orm\Adapter\ConnectionPool) {                    foreach ($pool->getStats() as $key => $value) {                        if ($key === 'size') {                            continue;                        }                        $serverInfo[] = ['Pool ' . str_replace('_', ' ', $key) => (string) $value];                    }                }            }            $io->definitionList(...$serverInfo);            // Capabilities            $io->section('Server Capabilities');            $rows = [];            foreach (ServerCapability::cases() as $capability) {                $supported = $adapter->supports($capability);                $rows[] = [                    $capability->value,                    $capability->name,                    $supported ? '<info>Yes</info>' : '<fg=red>No</>',                ];            }            $io->table(['Capability', 'Name', 'Supported'], $rows);            // Code schema summary            $io->section('Code Schema');            $collector = $orm->getSchemaCollector();            $schema = $collector->collect();            $errors = $collector->getErrors();            $warnings = $collector->getWarnings();            $totalColumns = 0;            $totalIndexes = 0;            foreach ($schema as $table) {                $totalColumns += count($table->getColumns());                $totalIndexes += count($table->getIndexes());            }            $io->definitionList(                ['Tables' => (string) count($schema)],                ['Total Columns' => (string) $totalColumns],                ['Total Indexes' => (string) $totalIndexes],                ['Validation Errors' => (string) count($errors)],                ['Validation Warnings' => (string) count($warnings)],            );            if ($errors !== []) {                $io->error('Validation errors:');                $io->listing($errors);            }            if ($warnings !== []) {                $io->warning('Warnings:');                $io->listing($warnings);            }            // Sync status            $io->section('Sync Status');            $comparator = $orm->getSchemaComparator();            $diff = $comparator->compare($schema);            if ($diff->isEmpty()) {                $io->success('Database is in sync with code.');            } else {                $syncEngine = $orm->getSyncEngine();                $plan = $syncEngine->buildPlan($diff);                $io->warning('Database is out of sync.');                $io->text($plan->getSummary());            }            return Command::SUCCESS;        } catch (\Throwable $e) {            $io->error('Status check failed: ' . $e->getMessage());            if ($output->isVerbose()) {                $io->text($e->getTraceAsString());            }            return Command::FAILURE;        }    }}

Recommended Flow

How teams should use the CLI

Start with orm:status or orm:diff when you need to understand schema state quickly.

Treat orm:sync --dry-run as the normal review path, not as an exotic extra flag.

Use --allow-destructive only when the team intentionally wants to include DROP and narrowing operations.

Export plans with --output when another human or deployment system needs the exact SQL artifact.

How it works

orm:status reports server capabilities and sync state, orm:diff shows the delta, orm:sync can dry-run or export the SQL plan, and orm:seed applies defaults() upserts for seedable resources.

Why it matters

A framework should not stop at attributes and repositories. Real teams need an operational surface that explains what will happen before it changes production state.

Key concepts

orm:status
Reports database/server capabilities and whether the schema is currently in sync.
orm:diff
Shows structural differences between the code schema and the live database.
--output
Exports the computed SQL plan to a file for audit, review, or deployment pipelines.

Support Semitexa
Built for developers who prefer control over magic. Your support helps keep it fast, open, and evolving.

Donate via PayPal