Complete reference guide for all GEMVC command-line interface commands.
- CLI Architecture
- Installation & Setup
- Command Structure
- Project Management
- Code Generation
- Database Commands
- Flags & Options
- Examples
- Troubleshooting
CLI functionality is split so the foundation can be versioned and tested independently:
| Package | Namespace / path | Responsibility |
|---|---|---|
gemvc/cli-base |
vendor/gemvc/cli-base/src/ → Gemvc\CLI\ |
Command, CliColor, CliLine, FileSystemManager, Commands\CliBoxShow, AbstractBaseGenerator, AbstractBaseCrudGenerator, InstallControl |
gemvc/library |
vendor/gemvc/library/src/CLI/ |
AbstractInit, Docker helpers, CommandCategories, concrete commands, templates/cli/, InstallationTest |
AI / maintainer docs for cli-base: vendor/gemvc/cli-base/AI-Assistant.md (shipped with the package; not in application repos’ tests/ after 1.0.1).
End users: Still run php vendor/bin/gemvc … — no change to command names.
Gemvc\CLI\Command ← gemvc/cli-base
↓
├── Gemvc\CLI\Commands\AbstractBaseGenerator
│ └── AbstractBaseCrudGenerator
│ └── Gemvc\CLI\Commands\Create* ← gemvc/library (concrete generators)
│
├── Gemvc\CLI\AbstractInit ← gemvc/library (Template Method)
│ ├── InitApache
│ ├── InitSwoole
│ └── InitNginx
│
└── Gemvc\CLI\Commands\* ← gemvc/library (db:*, admin, …)
Purpose: Base class for every CLI command (framework and custom).
Features:
- Argument / option handling (
$args,$options) CliColorenum forwrite()— useCliColor::Blue, not string names like'green'info(),success(),error(),warning()with cross-platform ANSI (blue accents; macOS-safe)- Abstract
execute(): bool
Usage:
use Gemvc\CLI\CliColor;
use Gemvc\CLI\Command;
class MyCommand extends Command
{
public function execute(): bool
{
$this->write("Prompt: ", CliColor::Blue);
$this->info('Done.');
return true;
}
}Typical extensions:
class CreateService extends AbstractBaseGenerator // cli-base → Command
class DbInit extends Command // library, extends cli-base CommandPurpose: Defines the skeleton of project initialization for all webservers.
Template Method Pattern:
final public function execute(): bool
{
// 1. Initialize (same for all)
$this->initializeProject();
// 2. Setup structure (same for all)
$this->setupProjectStructure();
// 3. Copy common files (same for all)
$this->copyCommonProjectFiles();
// 4. Copy webserver-specific files (DIFFERENT per webserver)
$this->copyWebserverSpecificFiles(); // Abstract method
// 5. Setup autoload (same for all)
$this->setupPsr4Autoload();
// 6. Create .env file (same for all)
$this->createEnvFile();
// 7. Create global command wrapper (same for all)
$this->createGlobalCommand();
// 8. Finalize autoload (same for all)
$this->finalizeAutoload();
// 9. Offer Docker services (same for all)
$this->offerDockerServices();
// 10. Display next steps (same for all)
$this->displayNextSteps();
// 11. Offer optional tools (same for all)
$this->offerOptionalTools();
// 12. Offer container build (same for all)
$this->offerContainerBuild();
// 13. Display success graphic (same for all)
$this->displaySuccessGraphic();
}Abstract Methods (must be implemented by webserver classes):
getWebserverType(): string- Return 'Apache', 'OpenSwoole', etc.getWebserverSpecificDirectories(): array- Additional directoriescopyWebserverSpecificFiles(): void- Webserver-specific filesgetStartupTemplatePath(): string- Path to startup templatesgetDefaultPort(): int- Default server portgetStartCommand(): string- Command to start server
Shared Methods (used by all webservers):
createDirectories()- Creates all directoriescopyTemplatesFolder()- Copies templates to project rootsetupPsr4Autoload()- Configures composer.jsoncreateEnvFile()- Creates .env from examplecreateGlobalCommand()- Creates local wrapper scripts (bin/gemvc,bin/gemvc.bat)finalizeAutoload()- Runscomposer dump-autoloadto finalize PSR-4 autoloadingofferDockerServices()- Docker setup wizardofferContainerBuild()- Docker container building with pre-flight checksofferOptionalTools()- PHPStan, PHPUnit installation
Result: Webserver-specific init classes only implement what's different!
Purpose: Main entry point that orchestrates webserver selection and delegates to specific init classes.
How It Works:
1. User runs: gemvc init
2. InitProject displays webserver menu
3. User selects: Apache, OpenSwoole, or Nginx
4. InitProject creates appropriate Init class:
- InitApache for Apache
- InitSwoole for OpenSwoole
- InitNginx for Nginx (coming soon)
5. Delegates execution to selected classWebserver Selection:
// Interactive mode
gemvc init
// Shows menu:
// 1. OpenSwoole
// 2. Apache
// 3. Nginx
// Non-interactive mode
gemvc init --apache
gemvc init --swoole
gemvc init --server=apacheDelegation:
// InitProject.php
$initCommand = new $className($this->args, $this->options);
return $initCommand->execute(); // Delegates to InitApache or InitSwoolePurpose: Implement webserver-specific initialization logic.
Both extend AbstractInit and implement abstract methods:
InitApache:
class InitApache extends AbstractInit
{
protected function getWebserverType(): string
{
return 'Apache';
}
protected function copyWebserverSpecificFiles(): void
{
// Copy Apache-specific files:
// - .htaccess
// - public/ directory structure
// - Apache composer.json
}
protected function getDefaultPort(): int
{
return 80;
}
}InitSwoole:
class InitSwoole extends AbstractInit
{
protected function getWebserverType(): string
{
return 'OpenSwoole';
}
protected function copyWebserverSpecificFiles(): void
{
// Copy OpenSwoole-specific files:
// - index.php (OpenSwoole bootstrap)
// - Dockerfile (OpenSwoole container configuration)
// - Swoole composer.json (with Hyperf dependencies)
}
protected function getDefaultPort(): int
{
return 9501;
}
}Key Point: Only differences are implemented. Common logic stays in AbstractInit!
Purpose: Central registry for all CLI commands, their categories, and examples.
Features:
- ✅ Command categories (Project Management, Code Generation, Database)
- ✅ Command-to-class mapping
- ✅ Command descriptions
- ✅ Usage examples
Usage:
// Get command class name
$className = CommandCategories::getCommandClass('create:service');
// Returns: 'CreateService'
// Get command category
$category = CommandCategories::getCategory('create:service');
// Returns: 'Code Generation'
// Get command description
$description = CommandCategories::getDescription('create:service');
// Returns: 'Create a new service with optional components...'
// Get examples
$examples = CommandCategories::getExamples();
// Returns: ['create:service' => ['gemvc create:service User', ...]]Purpose: Centralized file and directory operations for all CLI commands (used heavily by AbstractInit and generators).
Features:
- ✅ Directory creation (
createDirectories()) - ✅ File copying with overwrite confirmation (
copyFileWithConfirmation()) - ✅ Template folder copying (
copyTemplatesFolder()) - ✅ File content reading (
getFileContent()) - ✅ Non-interactive mode support
Usage:
$fileSystem = new FileSystemManager($nonInteractive, $verbose);
// Create directories
$fileSystem->createDirectories(['app/api', 'app/controller']);
// Copy file with confirmation
$fileSystem->copyFileWithConfirmation($source, $target, 'file.php');
// Copy templates folder
$fileSystem->copyTemplatesFolder($packagePath, $basePath);Benefit: All CLI commands use the same file operations, ensuring consistency!
Purpose: Interactive Docker Compose setup wizard integrated into project initialization.
Features:
- ✅ Interactive service selection (Redis, phpMyAdmin, MySQL)
- ✅ Dynamic
docker-compose.ymlgeneration - ✅ Development/Production mode selection
- ✅ Docker volume cleanup
- ✅ Service dependencies handling
- ✅ Webserver-specific configuration
How It Works:
// Called from AbstractInit::execute()
$this->offerDockerServices();
// Inside offerDockerServices():
$dockerInit = new DockerComposeInit($basePath, $nonInteractive, $webserverType, $port);
$dockerInit->offerDockerServices();Service Selection Flow:
1. Display available services
- Redis (default: yes)
- phpMyAdmin (default: yes)
- MySQL (default: yes)
2. User selects services
- Press Enter for defaults
- Or type 'y'/'n' for each
3. Ask for MySQL mode
- Development Mode (clean logs)
- Production Mode (verbose logs)
4. Generate docker-compose.yml
- Webserver service (OpenSwoole/Apache)
- Selected services
- Volumes and networks
5. Offer Docker cleanup
- Clean existing containers/volumes
Usage in AbstractInit:
// AbstractInit.php - Line 74
protected function offerDockerServices(): void
{
$webserverType = strtolower($this->getWebserverType());
$port = $this->getDefaultPort();
$dockerInit = new DockerComposeInit(
$this->basePath,
$this->nonInteractive,
$webserverType,
$port
);
$dockerInit->offerDockerServices();
}Available Services:
- Redis - Cache and session storage (port 6379)
- phpMyAdmin - MySQL administration (port 8080)
- MySQL - Database server (port 3306)
Configuration Modes:
- Development Mode: Clean logs, optimized settings
- Production Mode: Verbose logs, full security warnings
Generated docker-compose.yml Structure:
services:
openswoole: # or 'web' for Apache
build: ...
ports: ...
depends_on: [db, redis] # Auto-added
db:
image: mysql:8.0
command: [--optimized-settings]
redis:
image: redis:latest
phpmyadmin:
image: phpmyadmin/phpmyadmin
depends_on: [db]
volumes:
mysql-data:
redis-data:
networks:
backend-network:Integration: Called automatically during gemvc init process!
Purpose: Handles automatic Docker container building with comprehensive pre-flight checks.
Features:
- ✅ Docker Desktop status verification
- ✅ Port availability checking (MySQL, phpMyAdmin, application port)
- ✅ Existing container conflict detection
- ✅ Automatic port conflict resolution with suggestions
- ✅ Container name conflict handling
- ✅ Interactive container building
Pre-Flight Checks:
- Docker Desktop Status: Verifies Docker Desktop is running
- Port Availability: Checks if required ports are available:
- MySQL (3306)
- phpMyAdmin (8080)
- Application port (80 for Apache/Nginx, 9501 for OpenSwoole)
- Container Conflicts: Detects existing containers using same ports
- Name Conflicts: Checks for container name conflicts
Port Conflict Resolution:
- If ports are blocked by Docker containers: Offers to stop/remove conflicting containers
- If ports are blocked by system processes: Suggests alternative ports
- Automatically updates
docker-compose.ymlwith new ports if user accepts suggestions
Usage in AbstractInit:
// AbstractInit.php - Line 78
$this->offerContainerBuild(); // Offer to build Docker containers
// Inside offerContainerBuild():
$containerBuilder = new DockerContainerBuilder(
$this->basePath,
$this->nonInteractive,
$this->getDefaultPort(),
$webserverType
);
$containerBuilder->offerContainerBuild();Interactive Flow:
1. Display build prompt with port information
2. Get user confirmation
3. Run pre-flight checks:
- Check Docker Desktop
- Check port availability
- Check container conflicts
4. Handle conflicts (stop containers, suggest ports)
5. Build containers with: docker compose up -d --build
Non-Interactive Mode: Skipped automatically
Integration: Called automatically during gemvc init process after Docker services setup!
Purpose: Provides path resolution and environment loading for CLI commands.
Key Methods:
rootDir() - Finds project root via composer.lock:
// Traverses up directory tree looking for composer.lock
ProjectHelper::rootDir(); // Returns: /path/to/projectappDir() - Gets app directory:
ProjectHelper::appDir(); // Returns: /path/to/project/apploadEnv() - Loads .env file:
// Tries root/.env first, then app/.env
ProjectHelper::loadEnv(); // Loads environment variablesAlso used by Core (paths, env, dev): isDevEnvironment(), getAppEnv(), getBaseUrl(), getApiBaseUrl(), getLibrarySystemPagesPath(), disableOpcacheIfDev().
Used By:
- Database commands (
DbInit,DbMigrate, etc.) - Code generation commands
- All commands that need project paths
Example:
// In DbInit.php
ProjectHelper::loadEnv();
$dbName = $_ENV['DB_NAME'];User runs: gemvc init
↓
bin/gemvc (entry point)
↓
CommandCategories::getCommandClass('init')
Returns: 'InitProject'
↓
new InitProject($args, $options)
↓
InitProject::execute()
- Displays webserver menu
- User selects Apache
↓
new InitApache($args, $options)
↓
InitApache::execute() // Inherited from AbstractInit
↓
AbstractInit::execute() // Template Method
├─ initializeProject()
├─ setupProjectStructure()
│ └─ FileSystemManager::createDirectories()
├─ copyCommonProjectFiles()
├─ copyWebserverSpecificFiles() // Implemented in InitApache
├─ setupPsr4Autoload()
├─ createEnvFile()
│ └─ ProjectHelper::rootDir() // Find project root
├─ offerDockerServices()
│ └─ DockerComposeInit::offerDockerServices()
│ ├─ Display service selection menu
│ ├─ Get user selections
│ ├─ Generate docker-compose.yml
│ └─ Display Docker instructions
└─ offerOptionalTools()
- Template Method -
AbstractInit::execute()defines skeleton - Strategy -
InitApache,InitSwooleare different strategies - Orchestrator -
InitProjectorchestrates webserver selection - Factory -
CommandCategoriesmaps commands to classes - Singleton-like -
ProjectHelper::rootDir()caches result - Facade -
FileSystemManagersimplifies file operations - Builder -
DockerComposeInitbuilds docker-compose.yml dynamically
Command Discovery (in bin/gemvc):
// 1. Parse command from CLI
$command = $argv[1]; // e.g., 'create:service'
// 2. Convert to class name
$className = CommandCategories::getCommandClass($command);
// 'create:service' → 'CreateService'
// 3. Find command class
$commandClass = 'Gemvc\\CLI\\Commands\\' . $className;
// 4. Instantiate and execute
$commandObj = new $commandClass($args);
$commandObj->execute();Command Naming Convention:
create:service→CreateServicedb:migrate→DbMigrateinit→InitProject
AbstractBaseGenerator extends Command
↓
├── CreateService
├── CreateController
├── CreateModel
├── CreateTable
└── CreateCrud extends AbstractBaseCrudGenerator extends AbstractBaseGenerator
AbstractBaseGenerator provides:
- Template loading (
getTemplate()) - Variable replacement (
replaceTemplateVariables()) - File writing (
writeFile()) - Project root detection (
determineProjectRoot())
Result: All generation commands share common functionality!
composer require gemvc/swoole# Using vendor binary
php vendor/bin/gemvc <command>
# Or add to composer.json scripts
composer gemvc <command>gemvc <command> [arguments] [flags]GEMVC commands are organized into four categories:
- Project Management - Initialize and configure projects
- Code Generation - Generate Services, Controllers, Models, Tables
- Database - Database management and migrations
- Admin - Admin user and password management
Initialize a new GEMVC project with webserver selection.
gemvc initInteractive Mode:
- Prompts you to select webserver (OpenSwoole, Apache, Nginx)
- Prompts you to select a database (MySQL, PostgreSQL, SQLite)
- Offers optional PHPStan installation
- Offers optional testing framework (PHPUnit/Pest)
- Sets up project structure
- Copies templates to project root
Non-Interactive Mode:
# OpenSwoole
gemvc init --swoole
# Apache
gemvc init --apache
# Nginx
gemvc init --nginx
# Or use --server flag
gemvc init --server=swoole
gemvc init --server=apache
# Database driver (defaults to mysql if omitted)
gemvc init --apache --db=postgres --non-interactive
gemvc init --swoole --sqlite --non-interactive
gemvc init --nginx --mysql --non-interactiveWhat It Does:
- ✅ Creates project directory structure (
app/api,app/controller,app/model,app/table) - ✅ Copies webserver-specific files (
index.php,Dockerfile, etc.) - ✅ Copies templates (
templates/cli/) for code generation - ✅ Sets up
.envfile fromexample.env, rewritten for the selected database driver (DB_DRIVER,DB_PORT,DB_USER,DB_CHARSET; SQLite gets adatabase/folder instead of host/port/user) - ✅ Installs dependencies (
composer.json) - ✅ Automatically installs OpenSwoole-specific packages (when OpenSwoole is selected:
gemvc/connection-openswoolepackage, which includes all required Hyperf dependencies) - ✅ Offers Docker setup (interactive service selection via
DockerComposeInit, driver-aware: MySQL+phpMyAdmin, PostgreSQL+pgAdmin, or no DB container for SQLite) - ✅ Offers PHPStan installation (optional)
- ✅ Offers testing framework (optional)
Note: The gemvc/connection-openswoole package is only installed when OpenSwoole is selected. Apache and Nginx projects do not include this package, reducing default package size.
Flags:
--swoole- Initialize for OpenSwoole--apache- Initialize for Apache--nginx- Initialize for Nginx--server=<name>- Specify webserver (swoole,apache,nginx)--db=<driver>- Specify database (mysql,postgres,sqlite) - defaults tomysqlif omitted--mysql/--postgres/--sqlite- Shorthand equivalents of--db=<driver>--non-interactiveor-n- Skip prompts, use defaults
Example:
# Interactive initialization (asks for webserver, then database)
gemvc init
# Non-interactive OpenSwoole initialization (defaults to MySQL)
gemvc init --swoole --non-interactive
# Non-interactive Apache + PostgreSQL initialization
gemvc init --apache --db=postgres --non-interactiveGenerate a new API service with optional components.
gemvc create:service <ServiceName> [flags]Flags:
-c- Also create Controller-m- Also create Model-t- Also create Table- Combine flags:
-cmtcreates all components
Examples:
# Create service only
gemvc create:service Product
# Create service + controller
gemvc create:service Product -c
# Create service + controller + model + table
gemvc create:service Product -cmtGenerated Files:
app/api/Product.php- API endpoint serviceapp/controller/ProductController.php(if-c)app/model/ProductModel.php(if-m)app/table/ProductTable.php(if-t)
Generate a new controller for business logic.
gemvc create:controller <ControllerName> [flags]Flags:
-m- Also create Model-t- Also create Table
Examples:
# Create controller only
gemvc create:controller Product
# Create controller + model + table
gemvc create:controller Product -mtGenerated Files:
app/controller/ProductController.phpapp/model/ProductModel.php(if-m)app/table/ProductTable.php(if-t)
Generate a new model for data logic.
gemvc create:model <ModelName> [flags]Flags:
-t- Also create Table
Examples:
# Create model only
gemvc create:model Product
# Create model + table
gemvc create:model Product -tGenerated Files:
app/model/ProductModel.phpapp/table/ProductTable.php(if-t)
Generate a new table class for database operations.
gemvc create:table <TableName>Examples:
gemvc create:table ProductGenerated Files:
app/table/ProductTable.php
Generate full CRUD operations (Service, Controller, Model, Table).
gemvc create:crud <ServiceName>Examples:
gemvc create:crud ProductGenerated Files:
app/api/Product.phpapp/controller/ProductController.phpapp/model/ProductModel.phpapp/table/ProductTable.php
What Gets Generated:
- ✅ Full CRUD methods:
create(),read(),update(),delete(),list() - ✅ Schema validation in API layer
- ✅ Business logic in Controller layer
- ✅ Data logic in Model layer
- ✅ Database operations in Table layer
- ✅ Helper methods (
selectById(),selectByName(), etc.)
PHP 8.5+ (since 5.6.7): CLI database connections use
Pdo\Mysql::ATTR_INIT_COMMANDwhen available to avoid PDO deprecation warnings. No configuration changes required.
Create the database if it doesn't exist.
gemvc db:initWhat It Does:
- Reads
DB_NAMEfrom.env - Connects as root user (
DB_ROOT_USER,DB_ROOT_PASSWORD) - Creates database if not exists
Example:
gemvc db:init
# Output: ✅ Database 'myapp' initialized successfully!Environment Variables Required:
DB_HOST_CLI_DEV=localhost
DB_ROOT_USER=root
DB_ROOT_PASSWORD=password
DB_NAME=myappCreate or update database tables based on PHP class definitions.
gemvc db:migrate <TableClassName> [flags]Multi-database support:
db:migrategenerates correct, engine-specific DDL for MySQL, PostgreSQL, and SQLite — it auto-detects the engine from your.env'sDB_DRIVER(viaDialectResolver) and requires no changes to yourTableclasses. Known limitation: SQLite cannotALTERan existing column's type/nullability/default or drop a primary key without a full table rebuild (unsupported operations are skipped with a clear warning instead of emitting invalid SQL); adding/removing columns, new tables, and indexes work normally on all three engines.
Flags:
--force- Remove columns not in class definition--enforce-not-null- Enforce NOT NULL constraints--sync-schema- Sync schema constraints (unique, indexes, foreign keys)--default=<value>- Set default value for new columns
Examples:
# Create/update table from class
gemvc db:migrate UserTable
# Force sync (remove missing columns)
gemvc db:migrate UserTable --force
# Sync schema constraints
gemvc db:migrate UserTable --sync-schema
# Set default value for new columns
gemvc db:migrate UserTable --default="Active"What It Does:
- ✅ Creates table if it doesn't exist
- ✅ Adds new columns for new properties
- ✅ Updates column types if changed
- ✅ Updates nullable status
- ✅ Manages indexes
- ✅ Applies schema constraints (unique, foreign keys)
- ✅ Removes obsolete constraints (with
--sync-schema)
How It Works:
- Reads your Table class (e.g.,
UserTable.php) - Analyzes properties and types
- Generates SQL schema
- Compares with existing database
- Creates/updates as needed
Show all tables in the database.
gemvc db:listExample Output:
Tables in database 'myapp':
- users
- products
- orders
- categories
Show detailed structure of a table.
gemvc db:describe <TableName>Examples:
gemvc db:describe usersExample Output:
Table: users
Columns:
- id (INT, PRIMARY KEY, AUTO_INCREMENT)
- name (VARCHAR(255), NOT NULL)
- email (VARCHAR(320), UNIQUE, NOT NULL)
- created_at (DATETIME, NULL)
Indexes:
- PRIMARY (id)
- UNIQUE (email)
Drop a database table.
gemvc db:drop <TableName>Examples:
gemvc db:drop usersAdd a unique constraint to a table column or multiple columns.
gemvc db:unique <TableName>/<ColumnName>[,<ColumnName2>...]Examples:
# Single column
gemvc db:unique users/email
# Multiple columns (composite unique constraint)
gemvc db:unique users/email,nameWhat It Does:
- ✅ Checks for duplicate values in the specified column(s)
- ✅ If no duplicates, adds a unique constraint
- ✅ If duplicates exist, aborts and lists the duplicates
Set admin password for accessing system pages in development mode.
gemvc admin:setpasswordWhat It Does:
- ✅ Prompts for password (hidden on Unix/Linux, visible on Windows)
- ✅ Confirms password entry
- ✅ Updates
ADMIN_PASSWORDin.envfile - ✅ Stores password in plain text (acceptable for dev-only admin access)
Example:
gemvc admin:setpassword
# Enter admin password: ****
# Confirm admin password: ****
# ✓ Admin password set successfully!Note: This password is used for accessing system pages in development mode. The .env file should not be committed to version control.
Create the first admin user in the database.
gemvc admin:setadminWhat It Does:
- ✅ Checks if database is initialized (offers to initialize if not)
- ✅ Checks if UserTable is migrated (offers to migrate if not)
- ✅ Validates that no users exist (security check)
- ✅ Prompts for admin name, email, and password
- ✅ Creates admin user with role 'admin'
- ✅ Automatically handles Docker hostname to localhost conversion for CLI
Example:
gemvc admin:setadmin
# Enter admin name: John Doe
# Enter admin email: admin@example.com
# Enter admin password: ****
# Confirm admin password: ****
# ✓ Admin user created successfully!Security Features:
⚠️ Can only be used when database is empty (no existing users)- ✅ Validates email format
- ✅ Requires password confirmation
- ✅ Automatically sets user role to 'admin'
Prerequisites:
- Database must be initialized (
gemvc db:init) - UserTable must be migrated (
gemvc db:migrate UserTable)
| Flag | Description | Commands |
|---|---|---|
-c |
Create Controller | create:service |
-m |
Create Model | create:service, create:controller |
-t |
Create Table | create:service, create:controller, create:model |
-cmt |
Create all components | create:service |
| Flag | Description | Commands |
|---|---|---|
--force |
Remove columns not in class | db:migrate |
--enforce-not-null |
Enforce NOT NULL constraints | db:migrate |
--sync-schema |
Sync schema constraints | db:migrate |
--default=<value> |
Set default for new columns | db:migrate |
| Flag | Description | Commands |
|---|---|---|
--swoole |
Initialize for OpenSwoole | init |
--apache |
Initialize for Apache | init |
--nginx |
Initialize for Nginx | init |
--server=<name> |
Specify webserver | init |
--db=<driver> |
Specify database (mysql|postgres|sqlite), defaults to mysql |
init |
--mysql / --postgres / --sqlite |
Shorthand for --db=<driver> |
init |
--non-interactive or -n |
Skip prompts | init |
# 1. Initialize project
gemvc init --swoole
# 2. Initialize database
gemvc db:init
# 3. Create CRUD for Product
gemvc create:crud Product
# 4. Migrate Product table
gemvc db:migrate ProductTable
# 5. List all tables
gemvc db:list
# 6. Describe Product table
gemvc db:describe products# Create service with all components
gemvc create:service User -cmt
# Create controller with model and table
gemvc create:controller Order -mt
# Create model with table
gemvc create:model Category -t
# Create complete CRUD
gemvc create:crud Product# Initialize database
gemvc db:init
# Create/update User table
gemvc db:migrate UserTable
# Force sync User table
gemvc db:migrate UserTable --force --sync-schema
# Add unique constraint
gemvc db:unique users email
# List all tables
gemvc db:list
# Describe table
gemvc db:describe users
# Drop table (careful!)
gemvc db:drop test_tableError: Command 'create:service' not found
Solution:
# Ensure you're in project root
cd /path/to/your/project
# Ensure Composer autoload is up to date
composer dump-autoload
# Try running command
php vendor/bin/gemvc create:service ProductError: Template not found: service
Solution:
# Ensure templates are copied during init
gemvc init
# Or manually copy templates
cp -r vendor/gemvc/swoole/src/CLI/templates templates
# Verify templates exist
ls templates/cli/Error: Failed to connect to database
Solution:
- Check
.envfile exists - Verify database credentials:
DB_HOST_CLI_DEV=localhost DB_ROOT_USER=root DB_ROOT_PASSWORD=password DB_NAME=myapp
- Ensure database server is running
- Test connection:
gemvc db:init
Symptom: db:describe table borders or init prompts are hard to read or missing on macOS Terminal.
Solution (fixed in 5.6.7+): Update to GEMVC 5.6.7 or later. The CLI uses blue ANSI accents instead of cyan for cross-platform compatibility. Ensure your terminal has ANSI colors enabled.
Error: Failed to migrate table
Solution:
- Check Table class exists:
app/table/ProductTable.php - Verify class extends
Table - Check
getTable()method returns table name - Check property types are valid PHP types
- Try with
--forceflag:gemvc db:migrate ProductTable --force
Error: File already exists: app/api/Product.php
Solution:
- Delete existing file and regenerate
- Or manually edit the existing file
- Or use a different name
Instead of creating components separately:
# ✅ Recommended
gemvc create:crud Product
# ❌ Not recommended
gemvc create:service Product -cmtAlways migrate after creating table classes:
gemvc create:crud Product
gemvc db:migrate ProductTableCustomize templates in templates/cli/:
# Edit templates
vim templates/cli/service.template
# Generate code (uses your custom template)
gemvc create:crud ProductCheck code quality:
gemvc create:crud Product
vendor/bin/phpstan analyseUse PascalCase for service/model names:
# ✅ Good
gemvc create:crud Product
gemvc create:crud UserProfile
# ❌ Avoid
gemvc create:crud product
gemvc create:crud user_profile| Command | Description | Example |
|---|---|---|
init |
Initialize project | gemvc init --swoole |
create:service |
Create API service | gemvc create:service Product -cmt |
create:controller |
Create controller | gemvc create:controller Product -mt |
create:model |
Create model | gemvc create:model Product -t |
create:table |
Create table class | gemvc create:table Product |
create:crud |
Create complete CRUD | gemvc create:crud Product |
db:init |
Initialize database | gemvc db:init |
db:migrate |
Migrate table | gemvc db:migrate ProductTable |
db:list |
List tables | gemvc db:list |
db:describe |
Describe table | gemvc db:describe products |
db:drop |
Drop table | gemvc db:drop products |
db:unique |
Add unique constraint | gemvc db:unique users/email |
admin:setpassword |
Set admin password | gemvc admin:setpassword |
admin:setadmin |
Create first admin user | gemvc admin:setadmin |
GEMVC CLI provides:
- ✅ Project Management - Initialize projects with different webservers
- ✅ Code Generation - Generate Services, Controllers, Models, Tables
- ✅ Database Management - Migrate, list, describe, drop tables, add constraints
- ✅ Admin Management - Set admin password, create first admin user
- ✅ Docker Integration - Automatic container building with pre-flight checks
- ✅ Template System - Customizable code generation templates
- ✅ Non-Interactive Mode - Suitable for CI/CD pipelines
Start Building:
gemvc init --swoole
gemvc create:crud Product
gemvc db:migrate ProductTableHappy coding! 🚀
Want to create your own CLI command? Follow this pattern:
1. Create Command Class:
<?php
namespace App\CLI\Commands;
use Gemvc\CLI\Command;
class MyCustomCommand extends Command
{
public function execute(): bool
{
// Use ProjectHelper for paths
$rootDir = \Gemvc\Helper\ProjectHelper::rootDir();
// Use inherited methods
$this->info("Processing...");
// Access arguments
$name = $this->args[0] ?? 'default';
// Do your work
$this->success("Done!");
return true;
}
}2. Register in CommandCategories (or create custom mapping):
// Add to CommandCategories.php
public static function getCommandClass(string $command): string
{
$commandMappings = [
// ... existing commands
'my:command' => 'MyCustomCommand',
];
}3. Use Command:
gemvc my:commandDuring gemvc init, you'll be asked about Docker services:
gemvc init
# ... webserver selection ...
# Docker Services Setup prompt appearsInteractive Flow:
-
Service Selection:
Set up Docker services? (y/N): y Select services: - Redis [Y/n]: y - phpMyAdmin [Y/n]: y - MySQL [Y/n]: y -
MySQL Mode Selection:
MySQL Configuration Mode: [1] Development Mode - Clean logs [2] Production Mode - Verbose logs -
Docker Cleanup (optional):
Clean up existing Docker containers? (y/N): y
Result: docker-compose.yml is generated with selected services!
Usage:
# Start all services
docker compose up -d
# View logs
docker compose logs -f
# Stop services
docker compose downWant to add support for a new webserver? Follow this pattern:
1. Create Init Class:
<?php
namespace Gemvc\CLI\Commands;
use Gemvc\CLI\AbstractInit;
class InitNginx extends AbstractInit
{
protected function getWebserverType(): string
{
return 'Nginx';
}
protected function getWebserverSpecificDirectories(): array
{
return ['nginx-config'];
}
protected function copyWebserverSpecificFiles(): void
{
// Copy Nginx-specific files
// - nginx.conf
// - Nginx composer.json
}
protected function getDefaultPort(): int
{
return 80;
}
protected function getStartCommand(): string
{
return 'nginx -g "daemon off;"';
}
}2. Register in InitProject:
// Add to InitProject::WEBSERVER_OPTIONS
'3' => [
'name' => 'Nginx',
'class' => InitNginx::class,
'package' => 'gemvc/nginx',
'status' => 'available',
]3. Done! You've added a new webserver using the Template Method pattern.
Finding Project Root:
// ProjectHelper::rootDir()
// 1. Starts from current directory
// 2. Walks up directory tree
// 3. Looks for composer.lock file
// 4. Returns directory containing composer.lock
// 5. Caches result for performanceLoading Environment:
// ProjectHelper::loadEnv()
// 1. Try root/.env first
// 2. Fallback to app/.env
// 3. Uses Symfony Dotenv for parsing
// 4. Throws exception if neither foundUsed By:
- Database commands (find project root, load DB config)
- Code generation (find project root, determine paths)
- All commands that need environment variables
CLI: gemvc create:crud Product
↓
bin/gemvc parses: command='create:crud', args=['Product']
↓
CommandCategories::getCommandClass('create:crud')
Returns: 'CreateCrud'
↓
new CreateCrud(['Product'])
↓
CreateCrud extends AbstractBaseCrudGenerator extends AbstractBaseGenerator extends Command
↓
CreateCrud::execute()
├─ Uses AbstractBaseGenerator::getTemplate()
│ ├─ Checks: templates/cli/service.template (project root)
│ └─ Fallback: vendor/.../templates/cli/service.template
├─ Uses AbstractBaseGenerator::replaceTemplateVariables()
│ - Replaces: {$serviceName} → Product
└─ Uses AbstractBaseGenerator::writeFile()
└─ FileSystemManager::writeFile()
└─ Asks for overwrite confirmation
- Code Reuse: Common functionality in base classes
- Consistency: All commands follow same patterns
- Extensibility: Easy to add new commands or webservers
- Maintainability: Changes in base classes affect all commands
- Testability: Each command is isolated and testable
CLI architecture:
- ✅
gemvc/cli-base:Command,CliColor,CliLine,CliBoxShow,FileSystemManager, generator abstracts,InstallControl - ✅
gemvc/library:AbstractInit,InitProject, webserver inits,CommandCategories,db:*/create:*, Docker wizards,templates/cli/,InstallationTest - ✅ Docs: CLI.md (this file) +
vendor/gemvc/cli-base/AI-Assistant.md
Design Patterns:
- Template Method (AbstractInit)
- Strategy (webserver selection)
- Orchestrator (InitProject)
- Factory (CommandCategories)
- Facade (FileSystemManager)
Result: Clean, maintainable, extensible CLI architecture! 🎯