Files
michaelschiemer/docs/claude/migration-helper-examples.md
2025-11-24 21:28:25 +01:00

369 lines
9.8 KiB
Markdown

# Migration Helper Examples
This document shows how to use the new `MigrationHelper` service to simplify migration code and explains the improved migration generation system.
## Overview
The `MigrationHelper` provides a simplified API for common migration operations without requiring abstract classes. It uses composition and follows framework principles.
The improved `MigrationGenerator` now:
- Automatically determines namespace from file path using `PathProvider`
- Uses `PhpNamespace` Value Object for type-safe namespace handling
- Automatically detects if migration should be `SafelyReversible` based on name patterns
- Generates cleaner code using `MigrationHelper` by default
## Basic Usage
### Before (Old Way)
```php
final readonly class CreateUsersTable implements Migration
{
public function up(ConnectionInterface $connection): void
{
$schema = new Schema($connection);
$schema->createIfNotExists('users', function (Blueprint $table) {
$table->ulid('ulid')->primary();
$table->string('name', 255);
$table->timestamps();
});
$schema->execute();
}
public function getVersion(): MigrationVersion
{
return MigrationVersion::fromTimestamp('2024_01_15_000001');
}
public function getDescription(): string
{
return 'Create Users Table';
}
}
```
### After (New Way with Helper)
```php
final readonly class CreateUsersTable implements Migration, SafelyReversible
{
public function up(ConnectionInterface $connection): void
{
$helper = new MigrationHelper($connection);
$helper->createTable('users', function (Blueprint $table) {
$table->ulid('ulid')->primary();
$table->string('name', 255);
$table->timestamps();
});
}
public function down(ConnectionInterface $connection): void
{
$helper = new MigrationHelper($connection);
$helper->dropTable('users');
}
public function getVersion(): MigrationVersion
{
return MigrationVersion::fromTimestamp('2024_01_15_000001');
}
public function getDescription(): string
{
return 'Create Users Table';
}
}
```
## Available Helper Methods
### Creating Tables
```php
$helper = new MigrationHelper($connection);
$helper->createTable('users', function (Blueprint $table) {
$table->ulid('ulid')->primary();
$table->string('name', 255);
$table->string('email', 255)->unique();
$table->timestamps();
});
```
### Dropping Tables
```php
$helper->dropTable('users');
```
### Adding Columns
```php
// Simple column
$helper->addColumn('users', 'phone', 'string', ['length' => 20]);
// Column with options
$helper->addColumn('users', 'age', 'integer', [
'nullable' => false,
'default' => 0
]);
// Decimal column
$helper->addColumn('orders', 'total', 'decimal', [
'precision' => 10,
'scale' => 2,
'nullable' => false
]);
```
### Adding Indexes
```php
// Simple index
$helper->addIndex('users', ['email']);
// Named index
$helper->addIndex('users', ['name', 'email'], 'idx_users_name_email');
// Unique index
$helper->addUniqueIndex('users', ['email'], 'uk_users_email');
```
### Dropping Columns
```php
$helper->dropColumn('users', 'phone');
```
### Renaming Tables
```php
$helper->renameTable('old_table_name', 'new_table_name');
```
## Advanced: Using Schema Directly
For complex operations, you can still use Schema directly:
```php
public function up(ConnectionInterface $connection): void
{
$helper = new MigrationHelper($connection);
$schema = $helper->schema();
// Complex operations
$schema->table('users', function (Blueprint $table) {
$table->dropColumn('old_column');
$table->renameColumn('old_name', 'new_name');
$table->addColumn('new_column', 'string', 255);
});
$schema->execute();
}
```
## Benefits
1. **Less Boilerplate**: No need to manually create Schema and call execute()
2. **Consistent API**: All helper methods follow the same pattern
3. **Type Safe**: Uses Value Objects and proper types
4. **Composable**: Can mix helper methods with direct Schema usage
5. **No Inheritance**: Uses composition, follows framework principles
## Migration Generator
The `MigrationGenerator` has been significantly improved with automatic namespace detection and smarter code generation.
### Automatic Namespace Detection
The generator uses `PathProvider` to automatically determine the correct namespace from the file path, making it work with any project structure:
```php
// When generating: php console.php make:migration CreateUsersTable User
// Path: src/Domain/User/Migrations/CreateUsersTable.php
// Namespace automatically determined: App\Domain\User\Migrations
```
**How it works:**
1. `PathProvider` reads `composer.json` to understand PSR-4 autoloading rules
2. The migration directory path is analyzed
3. `PhpNamespace` Value Object is created from the path
4. Fallback to default structure if path doesn't match PSR-4 rules
**Benefits:**
- Works with any namespace structure configured in `composer.json`
- No hardcoded namespace assumptions
- Type-safe namespace handling via `PhpNamespace` Value Object
- Automatically adapts to project structure changes
### Automatic SafelyReversible Detection
The generator analyzes migration names to determine if they should implement `SafelyReversible`:
**Safe patterns (automatically reversible):**
- `Create*` - Creating tables can be rolled back
- `Add*` - Adding columns/indexes can be removed
- `Index*` - Index operations are reversible
- `Constraint*` - Constraints can be dropped
- `Rename*` - Renaming can be reversed
**Unsafe patterns (forward-only):**
- `Drop*` - Dropping tables/columns loses data
- `Delete*` - Data deletion cannot be reversed
- `Remove*` - Removing columns loses data
- `Truncate*` - Truncating loses all data
- `Alter*Type` - Type changes may lose data
**Example:**
```bash
# Creates SafelyReversible migration
php console.php make:migration CreateUsersTable User
# Creates forward-only migration
php console.php make:migration DropOldColumns User
```
### Generated Code Examples
**Create Table Migration (SafelyReversible):**
```bash
php console.php make:migration CreateUsersTable User
```
Generates:
```php
<?php
declare(strict_types=1);
namespace App\Domain\User\Migrations;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Migration\Services\MigrationHelper;
use App\Framework\Database\Migration\SafelyReversible;
final readonly class CreateUsersTable implements Migration, SafelyReversible
{
public function up(ConnectionInterface $connection): void
{
$helper = new MigrationHelper($connection);
// TODO: Implement your migration here
// Example:
// $helper->createTable('users', function($table) {
// $table->ulid('ulid')->primary();
// $table->string('name', 255);
// $table->timestamps();
// });
}
public function down(ConnectionInterface $connection): void
{
$helper = new MigrationHelper($connection);
$helper->dropTable('users'); // Auto-generated from name
}
public function getVersion(): MigrationVersion
{
return MigrationVersion::fromTimestamp('2024_01_15_120000');
}
public function getDescription(): string
{
return 'Create Users Table';
}
}
```
**Forward-Only Migration:**
```bash
php console.php make:migration DropOldColumns User
```
Generates:
```php
<?php
declare(strict_types=1);
namespace App\Domain\User\Migrations;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\Migration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Migration\Services\MigrationHelper;
final readonly class DropOldColumns implements Migration
{
public function up(ConnectionInterface $connection): void
{
$helper = new MigrationHelper($connection);
// TODO: Implement your migration here
// Example:
// $helper->dropColumn('users', 'old_column');
}
// No down() method - forward-only migration
public function getVersion(): MigrationVersion
{
return MigrationVersion::fromTimestamp('2024_01_15_120001');
}
public function getDescription(): string
{
return 'Drop Old Columns';
}
}
```
### Custom Namespace Structures
The generator works with any namespace structure defined in `composer.json`:
```json
{
"autoload": {
"psr-4": {
"App\\": "src/",
"Custom\\Namespace\\": "custom/path/"
}
}
}
```
If you generate a migration in `custom/path/Migrations/`, the namespace will automatically be `Custom\Namespace\Migrations`.
### MigrationMetadata Value Object
For advanced use cases, you can use `MigrationMetadata` to work with migration metadata:
```php
use App\Framework\Database\Migration\ValueObjects\MigrationMetadata;
use App\Framework\Core\ValueObjects\PhpNamespace;
$namespace = PhpNamespace::fromString('App\Domain\User\Migrations');
$metadata = MigrationMetadata::create(
version: MigrationVersion::fromTimestamp('2024_01_15_120000'),
description: 'Create Users Table',
namespace: $namespace,
domain: 'User',
author: 'John Doe'
);
// Metadata automatically extracts domain from namespace
// $metadata->domain === 'User'
```
## Best Practices
1. **Use MigrationHelper for common operations** - Reduces boilerplate
2. **Let generator detect SafelyReversible** - Don't manually add it unless needed
3. **Trust PathProvider for namespaces** - It handles PSR-4 correctly
4. **Use PhpNamespace Value Objects** - Type-safe namespace operations
5. **Keep migrations simple** - Use helper methods, fall back to Schema for complex cases