I'm trying to make a bidirectional ManyToMany relationship on my Tag model, but I ran into this "issue".
My model looks like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
protected $table = 'tags';
public $timestamps = false;
public function tags()
{
return $this->belongsToMany(Tag::class, 'tag_tag', 'tag_one_id', 'tag_two_id');
}
}
So at the moment let's say I have Tag1 and Tag2 in my tags table, and then I'll relate the Tag2 with the Tag1. Now my pivot table will look like this:
+----+------------+------------+
| id | tag_one_id | tag_two_id |
+----+------------+------------+
| 1 | 1 | 2 |
+----+------------+------------+
When I try this code:
$tag = Tag::find(1);
$tag->tags()->get();
I get the Tag2 instance, and it is correct.
But when I try to run this code:
$tag = Tag::find(2);
$tag->tags()->get();
I would like to receive the Tag1 instance, but I don't.
Is it possible to get it done with Laravel default Eloquent using just one method on the model?
I found a solution and I solved it like this.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
/**
* @inheritdoc
*
* @var string
*/
protected $table = 'tags';
/**
* @inheritdoc
*
* @var bool
*/
public $timestamps = false;
/*
|--------------------------------------------------------------------------
| RELATIONS
|--------------------------------------------------------------------------
*/
/**
* Every tag can contain many related tags (TagOne has many TagTwo).
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
protected function tagsOneTwo()
{
return $this->belongsToMany(Tag::class, 'tag_tag', 'tag_one_id', 'tag_two_id');
}
/**
* Every tag can contain many related tags (TagTwo has many TagOne).
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
protected function tagsTwoOne()
{
return $this->belongsToMany(Tag::class, 'tag_tag', 'tag_two_id', 'tag_one_id');
}
/**
* This method returns a collection with all the tags related with this tag.
* It is not a real relation, but emulates it.
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function tags()
{
return $this->tagsOneTwo()->get()->merge($this->tagsTwoOne()->get())->unique('id');
}
/*
|--------------------------------------------------------------------------
| FUNCTIONS
|--------------------------------------------------------------------------
*/
/**
* Function to relate two tags together.
*
* @param Tag $tag
* @return void;
*/
public function attach(Tag $tag)
{
if ($this->tags()->contains('id', $tag->getKey())) {
return;
}
$this->tagsOneTwo()->attach($tag->getKey());
}
/**
* Function to un-relate two tags.
*
* @param Tag $tag
* @return void;
*/
public function detach(Tag $tag)
{
if ($this->tags()->contains('id', $tag->getKey())) {
// Detach the relationship in both ways.
$this->tagsOneTwo()->detach($tag->getKey());
$this->tagsTwoOne()->detach($tag->getKey());
}
}
/*
|--------------------------------------------------------------------------
| ACCESORS
|--------------------------------------------------------------------------
*/
/**
* Let access the related tags like if it was preloaded ($tag->tags).
*
* @return mixed
*/
public function getTagsAttribute()
{
if (! array_key_exists('tags', $this->relations)) {
$this->setRelation('tags', $this->tags());
};
return $this->getRelation('tags');
}
}
Why it's not working?
It's not working because in the relationship you added in Tag
model is defined to work for one way. But not the reverse way.
It would work if we could have defined two method called tags()
as below:
public function tags()
{
return $this->belongsToMany(Tag::class, 'tag_tag', 'tag_one_id', 'tag_two_id');
}
//and
public function tags()
{
return $this->belongsToMany(Tag::class, 'tag_tag', 'tag_two_id', 'tag_one_id');
}
Unfortunately, it's not possible.
So, what can be possible solution
One possible solution can be, don't touch the relationship. Instead if you somehow can manage to insert two relationship for these relationship then it will work. For example:
+----+------------+------------+
| id | tag_one_id | tag_two_id |
+----+------------+------------+
| 1 | 1 | 2 |
+----+------------+------------+
| 1 | 2 | 1 |
+----+------------+------------+
This is the solution coming out of my mind right now. There might be a better solution too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With