laravel-freelancer-nl/arangodb-php-client
A PHP client for ArangoDB with Laravel integration, offering fluent query building, document and edge handling, transactions, and connection management for multi-model graph and document databases.
This is a framework-agnostic, low-level HTTP client — there is no service provider, no facade, no config file to publish. Treat it as a thin transport layer you wire into Laravel yourself.
composer require laravel-freelancer-nl/arangodb-php-client
Requirements are PHP 8.x plus ext-json; it talks to ArangoDB over its HTTP API, so there is no extension or driver to compile.
Where to look first (in this order):
composer.json — confirms the PSR-4 namespace. The snippets below assume LaravelFreelancerNL\ArangoDbClient\, but verify before copy-pasting.src/Connection.php — the entry point; everything hangs off it.src/Database.php, src/Collection.php, src/Document.php, src/Cursor.php — the object model mirrors ArangoDB's own terminology, so ArangoDB's HTTP docs map almost 1:1.tests/ — with a 14-star archived package, the test suite is the real documentation.Environment config:
ARANGO_ENDPOINT=tcp://127.0.0.1:8529
ARANGO_DATABASE=app
ARANGO_USER=root
ARANGO_PASSWORD=secret
First use case — connect, ensure a collection, write a document, read it back:
use LaravelFreelancerNL\ArangoDbClient\Connection;
$connection = new Connection([
'endpoint' => config('arangodb.endpoint'),
'database' => config('arangodb.database'),
'user' => config('arangodb.user'),
'password' => config('arangodb.password'),
]);
$db = $connection->getDatabase(); // or new Database($connection, 'app')
$col = $db->getCollection('users'); // create it first if missing
$col->saveDocument(['_key' => 'u1', 'name' => 'Ada']);
$doc = $col->getDocument('u1');
$cursor = $db->query('FOR u IN users FILTER u.name == @n RETURN u', ['n' => 'Ada']);
foreach ($cursor as $row) { /* ... */ }
If those method names don't match your version, read src/ — the surface is small and the classes are short.
Bind the connection as a singleton. Never construct Connection per call; each instance opens its own HTTP session and you'll pay handshake cost plus leak sockets under load.
// app/Providers/ArangoServiceProvider.php
public function register(): void
{
$this->app->singleton(Connection::class, fn () => new Connection([
'endpoint' => config('services.arangodb.endpoint'),
'database' => config('services.arangodb.database'),
'user' => config('services.arangodb.user'),
'password' => config('services.arangodb.password'),
]));
$this->app->bind(Database::class, fn ($app) => $app->make(Connection::class)->getDatabase());
}
Wrap it in repositories, not models. There is no Eloquent equivalent here. A UserRepository holding collection name + AQL strings keeps the low-level calls out of controllers and makes the DB swappable later.
Always use AQL bind parameters. The client does not sanitise interpolated strings. 'FOR u IN users FILTER u.email == @e' with ['e' => $email] — never string concatenation.
Batch writes with a single call. ArangoDB's per-document endpoints are chatty. Prefer the client's bulk document methods or an AQL INSERT @docs INTO users with a bind parameter to send N documents in one round trip. For imports above a few thousand records, chunk into 1–5k batches.
Use server-side cursors for large result sets. Create with a batchSize, iterate lazily, and make sure the cursor is drained or explicitly destroyed — abandoned cursors hold server memory until timeout. If the client exposes a hasMore()/getNext() style pair or an Iterator, prefer it over materialising the full array.
Transactions for multi-collection invariants. ArangoDB transactions are stream- or JS-based and must declare every collection they touch up front. Keep transactions short and single-purpose; they are not a substitute for the AQL-level atomicity of a single query.
Schema bootstrap as an Artisan command. Collection and index creation are not migrations. Write php artisan arango:setup that is idempotent — check existence, create collections and indexes, and swallow the "duplicate name" error code rather than assuming a clean database.
Testing. Point the test suite at a dedicated database (e.g. app_test) and truncate collections in setUp(). Because the client is low-level, the cheapest isolation is often a real ArangoDB container in CI (arangodb/arangodb:3.11) rather than mocking the HTTP layer.
Integration tip: the same author's laravel-arangodb package is the Laravel glue — check whether it pulls this client in as a dependency and at what pinned version before adding both. Since this repo is archived, avoid unpinned dev-main requirements anywhere in the chain.
composer.json, commit composer.lock, and consider vendoring or forking if it's load-bearing. Have a migration plan: the fallback is a 100-line Guzzle wrapper around the same HTTP endpoints, because this library adds little beyond request shaping.src/ when something behaves unexpectedly.save(). Everything is documents-as-arrays._key ≠ _id. _id is collection/_key; updates and deletes generally want the key plus the collection context, while AQL filters use _id or _key. Mixing them up produces silent empty results rather than errors.1202 (document not found), 1203 (conflict), 1207 (duplicate collection), 1210 (unique constraint violated). Distinguish these from 5xx/transport failures — retrying a 1207 is pointless, retrying a timeout is not._key collision as an application error — that's a 1203 conflict. Handle upserts explicitly (REPLACE/UPSERT in AQL) instead of relying on exception flow.json_decode(..., true) gives arrays, which is what the client generally hands back — don't expect objects.hasCollection() or catch 1207 before creating at boot, otherwise a fresh deploy race on multiple pods will 500.Creation of dynamic property and nullable-parameter deprecations in a Laravel upgrade.How can I help you explore Laravel packages today?