我试图在我的Tag模型上建立双向的ManyToMany关系,但是遇到了这个“问题”。
我的模型如下:
critcl::cproc
因此,现在让我们说我的标签表中有Tag1和Tag2,然后将Tag2与Tag1相关联。 现在我的数据透视表将如下所示:
<?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');
}
}
当我尝试此代码时:
+----+------------+------------+
| id | tag_one_id | tag_two_id |
+----+------------+------------+
| 1 | 1 | 2 |
+----+------------+------------+
我得到了Tag2实例,它是正确的。
但是当我尝试运行此代码时:
$tag = Tag::find(1);
$tag->tags()->get();
我想接收Tag1实例,但是我没有。
是否可以仅使用模型上的一种方法使用Laravel default Eloquent完成它?
答案 0 :(得分:2)
为什么不起作用?
它不起作用,因为在Tag
模型中添加的关系被定义为以一种方式起作用。但不是相反的方式。
如果我们可以如下定义两个称为tags()
的方法,它将起作用:
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');
}
不幸的是,这是不可能的。
那么,什么是可能的解决方案
一个可能的解决方案是,不要碰关系。相反,如果您能够以某种方式设法为这些关系插入两个关系,那么它将起作用。 例如:
+----+------------+------------+
| id | tag_one_id | tag_two_id |
+----+------------+------------+
| 1 | 1 | 2 |
+----+------------+------------+
| 1 | 2 | 1 |
+----+------------+------------+
这是我现在想到的解决方案。也可能会有更好的解决方案。
答案 1 :(得分:0)
有可能。您将必须将参数传递给tags()
方法,并使用该参数修改关系上的主键/外键字段。不过,这可能是一个总要解决的麻烦,而且几乎可以肯定的是,只需再建立一种关系方法即可。最终看起来像这样:
public function tags($tag1 = 'tag_one_id', $tag2 = 'tag_two_id')
{
return $this->belongsToMany(Tag::class, 'tag_tag', $tag1, $tag2);
}
然后您只需要在需要Tag::find(2)->tags('tag_two_id', 'tag_one_id')
可以急于加载此 ,如下所述:https://laracasts.com/discuss/channels/general-discussion/eager-load-with-parameters
您的用例可能比您的帖子所建议的要复杂得多,这可能会使它更合理。既然如此,我会考虑其他一些选择。
答案 2 :(得分:0)
我找到了一个解决方案,我就这样解决了。
<?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');
}
}