composer require pelfox/laravel-bigquery
config/database.php:
'bigquery' => [
'driver' => 'bigquery',
'dataset' => env('BIGQUERY_DATASET'), // Required
'keyFilePath' => env('BIGQUERY_KEY_PATH'), // Required (path to .json service account key)
'database' => '', // Optional (default: empty)
'prefix' => '', // Optional (default: empty)
],
// Query Builder
$results = DB::connection('bigquery')->table('your_table')->get();
// Eloquent
class UserAnalytics extends Model {
protected $connection = 'bigquery';
protected $table = 'your_dataset.your_table';
public $incrementing = false;
public $timestamps = false;
}
Replace a PostgreSQL analytics query with BigQuery:
// Before (PostgreSQL)
$users = DB::table('users')->where('created_at', '>', now()->subDays(30))->count();
// After (BigQuery)
$users = DB::connection('bigquery')
->table('analytics.users')
->where('created_at', '>', \Pelfox\LaravelBigQuery\Escape::date(now()->subDays(30)))
->count();
Dataset Switching:
// Default dataset (from config)
DB::connection('bigquery')->table('events')->get();
// Explicit dataset
DB::connection('bigquery')->table('analytics.events')->get();
// Dynamic via Facade
\Pelfox\LaravelBigQuery\Facades\BigQuery::dataset('marketing')->table('campaigns')->get();
Complex Queries:
$query = DB::connection('bigquery')
->select('user_id', DB::raw('COUNT(*) as event_count'))
->from('events')
->join('users', 'events.user_id', '=', 'users.id')
->where('events.timestamp', '>', \Pelfox\LaravelBigQuery\Escape::timestamp(now()->subDays(7)))
->groupBy('user_id')
->orderBy('event_count', 'desc')
->limit(100);
Batch Operations:
// Insert (use chunking for large datasets)
DB::connection('bigquery')->table('logs')->insert([
['user_id' => 1, 'action' => 'login', 'timestamp' => now()],
['user_id' => 2, 'action' => 'purchase', 'timestamp' => now()],
]);
// Update (requires WHERE clause)
DB::connection('bigquery')->table('users')->where('last_active', '<', now()->subDays(90))->update(['status' => 'inactive']);
Model Configuration:
class UserEvent extends Model {
protected $connection = 'bigquery';
protected $table = 'analytics.user_events';
public $incrementing = false; // BigQuery uses arbitrary IDs
public $timestamps = false; // BigQuery lacks native timestamps
protected $casts = [
'user_id' => 'integer',
'event_time' => \Pelfox\LaravelBigQuery\Eloquent\Casts\AsTimestamp::class,
'metadata' => \Pelfox\LaravelBigQuery\Eloquent\Casts\AsJson::class,
];
}
Repeated Fields:
class Product extends Model {
protected $casts = [
'tags' => \Pelfox\LaravelBigQuery\Eloquent\Casts\AsString::class . ':1', // Repeated STRING
'dimensions' => \Pelfox\LaravelBigQuery\Eloquent\Casts\AsFloat::class . ':1', // Repeated FLOAT
];
}
Struct/Record Types:
class Order extends Model {
protected $casts = [
'shipping_address' => \Pelfox\LaravelBigQuery\Eloquent\Casts\AsStruct::class . ':0,getAddressSchema',
];
public function getAddressSchema(): array {
return [
'street' => \Pelfox\LaravelBigQuery\Types\StringType::class,
'city' => \Pelfox\LaravelBigQuery\Types\StringType::class,
'zip' => \Pelfox\LaravelBigQuery\Types\StringType::class,
];
}
}
Hybrid Applications:
Use Laravel’s connection() method to switch between databases:
// Transactional data (PostgreSQL)
$user = DB::connection('pgsql')->table('users')->find(1);
// Analytics (BigQuery)
$events = DB::connection('bigquery')->table('user_events')->where('user_id', $user->id)->get();
Caching Queries: Cache frequent BigQuery results (e.g., dashboards) to reduce costs:
$revenue = Cache::remember('bigquery_revenue_daily', now()->addHours(1), function () {
return DB::connection('bigquery')->table('sales')->sum('amount');
});
Error Handling: Wrap BigQuery queries in try-catch to handle quota/execution errors:
try {
$results = DB::connection('bigquery')->select('SELECT * FROM large_table LIMIT 1000');
} catch (\Google\Cloud\BigQuery\Exception\BigQueryException $e) {
Log::error('BigQuery query failed: ' . $e->getMessage());
// Fallback to cached data or notify admin
}
Migrations: Use raw SQL for schema changes (BigQuery lacks Laravel migrations):
DB::connection('bigquery')->statement('
ALTER TABLE analytics.users
ADD COLUMN IF NOT EXISTS last_login TIMESTAMP
');
Connection Management:
config/database.php:
'bigquery' => [
'driver' => 'bigquery',
'dataset' => '...',
'keyFilePath' => '...',
'cache_connection' => true, // Add this
],
Data Type Mismatches:
integer) may fail for BigQuery types like INT64 or FLOAT64.protected $casts = [
'price' => \Pelfox\LaravelBigQuery\Eloquent\Casts\AsFloat::class, // Not \Illuminate\Database\Eloquent\Casts\Attribute
];
Repeated Fields:
ARRAY<STRING>) require special cast syntax (:1 suffix) and may return arrays unexpectedly.$model->tags = collect($model->tags)->map(fn($tag) => (string)$tag); // Ensure strings
Timestamp Handling:
TIMESTAMP type may not serialize correctly with Laravel’s Carbon.$query->where('created_at', '>', \Pelfox\LaravelBigQuery\Escape::timestamp(now()->subDays(1)));
JSON Data:
DB::connection('bigquery')->table('logs')->insert([
'metadata' => \Pelfox\LaravelBigQuery\Escape::json(['key' => 'value']),
]);
Query Performance:
LIMIT and avoid SELECT *:
$query->select(['user_id', 'COUNT(*) as event_count'])
->groupBy('user_id')
->limit(1000);
Service Account Permissions:
bigquery.tables.getData) cause silent failures.BigQuery Data Viewer (for reads)BigQuery Data Editor (for writes)BigQuery Job User (for query execution)Case Sensitivity:
How can I help you explore Laravel packages today?