在 Laravel Eloquent 模型中使用特殊的联合函数

Posted

技术标签:

【中文标题】在 Laravel Eloquent 模型中使用特殊的联合函数【英文标题】:Using special union functions inside Laravel Eloquent model 【发布时间】:2019-03-29 19:57:54 【问题描述】:

我已经开始使用 Laravel 开发一个网站,我几乎可以通过我在这里找到的官方文档和答案找到所有内容。然而,有一件事——即使我找到了一种方法——我有一种感觉,可以用另一种比我现在做的更优化的方式来完成。让我解释一下。

对于我的网站,我有一个名为“球员”的表格,其中包含从足球比赛中提取的一些球员的数据。假设结构是这样的:


ID(整数,主键,A_I)

GameID (int, unique) //游戏使用什么

玩家姓名

数据(基本上很多不同的列)


由于我的网站的目的是允许用户对游戏内容进行修改,我还有另一个表,我称之为“userplayers”,用于对原始表中存在的玩家进行修改,或添加新的。该结构类似于“玩家”表,但只添加了一个名为 userID 的列,用于标识哪个修改属于哪个用户。


ID(整数,主键,A_I)

GameID(整数,与用户 ID 一起唯一)

玩家姓名

数据(基本上很多不同的列)

UserID(整数,与 GameID 一起唯一)


如果我在 userplayers 表上添加一个条目,如果该条目与玩家表上的任何条目具有相同的 GameID(并且创建它的用户已登录),那么在运行时它会覆盖玩家表条目.如果新条目的 GameID 不存在于原始玩家表中,那么它只是被附加到它上面。

如果用户没有登录,那么我只返回玩家表。

通过使用 eloquent laravel 模型,我可以轻松地检索用户未登录时的玩家表。但是,我无法找到一种有效的方法来返回整个数据库 + 仅使用核心 eloquent 的用户创建的内容模型函数。

过去(没有 Laravel)我使用这个数据库查询:

SELECT * FROM (SELECT *, NULL FROM players WHERE NOT EXISTS (SELECT * FROM userplayers WHERE players.gameid=userplayers.gameid AND userId=$userId) UNION (SELECT * FROM userplayers WHERE userId=$userId)) AS pl;

而我“发现”在 Laravel 中执行此类操作的方法是在 Players 模型中添加一个本地范围,如下所示:

public function scopeIncludeCustom($query, $user)

    return \DB::select('SELECT * FROM (SELECT *, NULL FROM players WHERE NOT EXISTS (SELECT * FROM userplayers WHERE userplayers.gameid=players.gameid AND userId='.$user.') UNION (SELECT * FROM userplayers WHERE userId='.$user.')) AS players_full);

但是您可以理解,这并没有按照作用域的预期返回 $query,而只是返回了一个 php 数组,我认为这不是正确的方法。例如,当我只搜索玩家表时(不使用用户创建的内容),返回结果所需的时间比返回自定义内容的结果要短得多。

非常感谢任何想法。

【问题讨论】:

已经定义了PlayerUserPlayer 模型?你熟悉 Eloquent 关系和whereHas()吗? @JonasStaudenmeir 是的,我已经定义了这两个模型,但我似乎不明白如何通过whereHas()等关系来表示上述 mysql 语法 你可以在 Eloquent 中使用 UNION 查询,但结果都是 Player 模型的实例:如果你想要 PlayerUserPlayer 实例,你必须执行单独的查询。 【参考方案1】:

所以,在研究了我的问题和可能的解决方案几天后,我想出了这个解决方案。

那么,让我们一步一步来吧。


我有一个“Player”模型,它从“players”表中获取数据。

我还有一个“Userplayer”模型,它从“userplayers”表中获取数据。


我必须在这两个模型之间建立关系。 “players”表中的条目可能在“userplayers”表中有许多与之相关的条目。所以,在 Player 模型中我添加了这个:

public function userplayers()

    return $this->hasMany('App\Userplayer', 'gameid', 'gameid');

Userplayer 模型中,我添加了这个:

public function player()

    return $this->belongsTo('App\Player', 'gameid', 'gameid');


请求数据时,第一步是从“players”表中删除与“userplayers”表,该表还有一个限制,即该表每一行的“userId”必须是特定的。

这是在我的 SearchPlayerController 中执行此操作的代码:

$orig_players = \App\Player::whereDoesntHave('userplayers', function ($query) use($user) 

    $query->where('userId', $user);
)->get();

同时,我需要从“userplayers”表中获取我想要的“userid”的每一行

$userplayers = \App\Userplayer::where([
                ['pesid', $search]
            ])->get();

现在,我只是合并 2 个结果(我不能使用 UNION,因为我从“players”表中获取的数据少了一列)。

$players = $orig_players->merge($userplayers)
            ->sortBy('registeredName')
            ->take($limit);

一切都运行良好,而且比以前快很多!

【讨论】:

以上是关于在 Laravel Eloquent 模型中使用特殊的联合函数的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5 - 使用 Mockery 模拟 Eloquent 模型

在 Laravel 中扩展 Eloquent 模型(使用不同的表)

Laravel/Eloquent 建议覆盖 trait 属性?

如何在 Eloquent 模型中使用 Laravel Livewire?

Laravel - 使用 Eloquent 从连接关系模型中选择特定列

在 PHPUnit 中调用路由时如何在 Laravel 8 中模拟 Eloquent 模型