kaigrassnick/snowflake-bundle
Laravel bundle for generating Snowflake-style unique IDs (distributed, time-ordered 64-bit identifiers). Provides easy integration/configuration for producing sortable IDs across services and high-throughput apps without relying on auto-incrementing database keys.
Installation Add the bundle via Composer:
composer require kaigrassnick/snowflake-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Kaigrassnick\SnowflakeBundle\SnowflakeBundle::class => ['all' => true],
];
Configuration
Define Snowflake connection settings in config/packages/kaigrassnick_snowflake.yaml:
snowflake:
account: 'your_account_identifier'
username: 'your_username'
password: 'your_password'
database: 'your_database'
schema: 'your_schema'
warehouse: 'your_warehouse'
role: 'your_role'
host: 'your_host' # Optional (defaults to Snowflake's regional host)
First Use Case
Inject the SnowflakeClient service into a controller or service and execute a query:
use Kaigrassnick\SnowflakeBundle\Client\SnowflakeClient;
class MyController extends AbstractController
{
public function __construct(private SnowflakeClient $snowflake)
{
}
public function queryData()
{
$result = $this->snowflake->query('SELECT * FROM my_table LIMIT 10');
return $this->json($result->fetchAll());
}
}
Query Execution
Use the query() method for direct SQL execution:
$result = $snowflake->query('SELECT * FROM users WHERE active = true');
$rows = $result->fetchAll(); // Array of associative arrays
Parameterized Queries Prevent SQL injection with named parameters:
$result = $snowflake->query(
'SELECT * FROM users WHERE id = :id',
['id' => $userId]
);
Transactions Wrap operations in a transaction for atomicity:
$snowflake->beginTransaction();
try {
$snowflake->query('INSERT INTO logs (message) VALUES (:message)', ['message' => 'Test']);
$snowflake->commit();
} catch (\Exception $e) {
$snowflake->rollBack();
throw $e;
}
Stored Procedures
Execute stored procedures with call():
$result = $snowflake->call('my_procedure', ['param1' => 'value1']);
Data Migration Use the bundle to sync data between Symfony and Snowflake during deployments or migrations. Example:
$snowflake->query('CREATE TABLE IF NOT EXISTS users (...)');
$snowflake->query('INSERT INTO users (...) SELECT ... FROM staging_users');
Real-Time Analytics Fetch aggregated data for dashboards:
$result = $snowflake->query('SELECT COUNT(*) as total_users FROM users');
$totalUsers = $result->fetch()['total_users'];
Event-Driven Workflows Trigger Symfony events based on Snowflake data changes (e.g., using Snowflake Tasks + Symfony cron jobs).
Dependency Injection
Prefer constructor injection for SnowflakeClient to ensure testability:
public function __construct(private SnowflakeClient $snowflake) {}
Configuration Overrides
Override settings per environment (e.g., config/packages/dev/kaigrassnick_snowflake.yaml):
snowflake:
password: '%env(SNOWFLAKE_PASSWORD_DEV)%'
Doctrine Integration Use the bundle alongside Doctrine for hybrid ORM/Query approaches:
// Fetch IDs from Snowflake, then load entities via Doctrine
$snowflakeIds = $snowflake->query('SELECT id FROM users')->fetchFirstColumn();
$users = $entityManager->getRepository(User::class)->findBy(['id' => $snowflakeIds]);
Caching Results Cache frequent queries using Symfony’s cache system:
$cacheKey = 'snowflake_users_active';
$activeUsers = $cache->get($cacheKey, function () use ($snowflake) {
return $snowflake->query('SELECT * FROM users WHERE active = true')->fetchAll();
});
Connection Management
try {
$result = $snowflake->query('SELECT 1');
} catch (\RuntimeException $e) {
if (str_contains($e->getMessage(), 'connection')) {
// Reconnect or retry
}
throw $e;
}
Parameter Binding Quirks
:param vs $1).:param) for portability.Large Result Sets
fetch() in a loop or limit results:
$result = $snowflake->query('SELECT * FROM large_table');
while ($row = $result->fetch()) {
// Process row-by-row
}
Schema Changes
Enable Logging Configure Monolog to log Snowflake queries:
# config/packages/monolog.yaml
handlers:
snowflake:
type: stream
path: '%kernel.logs_dir%/snowflake.log'
level: debug
channels: ['snowflake']
Then enable Snowflake logging in the bundle config:
snowflake:
logging: true
Query Profiling Use Symfony’s profiler to measure query execution time:
$start = microtime(true);
$result = $snowflake->query('SELECT * FROM users');
$time = microtime(true) - $start;
Common Errors
Authentication failed: Verify account, username, and password in config.Table not found: Check database and schema settings.Operation not permitted: Ensure the role has sufficient privileges.Custom Query Builders
Extend the bundle’s SnowflakeClient to add domain-specific methods:
class CustomSnowflakeClient extends SnowflakeClient
{
public function getUserByEmail(string $email): array
{
return $this->query('SELECT * FROM users WHERE email = :email', ['email' => $email])
->fetchAssoc();
}
}
Register the service in services.yaml:
services:
App\Service\CustomSnowflakeClient:
decorates: 'kaigrassnick.snowflake.client'
arguments: ['@App\Service\CustomSnowflakeClient.inner']
Event Listeners Listen for Snowflake events (e.g., query execution) via Symfony’s event dispatcher:
use Kaigrassnick\SnowflakeBundle\Event\QueryEvent;
class SnowflakeLogger
{
public function onQuery(QueryEvent $event)
{
$this->logger->info('Snowflake Query', ['query' => $event->getQuery()]);
}
}
Bind the listener in services.yaml:
services:
App\EventListener\SnowflakeLogger:
tags:
- { name: 'kernel.event_listener', event: 'snowflake.query', method: 'onQuery' }
Custom Connection Factories Override the default connection factory for advanced use cases (e.g., multi-tenancy):
// src/Snowflake/ConnectionFactory.php
class CustomConnectionFactory implements ConnectionFactoryInterface
{
public function createConnection(array $config): ConnectionInterface
{
// Custom logic (e.g., dynamic account selection)
return new SnowflakeConnection($config);
}
}
Register it in services.yaml:
services:
kaigrassnick.snowflake.connection_factory:
class: App\Snowflake\CustomConnectionFactory
**Async Quer
How can I help you explore Laravel packages today?