dmamontov/clickhouse-migrations-bundle
Installation:
composer require dmamontov/clickhouse-migrations-bundle
Add the bundle to config/bundles.php if not auto-discovered.
Configure ClickHouse Client:
Define the ClickHouseDB\Client service in config/services.yaml:
services:
ClickHouseDB\Client:
arguments:
$connectParams:
host: 'http://localhost'
port: 8123
username: 'default'
password: ''
$settings:
database: 'your_database'
Verify Bundle Config:
Ensure config/packages/clickhouse_migrations.yaml exists (defaults are fine for most cases).
First Migration: Generate and run a migration:
bin/console clickhouse:migrations:generate
bin/console clickhouse:migrations:migrate
bin/console clickhouse:migrations:generate --name=create_users_table
Edit the generated file in clickhouse_migrations/ (e.g., YYYYMMDDHHMMSS_create_users_table.php) to include your SQL (e.g., CREATE TABLE users (...) ENGINE = MergeTree()).Migration Development:
--name for clarity:
bin/console clickhouse:migrations:generate --name=add_index_to_users
public function up(Connection $connection): void
{
$connection->execute("ALTER TABLE users ADD INDEX idx_email (email)");
}
public function down(Connection $connection): void
{
$connection->execute("ALTER TABLE users DROP INDEX idx_email");
}
Running Migrations:
bin/console clickhouse:migrations:migrate
bin/console clickhouse:migrations:rollback
bin:console clickhouse:migrations:reset
Integration with Symfony Lifecycle:
deploy.php):
$this->run("php bin/console clickhouse:migrations:migrate --no-interaction");
Custom Migration Paths:
Override migrations_path in clickhouse_migrations.yaml to store migrations in a version-controlled directory (e.g., config/migrations).
Environment-Specific Migrations:
Use Symfony’s %env% or %kernel.environment% to conditionally load migrations:
migrations_path: '%kernel.project_dir%/migrations/%kernel.environment%'
Dependency Management:
Ensure migrations run in a specific order by naming them logically (e.g., create_table_x_before_table_y.php).
Testing:
Use migrations:reset in phpunit.xml to ensure a clean state for tests:
<env name="KERNEL_CLASS" value="App\Tests\AppKernel"/>
<env name="APP_ENV" value="test"/>
<env name="APP_DEBUG" value="true"/>
<env name="CLICKHOUSE_MIGRATIONS_RESET" value="true"/>
Connection Issues:
ConnectionException.ClickHouseDB\Client is properly configured in services.yaml. Test connectivity with:
bin/console debug:container ClickHouseDB\Client
bin/console clickhouse:migrations:migrate --verbose
Migration Table Conflicts:
migrations_versions table already exists but is corrupted.DROP TABLE IF EXISTS migrations_versions;
Then run:
bin/console clickhouse:migrations:reset
SQL Syntax Errors:
down()).Namespace Collisions:
migrations_namespace in clickhouse_migrations.yaml:
migrations_namespace: 'App\Migrations\ClickHouse'
Dry Runs:
Use --dry-run to preview SQL without executing:
bin/console clickhouse:migrations:migrate --dry-run
Logging:
Enable debug mode in config/packages/dev/clickhouse_migrations.yaml:
clickhouse_migrations:
debug: true
Custom Migration Commands: Extend the bundle by creating a custom command (e.g., for batch migrations):
// src/Command/CustomMigrationCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\DBAL\Connection;
class CustomMigrationCommand extends Command
{
protected static $defaultName = 'app:clickhouse:batch-migrate';
private $connection;
public function __construct(Connection $connection)
{
parent::__construct();
$this->connection = $connection;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->connection->execute("SOURCE /path/to/batch.sql");
return Command::SUCCESS;
}
}
Pre/Post-Migration Hooks:
Subscribe to the clickhouse_migrations.migrate event in a service:
# config/services.yaml
services:
App\EventListener\MigrationListener:
tags:
- { name: kernel.event_listener, event: clickhouse_migrations.migrate, method: onMigrate }
Custom Migration Table:
Override the table_name to use an existing table (e.g., for multi-tenant setups):
clickhouse_migrations:
table_name: 'tenant_1_migrations'
Idempotent Migrations: Design migrations to be safely rerunnable (e.g., check for table existence):
public function up(Connection $connection): void
{
$connection->execute("CREATE TABLE IF NOT EXISTS users (...) ENGINE = MergeTree()");
}
Backup First: Always back up your ClickHouse database before running migrations in production:
clickhouse-client --query="CREATE DATABASE backup_db AS backup_db CLONE database_name"
Atomic Migrations: Use transactions where supported (ClickHouse 21.6+):
public function up(Connection $connection): void
{
$connection->beginTransaction();
try {
$connection->execute("CREATE TABLE users (...)");
$connection->commit();
} catch (\Exception $e) {
$connection->rollBack();
throw $e;
}
}
How can I help you explore Laravel packages today?