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 Bundle Laravel Package

mspa/neo4j-bundle

Symfony bundle for integrating the Neo4j graph database into your application. It provides configuration and service wiring so you can run Cypher queries, manage graph data, and use Neo4j alongside your existing Symfony stack with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require mspa/neo4j-bundle

This package has no Symfony Flex recipe (0 dependents, 0 stars), so do not assume config/bundles.php was updated. Register it manually:

// config/bundles.php
return [
    // ...
    Mspa\Neo4jBundle\MspaNeo4jBundle::class => ['all' => true],
];

Then dump the reference config before writing any YAML — you cannot trust README snippets to match the code:

bin/console config:dump-reference mspa_neo4j
bin/console debug:container --tag=neo4j   # or grep the bundle's Resources/config/*.xml|yaml

Where to look first (in this order)

  1. src/DependencyInjection/Configuration.php — the authoritative list of config keys, defaults, and whether multiple connections are supported.
  2. src/DependencyInjection/*Extension.php and Resources/config/services.* — the real service IDs and aliases. Autowiring aliases are frequently missing in small bundles; you may have to inject by ID.
  3. composer.json of the package — which Neo4j PHP driver it wraps. This determines everything downstream (session API, result types, Bolt protocol support, Neo4j 4/5 compatibility).

First use case: prove connectivity with a console command, not a controller

#[AsCommand('app:neo4j:ping')]
class Neo4jPingCommand extends Command
{
    public function __construct(private ClientInterface $client) { parent::__construct(); }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $result = $this->client->run('RETURN 1 AS ok');
        $output->writeln((string) $result->first()->get('ok'));
        return Command::SUCCESS;
    }
}

A command isolates driver/config failures from HTTP/firewall noise. Once RETURN 1 works, immediately create your constraints and indexes as a command too — the bundle almost certainly does not do it for you.

Implementation Patterns

Wrap the bundle's client immediately. With a score of 0.03 and zero dependents, the bundle is a liability, not an asset. Treat it as a thin DI shim and put your own interface in front of it:

interface GraphClient
{
    public function run(string $cypher, array $params = []): iterable;
    public function write(string $cypher, array $params = []): void;
}

Implement it once against whatever service the bundle exposes. If the bundle is abandoned (likely), you swap one class for laudis/neo4j-php-client or the official neo4j/neo4j-php-client and nothing else changes. This is the single highest-value pattern in this assessment.

Repository-per-aggregate, not OGM entities. Small Neo4j bundles often ship a partial OGM or none at all. Do not build on top of an undocumented entity manager — you'll hit broken unit-of-work semantics and no cascade rules. Instead:

final class UserGraphRepository
{
    public function __construct(private GraphClient $graph) {}

    public function follow(string $fromId, string $toId): void
    {
        $this->graph->write(
            'MATCH (a:User {id: $from}), (b:User {id: $to}) MERGE (a)-[:FOLLOWS]->(b)',
            ['from' => $fromId, 'to' => $toId]
        );
    }

    /** @return list<array{id:string,name:string}> */
    public function followersOf(string $id, int $limit = 50): array
    {
        return iterator_to_array($this->graph->run(
            'MATCH (:User {id: $id})<-[:FOLLOWS]-(u:User) RETURN u.id AS id, u.name AS name LIMIT $limit',
            ['id' => $id, 'limit' => $limit]
        ));
    }
}

Map results to plain arrays/DTOs at the repository boundary. Never leak driver Result/Record objects into controllers or templates.

Writes go through Messenger. Graph writes are the slow, failure-prone half of a polyglot-persistence setup (relational primary store + Neo4j read model is the common shape). Publish a domain event, handle it in a message handler that calls the repository, and let retries + DoctrineTransactionMiddleware-style ordering give you idempotency. Pair every handler with MERGE, never CREATE, so replays are safe.

Multi-connection if supported. If Configuration.php exposes more than one connection, define default (read) and writer explicitly and inject the right one per repository. If it only supports one, don't fight it — instantiate a second client yourself in services.yaml.

Testing. Use a real Neo4j container (testcontainers, or a docker-compose service in CI) rather than mocking the client; Cypher correctness is exactly what mocks cannot check. Reset between tests with MATCH (n) DETACH DELETE n in a transaction, or use separate databases if running Neo4j 4+.

Gotchas and Tips

Adoption risk is the headline. Zero stars and zero dependents means: no security review, no upgrade path, no one to ask. Pin the exact version ("mspa/neo4j-bundle": "1.0.*" or a commit hash), vendor-fork it if you must, and write down the exit plan (the GraphClient wrapper above) in your ADR. Budget for the bundle being dead within a year.

Bolt/protocol compatibility. Check the wrapped driver's version against your server. Neo4j 4.x/5.x dropped older Bolt versions; a bundle pinned to graphaware/neo4j-php-client (abandoned since ~2019) will fail to connect to any modern server with an opaque handshake error. If you see a connection reset before auth, it's protocol mismatch, not credentials.

No Flex recipe = no auto-config. Missing bundle registration produces a confusing "service not found" rather than a helpful error. Also expect the config root key to differ from the package name — confirm with config:dump-reference rather than guessing mspa_neo4j.

Autowiring aliases are often absent. If you get Cannot autowire ... no such service, add the alias yourself:

services:
  Mspa\Neo4jBundle\Client\ClientInterface:
    alias: mspa_neo4j.client
    public: true

Constraints and indexes are not managed for you. Create them explicitly and idempotently at deploy time — CREATE CONSTRAINT user_id IF NOT EXISTS FOR (u:User) REQUIRE u.id IS UNIQUE. Without this, MERGE is not atomic under concurrency and you will silently get duplicate nodes. This is the #1 correctness bug in Neo4j integrations.

Cypher injection. The driver's parameter binding only works for values, never for labels, relationship types, or property keys. Anything dynamic must be allow-listed:

$type = in_array($type, ['FOLLOWS', 'LIKES'], true) ? $type : throw new \InvalidArgumentException();

Result streaming surprises. Many drivers return a lazy cursor; iterating twice yields nothing the second time. Always convert to array at the repository boundary (as above), and be aware that large MATCH results without LIMIT will buffer in PHP memory.

Transactions need explicit sessions. Client::run() is auto-commit per statement. For multi-statement atomicity you need $client->transaction(...) or a session-level beginTransaction — check whether the bundle's wrapper even exposes this. If it doesn't, that's the point where you stop using the bundle's client and use the driver directly behind your interface.

Type mapping. Neo4j integers come back as PHP ints but temporal/spatial types come back as driver objects (Date, DateTime, Point). Serialize them deliberately in your DTOs; json_encode on a raw record will produce garbage.

Debugging. Enable Cypher logging (debug: level on the bundle's channel, or the driver's logger config) and watch for N+1-shaped queries: a loop calling followersOf() per user is the graph equivalent of Doctrine N+1. Batch with UNWIND $rows AS row instead.

Extension points. The only reliable ones are the service definitions and any tagged services/compiler passes in the bundle. Grep for addCompilerPass and findTaggedServiceIds before assuming you can hook in. If there are none, decorate the client service — that always works:

services:
  App\Neo4j\LoggingClient:
    decorates: mspa_neo4j.client
    arguments: ['@.inner', '@logger']
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager