Vinelab / NeoEloquent

The Neo4j OGM for Laravel
MIT License
634 stars 200 forks source link

Question for a relation #170

Closed m33ch closed 8 years ago

m33ch commented 8 years ago

Hi all,

first of all thank you for this package!

I have two entities : Song and Artist. Artist may have some Song. Song will belong to an Artist or, maybe in future, to another entities (for example Band or Album).

I was wondering what was the best way to create this type of relationship link

This is my code :

Artist entity

namespace App;

 class Artist extends NeoEloquent
 {
    public function songs($morph = null)
    {
        return $this->hyperMorph($morph, 'App\Song', 'PLAY', 'CREATED_BY');
    }
 } 

Song entity

namespace App;

class Song extends NeoEloquent
{
    public function author()
    {
        return $this->morphTo();
    }
}

Test

$artist = App\Artist::create(['name' => 'Artist']);

$artist->songs($artist)->create(['name' => 'Song']);

I think this type of approach is not correct. But i don't understend why! There is something not quite right.

Is there a better way?

Thank you very much!

KinaneD commented 8 years ago

Hi, A polymorphic relationship connects two nodes by means of a third node. polymorphic relations

This is not what you are looking for. You should be defining these relationships with the hasMany and belongsTo types.

Artist:

namespace App;

 class Artist extends NeoEloquent
 {
    public function songs()
    {
        return $this->hasMany('App\Song', 'CREATED_BY');
    }
 } 

Song:

namespace App;

class Song extends NeoEloquent
{
    public function author()
    {
        return $this->belongsTo('App\Artist', 'CREATED_BY');
    }
}

I would recommend calling the latter relationship definition as artist instead of author to avoid possible confusion.

Cheers.

m33ch commented 8 years ago

Hi @KinaneD, thank for your fast reply.

I try this approach, but for create two way relation 'PLAY' and 'CREATED_BY' need to write some like this :

$artist = App\Artist::create(['name' => 'Artist']);
$song = App\Song::create(['name' => 'Song']);

// This is PLAY relation
$artist->songs()->attach($song)->save();

// This is CREATED_BY relation
$song->author()->associate($artist)->save();  

if i don't do this $song->author()->associate($artist)->save();, CREATED_BY relation not be created by $artist->songs()->attach($song)->save();. So if i want to retrieve an author by Song like this :

$song = App\Song::with('author')->first();
// author is null
$author = $song->author;

I think to create a method to abstract author of song, because in future i need to create "Band" entity. Band entity has many Song.

I hope I explained myself Sorry for my bad english.

Thank you very much

KinaneD commented 8 years ago

No problem, In order to be able to query a related node from a parent node and vice versa, you will need to create a relationship definition method for each model. In the CREATED_BY case it would be, Artiste::songs() and Song::author() To create another relationship between the Artist and Song you should repeat the process with another relationship type, in this case Play.

Please note that associate is used for one-to-one relationships, while attach for one-to-many or many-to-many relationships.

m33ch commented 8 years ago

It's clear! Thank you @KinaneD !

m33ch commented 7 years ago

Hi @KinaneD,

i have confused about morphTo relation.

I have Song, Artist, Band entities. Band and Artist can has many Song.

Eg.:

return $this->hasMany('Song', 'COMPOSE');

And Song entity as author method that return morphTo relation.

return $this->morphTo();

When i create a Song i need to attach it to Artist or Band entity

$artist = Artist::find(1);
$song = Song::create(['name' => 'song']);
$artist->songs()->associate($song)->save();

This snippet create COMPOSE relation Artist -> Song.

If i want to retrieve the author of Song, so :

$song : Song::find(1);
return $song->author;

the author is null.

So i edit my snippet to :

$artist = Artist::find(1);
$song = Song::create(['name' => 'song']);
$artist->songs()->associate($song)->save();
$song->author()->associate($artist)->save()

Neo4j create a relation called id, i don't know why, it's possible to define? For example "COMPOSED_BY" ?

Anyway when i try to get author from Song:

$song : Song::find(1);
return $song->author;

an error occurred: Class name must be a valid object or a string

This is author() morphTo object :

  #edgeDirection: "in"
  #morphType: "author_type"
  #foreignKey: "id"
  #otherKey: null
  #relation: "author"

Where am I doing wrong? Sorry for bad english.

Thank you