event-engine/php-postgres-document-store
PostgreSQL-backed document store for Event Engine (PHP). Store, update, and query JSON documents efficiently using Postgres features like JSONB and indexes. Designed for read models/projections with a simple API and solid performance.
Installation
composer require event-engine/php-postgres-document-store
Add the service provider to config/app.php:
'providers' => [
// ...
EventEngine\PostgresDocumentStore\PostgresDocumentStoreServiceProvider::class,
],
Configuration Publish the config file:
php artisan vendor:publish --provider="EventEngine\PostgresDocumentStore\PostgresDocumentStoreServiceProvider"
Update .env with your PostgreSQL connection details (e.g., DB_CONNECTION=pgsql).
First Use Case: Storing a Document
use EventEngine\PostgresDocumentStore\Facades\DocumentStore;
$document = [
'id' => 'user-123',
'name' => 'John Doe',
'email' => 'john@example.com',
'metadata' => ['created_at' => now()->toIso8601String()]
];
DocumentStore::save($document);
Retrieving a Document
$retrieved = DocumentStore::get('user-123');
CRUD Operations
save() for both new and existing documents (idempotent).get($id) for single documents or find($query) for filtered queries.delete($id) or deleteMany($ids).save() with ON CONFLICT logic (handled internally).Querying Documents
// Basic query
$users = DocumentStore::find(['name' => 'John Doe']);
// Advanced query with JSON operators
$activeUsers = DocumentStore::find([
'metadata->active' => true,
'created_at' => ['>', '2024-01-01']
]);
Transactions Wrap operations in a transaction for atomicity:
DB::transaction(function () {
DocumentStore::save($doc1);
DocumentStore::save($doc2);
});
Event-Driven Patterns Listen for document events (if supported):
DocumentStore::onSave(function ($document) {
// Trigger side effects (e.g., cache updates)
});
Laravel Eloquent Integration Use the store alongside Eloquent for hybrid data models:
$user = User::find(1);
$user->document = DocumentStore::get("user-{$user->id}");
Caching Layer Cache frequently accessed documents:
$document = Cache::remember("doc:user-123", now()->addHours(1), function () {
return DocumentStore::get('user-123');
});
Schema Management Define a migration for custom indexes or constraints:
Schema::create('document_store', function (Blueprint $table) {
$table->jsonb('data')->index('idx_data_metadata');
});
Testing
Use DocumentStore::flush() to reset the store in tests:
public function test_document_store()
{
DocumentStore::flush();
// Test logic...
}
JSONB vs. JSON
The package defaults to jsonb for efficient querying. Avoid mixing json and jsonb columns.
Idempotency
save() overwrites existing documents. Use merge() for partial updates:
DocumentStore::merge('user-123', ['email' => 'new@example.com']);
Connection Issues
Ensure your PostgreSQL connection is configured in config/database.php with schema set to the correct table name (default: document_store).
Large Documents Avoid storing excessively large documents (>1MB). Use external storage (e.g., S3) for binaries and reference them via the document.
Concurrency
Use DB::transaction() for multi-document operations to prevent race conditions.
Query Logging
Enable PostgreSQL query logging in .env:
DB_LOG_QUERIES=true
Schema Inspection Check the raw table structure:
php artisan tinker
>>> \DB::select('SELECT * FROM information_schema.columns WHERE table_name = \'document_store\'');
Raw Queries
For complex queries, use DB::table('document_store')->whereRaw(...) directly.
Custom Query Builder Extend the query builder for domain-specific methods:
DocumentStore::extend(function ($builder) {
$builder->activeUsers = function () {
return $this->where('metadata->active', true);
};
});
Hooks Override default behavior via events (if the package supports them):
DocumentStore::listen('saving', function ($document) {
$document['audit'] = ['updated_by' => auth()->id()];
});
Custom Storage Engines
Implement a custom storage adapter by extending EventEngine\PostgresDocumentStore\Contracts\DocumentStore.
Validation Add validation before saving:
DocumentStore::validate(function ($document) {
Validator::make($document, ['email' => 'required|email'])->validate();
});
How can I help you explore Laravel packages today?