google/cloud-firestore
Idiomatic PHP client for Google Cloud Firestore. Install via Composer and use the generated gRPC-based API to read/write documents, run queries, and manage data at scale. Part of the googleapis/google-cloud-php project.
Installation:
composer require google/cloud-firestore
Ensure the grpc PHP extension is enabled (see gRPC guide).
Authentication: Configure credentials via environment variables (recommended) or service account JSON:
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json');
Alternative: Use GOOGLE_CLOUD_PROJECT for ADC (Application Default Credentials).
First Query:
use Google\Cloud\Firestore\FirestoreClient;
$firestore = new FirestoreClient();
$document = $firestore->document('users/user123');
$data = $document->create(['name' => 'John Doe', 'email' => 'john@example.com']);
Document/Collection methods.addSnapshotListener for live UI sync (e.g., chat apps).FirestoreClient config for PWA-like behavior.Document Management:
// Create/update
$docRef = $firestore->collection('posts')->document();
$docRef->set(['title' => 'Hello', 'views' => 0]);
// Read
$snapshot = $docRef->snapshot();
$data = $snapshot->data();
// Delete
$docRef->delete();
Collections & Queries:
// Query with filters
$query = $firestore->collection('posts')
->where('views', '>', 100)
->orderBy('createdAt', 'desc')
->limit(10);
$results = $query->documents();
Transactions:
$firestore->runTransaction(function ($transaction) {
$docRef = $firestore->document('counter');
$snapshot = $transaction->get($docRef);
$newValue = $snapshot->data()['count'] + 1;
$transaction->update($docRef, ['count' => $newValue]);
});
Real-Time Listeners:
$query = $firestore->collection('messages');
$query->addSnapshotListener(function ($snapshot) {
foreach ($snapshot->documents() as $doc) {
echo $doc->data()['text'] . PHP_EOL;
}
});
Service Provider:
Bind the client in AppServiceProvider:
$this->app->singleton(FirestoreClient::class, function () {
return new FirestoreClient();
});
Eloquent Alternative:
Use a FirestoreModel trait to wrap Firestore operations:
trait FirestoreModel {
public function save() {
$this->firestore->document($this->path)->set($this->toArray());
}
}
Query Builder:
Extend Laravel’s Builder to support Firestore queries:
class FirestoreBuilder extends Builder {
public function whereGreaterThan($field, $value) {
$this->query->where($field, '>', $value);
return $this;
}
}
Events: Dispatch Laravel events on Firestore changes:
$query->addSnapshotListener(function ($snapshot) {
event(new FirestoreUpdated($snapshot->documents()));
});
gRPC Dependencies:
grpc extension causes ClassNotFoundException.extension=grpc.so in php.ini and restart PHP-FPM/Nginx.Authentication:
GOOGLE_APPLICATION_CREDENTIALS not found leads to silent failures.if (!file_exists(getenv('GOOGLE_APPLICATION_CREDENTIALS'))) {
throw new RuntimeException('Credentials file not found.');
}
Offline Persistence:
$firestore = new FirestoreClient(['settings' => ['offline' => false]]);
Batching Writes:
INVALID_ARGUMENT.commit():
$batch = $firestore->batch();
foreach ($data as $item) {
$batch->set($firestore->document("items/{$item['id']}"), $item);
if ($batch->operations()->count() >= 500) {
$batch->commit();
$batch = $firestore->batch();
}
}
$batch->commit();
Field Paths:
user.profile.name) requires escaping:
$docRef->set(['user.profile.name' => 'John'], ['merge' => true]);
Enable Logging:
$firestore = new FirestoreClient([
'logger' => new \Monolog\Logger('firestore', [
new \Monolog\Handler\StreamHandler('php://stderr', \Monolog\Logger::DEBUG)
])
]);
Common Errors:
PERMISSION_DENIED: Check IAM roles (e.g., roles/datastore.user).NOT_FOUND: Verify document paths (e.g., projects/{project}/databases/{db}/documents/{path}).DEADLINE_EXCEEDED: Increase timeout in config:
$firestore = new FirestoreClient(['settings' => ['timeout' => 30]]);
Custom Document Hydration:
Override Document::data() to transform raw data:
$snapshot->data(); // Returns array
$snapshot->data(['casts' => ['createdAt' => 'datetime']]); // Casts fields
Security Rules Bypass: Avoid: Disabling Firestore security rules in PHP. Instead, validate data in Laravel middleware:
$request->validate([
'email' => 'required|email',
'role' => 'in:admin,user'
]);
Performance:
select() to fetch only needed fields:
$query->select(['title', 'views']);
$cacheKey = 'posts:featured';
return Cache::remember($cacheKey, 60, function () use ($query) {
return $query->documents();
});
Testing:
Use the FirestoreTest trait or mock the client:
$mock = Mockery::mock(FirestoreClient::class);
$mock->shouldReceive('document')->andReturnSelf();
$mock->shouldReceive('set')->andReturn(true);
Database Selection:
Defaults to (default); specify explicitly:
$firestore = new FirestoreClient(['database' => '(my-database)']);
Emulator Support: For local testing, use the emulator:
$firestore = new FirestoreClient([
'host' => 'localhost:8080',
'projectId' => 'my-project'
]);
Deprecated Options:
Avoid keyFile/keyFilePath (deprecated in v1.54+). Use environment variables instead.
How can I help you explore Laravel packages today?