Audience: customizing gemvc create:* output.
Related: cli.md · installation.md · controller.md · model.md
Customize gemvc create:* output (needs gemvc/cli-dev). Overrides live under {project}/templates/cli/; if missing, codegen falls back to vendor/gemvc/cli-dev/templates/cli/. Init does not guarantee create stubs — see How It Works.
| Need | Jump to |
|---|---|
| Lookup order (project → vendor) | How It Works |
| Placeholders | Template Variables |
| Per-layer files (roles) | Available Templates |
| Override path | Template Structure Reference |
| Failures | Troubleshooting |
gemvc create:* (from gemvc/cli-dev) resolves templates in this order:
{projectRoot}/templates/cli/{name}.template(your overrides)vendor/gemvc/cli-dev/templates/cli/{name}.template(shipped defaults)
gemvc init may copy gemvc/library’s src/CLI/templates/ into {project}/templates/ if that folder exists (FileSystemManager::copyTemplatesFolder). Create stubs themselves live in cli-dev, not library — so after install you usually either rely on the cli-dev vendor fallback or copy them once:
mkdir -p templates/cli
cp vendor/gemvc/cli-dev/templates/cli/*.template templates/cli/After initialization, you can edit templates in your project root:
# Edit with your own coding style
vim templates/cli/service.template
vim templates/cli/controller.template
# etc.Why customize?
- Match your team's coding standards
- Add custom helper methods
- Change code structure/patterns
- Add project-specific comments
- Integrate with your existing codebase patterns
When generating code, GEMVC uses a smart template lookup:
// DevGenerator::getTemplate() (cli-dev)
1. First checks: {projectRoot}/templates/cli/{templateName}.template (Custom)
2. Fallback: vendor/gemvc/cli-dev/templates/cli/{templateName}.template (Default)Example: Running gemvc create:crud Product
- Uses your custom
templates/cli/service.templateif it exists - Falls back to vendor template if not found
- Replaces
{$serviceName}→Product,{$tableName}→products - Generates:
app/api/Product.php,app/controller/ProductController.php, etc.
Templates use placeholder variables that get replaced during generation:
| Variable | Description | Example Input | Example Output |
|---|---|---|---|
{$serviceName} |
Class name (PascalCase) | Product |
Product |
{$tableName} |
Database table name (snake_case) | products |
products |
{$variableName} |
Custom variables (defined per generator) | Various | Various |
Code Reference: AbstractBaseGenerator::replaceTemplateVariables()
protected function replaceTemplateVariables(string $content, array $variables): string
{
foreach ($variables as $key => $value) {
$content = str_replace('{$' . $key . '}', $value, $content);
}
return $content;
}- Generates:
app/api/{ServiceName}.php - Extends:
ApiServiceorSwooleApiService - Methods:
create(),read(),update(),delete(),list() - Includes: Schema validation, JWT auth, mock responses
Variables:
{$serviceName}- Service class name
Usage: gemvc create:service Product
- Generates:
app/controller/{ServiceName}Controller.php - Extends:
Controller - Methods:
create(),read(),update(),delete(),list() - Includes: Model delegation, error handling
Variables:
{$serviceName}- Controller class name
Usage: gemvc create:controller Product
- Generates:
app/model/{ServiceName}Model.php - Extends:
{ServiceName}Table - Methods:
createModel(),readModel(),updateModel(),deleteModel() - Includes: CRUD operations, error handling
Variables:
{$serviceName}- Model class name
Usage: gemvc create:model Product
- Generates:
app/table/{ServiceName}Table.php - Extends:
Table - Includes: Properties, schema definition, type mapping, query methods
Variables:
{$serviceName}- Table class name{$tableName}- Database table name
Usage: gemvc create:table Product
Original template (service.template):
class {$serviceName} extends ApiService
{
public function create(): JsonResponse
{
// ... code
}
}Customized template:
/**
* {$serviceName} API Service
*
* @custom-note This service handles all {$serviceName} operations
* @team Backend Team
* @last-updated 2024
*/
class {$serviceName} extends ApiService
{
/**
* Create new {$serviceName}
* Custom implementation with additional logging
*/
public function create(): JsonResponse
{
// Log the request
error_log("Creating {$serviceName}: " . json_encode($this->request->post));
// ... original code
}
}Original: Uses mapPostToObject() pattern
Customized: Use direct property assignment
public function create(): JsonResponse
{
$model = new {$serviceName}Model();
$model->name = $this->request->post['name'] ?? '';
$model->description = $this->request->post['description'] ?? '';
return $model->createModel();
}class {$serviceName}Controller extends Controller
{
// ... standard CRUD methods ...
/**
* Custom helper method
*/
private function validateBusinessRules({$serviceName}Model $model): bool
{
// Your custom validation logic
return true;
}
/**
* Custom bulk operation
*/
public function bulkCreate(): JsonResponse
{
// Your bulk operation logic
}
}If your team uses Repository pattern:
class {$serviceName}Controller extends Controller
{
private {$serviceName}Repository $repository;
public function __construct(Request $request)
{
parent::__construct($request);
$this->repository = new {$serviceName}Repository();
}
public function create(): JsonResponse
{
return $this->repository->create($this->request->post);
}
}Developer runs: gemvc create:crud Product
↓
AbstractBaseGenerator::getTemplate('service')
↓
Check 1: {projectRoot}/templates/cli/service.template
├─ EXISTS → Use custom template
└─ NOT FOUND → Continue to Check 2
↓
Check 2: vendor/gemvc/cli-dev/templates/cli/service.template
├─ EXISTS → Use default template (with warning)
└─ NOT FOUND → Throw error
↓
Load template content
↓
AbstractBaseGenerator::replaceTemplateVariables()
Replace: {$serviceName} → Product
Replace: {$tableName} → products
↓
Write to: app/api/Product.php
# Add templates to git
git add templates/cli/*.template
git commit -m "Customize code generation templates"# Before customizing, backup originals
cp templates/cli/service.template templates/cli/service.template.backup/**
* Template Variables:
* - {$serviceName}: Product
* - {$tableName}: products
* - {$author}: John Doe (custom)
*/# Generate test code before committing template changes
gemvc create:crud TestEntity
# Review generated code
# Adjust template if neededCreate base templates and extend them:
templates/cli/
├── base-service.template # Common code
├── service.template # Includes base + specificsYou can extend generators to add custom variables:
// In your custom generator class
protected function getTemplateVariables(): array
{
return [
'serviceName' => $this->serviceName,
'tableName' => $this->tableName,
'author' => get_current_user(), // Custom variable
'date' => date('Y-m-d'), // Custom variable
];
}
// In template
/**
* Generated by: {$author}
* Date: {$date}
*/
class {$serviceName} extends ApiService
{
// ...
}- Vendor:
vendor/gemvc/cli-dev/templates/cli/ - Project:
{projectRoot}/templates/cli/
service.template- API service layercontroller.template- Controller layermodel.template- Model layertable.template- Table/data access layer
- PHP code with placeholder variables
- Variables use
{$variableName}syntax - Supports any valid PHP code structure
# 1. Initialize project
gemvc init
# Select: Apache
# 2. Review default templates
cat templates/cli/service.template
# 3. Customize templates for your team
vim templates/cli/service.template
# Add custom comments, change structure, etc.
# 4. Commit templates to version control
git add templates/cli/
git commit -m "Customize code generation templates"
# 5. Generate code using custom templates
gemvc create:crud Product
# Generated code matches your custom template!
# 6. Team members get same code style
git pull
gemvc create:crud Category
# Uses same custom templates!Template not found: service (checked: /path/to/templates/cli/service.template, /vendor/...)
Solution:
- Ensure templates were copied during
gemvc init - Check
templates/cli/directory exists - Verify template file names match exactly
Warning: Using vendor template for service - consider copying templates to project root
Solution: Templates in project root take priority. If you see this warning, your custom templates aren't being used. Check:
- File path:
templates/cli/service.template - File permissions
- Template file exists
If {$serviceName} appears in generated code:
Solution:
- Check variable name spelling (case-sensitive)
- Ensure generator calls
replaceTemplateVariables() - Verify variable name matches template placeholder
Key Benefits:
- Customize code style per project/team
- Maintain consistency across generated code
- Version control your templates
- Easy updates - just edit template files
- Team alignment - shared templates = shared style
Template Priority:
- Project root templates (custom) ← Highest priority
- Vendor templates (default) ← Fallback
Result: Write code generation templates once, generate consistent code forever! 🎉