oralunal/phpclickhouse-laravel
Laravel integration for ClickHouse built on phpClickHouse. Add ClickHouse as a database connection, run analytical queries through Laravel’s DB facade, and use familiar query builder and configuration patterns. Ideal for analytics and reporting workloads.
composer require oralunal/laravel-clickhouse
php artisan vendor:publish --tag="clickhouse-config"
config/database.php using the package's driver (typical keys: driver, host, port, database, username, password, plus the package's read-only flag). Keep a separate clickhouse_readonly connection if you have a reporting replica — the PHPStan extension keys off this.php artisan make:clickhouse-migration create_events_table
php artisan make:clickhouse-model Event
php artisan migrate
MergeTree, ORDER BY (event, created_at)) written in batches, read with aggregate queries. If you are coming from v3, run php artisan lc-upgrade-3x-to-4x before writing anything new.Schema-first workflow. Write the migration, run it, then generate the model. Because the PHPStan extension parses migration files, any table you create outside a migration (or via schema:dump) will be typed loosely or missed entirely. make:clickhouse-model gives you a skeleton; column typing comes from the migration, not from introspection.
Fluent schema commands. The schema builder's command objects are now statically typed, so PHPStan accepts ->drop()->sync() and ->index()->granularity(...) without suppression. Use the fluent form rather than raw DB::statement so the static analyser can follow it.
Write path. Batch inserts (Model::insert($chunk) in chunks of 10k–100k) are the normal pattern; avoid row-at-a-time save(). Reserve Eloquent for reads and use the query builder for aggregates, GROUP BY, and FINAL-style queries.
Parallel reads. Parallel::get([...]) and Parallel::getRows([...]) now accept a query from any model (not just one bound model), so you can fan out dashboard widgets across heterogeneous tables in a single round trip. get() returns hydrated models, getRows() returns plain arrays — prefer getRows() when you only need aggregates and want to skip hydration cost.
Read-only connections. Point reporting/BI connections at the read-only config. The extension now marks Float32/Float64 columns nullable there, and marks MATERIALIZED and ALIAS columns nullable everywhere, which matches ClickHouse semantics (they are computed/derived, not insertable).
Static analysis setup. Include the package's PHPStan extension alongside Larastan in phpstan.neon. It disables Larastan's migration cache, so it re-reads migrations on each run — correct, but slower; scope CI runs to changed paths if analysis time becomes a problem.
Derived-column discipline. Never put MATERIALIZED or ALIAS columns in $fillable or in insert payloads; read them only, and expect ?float/?string return types from the analyser.
DB::transaction() is meaningless against ClickHouse. Make writes idempotent (dedupe via ReplacingMergeTree, deterministic keys) instead of relying on rollback.update()/delete() become ALTER TABLE ... UPDATE/DELETE, which queue in system.mutations. Poll that table when debugging "my update didn't apply", or set mutations_sync on the statement when you genuinely need synchronous behaviour.save()-based update semantics. Set $timestamps = false and $incrementing = false deliberately.make:clickhouse-model / make:clickhouse-migration scaffold; they do not reverse-engineer an existing table. Create tables through migrations or the PHPStan types will be incomplete.lc-upgrade-3x-to-4x now only suggests schema:dump for tables no migration creates. If you adopted a dumped schema in 3.x for tables you do have migrations for, drop the dump so the analyser reads the migration instead.PHPStan\ConnectionConfig::isReadOnly() is deprecated in favour of hasReadOnlyClickHouseConnection(). If you wrote custom PHPStan rules or extensions against the old method, migrate them now.system.query_log for server-side timing and failures. toSql() will not show bindings; use the query log for values.How can I help you explore Laravel packages today?