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

Laravel relationship : one foreignKey for several localKey relationship

我必须用 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);
}

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

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

和用户 table :

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 中可能吗?谢谢:)

为了能够 return 所有玩家作为 user 数组的一部分,您可以将 MatchUser 之间的关系更改为 belongsToMany (many-to-many) 或在 return 从控制器获取匹配数据之前简单地操作匹配数据。

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

您需要:

  1. 将您的 matchs table 重命名为 matches(这也意味着您可以删除
    protected $table = 'matchs'; 来自您的 Match 型号 class ).
  2. 创建一个名为 match_user 的 table。如果您的项目正在使用迁移,那么您可以 运行:
    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']);
        });
    }
    
  3. 运行 php artisan migrate

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

    public function users()
    {
        return $this->belongsToMany(User::class)->withTimestamps();
    }
    
  5. 如果您在 table 中的数据是虚拟数据,您可以跳过此步骤。否则,您需要将 playerOne_idplayertwo_id 信息移动到新的枢轴 table。您可以执行以下操作:

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

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

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


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

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