PHPackages                             wtsvk/evitadb-php-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. wtsvk/evitadb-php-client

ActiveLibrary[API Development](/categories/api)

wtsvk/evitadb-php-client
========================

PHP gRPC client for EvitaDB — generated stubs + session-based wrapper.

v0.5.0(2mo ago)029Apache-2.0PHP ^8.5

Since Apr 30Compare

[ Source](https://github.com/wtsvk/evitadb-php-client)[ Packagist](https://packagist.org/packages/wtsvk/evitadb-php-client)[ Docs](https://github.com/wtsvk/evitadb-php-client)[ RSS](/packages/wtsvk-evitadb-php-client/feed)WikiDiscussions Synced 3w ago

READMEChangelogDependencies (11)Versions (15)Used By (0)

evitadb-php-client
==================

[](#evitadb-php-client)

PHP gRPC client for [EvitaDB](https://evitadb.io) — generated stubs + a thin session-based wrapper for managing read/write sessions.

Installation
------------

[](#installation)

```
composer require wtsvk/evitadb-php-client
```

System requirements:

- PHP 8.5+
- `ext-grpc` (`pecl install grpc`)
- `ext-protobuf` (`pecl install protobuf`)

Usage
-----

[](#usage)

The client has two layers: `EvitaDbConnection` for server-level operations, and `EvitaDbClient` for catalog-scoped data access.

### Quick start

[](#quick-start)

```
use Wtsvk\EvitaDbClient\EvitaDbClient;
use Wtsvk\EvitaDbClient\QueryBuilder;
use Wtsvk\EvitaDbClient\Transaction\ReadTransactionContext;

// Catalog-scoped client — all operations target this catalog
$client = EvitaDbClient::create(host: 'localhost', port: 5555, catalog: 'myCatalog');

$response = $client->readTransaction(function (ReadTransactionContext $tx) {
    $query = (new QueryBuilder('Product'))
        ->withLocale('sk')
        ->filterByAttribute('code', 'PROD-001')
        ->page(1, 20)
        ->build();

    return $tx->query($query);
});
```

### Connection + catalog split

[](#connection--catalog-split)

```
use Wtsvk\EvitaDbClient\EvitaDbConnection;
use Wtsvk\EvitaDbClient\Transaction\ReadTransactionContext;

$conn = EvitaDbConnection::create(host: 'localhost', port: 5555);

if (! $conn->isHealthy()) {
    throw new RuntimeException('EvitaDB unreachable');
}

// Server-level operations
$conn->defineCatalog('myCatalog'); // creates the catalog AND transitions it to ALIVE
$catalogs = $conn->getCatalogNames();

// Create catalog-scoped clients
$client = $conn->catalog('myCatalog');
$entity = $client->readTransaction(
    fn (ReadTransactionContext $tx) => $tx->getEntity(entityType: 'Product', primaryKey: 42),
);
```

For Laravel apps, register singletons in your `AppServiceProvider`:

```
use Wtsvk\EvitaDbClient\EvitaDbConnection;
use Wtsvk\EvitaDbClient\EvitaDbConnectionInterface;
use Wtsvk\EvitaDbClient\EvitaDbClientInterface;

$this->app->singleton(EvitaDbConnectionInterface::class, fn () => EvitaDbConnection::create(
    host: config('evitadb.host'),
    port: (int) config('evitadb.port'),
));

$this->app->singleton(EvitaDbClientInterface::class, fn ($app) =>
    $app->make(EvitaDbConnectionInterface::class)->catalog(config('evitadb.catalog')),
);
```

### Transactions

[](#transactions)

All data access goes through `writeTransaction()` and `readTransaction()`. Each call opens **one** gRPC session for the duration of the callable, giving you a consistent snapshot for reads and one server-side transaction for writes.

```
use Wtsvk\EvitaDbClient\Transaction\WriteTransactionContext;
use Wtsvk\EvitaDbClient\Transaction\ReadTransactionContext;

// Write batch — many mutations, one session
$client->writeTransaction(function (WriteTransactionContext $tx) use ($products) {
    foreach ($products as $product) {
        $tx->upsertEntity($product->toMutation());
    }
});

// Consistent read snapshot
$bundle = $client->readTransaction(function (ReadTransactionContext $tx) {
    return [
        'product' => $tx->getEntity('Product', 42),
        'category' => $tx->findEntity('Category', 7),
    ];
});

// Mixed: read existing entity then update it
$pk = $client->writeTransaction(function (WriteTransactionContext $tx) use ($newProductMutation) {
    if ($tx->findEntity('Product', 42) !== null) {
        $tx->deleteEntity('Product', 42);
    }
    return $tx->upsertEntity($newProductMutation);
});
```

`getEntity()` throws `EvitaDbEntityNotFoundException` if the entity is missing; use `findEntity()` if `null` is an acceptable outcome.

#### Rollback semantics

[](#rollback-semantics)

EvitaDB's gRPC `Close()` RPC has no rollback flag — calling it always commits. The PHP client therefore handles the two paths differently:

- **Normal return**: session is closed via `Close()` with the configured commit behavior (commits server-side).
- **Exception inside the callable**: the session is **intentionally orphaned** (no `Close()` is sent). The EvitaDB server's session timeout will discard the buffered transaction, giving you a deferred-but-real rollback. The exception propagates out of `writeTransaction()` unchanged.
- **Deterministic rollback**: pass `dryRun: true` — the server discards all changes on close regardless of outcome.

```
$client->writeTransaction(
    fn: function (WriteTransactionContext $tx) {
        // experiment freely — nothing will persist
        $tx->upsertEntity($mutation);
    },
    dryRun: true,
);
```

### Commit behavior

[](#commit-behavior)

Control how long the gRPC close call blocks before returning:

```
use Wtsvk\EvitaDbClient\SessionCommitBehavior;

// Fast bulk import — don't wait for indexes
$client = EvitaDbClient::create(
    host: 'localhost',
    port: 5555,
    catalog: 'myCatalog',
    defaultCommitBehavior: SessionCommitBehavior::WaitForConflictResolution,
);

// Override per call when needed
$client->writeTransaction(
    fn: fn (WriteTransactionContext $tx) => $tx->upsertEntity($mutation),
    commitBehavior: SessionCommitBehavior::WaitForChangesVisible,
);
```

### EntityFetch — control what content is returned

[](#entityfetch--control-what-content-is-returned)

By default the server returns identity-only entities (no attributes, prices, or references). Pass an `EntityFetch` to ask for specific content. Pass `EntityFetch::all()` for everything.

```
use Wtsvk\EvitaDbClient\EntityFetch;

// Fetch only specific attributes and prices
$entity = $client->readTransaction(
    fn (ReadTransactionContext $tx) => $tx->getEntity(
        entityType: 'Product',
        primaryKey: 42,
        require: (new EntityFetch())
            ->attributeContent('name', 'code')
            ->priceContentAll(),
    ),
);

// In QueryBuilder
$query = (new QueryBuilder('Product'))
    ->withEntityFetch((new EntityFetch())->attributeContent('name')->priceContentRespectingFilter())
    ->filterByAttribute('code', 'PROD-001')
    ->build();
```

### QueryBuilder — filtering and ordering

[](#querybuilder--filtering-and-ordering)

```
use Wtsvk\EvitaDbClient\SortDirection;

$query = (new QueryBuilder('Product'))
    ->withLocale('en')
    ->filterByAttribute('status', 'active')
    ->filterByAttributeGreaterThan('price', 10)
    ->filterByAttributeBetween('weight', 0.5, 10.0)
    ->filterByReferencePrimaryKeyInSet('Category', [1, 2, 3])
    ->filterPriceInCurrency('EUR')
    ->filterPriceInPriceLists(['retail', 'wholesale'])
    ->orderByAttributeNatural('name', SortDirection::Asc)
    ->orderByPriceNatural(SortDirection::Desc)
    ->page(1, 20)
    ->build();
```

### Testing your application

[](#testing-your-application)

The package ships with `EvitaDbMockClient` and `MockEvitaDbConnection` for unit-testing application code:

```
use Wtsvk\EvitaDbClient\Testing\EvitaDbMockClient;

public function testProductServiceReturnsPrice(): void
{
    // Consumer code calls $client->readTransaction(...) internally — the mock
    // routes the read through MockReadOnlySessionScopedContext, which looks up
    // the stubbed entity below.
    $client = (new EvitaDbMockClient('myCatalog'))
        ->withEntity(entityType: 'Product', primaryKey: 42, entity: $sealedEntity);

    $service = new ProductService($client);

    $this->assertSame(100, $service->getProductPrice(42));
}

public function testProductServiceUpsertsRecordsCall(): void
{
    $client = new EvitaDbMockClient('myCatalog');
    $service = new ProductService($client);

    $service->createProduct(name: 'iPhone', price: 999);

    // Consumer code calls $client->writeTransaction(fn ($tx) => $tx->upsertEntity(...))
    // — the mock records the mutation as a spy entry below.
    $this->assertCount(1, $client->upsertCalls);
}
```

The mock auto-assigns primary keys for `upsertEntity()` (start with `$client->nextPrimaryKey = 1000` to mimic existing data). Strict mode: any call without a matching stub throws — fail loud, not silent. See `tests/Unit/Testing/EvitaDbMockClientTest.php` for full API examples.

EvitaDB version compatibility
-----------------------------

[](#evitadb-version-compatibility)

This package follows independent semver. The targeted EvitaDB version is recorded in `composer.json` `extra.evitadb-version`. Wrapper-only fixes/refactors bump the package version (patch or minor) without changing the EvitaDB target — they don't get a row here.

Each row in the matrix below marks the **first** package version that introduced support for the listed EvitaDB version. Every later package release until the next row keeps the same EvitaDB target.

PackageEvitaDB0.5.x2026.1.90.2.x2026.1.80.1.x2026.1.7For a specific EvitaDB version, pin the package version that matches (e.g. `^0.2` if you target EvitaDB 2026.1.8).

Architecture
------------

[](#architecture)

- `src/EvitaDbConnectionInterface.php` / `src/EvitaDbConnection.php` — server-level operations (`isHealthy`, `defineCatalog`, `getCatalogNames`, `deleteCatalog`) and factory for catalog-scoped clients via `catalog()`.
- `src/EvitaDbClientInterface.php` / `src/EvitaDbClient.php` — catalog-scoped gRPC client with a transaction-only public surface: `writeTransaction()` and `readTransaction()`. The catalog is bound at construction. Session management, commit behavior, and rollback-on-exception (session orphaning) are all handled internally.
- `src/EntityFetch.php` — mutable fluent builder for specifying what entity content to return (attributes, prices, references, etc.). Construct fresh per query.
- `src/QueryBuilder.php` — mutable fluent builder producing EvitaQL `GrpcQueryRequest` messages. Supports filtering, ordering, pagination, and custom `EntityFetch`.
- `src/Transaction/` — `ReadTransactionContext` and `WriteTransactionContext` interfaces consumers receive inside `readTransaction()` / `writeTransaction()` callables. Concrete implementations: `ReadOnlySessionScopedContext` (read-only) and `ReadWriteSessionScopedContext` (read + write), both backed by one EvitaDB session and sharing read code via the `SessionScopedReads` trait.
- `src/SessionCommitBehavior.php` — enum controlling how long the close call blocks (`WaitForConflictResolution`, `WaitForLogPersistence`, `WaitForChangesVisible`).
- `src/SortDirection.php` — enum for query ordering (`Asc`, `Desc`).
- `src/GrpcStatus.php` — typed readonly value object wrapping the `stdClass` returned by gRPC `wait()`. Used by Connection, Client, and the session-scoped context implementations to render uniform error messages.
- `src/Testing/` — `EvitaDbMockClient`, `MockEvitaDbConnection`, mock context classes (`MockReadOnlySessionScopedContext`, `MockReadWriteSessionScopedContext`), and supporting DTOs for unit-testing consumer applications without a live EvitaDB.
- `src/Exception/` — custom exception hierarchy. The client throws on errors instead of logging — your app handles them.
- `src/Protocol/` — auto-generated PHP classes from EvitaDB `.proto` definitions. Do not edit manually.
- `proto/` — committed `.proto` source files synced from the EvitaDB Docker image.

Maintenance scripts
-------------------

[](#maintenance-scripts)

- `scripts/sync-protos.sh` — pulls `.proto` files from the pinned EvitaDB image and applies the PHP namespace patches. Run when bumping EvitaDB version.
- `scripts/build-proto.sh` — runs `protoc + grpc_php_plugin` to regenerate `src/Protocol/*` from `proto/*`.

GitHub Actions automates this via `auto-update-evitadb.yml` (daily cron) which detects new EvitaDB releases and opens a PR with regenerated stubs.

License
-------

[](#license)

Apache 2.0. See `LICENSE`.

EvitaDB itself is licensed under the Business Source License 1.1, converting to Apache 2.0 on 2027-01-01. The `.proto` files in this package are interface definitions extracted from the EvitaDB JAR — derivative work that should be permissible under BSL's "make non-production use" + "modify" grants for an interop client. Consult [EvitaDB's LICENSE](https://github.com/FgForrest/evitaDB/blob/master/LICENSE) for authoritative terms.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance87

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

7

Last Release

65d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/12497013?v=4)[Michal Forbak](/maintainers/mforbak)[@mforbak](https://github.com/mforbak)

---

Tags

gRPCnosqlecommercecatalogevitadb

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/wtsvk-evitadb-php-client/health.svg)

```
[![Health](https://phpackages.com/badges/wtsvk-evitadb-php-client/health.svg)](https://phpackages.com/packages/wtsvk-evitadb-php-client)
```

###  Alternatives

[clarifai/clarifai-php-grpc

Clarifai PHP gRPC client

1229.7k](/packages/clarifai-clarifai-php-grpc)[google/gax

Google API Core for PHP

267116.3M574](/packages/google-gax)[temporal/sdk

Temporal SDK

4072.9M25](/packages/temporal-sdk)[googleads/google-ads-php

Google Ads API client for PHP

3508.5M14](/packages/googleads-google-ads-php)[spiral/grpc-client

gRPC client

41491.6k4](/packages/spiral-grpc-client)[metaseller/tinkoff-invest-api-v2-php

Unofficial PHP SDK for T-Invest API v2

256.8k3](/packages/metaseller-tinkoff-invest-api-v2-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
