PHPackages                             gjbateup/neoeloquent - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. gjbateup/neoeloquent

ActiveLibrary[Database &amp; ORM](/categories/database)

gjbateup/neoeloquent
====================

Laravel wrapper for the Neo4j graph database REST interface

v0.2(2y ago)03MITPHPPHP &gt;=7.4

Since Nov 13Pushed 2y agoCompare

[ Source](https://github.com/gjbateup/NeoEloquent)[ Packagist](https://packagist.org/packages/gjbateup/neoeloquent)[ RSS](/packages/gjbateup-neoeloquent/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (13)Versions (3)Used By (0)

[![Build Status](https://camo.githubusercontent.com/780719514804f6a76404294c889137339d2c72bcbb0d2e8665d59c6cccb1fab6/68747470733a2f2f7472617669732d63692e6f72672f56696e656c61622f4e656f456c6f7175656e742e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/Vinelab/NeoEloquent)

NeoEloquent
===========

[](#neoeloquent)

Neo4j Graph Eloquent Driver for Laravel.

Quick Reference
---------------

[](#quick-reference)

- [Installation](#installation)
- [Configuration](#configuration)
- [Models](#models)
- [Relationships](#relationships)
- [Edges](#edges)
- [Migration](#migration)
- [Schema](#schema)
- [Aggregates](#aggregates)
- [Only in Neo](#only-in-neo)
- [Things To Avoid](#avoid)

Installation
------------

[](#installation)

Run `composer require vinelab/neoeloquent`

Or add the package to your `composer.json` and run `composer update`.

```
{
    "require": {
        "vinelab/neoeloquent": "1.8.*"
    }
}
```

Add the service provider in `app/config/app.php`:

```
Vinelab\NeoEloquent\NeoEloquentServiceProvider::class,
```

The service provider will register all the required classes for this package and will also alias the `Model` class to `NeoEloquent` so you can simply `extend NeoEloquent` in your models.

Configuration
-------------

[](#configuration)

### Connection

[](#connection)

in `app/config/database.php` or in case of an environment-based configuration `app/config/[env]/database.php`make `neo4j` your default connection:

```
'default' => 'neo4j',
```

Add the connection defaults:

```
'connections' => [
    'neo4j' => [
        'driver' => 'neo4j',
        'host'   => 'localhost',
        'port'   => 7687,
        'username' => 'username',
        'password' => 'password',
    ]
]
```

> Remember to update your ENV variables (`.env`) in case you're using them.

### Migration Setup

[](#migration-setup)

If you're willing to have migrations:

- create the folder `app/database/labels`
- modify `composer.json` and add `app/database/labels` to the `classmap` array
- run `composer dump-autoload`

### Documentation

[](#documentation)

Models
------

[](#models)

- [Node Labels](#namespaced-models)
- [Soft Deleting](#soft-deleting)

```
use NeoEloquent;

class User extends NeoEloquent {}
```

As simple as it is, NeoEloquent will generate the default node label from the class name, in this case it will be `:User`. Read about [node labels here](http://docs.neo4j.org/chunked/stable/rest-api-node-labels.html)

### Namespaced Models

[](#namespaced-models)

When you use namespaces with your models the label will consider the full namespace.

```
namespace App\Models;

use NeoEloquent;

class Admin extends NeoEloquent { }
```

The generated label from that relationship will be `VinelabCmsAdmin`, this is necessary to make sure that labels do not clash in cases where we introduce another `Admin` instance like `Vinelab\Blog\Admin` then things gets messy with `:Admin` in the database.

### Custom Node Labels

[](#custom-node-labels)

You may specify the label(s) you wish to be used instead of the default generated, they are also case sensitive so they will be stored as put here.

```
use NeoEloquent;

class User extends NeoEloquent {

    protected $label = 'User'; // or array('User', 'Fan')

    protected $fillable = ['name', 'email'];
}

$user = User::create(['name' => 'Some Name', 'email' => 'some@email.com']);
```

NeoEloquent has a fallback support for the `$table` variable that will be used if found and there was no `$label` defined on the model.

```
use NeoEloquent;

class User extends NeoEloquent {

    protected $table = 'User';

}
```

Do not worry about the labels formatting, You may specify them as `array('Label1', 'Label2')` or separate them by a column `:` and prepending them with a `:` is optional.

### Soft Deleting

[](#soft-deleting)

To enable soft deleting you'll need to `use Vinelab\NeoEloquent\Eloquent\SoftDeletingTrait`instead of `Illuminate\Database\Eloquent\SoftDeletingTrait` and just like Eloquent you'll need the `$dates` in your models as follows:

```
use Vinelab\NeoEloquent\Eloquent\SoftDeletingTrait;

class User extends NeoEloquent {

    use SoftDeletingTrait;

    protected $dates = ['deleted_at'];

}
```

Relationships
-------------

[](#relationships)

- [One-To-One](#one-to-one)
- [One-To-Many](#one-to-many)
- [Many-To-Many](#many-to-many)
- [Polymorphic](#polymorphic)

Let's go through some examples of relationships between Nodes.

### One-To-One

[](#one-to-one)

```
class User extends NeoEloquent {

    public function phone()
    {
        return $this->hasOne('Phone');
    }
```

This represents an `OUTGOING` relationship direction from the `:User` node to a `:Phone`.

##### Saving

[](#saving)

```
$phone = new Phone(['code' => 961, 'number' => '98765432'])
$relation = $user->phone()->save($phone);
```

The Cypher performed by this statement will be as follows:

```
MATCH (user:`User`)
WHERE id(user) = 1
CREATE (user)-[:PHONE]->(phone:`Phone` {code: 961, number: '98765432', created_at: 7543788, updated_at: 7543788})
RETURN phone;

```

##### Defining The Inverse Of This Relation

[](#defining-the-inverse-of-this-relation)

```
class Phone extends NeoEloquent {

    public function user()
    {
        return $this->belongsTo('User');
    }
}
```

This represents an `INCOMING` relationship direction from the `:User` node to this `:Phone` node.

##### Associating Models

[](#associating-models)

Due to the fact that we do not deal with **foreign keys**, in our case it is much more than just setting the foreign key attribute on the parent model. In Neo4j (and Graph in general) a relationship is an entity itself that can also have attributes of its own, hence the introduction of [**Edges**](#Edges)

> *Note:* Associated models does not persist relations automatically when calling `associate()`.

```
$account = Account::find(1986);

// $relation will be Vinelab\NeoEloquent\Eloquent\Edges\EdgeIn
$relation = $user->account()->associate($account);

// Save the relation
$relation->save();
```

The Cypher performed by this statement will be as follows:

```
MATCH (account:`Account`), (user:`User`)
WHERE id(account) = 1986 AND id(user) = 9862
MERGE (account)hasMany('Post', 'POSTED');
    }
}
```

This represents an `OUTGOING` relationship direction from the `:User` node to the `:Post` node.

```
$user = User::find(1);
$post = new Post(['title' => 'The Title', 'body' => 'Hot Body']);
$user->posts()->save($post);
```

Similar to `One-To-One` relationships the returned value from a `save()` statement is an `Edge[In|Out]`

The Cypher performed by this statement will be as follows:

```
MATCH (user:`User`)
WHERE id(user) = 1
CREATE (user)-[rel_user_post:POSTED]->(post:`Post` {title: 'The Title', body: 'Hot Body', created_at: '15-05-2014', updated_at: '15-05-2014'})
RETURN rel_user_post;

```

##### Defining The Inverse Of This Relation

[](#defining-the-inverse-of-this-relation-1)

```
class Post extends NeoEloquent {

    public function author()
    {
        return $this->belongsTo('User', 'POSTED');
    }
}
```

This represents an `INCOMING` relationship direction from the `:User` node to this `:Post` node.

### Many-To-Many

[](#many-to-many)

```
class User extends NeoEloquent {

    public function followers()
    {
        return $this->belongsToMany('User', 'FOLLOWS');
    }
}
```

This represents an `INCOMING` relationship between a `:User` node and another `:User`.

```
$jd = User::find(1012);
$mc = User::find(1013);
```

`$jd` follows `$mc`:

```
$jd->followers()->save($mc);
```

Or using the `attach()` method:

```
$jd->followers()->attach($mc);
// Or..
$jd->followers()->attach(1013); // 1013 being the id of $mc ($mc->getKey())
```

The Cypher performed by this statement will be as follows:

```
MATCH (user:`User`), (followers:`User`)
WHERE id(user) = 1012 AND id(followers) = 1013
CREATE (followers)-[:FOLLOWS]->(user)
RETURN rel_follows;

```

`$mc` follows `$jd` back:

```
$mc->followers()->save($jd);
```

The Cypher performed by this statement will be as follows:

```
MATCH (user:`User`), (followers:`User`)
WHERE id(user) = 1013 AND id(followers) = 1012
CREATE (user)-[rel_user_followers:FOLLOWS]->(followers)
RETURN rel_follows;

```

get the followers of `$jd`

```
$followers = $jd->followers;
```

The Cypher performed by this statement will be as follows:

```
MATCH (user:`User`), (followers:`User`), (user)-[rel_user_followers:FOLLOWS]-(followers)
WHERE id(user) = 1012
RETURN rel_follows;

```

### Dynamic Properties

[](#dynamic-properties)

```
class Phone extends NeoEloquent {

    public function user()
    {
        return $this->belongsTo('User');
    }

}

$phone = Phone::find(1006);
$user = $phone->user;
// or getting an attribute out of the related model
$name = $phone->user->name;
```

### Polymorphic

[](#polymorphic)

The concept behind Polymorphic relations is purely relational to the bone but when it comes to graph we are representing it as a [HyperEdge](http://docs.neo4j.org/chunked/stable/cypher-cookbook-hyperedges.html).

Hyper edges involves three models, the **parent** model, **hyper** model and **related** model represented in the following figure:

[![HyperEdges](https://camo.githubusercontent.com/f1291f47c505b67b5ac2da31f47eaa71e40e3b637600c35e80608cef76632697/68747470733a2f2f676f6f676c6564726976652e636f6d2f686f73742f30427a6e7a5a326c42625430634c573959636a4e6c646c4a6b6358632f4879706572456467652e706e67 "HyperEdges")](https://camo.githubusercontent.com/f1291f47c505b67b5ac2da31f47eaa71e40e3b637600c35e80608cef76632697/68747470733a2f2f676f6f676c6564726976652e636f6d2f686f73742f30427a6e7a5a326c42625430634c573959636a4e6c646c4a6b6358632f4879706572456467652e706e67)

Similarly in code this will be represented by three models `User` `Comment` and `Post`where a `User` with id 1 posts a `Post` and a `User` with id 6 `COMMENTED` a `Comment` `ON` that `Post`as follows:

```
class User extends NeoEloquent {

    public function comments($morph = null)
    {
        return $this->hyperMorph($morph, 'Comment', 'COMMENTED', 'ON');
    }

}
```

In order to keep things simple but still involving the three models we will have to pass the `$morph` which is any `commentable` model, in our case it's either a `Video` or a `Post` model.

> **Note:** Make sure to have it defaulting to `null` so that we can Dynamicly or Eager load with `$user->comments` later on.

Creating a `Comment` with the `create()` method.

```
$user = User::find(6);
$post = Post::find(2);

$user->comments($post)->create(['text' => 'Totally agree!', 'likes' => 0, 'abuse' => 0]);
```

As usual we will have returned an Edge, but this time it's not directed it is an instance of `HyperEdge`, read more about [HyperEdges here](#hyperedge).

Or you may save a Comment instance:

```
$comment = new Comment(['text' => 'Magnificent', 'likes' => 0, 'abuse' => 0]);

$user->comments($post)->save($comment);
```

Also all the functionalities found in a `BelongsToMany` relationship are supported like attaching models by Ids:

```
$user->comments($post)->attach([$id, $otherId]);
```

Or detaching models:

```
$user->comments($post)->detach($comment); // or $comment->id
```

Sync too:

```
$user->comments($post)->sync([$id, $otherId, $someId]);
```

#### Retrieving Polymorphic Relations

[](#retrieving-polymorphic-relations)

From our previous example we will use the `Video` model to retrieve their comments:

```
class Video extends NeoEloquent {

    public function comments()
    {
        return $this->morphMany('Comment', 'ON');
    }

}
```

##### Dynamicly Loading Morph Model

[](#dynamicly-loading-morph-model)

```
$video = Video::find(3);
$comments = $video->comments;
```

##### Eager Loading Morph Model

[](#eager-loading-morph-model)

```
$video = Video::with('comments')->find(3);
foreach ($video->comments as $comment)
{
    //
}
```

#### Retrieving The Inverse of a Polymorphic Relation

[](#retrieving-the-inverse-of-a-polymorphic-relation)

```
class Comment extends NeoEloquent {

    public function commentable()
    {
        return $this->morphTo();
    }

}
```

```
$postComment = Comment::find(7);
$post = $comment->commentable;

$videoComment = Comment::find(5);
$video = $comment->commentable;

// You can also eager load them
Comment::with('commentable')->get();
```

You may also specify the type of morph you would like returned:

```
class Comment extends NeoEloquent {

    public function post()
    {
        return $this->morphTo('Post', 'ON');
    }

    public function video()
    {
        return $this->morphTo('Video', 'ON');
    }

}
```

#### Polymorphic Relations In Short

[](#polymorphic-relations-in-short)

To drill things down here's how our three models involved in a Polymorphic relationship connect:

```
class User extends NeoEloquent {

    public function comments($morph = null)
    {
        return $this->hyperMorph($morph, 'Comment', 'COMMENTED', 'ON');
    }

}
```

```
class Post extends NeoEloquent { // Video is the same as this one

    public function comments()
    {
        return $this->morphMany('Comment', 'ON');
    }

}
```

```
class Comment extends NeoEloquent {

    public function commentable()
    {
        return $this->morphTo();
    }

}
```

### Eager Loading

[](#eager-loading)

```
class Book extends NeoEloquent {

    public function author()
    {
        return $this->belongsTo('Author');
    }
}
```

Loading authors with their books with the least performance overhead possible.

```
foreach (Book::with('author')->get() as $book)
{
    echo $book->author->name;
}
```

Only two Cypher queries will be run in the loop above:

```
MATCH (book:`Book`) RETURN *;

MATCH (book:`Book`), (book)associate($user);
$edge->last_visited = 'today';
$edge->save(); // true
```

#### EdgeIn

[](#edgein)

Represents an `INCOMING` direction relationship from the related model towards the parent model.

```
class Location extends NeoEloquent {

    public function user()
    {
        return $this->belongsTo('User', 'LOCATED_AT');
    }

}
```

To associate a `User` to a `Location`:

```
$location = Location::find(1922);
$user = User::find(3876);
$relation = $location->associate($user);
```

which in Cypher land will map to `(:Location)associate($user);
$location = $relation->parent();
$user = $relation->related();
```

#### EdgeOut

[](#edgeout)

Represents an `OUTGOING` direction relationship from the parent model to the related model.

```
class User extends NeoEloquent {

    public function posts()
    {
        return $this->hasMany('Post', 'POSTED');
    }

}
```

To save an outgoing edge from `:User` to `:Post` it goes like:

```
$post = new Post(['...']);
$posted = $user->posts()->save($post);
```

Which in Cypher would be `(:User)-[:POSTED]->(:Post)` and `$posted` being the `EdgeOut` instance.

And fetch the related models:

```
$edge = $user->posts()->save($post);
$user = $edge->parent();
$post = $edge->related();
```

#### HyperEdge

[](#hyperedge)

This edge comes as a result of a [Polymorphic Relation](#polymorphic) representing an edge involving two other edges **left** and **right** that can be accessed through the `left()` and `right()` methods.

This edge is treated a bit different than the others since it is not a direct relationship between two models which means it has no specific direction.

```
$edge = $user->comments($post)->attach($comment);
// Access the left and right edges
$left = $edge->left();
$user = $left->parent();
$comment = $left->related();

$right = $edge->right();
$comment = $right->parent();
$post = $right->related();
```

### Working With Edges

[](#working-with-edges)

As stated earlier **Edges** are entities to Graph unlike *SQL* where they are a matter of a foreign key having the value of the parent model as an attribute on the belonging model or in *Documents* where they are either embeds or ids as references. So we developed them to be *light models* which means you can work with them as if you were working with an `Eloquent` instance - to a certain extent, except [HyperEdges](#hyperedges).

```
// Create a new relationship
$relation = $location->associate($user); // Vinelab\NeoEloquent\Eloquent\Edges\EdgeIn

// Save the relationship to the database
$relation->save(); // true
```

In the case of a `HyperEdge` you can access all three models as follows:

```
$edge    = $user->comments($post)->save($comment);
$user    = $edge->parent();
$comment = $edge->hyper();
$post    = $edge->related();
```

#### Edge Attributes

[](#edge-attributes)

By default, edges will have the timestamps `created_at` and `updated_at` automatically set and updated **only if** timestamps are enabled by setting `$timestamps` to `true`on the parent model.

```
$located_at = $location->associate($user);
$located_at->since = 1966;
$located_at->present = true;
$located_at->save();

// $created_at and $updated_at are Carbon\Carbon instances
$created_at = $located_at->created_at;
$updated_at = $located_at->updated_at;
```

##### Retrieve an Edge from a Relation

[](#retrieve-an-edge-from-a-relation)

The same way an association will create an `EdgeIn` relationship we can retrieve the edge between two models by calling the `edge($model)` method on the `belongsTo`relationship.

```
$location = Location::find(1892);
$edge = $location->user()->edge();
```

You may also specify the model at the other side of the edge.

> Note: By default NeoEloquent will try to pefrorm the `$location->user` internally to figure out the related side of the edge based on the relation function name, in this case it's `user()`.

```
$location = Location::find(1892);
$edge = $location->user()->edge($location->user);
```

Only in Neo
-----------

[](#only-in-neo)

- [CreateWith](#createwith)

Here you will find NeoEloquent-specific methods and implementations that with the wonderful Eloquent methods would make working with Graph and Neo4j a blast!

### CreateWith

[](#createwith)

- [Creating Relations](#creating-new-records-and-relations)
- [Attaching Relations](#attaching-existing-records-as-relations)

This method will "kind of" fill the gap between relational and document databases, it allows the creation of multiple related models with one database hit.

#### Creating New Records and Relations

[](#creating-new-records-and-relations)

Here's an example of creating a post with attached photos and videos:

```
class Post extends NeoEloquent {

    public function photos()
    {
        return $this->hasMany('Photo', 'PHOTO');
    }

    public function videos()
    {
        return $this->hasMany('Video', 'VIDEO');
    }
}
```

```
Post::createWith(['title' => 'the title', 'body' => 'the body'], [
    'photos' => [
        [
            'url'      => 'http://url',
            'caption'  => '...',
            'metadata' => '...'
        ],
        [
            'url' => 'http://other.url',
            'caption' => 'the bay',
            'metadata' => '...'
        ]
    ],

    'videos' => [
        'title' => 'Boats passing us by',
        'description' => '...'
    ]
]);
```

> The keys `photos` and `videos` must be the same as the relation method names in the `Post` model.

The Cypher query performed by the example above is:

```
CREATE (post:`Post` {title: 'the title', body: 'the body'}),
(post)-[:PHOTO]->(:`Photo` {url: 'http://url', caption: '...', metadata: '...'}),
(post)-[:PHOTO]->(:`Photo` {url: 'http://other', caption: 'the bay', metadata: '...'}),
(post)-[:VIDEO]->(:`Video` {title: 'Boats passing us by', description: '...'});

```

We will get the nodes created with their relations as such:

[![CreateWith](https://camo.githubusercontent.com/6ac37c66cfd7da27d70f481afae4b753d046658909d7f4511a1fccbafcaa0ead/68747470733a2f2f676f6f676c6564726976652e636f6d2f686f73742f30427a6e7a5a326c42625430634c573959636a4e6c646c4a6b6358632f637265617465576974682d707265766965772e64622e706e67 "CreateWith")](https://camo.githubusercontent.com/6ac37c66cfd7da27d70f481afae4b753d046658909d7f4511a1fccbafcaa0ead/68747470733a2f2f676f6f676c6564726976652e636f6d2f686f73742f30427a6e7a5a326c42625430634c573959636a4e6c646c4a6b6358632f637265617465576974682d707265766965772e64622e706e67)

You may also mix models and attributes as relation values but it is not necessary since NeoEloquent will pass the provided attributes through the `$fillable`filter pipeline:

```
$videos = new Video(['title' => 'foo', 'description' => 'bar']);
Post::createWith($info, compact('videos'));
```

You may also use a single array of attributes as such:

```
class User extends NeoEloquent {

    public function account()
    {
        return $this->hasOne('Account');
    }
}

User::createWith(['name' => 'foo'], ['account' => ['guid' => 'bar', 'email' => 'some@mail.net']]);
```

#### Attaching Existing Records as Relations

[](#attaching-existing-records-as-relations)

`createWith` is intelligent enough to know the difference when you pass an existing model, a model Id or new records that you need to create which allows mixing new records with existing ones.

```
class Post extends NeoEloquent {

    public function tags()
    {
        return $this->hasMany('Tag', 'TAG');
    }
}
```

```
$tag1 = Tag::create(['title' => 'php']);
$tag2 = Tag::create(['title' => 'dev']);

$post = Post::createWith(['title' => 'foo', 'body' => 'bar'], ['tags' => [$tag1, $tag2]]);
```

And we will get the `Post` related to the existing `Tag` nodes.

Or using the `id` of the model:

```
Post::createWith(['title' => 'foo', 'body' => 'bar'], ['tags' => 1, 'privacy' => 2]);
```

The Cypher for the query that attaches records would be:

```
CREATE (post:`Post` {title: 'foo', 'body' => 'bar'})
WITH post
MATCH (tag:`Tag`)
WHERE id(tag) IN [1, 2]
CREATE (post)-[:TAG]->(tag);

```

Migration
---------

[](#migration)

For migrations to work please perform the following:

- create the folder `app/database/labels`
- modify `composer.json` and add `app/database/labels` to the `classmap` array

Since Neo4j is a schema-less database you don't need to predefine types of properties for labels. However you will be able to perform [Indexing](http://neo4j.com/docs/stable/query-schema-index.html) and [Constraints](http://neo4j.com/docs/stable/query-constraints.html) using NeoEloquent's pain-less [Schema](#schema).

#### Commands

[](#commands)

NeoEloquent introduces new commands under the `neo4j` namespace so you can still use Eloquent's migration commands side-by-side.

Migration commands are the same as those of Eloquent, in the form of `neo4j:migrate[:command]`

```
neo4j:make:migration                 Create a new migration file
neo4j:migrate                        Run the database migrations
neo4j:migrate:reset                  Rollback all database migrations
neo4j:migrate:refresh                Reset and re-run all migrations
neo4j:migrate:rollback               Rollback the last database migration

```

### Creating Migrations

[](#creating-migrations)

Like in Laravel you can create a new migration by using the `make` command with Artisan:

```
php artisan neo4j:make:migration create_user_label

```

Label migrations will be placed in `app/database/labels`

You can add additional options to commands like:

```
php artisan neo4j:make:migration foo --path=app/labels
php artisan neo4j:make:migration create_user_label --create=User
php artisan neo4j:make:migration create_user_label --label=User

```

### Running Migrations

[](#running-migrations)

##### Run All Outstanding Migrations

[](#run-all-outstanding-migrations)

```
php artisan neo4j:migrate

```

##### Run All Outstanding Migrations For A Path

[](#run-all-outstanding-migrations-for-a-path)

```
php artisan neo4j:migrate --path=app/foo/labels

```

##### Run All Outstanding Migrations For A Package

[](#run-all-outstanding-migrations-for-a-package)

```
php artisan neo4j:migrate --package=vendor/package

```

> Note: If you receive a "class not found" error when running migrations, try running the `composer dump-autoload` command.

#### Forcing Migrations In Production

[](#forcing-migrations-in-production)

To force-run migrations on a production database you can use:

```
php artisan neo4j:migrate --force

```

### Rolling Back Migrations

[](#rolling-back-migrations)

##### Rollback The Last Migration Operation

[](#rollback-the-last-migration-operation)

```
php artisan neo4j:migrate:rollback

```

##### Rollback all migrations

[](#rollback-all-migrations)

```
php artisan neo4j:migrate:reset

```

##### Rollback all migrations and run them all again

[](#rollback-all-migrations-and-run-them-all-again)

```
php artisan neo4j:migrate:refresh

php artisan neo4j:migrate:refresh --seed

```

Schema
------

[](#schema)

NeoEloquent will alias the `Neo4jSchema` facade automatically for you to be used in manipulating labels.

```
Neo4jSchema::label('User', function(Blueprint $label)
{
    $label->unique('uuid');
});
```

If you decide to write Migration classes manually (not using the generator) make sure to have these `use` statements in place:

- `use Vinelab\NeoEloquent\Schema\Blueprint;`
- `use Vinelab\NeoEloquent\Migrations\Migration;`

Currently Neo4j supports `UNIQUE` constraint and `INDEX` on properties. You can read more about them at

#### Schema Methods

[](#schema-methods)

CommandDescription`$label->unique('email')`Adding a unique constraint on a property`$label->dropUnique('email')`Dropping a unique constraint from property`$label->index('uuid')`Adding index on property`$label->dropIndex('uuid')`Dropping index from property### Droping Labels

[](#droping-labels)

```
Neo4jSchema::drop('User');
Neo4jSchema::dropIfExists('User');
```

### Renaming Labels

[](#renaming-labels)

```
Neo4jSchema::renameLabel($from, $to);
```

### Checking Label's Existence

[](#checking-labels-existence)

```
if (Neo4jSchema::hasLabel('User')) {

} else {

}
```

### Checking Relation's Existence

[](#checking-relations-existence)

```
if (Neo4jSchema::hasRelation('FRIEND_OF')) {

} else {

}
```

You can read more about migrations and schema on:

Aggregates
----------

[](#aggregates)

In addition to the Eloquent builder aggregates, NeoEloquent also has support for Neo4j specific aggregates like *percentile* and *standard deviation*, keeping the same function names for convenience. Check [the docs](http://docs.neo4j.org/chunked/stable/query-aggregation.html) for more.

> `table()` represents the label of the model

```
$users = DB::table('User')->count();

$distinct = DB::table('User')->countDistinct('points');

$price = DB::table('Order')->max('price');

$price = DB::table('Order')->min('price');

$price = DB::table('Order')->avg('price');

$total = DB::table('User')->sum('votes');

$disc = DB::table('User')->percentileDisc('votes', 0.2);

$cont = DB::table('User')->percentileCont('votes', 0.8);

$deviation = DB::table('User')->stdev('sex');

$population = DB::table('User')->stdevp('sex');

$emails = DB::table('User')->collect('email');

```

Changelog
---------

[](#changelog)

Check the [Releases](https://github.com/Vinelab/NeoEloquent/releases) for details.

Avoid
-----

[](#avoid)

Here are some constraints and Graph-specific gotchas, a list of features that are either not supported or not recommended.

### JOINS 😖

[](#joins-confounded)

- They make no sense for Graph, plus Graph hates them! Which makes them unsupported on purpose. If migrating from an `SQL`-based app they will be your boogie monster.

### Pivot Tables in Many-To-Many Relationships

[](#pivot-tables-in-many-to-many-relationships)

This is not supported, instead we will be using [Edges](#edges) to work with relationships between models.

### Nested Arrays and Objects

[](#nested-arrays-and-objects)

- Due to the limitations imposed by the objects map types that can be stored in a single, you can never have nested *arrays* or *objects* in a single model, make sure it's flat. *Example:*

```
// Don't
User::create(['name' => 'Some Name', 'location' => ['lat' => 123, 'lng'=> -123 ] ]);
```

Check out the [createWith()](#createwith) method on how you can achieve this in a Graph way.

Tests
-----

[](#tests)

- install a Neo4j instance and run it with the default configuration `localhost:7474`
- make sure the database graph is empty to avoid conflicts
- after running `composer install` there should be `/vendor/bin/phpunit`
- run `./vendor/bin/phpunit` after making sure that the Neo4j instance is running

> Tests marked as incomplete means they are either known issues or non-supported features, check included messages for more info.

Factories
---------

[](#factories)

> You can use default Laravel `factory()` helper for NeoEloquent models too.

- define needed factories inside `database/factories/`(read more)\[\];
- use `factory()` in the same style as default Laravel `factory()`.

###  Health Score

17

—

LowBetter than 6% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity3

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 58.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

2

Last Release

909d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8d9bf804218f98bd10201105710921ec5de3f7d4f663ccf1ac8101d8f95ed754?d=identicon)[gjbateup](/maintainers/gjbateup)

---

Top Contributors

[![transistive](https://avatars.githubusercontent.com/u/16435930?v=4)](https://github.com/transistive "transistive (37 commits)")[![Mulkave](https://avatars.githubusercontent.com/u/2647333?v=4)](https://github.com/Mulkave "Mulkave (13 commits)")[![iyhunko](https://avatars.githubusercontent.com/u/15122341?v=4)](https://github.com/iyhunko "iyhunko (8 commits)")[![KinaneD](https://avatars.githubusercontent.com/u/12595235?v=4)](https://github.com/KinaneD "KinaneD (3 commits)")[![mrbig](https://avatars.githubusercontent.com/u/102836?v=4)](https://github.com/mrbig "mrbig (2 commits)")

---

Tags

databaseneo4jogmgraphneoeloquent

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/gjbateup-neoeloquent/health.svg)

```
[![Health](https://phpackages.com/badges/gjbateup-neoeloquent/health.svg)](https://phpackages.com/packages/gjbateup-neoeloquent)
```

###  Alternatives

[vinelab/neoeloquent

Laravel wrapper for the Neo4j graph database REST interface

65393.1k1](/packages/vinelab-neoeloquent)[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k7.2M71](/packages/mongodb-laravel-mongodb)[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)[ulobby/neoeloquent

Laravel wrapper for the Neo4j graph database REST interface

4473.4k](/packages/ulobby-neoeloquent)[pdphilip/elasticsearch

An Elasticsearch implementation of Laravel's Eloquent ORM

145360.2k4](/packages/pdphilip-elasticsearch)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

44643.1k1](/packages/pressbooks-pressbooks)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
