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

Neo4Jphp Ogm Laravel Package

hirevoice/neo4jphp-ogm

Neo4j PHP Object Graph Mapper: an object management layer built on neo4jphp that lets you persist and retrieve entities in Neo4j via REST. Doctrine-style annotations, entity manager, repositories, and automatic relationship handling.

View on GitHub
Deep Wiki
Context7

Getting Started

What you're actually adopting. This is a Doctrine2-shaped OGM over the everyman/neo4jphp REST client. That single sentence dictates almost everything about how you'll use it day-to-day: you get familiar persist / flush / getRepository ergonomics, but every unit of work is HTTP requests against Neo4j's legacy REST endpoints. Treat it as a frozen-era library (Neo4j 1.9–2.x, PHP 5.4-era style) rather than a modern driver.

Minimal bootstrap:

composer require hirevoice/neo4jphp-ogm
# Neo4j must be running with the REST endpoint reachable, e.g. http://localhost:7474
<?php
$em = \HireVoice\Neo4j\EntityManager::create('http://localhost:7474', array(
    'proxy_dir' => __DIR__ . '/var/proxies', // must exist and be writable
    'debug'     => true,                     // regenerates proxies on change
));

$user = new \Entity\User;
$user->setFullName('Ada Lovelace');
$em->persist($user);
$em->flush();                 // one round trip per node/relationship

$found = $em->getRepository('Entity\User')->find($id);

Where to look first, in order:

  1. README.md — the User entity example is the whole mental model: @OGM\Auto id, @OGM\Property + @OGM\Index scalars, @OGM\ManyToMany / @OGM\ManyToOne relations.
  2. lib/HireVoice/Neo4j/Annotation/ — the authoritative list of what's supported. The README shows a subset; the annotation classes are the schema you're actually programming against.
  3. EntityManager.php — the real set of accepted options (proxy_dir, debug, annotation/cache wiring) and the transport hook.
  4. lib/HireVoice/Neo4j/Repository.php and Query/ — how finders and Cypher are constructed.
  5. tests/ — the closest thing to docs for behaviour the README never covers (cascades, removal, hydration).

First use case to build: a social/follow graph. Create User with follows (ManyToMany) and referrer (ManyToOne), persist two users, wire a relationship, flush, then find() and traverse. That exercises node creation, relationship creation, indexing, and lazy hydration — i.e. every part of the stack that can surprise you — in about twenty lines.

Implementation Patterns

Entity design is your schema. There are no migrations. The graph shape is the class shape, so model deliberately:

/**
 * @OGM\Entity
 */
class User
{
    /** @OGM\Auto */
    protected $id;

    /** @OGM\Property @OGM\Index */
    protected $email;

    /** @OGM\ManyToMany */
    protected $follows;

    public function __construct()
    {
        $this->follows = new \Doctrine\Common\Collections\ArrayCollection;
    }

    public function addFollow(User $u) { $this->follows->add($u); }
}

Conventions that hold up in practice:

  • Always initialize collections in the constructor. Every add*/remove* helper assumes you did.
  • Index exactly the fields you look up by. @OGM\Index is the entry point that makes findOneBy… a cheap legacy-index lookup instead of a full scan.
  • Name properties for the relationship type you want — the property name drives the graph edge label, so $follows becomes a FOLLOWS-ish edge. Renaming a property is a data migration, even though nothing warns you.

Repository + finders for lookups, Cypher for traversals. The OGM is a fine CRUD layer and a poor query language. Anything past one hop should drop to Cypher and map results back yourself:

$repo = $em->getRepository('Entity\User');

$u = $repo->findOneBy(array('email' => $email));  // uses the index
$users = $repo->findAll();

// Reach for the underlying client for real graph queries
$client = $em->getClient(); // confirm accessor in EntityManager
$result = $client->cypher()->run('MATCH (u:User)-[:FOLLOWS*2]->(f) ...', []);

