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

kwattro/neo4j-ogm-bundle

Symfony bundle integrating Neo4j OGM so you can map PHP objects to graph nodes and relationships, manage entities through an object graph mapper, and use Neo4j in Symfony apps with bundle configuration and service integration.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Confirm you actually want this package first. kwattro/neo4j-ogm-bundle is a thin Symfony integration around GraphAware's PHP OGM (graphaware/neo4j-php-ogm). It targets the Symfony 2.x/3.x era and has almost no downstream usage, so treat it as a starting point you may end up forking rather than a long-term dependency. Pin exact versions in composer.json before you build anything on top of it.

2. Install and register.

composer require kwattro/neo4j-ogm-bundle
// app/AppKernel.php (Symfony 2/3)
$bundles = [
    // ...
    new Kwattro\Neo4jOGMBundle\KwattroNeo4jOGMBundle(),
];

3. Read the bundle source before you read the README. The two files that tell you the truth are:

  • DependencyInjection/Configuration.php — the exact config tree (host, port, scheme, auth, entity paths).
  • Resources/config/services.xml (or .yml) — the service IDs you'll inject, typically an entity manager and a connection/client service.

Configure the connection in config.yml:

kwattro_neo4j_ogm:
    host: 127.0.0.1
    port: 7474
    username: neo4j
    password: secret
    # scheme/transport key name depends on the OGM version — verify in Configuration.php

4. Map your first entity. OGM entities are plain PHP classes annotated with the OGM's annotations (Doctrine annotation reader under the hood):

namespace AppBundle\Entity;

use GraphAware\Neo4j\OGM\Annotations as OGM;

/**
 * @OGM\Node(label="User")
 */
class User
{
    /** @OGM\GraphId */
    protected $id;

    /** @OGM\Property(type="string") */
    protected $name;

    /** @OGM\Relationship(type="FOLLOWS", direction="OUTGOING", targetEntity=User::class, collection=true) */
    protected $follows;
}

5. First use case — persist and read back. Inject the entity manager service (check the id in services.xml; commonly something like neo4j.ogm.entity_manager) and run the canonical loop:

$user = new User();
$user->setName('Alice');

$em->persist($user);
$em->flush();                          // writes to Neo4j

$found = $em->getRepository(User::class)->findOneBy(['name' => 'Alice']);

If that round-trip works, everything else is variations on it.

Implementation Patterns

Repository-per-entity, not query-in-controller. The OGM's repository layer is the main reason to use it over raw Cypher. Create a class extending BaseRepository, annotate it, and let getRepository() resolve it:

/**
 * @OGM\Repository(entity=User::class)
 */
class UserRepository extends BaseRepository
{
    public function findPopular(int $minFollowers)
    {
        return $this->entityManager
            ->createQuery('MATCH (u:User)<-[:FOLLOWS]-(f) 
                           WITH u, count(f) AS c WHERE c >= {min} 
                           RETURN u', ['min' => $minFollowers])
            ->getResult();
    }
}

Two-tier access: OGM for writes, Cypher for reads. The OGM is good at identity management and relationship hydration for small subgraphs. For reporting, aggregation, or anything returning more than a few hundred nodes, drop to createQuery(...)->getResult() and map into DTOs — don't hydrate full entity graphs you'll never mutate.

Flush in batches. The unit of work keeps every persisted entity in memory until flush(). For imports/fixtures, persist N objects, flush() every 100–500, and be explicit about whether you want identity-map clearing after.

Wire it into Symfony like any other persistence layer. The entity manager is a plain service, so it goes into controllers, console commands, and message handlers via constructor injection. Wrap it behind your own small "graph store" service if you expect to swap the OGM out later — given the package's trajectory, that seam is cheap insurance.

Test against a real Neo4j instance. There's no meaningful in-memory fake. Use a dedicated test database and clean between tests:

$em->createQuery('MATCH (n) DETACH DELETE n')->execute();

Wrap each test in a transaction if the OGM version exposes one, otherwise accept the cleanup cost.

Data fixtures are a natural fit. Model your seed graph in YAML/arrays and loop persist/flush — it's one of the few places where the OGM's relationship handling genuinely saves time over hand-written Cypher.

Gotchas and Tips

Version compatibility is the real risk, not the API. This bundle pins old Symfony and OGM versions; Symfony 4+ removes AppKernel and may fight the bundle's DI conventions. Check composer.json constraints before adopting, and expect to either stay on an older Symfony or fork the bundle's DependencyInjection folder. Zero dependents means nobody else is finding these bugs for you.

Annotation reader deprecation. The OGM relies on Doctrine annotations. On newer PHP/Symfony versions you'll hit deprecation noise or outright failures from the annotation reader. Budget time for this; it's usually the first thing that breaks.

Proxies and cache directories need to be writable. The OGM generates proxy classes for lazy-loaded relationships. If entity directories aren't configured correctly (often a paths or bundle-scan convention), you get silent proxy failures or "class not found" on hydration rather than a clear config error. When a relationship comes back as null, check proxy generation before you check your Cypher.

GraphId is not optional. Entities without a graph id can't be tracked by the identity map, and merge-style behavior degrades into duplicate nodes. Every @OGM\Node gets one.

Relationship direction and type are load-bearing. direction="OUTGOING" vs "INCOMING" changes which side owns the edge. Get it wrong and you'll write edges that your read queries never see — the classic "it persisted but returns nothing" bug. Verify in Neo4j Browser (MATCH (a)-[r]->(b) RETURN r) rather than trusting the ORM.

Lazy loading means N+1 queries. Traversing a collection of relationships triggers a query per entity unless you fetch eagerly or constrain depth. The OGM exposes a load-depth concept on relationships/queries; use it, and profile against a realistic graph size. Depth 1 is usually enough; depth 3 on a dense graph will hurt.

Dates, arrays, and custom types don't round-trip for free. Neo4j stores primitives. Check how your OGM version converts \DateTime and arrays, and register a custom type handler if the default loses information. This is one of the cleaner extension points the OGM offers.

Transaction semantics are thinner than Doctrine ORM. flush() batches writes but doesn't give you Doctrine-style transactional isolation. For multi-step mutations that must be atomic, issue explicit Cypher transactions through the underlying connection rather than chaining flush() calls.

Debugging: log the Cypher. The fastest path through any hydration mystery is seeing the generated query. Either enable the OGM's logger or wrap the connection service with a decorator that dumps statements — then run the same statement in Neo4j Browser and compare results.

Extension points worth knowing: custom repositories (primary), custom type handlers (dates/complex properties), and overriding the entity manager service definition in your app config. Beyond those three, the bundle is thin enough that forking the DependencyInjection and Resources/config folders is often more productive than fighting the existing wiring.

Have an exit plan. If the project is long-lived, isolate the OGM behind your own repository interfaces and keep raw Cypher available via neoxygen/neo4j-neoclient or the official graphaware/neo4j-php-client. Migrating off the OGM later is much cheaper from that position than from entities scattered across controllers.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel