laudis/neo4j-php-client
PHP client and driver for Neo4j, giving Laravel and PHP apps a clean, intuitive API to the graph database. Choose Bolt or auto-routed drivers, configure easily, and rely on full psalm typing, extensibility, and official Neo4j testkit validation.
Architecture fit
PDO/DB::connection() client, meaning the persistence abstraction, DTO hydration, and query ownership are ours to build.Integration feasibility
composer require laudis/neo4j-php-client, then wire a singleton Client into the container. Realistic build cost for a first working integration: 1–3 engineer-days, dominated by config/DI, connection verification, and a health check rather than the driver itself.ext-bcmath, ext-json, ext-sockets, plus outbound TCP to Bolt (typically 7687) and TLS for bolt+s:///neo4j+s://. Serverless/FaaS and restricted-egress environments are a real risk here.^3.x requires PHP ^8.0 and supports Neo4j ^4.0, ^5.0; ^2.8 covers PHP 7.4/8.x against Neo4j 3.5/4.x. Legacy PHP applications cannot take latest.Technical risk
GraphClient interface so substitution is a bounded change.CypherList/CypherMap with typed Node/Relationship/Path objects and optional summarized metadata. Unbounded MATCH (n) RETURN n will exhaust PHP memory rather than stream; query-review discipline (LIMIT, pagination, projections) is mandatory.ParameterHelper; DateTimeInterface maps to DateTimeZoneId; Vector is decode-only. Each of these is a small, discoverable trap that belongs in a team style guide.Key questions
ltree satisfy the traversal workload at our scale? What query would be intractable otherwise?bolt:// or neo4j:// and whether routing/leader-follower semantics matter to us.^3.x matrix (^4.0, ^5.0) cover our target including current 2025.x releases?Stack fit
^3.x is the clean combination; anything older forces ^2.8 and constrains us to Neo4j 3.5/4.x.config/neo4j.php reading drivers, URIs, credentials, and default alias from env.Neo4jServiceProvider registering the built client as a singleton and exposing a thin GraphClient interface (run, readTransaction, writeTransaction).CypherMap/Node results into our own DTOs or Eloquent-backed read models.php artisan neo4j:health plus a /health/graph probe using a trivial RETURN 1 or CALL db.ping().Laudis\Neo4j\Types\Node directly into API responses couples our contract to the package and makes replacement expensive.Migration path
graphaware/neo4j-php-client package: namespace-level rename to Laudis\Neo4j; mostly mechanical but touches every call site, so do it as an isolated, test-covered change.GraphClient seam.Compatibility
composer.json, commit the lockfile, and gate upgrades behind integration tests against the exact Neo4j server version we run in production.+s schemes, Neo4j user privileges adequate for the queries we issue.neo4j:// changes read/write targeting, so tests must cover readTransaction against a follower.Sequencing
GraphClient interface, health check, Docker/CI service container.readTransaction. Lowest blast radius, proves value, exercises type mapping.writeTransaction with an idempotency review and a documented "no side effects inside the closure" rule; add retry-aware tests.Maintenance
CREATE CONSTRAINT, CREATE INDEX) need an explicit, versioned mechanism of our own — a plain Artisan command with idempotent Cypher is sufficient, but it must exist before go-live.Support
GraphClient abstraction, (b) integration tests against a real server so a swap is verifiable, and (c) a Neo4j commercial relationship for the database side.Scaling
neo4j:// driver in clustered/Aura deployments and route reads through readTransaction and writes through writeTransaction to keep load off the leader.LIMIT, projections, and pagination as a hard convention. Prefer summarized results only where the metadata is actually consumed, since it adds per-query overhead.Failure modes
Ramp-up
ParameterHelper, temporal types), and result-set bounds. Codify these as review checklist items and a short internal style guide at the time of the spike, not after the first incident.How can I help you explore Laravel packages today?