Schema Management (Sculpt CLI)
Sculpt is ObjectQuel's command-line tool for database schema management. It generates entities, creates and applies migrations, and keeps your database schema in sync with your entity definitions.
Creating Entities
The make:entity command walks you through creating a new entity interactively. It prompts for a class name, table name, properties (with type, length, and nullability), and relationships. When finished it writes the entity file with all annotations in place.
$ php bin/sculpt make:entity
Entity name: > Product
Table name [products]: > products
Property name: > price
Property type [string]: > decimal
Property precision [10,2]: > 10,2
Can this property be null? [no]: > no
Property name: >
Relationship type (InverseOf, ManyToOne, OneToOne, or <return> to skip): > ManyToOne
Related entity: > Category
Success! Created: src/Entity/ProductEntity.php
The generated entity contains @Orm\Column annotations for each property, a primary key with @Orm\PrimaryKeyStrategy, and relationship annotations with the correct targetEntity and inversedBy values. Getters and setters are included for all properties.
Reverse Engineering
make:entity-from-table generates an entity from an existing database table. Sculpt reads the table's columns, types, and foreign keys, then produces a fully annotated entity. Foreign key columns become relationship properties; inverse sides are detected automatically from other tables that reference this one.
$ php bin/sculpt make:entity-from-table
Available tables: categories, products, users, orders, order_items
Select table: > products
Entity name [ProductEntity]: > ProductEntity
Namespace [App\Entity]: > App\Entity
Generate relationships? [yes]: > yes
Success! Created: src/Entity/ProductEntity.php
Detected: ManyToOne category_id -> CategoryEntity
Detected: InverseOf referenced by OrderItemEntity.product_id
This is the fastest way to bring ObjectQuel into an existing project. Run the command once per table, then adjust any annotations the wizard could not infer automatically.
Migrations
make:migrations diffs your entity annotations against the live database schema and generates a migration file covering every detected change — new tables, altered columns, added or removed indexes, and dropped columns.
$ php bin/sculpt make:migrations
Changes detected:
✓ New table: products
✓ New index: products.idx_category_id
✓ Modified column: users.email (added unique constraint)
✓ Dropped column: orders.legacy_status
Success! Created: database/migrations/20250603145623_EntitySchemaMigration.php
The generated file contains both up() and down() methods. Review it before applying — particularly column drops, which cannot be recovered from without a backup.
JSON column types
Columns declared as type="json" in an entity annotation are translated to the correct DDL type for the connected database engine automatically. No annotation change is needed when deploying to a different engine:
| Engine | Generated DDL type |
|---|---|
| MySQL / MariaDB | json |
| PostgreSQL | jsonb |
| SQLite | json |
Schema comparison is also engine-aware: PostgreSQL's jsonb is normalized to json before diffing, so existing JSON columns never appear as modified on subsequent make:migrations runs.
Applying migrations
# Preview what would run without touching the database
$ php bin/sculpt quel:migrate --dry-run
# Apply all pending migrations
$ php bin/sculpt quel:migrate
# Check which migrations have run
$ php bin/sculpt quel:migrate --status
Rolling back
# Rollback the last migration
$ php bin/sculpt quel:migrate --rollback
# Rollback multiple steps
$ php bin/sculpt quel:migrate --rollback --steps=3
Always take a database backup before applying migrations in production. --rollback executes the down() method of the migration, but if the down() method drops a table, rolling back does not recover the data.
Analysing Indexes
quel:analyze-indexes scans every table and reports redundant indexes — duplicates and indexes already covered by a wider composite index. Primary key indexes are never flagged.
$ php bin/sculpt quel:analyze-indexes
| Status | Meaning |
|---|---|
ok |
Index is not redundant. |
Duplicate of <index> |
Covers the exact same columns in the same order as another index. Safe to drop. |
Prefix of <index> (<columns>) |
Covered by a wider index whose leading columns are identical. |
On MySQL and MariaDB, Reads and Writes columns are appended from performance_schema.table_io_waits_summary_by_index_usage when performance_schema is enabled. A redundant index with zero reads is a confident removal candidate.
Index Visibility
quel:index-hide marks an index invisible to the optimizer without dropping it (INVISIBLE on MySQL ≥ 8.0, IGNORED on MariaDB). quel:index-show restores it instantly. Both commands accept an entity name; omitting arguments triggers interactive prompts.
$ php bin/sculpt quel:index-hide OrderLine idx_created_at
$ php bin/sculpt quel:index-show OrderLine idx_created_at
For the full analysis and safe-removal workflow, see Performance & Profiling.
Clearing the Annotation Cache
ObjectQuel caches parsed entity annotations in /storage/annotations to avoid re-reading and re-parsing entity files on every request. quel:clear-cache deletes all files in that directory, forcing a clean rebuild on the next request.
$ php bin/sculpt quel:clear-cache
Cache cleared.
Run this command after modifying entity annotations manually. Changes to annotations are not picked up until the cache is cleared.