Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Neo4J Common Laravel Package

graphaware/neo4j-common

GraphAware Neo4j Common provides shared PHP utilities for Neo4j integration, including Cypher statement and result abstractions, common interfaces, and helpers used across GraphAware’s Neo4j libraries. A lightweight foundation for building Neo4j-backed PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Know what you're actually installing. graphaware/neo4j-common is not a Neo4j client. It ships the shared contracts and value objects — Cypher statements, results/records, graph entities, auth tokens, exceptions — that the GraphAware driver and OGM implement against. There is no connection, no HTTP/Bolt transport, and no Laravel service provider in here. In practice you get it transitively:

composer why graphaware/neo4j-common   # usually required by graphaware/neo4j-php-client

If you install it alone, the first thing to do is add a driver that consumes these interfaces:

composer require graphaware/neo4j-php-client

(For new projects prefer the maintained fork, laudis/neo4j-php-client — see Gotchas.)

2. Where to look first. Read the src/ tree in this order:

Directory What it gives you
Cypher/ Statement, StatementInterface, StatementCollection — build queries as objects
Result/ Result, Record, RecordView, ResultSummary — read rows without knowing the driver
Graph/ + Type/ Node, Relationship, Path interfaces used when a row returns graph entities
Driver/ DriverInterface / SessionInterface — the seam your code should depend on
Exception/ Neo4jException with Neo4j status code + message accessors
Auth/ AuthToken::basic($user, $pass)

3. First use case — build a statement, run it, read a record. This is the 90% path: a Cypher string plus bound parameters, wrapped as an immutable object, then consumed through the RecordView interface rather than a raw array.

use GraphAware\Common\Cypher\Statement;

$statement = Statement::create(
    'MATCH (u:User {id: $id}) RETURN u',
    ['id' => $userId],
    'find_user'          // tag, useful for logs/profiling
);

$result = $session->runStatement($statement);

foreach ($result->records() as $record) {
    $node = $record->get('u');           // NodeInterface
    $props = $node->getProperties();    // array
}

That loop is the whole point of the package: your app code never touches a transport-specific class.

Implementation Patterns

Inject the session, depend on the interfaces. Bind a single session in a service provider and type-hint GraphAware\Common\Driver\SessionInterface in your services. Nothing in neo4j-common is static or global, so it containerizes cleanly — you just have to write the wiring yourself.

// app/Providers/Neo4jServiceProvider.php
$this->app->singleton(SessionInterface::class, function () {
    $driver = \GraphAware\Neo4j\Client\ClientBuilder::create()
        ->addConnection('default', config('neo4j.dsn'))   // e.g. bolt://neo4j:secret@host:7687
        ->build();
    return $driver->session();
});

Query objects per use case. Return a Statement from a small class instead of scattering Cypher strings through controllers. It gives you one place to change an index hint, one place to add a parameter, and a natural unit-test target (assert on getText() / parameters() with no DB at all).

final class RecentOrdersForUser
{
    public function __invoke(string $userId, int $limit = 25): StatementInterface
    {
        return Statement::create(
            'MATCH (u:User {id:$id})-[:PLACED]->(o:Order)
             RETURN o ORDER BY o.createdAt DESC LIMIT $limit',
            ['id' => $userId, 'limit' => $limit],
            'recent_orders'
        );
    }
}

Repository returns your domain, not Records. Record/Node objects leaking into Blade views and Eloquent models is the fastest way to couple the app to the driver. Map at the boundary:

return collect($result->records())
    ->map(fn ($r) => UserDto::fromProperties($r->get('u')->getProperties()))
    ->all();

Bulk conversion ($record->values(), $record->keys()) is cheaper than repeated get() calls when you only need scalars.

Transactions via StatementCollection. Batch related statements and push them through a transaction so you can retry as a unit:

$tx = $session->transaction();
$tx->push(Statement::create('CREATE (u:User {id:$id})', ['id' => $id], 'create_user'));
$tx->push(Statement::create('MATCH (u:User {id:$id}) SET u.verified = true', ['id' => $id], 'verify'));
$tx->commit();

Queue jobs should carry the statement text + scalar parameters (both are serializable), then rebuild the callable-side work in the job; don't serialize a SessionInterface.

Test with fakes, not a live database. Because RecordView and Result are interfaces, you can hand-write a tiny in-memory double or implement AbstractRecord for a fake row. That gets you 90% of your mapping/formatting tests on CI without Neo4j running.

Exception translation at the edge. Catch Neo4jException in a middleware or the job handler and translate known codes: Neo.ClientError.Schema.ConstraintValidationFailed → 422, deadlock/transient codes → retry with backoff, everything else → 500 + log the Neo4j code and status.

Gotchas and Tips

  • It won't connect to anything. Expecting a client out of this package is the #1 mistake. If composer require left you with only GraphAware\Common\*, you are missing the driver.
  • 24 stars and zero dependents is the real risk. This is effectively an unmaintained leaf package. Pin the version in composer.json, vendor a copy if you must, and don't plan on upstream fixes. Answers to obscure questions won't exist on Stack Overflow.
  • Check the fork before committing. laudis/neo4j-php-client is the actively maintained continuation and bundles its own commons classes. Pulling both this package and a driver that bundles a newer commons gives you duplicate/conflicting GraphAware\Common\* interfaces — check composer why and composer show before adding.
  • PHP 8 / newer Laravel compatibility. Old GraphAware releases target PHP 5.5/7.x. The interfaces have no return types, so implementing them with typed returns in PHP 8 usually works (covariance), but extending a concrete class with a changed signature will fatal. Run the full suite on your target PHP version before adopting.
  • License is NOASSERTION on GitHub. GraphAware's libraries were MIT, but GitHub can't detect it — open LICENSE in the package root and get a one-line sign-off from whoever owns legal before shipping. Don't assume from the README.
  • Record::get() behaviour on a missing alias differs by version (null vs. exception). Don't rely on your memory; use hasValue($alias) first, or pin a version and write a test that asserts the behaviour.
  • Statement is immutable. withParameter() / withParameters() return new instances. $statement->withParameter('id', 1); as a bare statement silently does nothing — this bug pattern is common in refactors.
  • Never interpolate into the Cypher string. Property keys and labels can't be parameterised, so validate them against an allow-list; everything else goes through the $params array. This package does no escaping for you.
  • Graph entity shape depends on the driver. The HTTP driver historically returned GraphAware\Common\Graph\Node objects while Bolt returned plain maps. Normalise to arrays in your repository so a driver swap doesn't ripple through your code.
  • Transaction hygiene. There is no auto-retry and no auto-rollback on exceptions — wrap $tx work in try/catch and rollBack() explicitly, or a failed statement leaves the transaction open and the connection wedged.
  • Statement tags are free-form and unvalidated. Two statements with the same tag make your logs ambiguous; namespace them (e.g. orders.create, orders.verify).
  • Debugging tip: log the statement tag plus getText() and the parameter keys only (values often contain PII), and pull timings from ResultSummary rather than wrapping every call in microtime().
  • Extension points are the interfaces, not the classes. If you need behaviour the package lacks — a retrying executor, a record-to-DTO mapper, a profiler — implement against DriverInterface/Result, keep it in app/, and you stay upgradable if a maintained fork lands.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels