phpclickhouse-laravel

repository·master·Indexed 42 days ago

https://github.com/glushkovds/phpclickhouse-laravel

A Laravel and Lumen adapter for ClickHouse that provides a bridge to the phpClickHouse and ClickhouseBuilder libraries. It features an Eloquent-like model layer via BaseModel, a specialized Schema Builder for ClickHouse migrations, and support for cluster mode, lightweight updates, and bulk insertions.

Tokens
8.2K
Snippets
33
Records
37
Agent score
52%

What's inside phpclickhouse-laravel

  1. Configure and use ClickHouse Cluster mode

    master

    Cluster mode allows you to define multiple nodes in config/database.php.

    Important Constraints:

    • All nodes must use the same database name, username, and password.
    • Reads and writes use the first available node.
    • Migrations run on all nodes (will fail if one node is unavailable).

    You can manually switch to the next node in the cluster using slideNode().

    // config/database.php
    'clickhouse' => [
        'driver' => 'clickhouse',
        'cluster' => [
            ['host' => 'clickhouse01', 'port' => 8123],
            ['host' => 'clickhouse02', 'port' => 8123],
        ],
        // ... other settings
    ];
    
    // Switching nodes
    $row = new MyTable();
    echo $row->getThisClient()->getConnectHost(); // 'clickhouse01'
    $row->resolveConnection()->getCluster()->slideNode();
    echo $row->getThisClient()->getConnectHost(); // 'clickhouse02'
  2. Update query results when using fix_default_query_builder

    master

    Enabling 'fix_default_query_builder' => true changes the return type of certain query builder methods. Specifically, methods that previously returned a Laravel Collection via ->get() will now return a Statement object.

    To retrieve the actual data as an array, you must chain the ->rows() method after ->get().

    // Old way (returns Collection - will break with fix enabled):
    $rows = DB::table('my-table')->get();
    
    // New way (returns Statement, then convert to array):
    $rows = DB::table('my-table')->get()->rows();
  3. Configure multiple ClickHouse connections

    master

    To use multiple ClickHouse instances, add a new connection to config/database.php, specify the $connection property in your model, and use the corresponding connection in your migrations.

    // 1. config/database.php
    'clickhouse2' => [
        'driver' => 'clickhouse',
        'host' => 'clickhouse2',
        // ... other settings
    ],
    
    // 2. Model
    class MyTable2 extends BaseModel
    {
        protected $connection = 'clickhouse2';
        protected $table = 'my_table2';
    }
    
    // 3. Migration
    return new class extends \PhpClickHouseLaravel\Migration
    {
        protected $connection = 'clickhouse2';
        public function up() { ... }
    };
  4. Run local tests using Docker Compose

    master

    To run the project's test suite locally, use the provided Docker Compose configuration. This command starts the necessary services, executes the test bootstrap script within the php container, and then tears down the environment, removing orphaned containers and volumes to ensure a clean state.

    sudo sh -c "docker compose -f docker-compose.test.yaml run php sh /src/tests.bootstrap.sh; docker compose -f docker-compose.test.yaml down --remove-orphans -v"
  5. Create ClickHouse migrations

    master

    Migrations in this package extend \PhpClickHouseLaravel\Migration. You can write raw SQL or use the Schema Builder.

    Raw SQL Migrations

    Use static::write() to execute raw DDL statements.

    class CreateMyTable extends \PhpClickHouseLaravel\Migration
    {
        public function up()
        {
            static::write("
                CREATE TABLE my_table (
                    id UInt32,
                    created_at DateTime,
                    field_one String,
                    field_two Int32
                )
                ENGINE = MergeTree()
                ORDER BY (id)
            ");
        }
    
        public function down()
        {
            static::write('DROP TABLE my_table');
        }
    }

    Schema Builder Migrations

    Use the createMergeTree method for a more programmatic approach.

    use PhpClickHouseLaravel\MergeTree;
    use PhpClickHouseLaravel\Expression;
    
    class CreateMyTable extends \PhpClickHouseLaravel\Migration
    {
        public function up()
        {
            static::createMergeTree('my_table', fn(MergeTree $table) => $table
                ->columns([
                    $table->uInt32('id'),
                    $table->datetime('created_at', 3)->default(new Expression('now64()')),
                    $table->string('field_one'),
                    $table->int32('field_two'),
                ])
                ->orderBy('id')
            );
        }
    
        public function down()
        {
            static::write('DROP TABLE my_table');
        }
    }
    <?php
    
    class CreateMyTable extends \PhpClickHouseLaravel\Migration
    {
        public function up()
        {
            static::createMergeTree('my_table', fn(\PhpClickHouseLaravel\MergeTree $table) => $table
                ->columns([
                    $table->uInt32('id'),
                    $table->datetime('created_at', 3)->default(new \PhpClickHouseLaravel\Expression('now64()')),
                    $table->string('field_one'),
                    $table->int32('field_two'),
                ])
                ->orderBy('id')
            );
        }
    
        public function down()
        {
            static::write('DROP TABLE my_table');
        }
    }
  6. Install phpClickHouse-laravel

    master

    Install the package via Composer. In Laravel, the service provider is discovered automatically. If you are using Lumen or have disabled package discovery, you must register the service provider manually in your application bootstrap.

    $ composer require glushkovds/phpclickhouse-laravel
    // Manual registration for Lumen or disabled discovery
    $app->register(\PhpClickHouseLaravel\ClickhouseServiceProvider::class);
  7. Use the Lightweight Eloquent-like model layer

    master

    The package provides a BaseModel to interact with ClickHouse tables using an Eloquent-like syntax.

    1. Define a Model

    Extend PhpClickHouseLaravel\BaseModel. The table name is resolved from the $table property (defaults to the snake_case version of the class name).

    namespace App\Models\Clickhouse;
    
    use PhpClickHouseLaravel\BaseModel;
    
    class MyTable extends BaseModel
    {
        protected $table = 'my_table';
    }

    2. Insert Data

    Single Row Insertion:

    // Using create
    $model = MyTable::create(['model_name' => 'model 1', 'some_param' => 1]);
    
    // Using make and save
    $model = MyTable::make(['model_name' => 'model 1']);
    $model->some_param = 1;
    $model->save();
    
    // Using new and fill
    $model = new MyTable();
    $model->fill(['model_name' => 'model 1', 'some_param' => 1])->save();

    Bulk Insertion:

    // Non-associative (positional values)
    MyTable::insertBulk([['model 1', 1], ['model 2', 2]], ['model_name', 'some_param']);
    
    // Associative (key-value pairs)
    MyTable::insertAssoc([['model_name' => 'model 1', 'some_param' => 1], ['some_param' => 2, 'model_name' => 'model 2']]);

    3. Query Data

    Use a fluent interface to build queries. Use RawColumn for aggregate functions and getRows() to execute.

    use PhpClickHouseLaravel\RawColumn;
    
    $rows = MyTable::select(['field_one', new RawColumn('sum(field_two)', 'field_two_sum')])
        ->where('created_at', '>', '2020-09-14 12:47:29')
        ->groupBy('field_one')
        ->settings(['max_threads' => 3])
        ->getRows();
    <?php
    
    namespace App\Models\Clickhouse;
    
    use PhpClickHouseLaravel\BaseModel;
    
    class MyTable extends BaseModel
    {
        protected $table = 'my_table';
    }
  8. Configure Buffer engine for inserts

    master

    When using ClickHouse's Buffer engine, you can separate the table used for reading from the table used for writing by using $tableForInserts.

    If you want OPTIMIZE and DELETE operations to target the original source table instead of the buffer, use the $tableSources property.

    namespace App\Models\Clickhouse;
    
    use PhpClickHouseLaravel\BaseModel;
    
    class MyTable extends BaseModel
    {
        // SELECT and INSERT queries use $table.
        // OPTIMIZE and DELETE queries use $tableSources.
        protected $table = 'my_table_buffer';
        protected $tableSources = 'my_table';
    }
  9. How Schema Grammars translate Laravel Blueprints to ClickHouse

    master

    The SchemaGrammar class extends Laravel's Grammar to translate standard Laravel Schema Blueprints into ClickHouse-compatible SQL.

    When using Laravel migrations with this package, the grammar handles:

    1. Table Existence Checks: Uses system.tables to verify if a table exists.
    2. Table Creation: Generates CREATE TABLE statements using the MergeTree() engine.
    3. Ordering: Automatically sets the ORDER BY clause using the first column added to the blueprint.
    4. Type Mapping: Maps Laravel column types to ClickHouse types (e.g., bigInteger becomes Int64, string becomes String).
  10. Configure ClickHouse connection settings

    master

    The package automatically registers a clickhouse connection. You can configure it using environment variables in your .env file or by explicitly defining the connection in config/database.php to override defaults or add advanced options.

    Environment Variables:

    VariableDefaultDescription
    CLICKHOUSE_HOSTlocalhostClickHouse server host
    CLICKHOUSE_PORT8123ClickHouse server port
    CLICKHOUSE_DATABASEdefaultDatabase name
    CLICKHOUSE_USERNAMEdefaultUsername
    CLICKHOUSE_PASSWORDPassword
    CLICKHOUSE_TIMEOUT_CONNECT2Connection timeout in seconds
    CLICKHOUSE_TIMEOUT_QUERY2Query timeout in seconds
    CLICKHOUSE_HTTPSfalseUse HTTPS
    CLICKHOUSE_RETRIES0Number of retries
    CLICKHOUSE_MAX_PARTITIONS_PER_INSERT_BLOCK300Max partitions per insert block
    CLICKHOUSE_FIX_DEFAULT_QUERY_BUILDERtrueFix default query builder

    Manual Configuration in config/database.php:

    'connections' => [
        'clickhouse' => [
            'driver' => 'clickhouse',
            'host' => env('CLICKHOUSE_HOST'),
            'port' => (int) env('CLICKHOUSE_PORT', 8123),
            'database' => env('CLICKHOUSE_DATABASE', 'default'),
            'username' => env('CLICKHOUSE_USERNAME', 'default'),
            'password' => env('CLICKHOUSE_PASSWORD', ''),
            'timeout_connect' => env('CLICKHOUSE_TIMEOUT_CONNECT', 2),
            'timeout_query' => env('CLICKHOUSE_TIMEOUT_QUERY', 2),
            'https' => (bool) env('CLICKHOUSE_HTTPS', false),
            'retries' => env('CLICKHOUSE_RETRIES', 0),
            'settings' => [
                'max_partitions_per_insert_block' => 300,
            ],
            'fix_default_query_builder' => true,
        ],
    ],
  11. Manage ClickHouse clusters with the Cluster class

    master

    The PhpClickHouseLaravel\Cluster class manages a collection of ClickHouse nodes, providing high availability by automatically selecting an active node during instantiation and allowing manual switching between nodes.

    Initialization

    When you instantiate Cluster, it iterates through the provided $nodeConfigs and attempts to connect to each node using ping(true). The first node that responds successfully becomes the activeNode. If no nodes are reachable, a TransportException is thrown.

    Writing Data

    The write() method executes a SQL statement across all configured nodes in the cluster. This is useful for ensuring data consistency across a distributed setup. It returns the last successful Statement object.

    Node Management

    • getActiveNode(): Returns the ClickHouseDB\Client instance for the currently active node. Use this for read operations or when you need direct access to the client.
    • slideNode(): Attempts to switch the active node to the next available node in the configuration list. It iterates through the nodes and selects the first one that responds to a ping(). If there is only one node configured, this method does nothing.
    use PhpClickHouseLaravel\Cluster;
    
    $nodeConfigs = [
        [
            'host' => '127.0.0.1',
            'database' => 'default',
            'timeout_query' => 30,
            'timeout_connect' => 10,
        ],
        [
            'host' => '127.0.0.2',
            'database' => 'default',
            'timeout_query' => 30,
            'timeout_connect' => 10,
        ]
    ];
    
    // Initializes and finds the first available node
    $cluster = new Cluster($nodeConfigs);
    
    // Writes to ALL nodes in the cluster
    $cluster->write('INSERT INTO my_table (col1) VALUES (?)', ['value']);
    
    // Switch to the next available node if the current one is problematic
    $cluster->slideNode();
    
    // Get the client for the current active node
    $client = $cluster->getActiveNode();