数据库一对多,在Laravel中有两个外键字段

时间:2013-03-26 20:24:04

标签: database laravel eloquent

我一直在尝试定义一些数据库模式来使用laravel框架。我想模拟足球比赛。我想要做的第一步是定义实体关系图,但我发现这(我认为这很简单)在某些方面令人困惑。

首先,显而易见的方法是,匹配与两个团队相关,团队与任意数量的匹配相关。所以,我们会有一个“多对多”的关系。

但是多对多关系的实现是有两个表和一个中间表来关联两个实体。我认为这太过分了,当我知道一个Match总是有两个团队而且只有两个列(local_id和visitant_id)和Teams表的外键就足够了。另外,我希望能够做到:

Match::find(1)->local() or Match::find(1)->visitant();

所以,考虑到这一点,我正在实施“一对多”关系,但有了这个,我还有另外一个问题。要检索团队已经玩过的所有比赛,我想这样做:

Team::find(1)->matches(); 

但是我不能这样做,因为我只能在eloquent中定义matches()方法时指定一个键列(默认情况下它是team_id,但它应该是visitant_id和local_id)。

2 个答案:

答案 0 :(得分:32)

在进一步深入挖掘源代码之后,我发现有一种方法可以实际保持我的数据库模式并实现我想要的(至少在Laravel 4中)。我在github上发布了我的问题,Taylor Otwell(框架的创建者)给了我正确答案:https://github.com/laravel/framework/issues/1272

引用他,它应该像这样简单:

class Team extends Eloquent  {
    public function allMatches()
    {
        return $this->hasMany('Match', 'visitant_id')->orWhere('local_id', $this->id);
    }
}

然后......

$team = Team::find(2);
$matches = $team->allMatches;

更新: github链接无效,因为laravel不再以这种方式接收错误报告:http://laravel-news.com/2014/09/laravel-removes-github-issues/

答案 1 :(得分:3)

这是着名的数据库设计问题之一。例如,友谊关系遭受同样的困难。由于您使用的是Eloquent,我建议您坚持使用多对多方法,并在中间表上添加一个布尔列local

class Match extends Eloquent {
    public $includes = array('team'); // Always eager load teams
    public function teams() {
        return $this->has_many_and_belongs_to('team')->with('local');
    }
    public function get_local() {
        foreach ($this->teams as $team) {
            if ($team->pivot->local) return $team;
        }
    }
    public function get_visitant()   {
        foreach ($this->teams as $team) {
            if (!$team->pivot->local) return $team;
        }
    }
}

class Team extends Eloquent  {
    public function matches() {
        return $this->has_many_and_belongs_to('match')->with('local');
    }
    // I'm doing separate queries here because a team may have
    // hundreds of matches and it's not worth looping through
    // all of them to retrieve the local ones
    public function matches_as_local()  {
        return $this->has_many_and_belongs_to('match')->with('local')
            ->where('pivot_local', '=', 1);
    }
    public function matches_as_visitant()  {
        return $this->has_many_and_belongs_to('match')->with('local')
            ->where('pivot_local', '=', 0);
    }
}

<强>观测值:

方法has_many_and_belongs_to(...)->with('field')与预先加载无关。它告诉Eloquent加载中间表列field并将其放在数据透视中。

<强>用法:

$match = Match::find(1);

$match->local; // returns local team
$match->visitant; // returns visitant team

$team = Team::find(1);
$team->matches; // returns all matches
$team->matches_as_local; // ...
$team->matches_as_visitant; // ...

foreach ($team->matches as $match) {
    if ($match->pivot->local) {
        // put nice local icon here
    } else {
        // put nice visitant icon here
    }
}