Files
michaelschiemer/docs/database/new-features.md
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

330 lines
9.6 KiB
Markdown

# Database Module New Features
This document provides an overview of the new features added to the Database module.
## Table of Contents
- [Stored Procedures and Functions](#stored-procedures-and-functions)
- [Extended Index Management](#extended-index-management)
- [Enhanced Schema Versioning](#enhanced-schema-versioning)
- [Usage Examples](#usage-examples)
## Stored Procedures and Functions
The Database module now supports defining, executing, and managing stored procedures and functions across different database systems.
### StoredProcedureDefinition
The `StoredProcedureDefinition` class provides a fluent interface for defining stored procedures:
```php
use App\Framework\Database\StoredProcedure\StoredProcedureDefinition;
$procedure = StoredProcedureDefinition::create('get_user_by_id')
->withParameter('user_id', 'INT')
->withBody('SELECT * FROM users WHERE id = user_id')
->returnsType('TABLE');
```
The definition can then be converted to SQL for different database systems:
```php
// Generate MySQL-specific SQL
$mysqlSql = $procedure->toSql('mysql');
// Generate PostgreSQL-specific SQL
$pgsqlSql = $procedure->toSql('pgsql');
```
### StoredProcedureManager
The `StoredProcedureManager` class provides methods for managing stored procedures:
```php
use App\Framework\Database\StoredProcedure\StoredProcedureManager;
// Create a new manager with a database connection
$manager = new StoredProcedureManager($connection);
// Create a stored procedure
$manager->createProcedure($procedure);
// Check if a procedure exists
if ($manager->procedureExists('get_user_by_id')) {
// Execute the procedure with parameters
$result = $manager->executeProcedure('get_user_by_id', [42]);
// Process the results
foreach ($result->fetchAll() as $row) {
// ...
}
}
// Execute a stored function
$value = $manager->executeFunction('calculate_order_total', [123, true]);
// Drop a procedure
$manager->dropProcedure('get_user_by_id');
// List all procedures
$procedures = $manager->listProcedures();
```
## Extended Index Management
The Database module now supports advanced index types and features across different database systems.
### AdvancedIndexDefinition
The `AdvancedIndexDefinition` class provides a fluent interface for defining advanced indexes:
```php
use App\Framework\Database\Schema\Index\AdvancedIndexDefinition;
use App\Framework\Database\Schema\Index\AdvancedIndexType;
// Create a standard index
$index = AdvancedIndexDefinition::create(
'idx_users_email',
['email'],
AdvancedIndexType::INDEX
);
// Create a partial index (only indexes rows that match a condition)
$partialIndex = AdvancedIndexDefinition::partial(
'idx_active_users',
['email'],
AdvancedIndexType::INDEX,
'active = true'
);
// Create a functional index (indexes the result of a function)
$functionalIndex = AdvancedIndexDefinition::functional(
'idx_lower_email',
AdvancedIndexType::INDEX,
['LOWER(email)']
);
// Create a PostgreSQL GIN index (for full-text search, JSON, arrays)
$ginIndex = AdvancedIndexDefinition::gin(
'idx_document_content',
['content']
);
// Create a PostgreSQL GiST index (for geometric data, ranges)
$gistIndex = AdvancedIndexDefinition::gist(
'idx_location_position',
['position']
);
// Create a MySQL BTREE index with options
$btreeIndex = AdvancedIndexDefinition::btree(
'idx_users_name',
['first_name', 'last_name'],
['fillfactor' => 70]
);
```
The index definition can then be converted to SQL for different database systems:
```php
// Generate SQL for PostgreSQL
$sql = $index->toSql('pgsql', 'users');
// Generate SQL for MySQL
$sql = $index->toSql('mysql', 'users');
```
## Enhanced Schema Versioning
The Database module now includes enhanced schema versioning capabilities, including dependency tracking and schema comparison tools.
### Schema Comparison
The schema comparison tools allow you to compare two database schemas and generate migration code to update one schema to match the other.
```php
use App\Framework\Database\Schema\Comparison\SchemaComparator;
// Create a comparator with source and target connections
$comparator = new SchemaComparator($sourceConnection, $targetConnection);
// Compare the schemas
$difference = $comparator->compare();
// Check if there are differences
if ($difference->hasDifferences()) {
// Get a summary of the differences
$summary = $difference->getSummary();
// Get a detailed description of the differences
$description = $difference->getDescription();
// Generate migration code to update the target schema to match the source schema
$migrationCode = $difference->generateMigrationCode('UpdateSchema');
// Save the migration code to a file
file_put_contents('database/migrations/UpdateSchema.php', $migrationCode);
}
```
### Dependent Migrations
The enhanced migration system now supports dependencies between migrations:
```php
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\AbstractDependentMigration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Schema\Schema;
final class CreateCommentsTable extends AbstractDependentMigration
{
public function getVersion(): MigrationVersion
{
return MigrationVersion::fromString('20250805123456');
}
public function getDescription(): string
{
return 'Create comments table';
}
public function getDependencies(): array
{
// This migration depends on the CreateUsersTable and CreatePostsTable migrations
return [
MigrationVersion::fromString('20250805123455'), // CreateUsersTable
MigrationVersion::fromString('20250805123454') // CreatePostsTable
];
}
public function up(ConnectionInterface $connection): void
{
$schema = new Schema($connection);
$schema->create('comments', function ($table) {
$table->id();
$table->text('content');
$table->foreignId('user_id')->references('id')->on('users');
$table->foreignId('post_id')->references('id')->on('posts');
$table->timestamps();
});
$schema->execute();
}
public function down(ConnectionInterface $connection): void
{
$schema = new Schema($connection);
$schema->dropIfExists('comments');
$schema->execute();
}
}
```
The migration runner will ensure that migrations are executed in the correct order based on their dependencies.
## Usage Examples
### Creating and Using a Stored Procedure
```php
use App\Framework\Database\StoredProcedure\StoredProcedureDefinition;
use App\Framework\Database\StoredProcedure\StoredProcedureManager;
// Define a stored procedure to get active users
$procedure = StoredProcedureDefinition::create('get_active_users')
->withParameter('min_login_count', 'INT')
->withBody('
SELECT * FROM users
WHERE active = true AND login_count >= min_login_count
ORDER BY last_login DESC;
')
->returnsType('TABLE');
// Create the procedure in the database
$manager = new StoredProcedureManager($connection);
$manager->createProcedure($procedure);
// Execute the procedure
$result = $manager->executeProcedure('get_active_users', [5]);
// Process the results
foreach ($result->fetchAll() as $user) {
echo "User: {$user['name']} (Last login: {$user['last_login']})\n";
}
```
### Creating Advanced Indexes
```php
use App\Framework\Database\Schema\Index\AdvancedIndexDefinition;
use App\Framework\Database\Schema\Index\AdvancedIndexType;
// In a migration
public function up(ConnectionInterface $connection): void
{
$schema = new Schema($connection);
$schema->create('posts', function ($table) {
$table->id();
$table->string('title');
$table->text('content');
$table->boolean('published')->default(false);
$table->timestamps();
});
$schema->execute();
// Create a partial index on published posts
$partialIndex = AdvancedIndexDefinition::partial(
'idx_published_posts',
['title'],
AdvancedIndexType::INDEX,
'published = true'
);
// Create a functional index for case-insensitive search
$functionalIndex = AdvancedIndexDefinition::functional(
'idx_lower_title',
AdvancedIndexType::INDEX,
['LOWER(title)']
);
// Generate and execute SQL for the current database
$driver = $connection->getPdo()->getAttribute(\PDO::ATTR_DRIVER_NAME);
$connection->execute($partialIndex->toSql($driver, 'posts'));
$connection->execute($functionalIndex->toSql($driver, 'posts'));
}
```
### Comparing Schemas and Generating Migrations
```php
use App\Framework\Database\Schema\Comparison\SchemaComparator;
// Compare development and production schemas
$comparator = new SchemaComparator($devConnection, $prodConnection);
$difference = $comparator->compare();
if ($difference->hasDifferences()) {
// Print a summary of the differences
$summary = $difference->getSummary();
echo "Found {$summary['total_differences']} differences:\n";
echo " Missing tables: {$summary['missing_tables']}\n";
echo " Extra tables: {$summary['extra_tables']}\n";
echo " Modified tables: {$summary['modified_tables']}\n";
// Generate migration code
$timestamp = date('YmdHis');
$className = "UpdateProductionSchema{$timestamp}";
$migrationCode = $difference->generateMigrationCode($className);
// Save the migration
$migrationPath = "database/migrations/{$className}.php";
file_put_contents($migrationPath, $migrationCode);
echo "Generated migration: {$migrationPath}\n";
}
```