Persist the graph by hand. Doctrine's cascade options are not a thing you should assume here. Persist each new node, then attach relationships, then flush once:

$em->persist($a);
$em->persist($b);
$a->addFollow($b);
$em->flush();

Wire it as a singleton in your framework. One EntityManager per request/worker, proxy dir under storage/ (or the Symfony cache dir), entities under a dedicated Entity\ autoload namespace. Registering it as a container singleton is the difference between "one identity map" and "five entity managers fighting over proxies."

Batch work: seed with Cypher, not the OGM. For fixtures or imports of more than a few hundred nodes, generate CREATE/MERGE statements and run them through the client in one or a handful of requests. Using persist() in a loop is one HTTP request per node and will take minutes.

Testing pattern. Docker-pin neo4j:2.3 (not latest), point tests at a dedicated database, and reset between tests with MATCH (n) DETACH DELETE n. Assert on graph state, not just hydrated objects — hydration bugs are where this library hides.

Gotchas and Tips

The version lock is the biggest risk. The library depends on the legacy REST API. Neo4j 3.x changed transaction semantics, and 4.x removed the REST interface entirely. If your infrastructure is on a modern Neo4j, this package is not a candidate — no amount of configuration fixes it. Check the underlying everyman/neo4jphp version and the server version together before committing.

PHP compatibility is a real unknown. The codebase is from the PHP 5.4/5.5 era. It will often run on 7.x but expect deprecation noise and possible hard breaks on newer runtimes. Pin your PHP in CI immediately so you find out in an afternoon, not at deploy.

Stale proxies are your #1 mysterious bug. Changed an annotation or added a property and now behaviour is weird or fields vanish? It's a cached proxy. Delete the proxy directory. In production, leave debug off (performance) but make proxy clearing part of your deploy script — this is the Doctrine gotcha, and it's sharper here because there's no schema diff to tell you something moved.

No transactions at the OGM level. flush() walks the unit of work issuing individual REST calls. If call #7 fails, calls #1–6 are already committed and your graph is half-written. For anything where partial state is unacceptable, either:

  • keep units of work tiny and idempotent (MERGE semantics via Cypher), or
  • bypass the OGM and use the client's Cypher endpoint for that operation.

N+1 over HTTP is the performance ceiling. Lazy-loading relationships means a round trip per hop. Hydrating 100 users with their follows is 100+ requests. Mitigations: findAll() returns bare nodes cheaply; use Cypher with LIMIT/projections for list screens; call $em->clear() between chunks on long-running jobs or the identity map grows unboundedly.

Index drift if you write outside the OGM. The OGM maintains the legacy index on persist(). Data loaded by raw Cypher, by another service, or by a bulk import will not be indexed unless you write the index entries yourself, and then findOneBy silently returns nothing. Either route all writes through the OGM or maintain indexes explicitly in your import scripts.

remove() deletes the node and its relationships. There's no relational-DB style referential integrity stopping you from orphaning half a subgraph. Audit deletion paths carefully; consider soft deletes (@OGM\Property deletedAt) for anything users can trigger.

Annotation registration. If entities silently fail to map, check that the annotation autoloader is registered for the OGM namespace (AnnotationRegistry::registerLoader(...) on newer doctrine/annotations). A missing registration surfaces as confusing "no metadata" errors rather than a clean failure.

Debugging toolkit: the Neo4j browser/webadmin at :7474 is your source of truth — inspect the actual nodes and edge labels the OGM produced. Log or dump generated Cypher from the Query/ classes when a finder misbehaves. And keep $em->getClient() handy as the escape hatch: you are never trapped by the OGM API, which is genuinely the library's best property given its age.

Extension points worth knowing: custom repository classes (per-entity query logic), the underlying client for raw Cypher and bulk operations, and the annotation classes themselves if you need to model something the bundled set omits. The OGM is thin enough that dropping a layer is normal, not a smell.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings