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
This commit is contained in:
329
docs/database/new-features.md
Normal file
329
docs/database/new-features.md
Normal file
@@ -0,0 +1,329 @@
|
||||
# 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";
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user