Laravel 关系:一个foreignKey 对应多个localKey 关系

Posted

技术标签:

【中文标题】Laravel 关系:一个foreignKey 对应多个localKey 关系【英文标题】:Laravel relationship : one foreignKey for several localKey relationship 【发布时间】:2019-06-07 07:52:08 【问题描述】:

我必须使用 Laravel 开发一个 api,但我遇到了这个问题: 我需要获取所有匹配项,对于每场匹配项,我需要获取属于该匹配项的用户(每场匹配有 2 个用户)

我已经搜索了这个问题的答案,我找到了这个https://github.com/topclaudy/compoships,但它对我没有用。

到目前为止,我有这个:

class Match extends Model

    protected $table = 'matchs';

    public $primaryKey = 'id';

    public function playerOne() 
    return $this->hasMany('App\User', 'id', 'playerOne_id');
    

    public function playerTwo() 
        return $this->hasMany('App\User', 'id', 'playerTwo_id');
    

控制器方法:

public function index()

    $matchs = Match::with('playerOne', 'playerTwo')->orderBy('created_at', 'asc')->get();

    return $matchs; 
    //return new MatchsCollection($matchs);

我的数据库如下所示: (匹配表)

id    | playerOne_id   | playertwo_id | winner_id | status 
------------------------------------------------------------
1     | 1              | 3            | 0         | PENDING
2     | 2              | 1            | 0         | PENDING
3     | 3              | 2            | 0         | PENDING

和用户表:

id    | name      | email           
-----------------------------------
1     | John      | John@email.com 
2     | Mark      | Mark@email.com
3     | Harry     | Harry@email.com

当我调用我的 api 时,我得到了这个结果:

[
  
    id: 1,
    playerOne_id: 1,
    playerTwo_id: 3,
    winner_id: 0,
    status: "PENDING",
    created_at: "2019-01-10 00:00:00",
    updated_at: null,
    users: [
      
        id: 1,
        name: "John",
        email: "John@email.com",
        email_verified_at: null,
        created_at: "2019-01-11 10:38:26",
        updated_at: "2019-01-11 10:38:26"
      
    ]
  ,
  
    id: 2,
    playerOne_id: 2,
    playerTwo_id: 1,
    winner_id: 0,
    status: "PENDING",
    created_at: "2019-01-11 08:26:28",
    updated_at: "2019-01-11 08:26:28",
    users: [
      
        id: 2,
        name: "Mark",
        email: "Mark@email.com",
        email_verified_at: null,
        created_at: "2019-01-11 10:40:13",
        updated_at: "2019-01-11 10:40:13"
      
    ]
  ,
  
    id: 3,
    playerOne_id: 3,
    playerTwo_id: 2,
    winner_id: 0,
    status: "PENDING",
    created_at: "2019-01-11 08:45:22",
    updated_at: "2019-01-11 08:45:22",
    users: [
      
        id: 3,
        name: "harry",
        email: "harry@email.com",
        email_verified_at: null,
        created_at: "2019-01-11 10:40:13",
        updated_at: "2019-01-11 10:40:13"
      
    ]
  
]

我不想得到的是这个结果(我只是给你看第一场比赛)

[
  
    id: 1,
    playerOne_id: 1,
    playerTwo_id: 3,
    winner_id: 0,
    status: "PENDING",
    created_at: "2019-01-10 00:00:00",
    updated_at: null,
    users: [
      
        id: 1,
        name: "John",
        email: "John@email.com",
        email_verified_at: null,
        created_at: "2019-01-11 10:38:26",
        updated_at: "2019-01-11 10:38:26"
      ,
      
        id: 3,
        name: "Harry",
        email: "Harry@email.com",
        email_verified_at: null,
        created_at: "2019-01-11 10:38:26",
        updated_at: "2019-01-11 10:38:26"
      
    ]
  
]

这在 Laravel 中可行吗?谢谢:)

【问题讨论】:

你如何准确地调用你的 api?你是怎么查询的? 为什么不在匹配中添加两个关系 Modelpublic function playerOne() return $this->hasMany(App\User::class, 'id', 'playerOne_id'); public function playerTwo() return $this->hasMany(App\User::class, 'id', 'playerTwo_id'); 并使用急切加载获取它们。 @AliMrj $matchs = Match::with('users')->get(); @RossWilson $matchs = Match::with('users')->get(); @FarrukhAyyaz 我如何在我的控制器中查询/获取这个? 【参考方案1】:

为了能够将所有玩家作为user 数组的一部分返回,您可以将MatchUser 之间的关系更改为belongsToMany (many-to-many),或者在从控制器。

我建议将其更改为 BelongsToMany,因为它在语义上更有意义,但是,我知道(取决于您到目前为止所做的工作量)它可能不实用。

您需要:

    matchs 表重命名为matches(这也意味着您可以从Match 模型类中删除protected $table = 'matchs';

    创建一个名为match_user 的表。如果您的项目正在使用迁移,那么您可以运行:php artisan make:migration create_match_user_table --create=match_user 在该迁移中,您需要将 up() 方法的内容更改为:

    public function up()
    
        Schema::create('match_user', function (Blueprint $table) 
            $table->integer('match_id')->unsigned();
            $table->integer('user_id')->unsigned();
            $table->timestamps();
    
            $table->primary(['match_id', 'user_id']);
        );
    
    

    运行php artisan migrate

    在您的 Match 模型中,将 users() 关系更改为:

    public function users()
    
        return $this->belongsToMany(User::class)->withTimestamps();
    
    

    如果您的表中的数据是虚拟数据,您可以跳过此步骤。否则,您需要将playerOne_idplayertwo_id 信息移动到新的数据透视表。你可以这样做:

    Match::all()->each(function ($match) 
        $match->users()->attach([$match->playerOne_id, $match->playertwo_id]);
    );
    

    在哪里运行它并不重要,因为它只需要运行一次,所以在你运行它之后你可以删除它。我通常会在路由或修补程序中运行它。

    然后是清理工作。同样,如果您拥有的数据只是虚拟数据,那么我将删除 playerOne_idplayertwo_id 原始迁移,否则创建一个删除这些字段的新迁移 Dropping columns docs


完成上述操作后,您只需在控制器中执行以下操作即可获得结果:

$matches = Match::with('users')->latest()->get();

【讨论】:

以上是关于Laravel 关系:一个foreignKey 对应多个localKey 关系的主要内容,如果未能解决你的问题,请参考以下文章

Mysql foreignkey 相关

Laravel:覆盖包特征功能

外键的变种 三种关系

任何人都可以仅使用 ForeignKey 在两个 django 模型之间建立多对多关系吗?

外键的变种 三种关系

7-外键的变种 三种关系