event-engine/php-document-store
composer require event-engine/php-document-store
app/Contracts/DocumentStore.php:
namespace App\Contracts;
use EventEngine\DocumentStore\DocumentStore as BaseDocumentStore;
interface DocumentStore extends BaseDocumentStore {}
AppServiceProvider@boot():
$this->app->bind(
\App\Contracts\DocumentStore::class,
\EventEngine\DocumentStore\InMemoryDocumentStore::class
);
use App\Contracts\DocumentStore;
use EventEngine\DocumentStore\Document;
class MyService {
public function __construct(private DocumentStore $store) {}
public function createUser(string $id, array $data): void {
$doc = new Document($id, $data);
$this->store->saveDoc($doc);
}
}
$doc = new Document('user-123', ['name' => 'John']);
$this->store->saveDoc($doc); // Upsert
$this->store->replaceDoc($doc); // Force replace (no merge)
$this->store->updateDoc('user-123', ['name' => 'John Doe']);
$this->store->replaceDoc($doc); // Atomic replace
$this->store->deleteDoc('user-123');
$this->store->deleteMany(['user-123', 'user-456']);
$doc = $this->store->findDoc('user-123');
$partial = $this->store->getPartialDoc('user-123', ['name', 'email']);
use EventEngine\DocumentStore\Filter\EqualsFilter;
$filter = new EqualsFilter('status', 'active');
$docs = $this->store->findDocs($filter);
use EventEngine\DocumentStore\Filter\AndFilter;
use EventEngine\DocumentStore\Filter\EqualsFilter;
$filter = new AndFilter(
new EqualsFilter('status', 'active'),
new EqualsFilter('role', 'admin')
);
$docs = $this->store->findDocs($filter);
$this->store->createIndex('email', 'email', true); // Unique index
$this->store->dropIndex('email');
$this->store->saveMany([$doc1, $doc2]);
$this->store->deleteMany(['id1', 'id2']);
Use the in-memory store for unit tests:
use EventEngine\DocumentStore\InMemoryDocumentStore;
public function testDocumentStore()
{
$store = new InMemoryDocumentStore();
$store->saveDoc(new Document('test', ['key' => 'value']));
$this->assertEquals('value', $store->findDoc('test')->getData()['key']);
}
Bind the store to Laravel’s container with environment-based configurations:
// config/document_store.php
return [
'default' => env('DOCUMENT_STORE_DRIVER', 'in_memory'),
'drivers' => [
'in_memory' => EventEngine\DocumentStore\InMemoryDocumentStore::class,
'postgres' => EventEngine\PostgresDocumentStore\PostgresDocumentStore::class,
],
];
// AppServiceProvider
$this->app->bind(\App\Contracts\DocumentStore::class, function ($app) {
$config = $app['config']['document_store'];
return new $config['drivers'][$config['default']](
$app->make('db.connection')->getPdo()
);
});
Dispatch Laravel events for document changes:
use Illuminate\Support\Facades\Event;
$this->store->saveDoc($doc);
Event::dispatch(new DocumentStored($doc));
Cache document queries to reduce database load:
use Illuminate\Support\Facades\Cache;
public function getCachedDoc(string $id): ?Document
{
return Cache::remember("doc_{$id}", now()->addMinutes(5), function () use ($id) {
return $this->store->findDoc($id);
});
}
Wrap the store in a repository for business logic:
class UserDocumentRepository
{
public function __construct(private DocumentStore $store) {}
public function getActiveUsers(): array
{
$filter = new AndFilter(
new EqualsFilter('status', 'active'),
new EqualsFilter('role', 'user')
);
return $this->store->findDocs($filter);
}
}
Unique Index Conflicts:
saveDoc() or replaceDoc() will throw an exception if a unique index is violated.updateDoc() for partial updates or handle exceptions gracefully:
try {
$this->store->saveDoc($doc);
} catch (UniqueConstraintViolationException $e) {
// Handle conflict (e.g., retry with updated data)
}
In-Memory Store Limitations:
BC Breaks:
dropIndex() now accepts either a string or Index object (not backward compatible).composer.json during migration:
"event-engine/php-document-store": "0.2.*"
Partial Document Merges:
updateDoc() performs a shallow merge (not recursive).replaceDoc() for atomic updates or implement custom merge logic.Circular Dependencies in Tests:
event-engine/persistence.Enable Logging: Add debug logs for store operations:
$this->store->setLogger(new \Monolog\Logger('document_store'));
Check Index Names: Ensure index names match exactly when dropping them:
// Correct:
$this->store->dropIndex('email_index');
// Incorrect (will fail silently):
$this->store->dropIndex('email'); // If the index was named 'email_index'
Verify Document IDs:
$this->store->findDoc((string) $id);
Filter Performance:
AndFilter/OrFilter) can be slow in the in-memory store.filterDocIds() for large datasets to reduce memory usage:
$docIds = $this->store->filterDocIds($filter);
$docs = $this->store->findMany($docIds);
Custom Filters:
Extend the filter system by implementing FilterInterface:
class GreaterThanFilter implements FilterInterface {
public function __construct(private string $field, private $value) {}
public function matches(Document $doc): bool {
return $doc->getData()[$this->field] > $this->value;
}
}
Custom DocumentStore: Implement the interface for a new backend (e.g., Elasticsearch):
class ElasticsearchDocumentStore implements DocumentStore {
public function saveDoc
How can I help you explore Laravel packages today?