- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
330 lines
9.6 KiB
Markdown
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";
|
|
}
|
|
```
|