如何将此 Laravel PHP 代码简化为一个 Eloquent 查询?

Posted

技术标签:

【中文标题】如何将此 Laravel PHP 代码简化为一个 Eloquent 查询?【英文标题】:How to simplify this Laravel PHP code to one Eloquent query? 【发布时间】:2017-06-10 09:59:09 【问题描述】:

我假设所有这些都应该在一个查询中,以防止数据库中出现重复数据。这是正确的吗?

如何将这段代码简化为一个 Eloquent 查询?

$user = User::where( 'id', '=', $otherID )->first();

if( $user != null )

    if( $user->requestReceived() )
        accept_friend( $otherID );
    else if( !$user->requestSent() )
    
        $friend = new Friend;
        $friend->user_1= $myID;
        $friend->user_2 = $otherID;
        $friend->accepted = 0;
        $friend->save();
    

【问题讨论】:

$user->requestReceived() 在做什么? 生成的SQL是什么? 你说的“防止数据库重复数据”是什么意思 在确定“重复”的列上是否有UNIQUE 索引? 可能超出主题但使用 $user = User::find($otherID);而不是 $user = User::where( 'id', '=', $otherID )->first(); 【参考方案1】:

我认为这应该全部在一个查询中,以防止 数据库中的重复数据。这是正确的吗?

这是不正确的。您可以通过在数据库级别设置 unique 约束来防止重复。

实际上没有你可以用 php 或任何其他语言来做这件事,这将防止重复,如果你的桌子上没有唯一的键( s)。这是一个简单的事实,如果有人告诉你任何不同的事情——那个人是公然错误的。我可以解释原因,但解释会很长,所以我会跳过它。

您的代码应该非常简单 - 只需插入数据。由于不清楚确切如何处理唯一性(它似乎是user_2, accepted,但有一个边缘情况),如果没有更多数据来自您 - 不可能提出一个完整的解决方案。

您总是可以忽略我写的内容并尝试使用建议的解决方案,但它们会惨遭失败,并且您最终会得到重复。

【讨论】:

我不知道该怎么办。请求友谊的可能是用户 1 或用户 2。如何为这两种方式添加唯一键? @Z0q 对于唯一约束,您可以在friends 表中设置要使用的迁移:$table->unique(['user_1', 'user_2'], 'user_friend_unique');。您唯一需要确保的是检查您的应用程序中的任何位置是否这些对已经在一起......如果我要这样做,那么我会在我的模型中这样做。【参考方案2】:

我会说如果UserFriend 之间存在关系,您可以简单地使用Laravel 的 模型关系,例如:

$status = User::find($id)->friends()->updateOrCreate(['user_id' => $id], $attributes_to_update));

我会这样做以确保更新新数据或创建新数据。

PS:我只在 Laravel 5.2.* 上使用过 updateOrCreate()。而且在更新之前对用户的存在做一些检查会很好,否则可能会为 null 抛出一些错误。

更新

我不知道该怎么办。你能解释一下我应该怎么做吗? $attributes_to_update 呢?

好的。根据朋友表中的哪些字段标记两个朋友,现在使用您的示例user_1user_2。通过我给出的示例,$attributes_to_update 将是(假设 otherID 是新朋友的 id):

$attributes_to_update = ['user_2' => otherID, 'accepted' => 0 ];

如果UserFriend 之间的关系设置正确,则user_1 将已包含在插入中。

此外,关于这个 updateOrCreate 函数:

updateOrCreate($attributes_to_check, $attributes_to_update);

$attributes_to_check 表示您要在创建/更新新字段之前检查它们是否已经存在的那些字段,所以如果我想确保,当accepted0 时进行检查,那么我可以通过两者说`['user_1' => 1, '接受' => 0]

希望现在更清楚了。

【讨论】:

我不知道该怎么办。你能解释一下我应该怎么做吗? $attributes_to_update 呢?【参考方案3】:

我假设这里的“朋友”代表用户之间的多对多关系。显然是一位用户 (myID) 向另一位用户 (otherId) 提出的好友请求。

你可以用 Eloquent 来表示:

class User extends Model

    //...

    public function friends()
    
        return $this->belongsToMany(User::class, 'friends', 'myId', 'otherId')->withPivot('accepted');
    

也就是说,不需要Friend模型。

那么,我认为这相当于你想要完成的(如果不是,请更新说明):

$me = User::find($myId);

$me->friends()->syncWithoutDetaching([$otherId => ['accepted' => 0]]);

accepted0 或 1,根据您的业务逻辑)。

sync 方法可防止重复插入,并为给定的“myId - otherId”对更新或创建任何行。您可以使用此方法在数据透视表中设置任意数量的附加字段。

不过,我同意@Mjh 关于在数据库级别设置唯一约束的观点。

【讨论】:

【参考方案4】:

对于此类问题,首先,如果你在 laravel 中工作,你必须享受代码和数据库。首先,您在数据库和 Models 中的表 frienduser 之间创建关系。您还必须在数据库中使用unique

$data= array('accepted' => 0);
User::find($otherID)->friends()->updateOrCreate(['user_id', $otherID], $data));

这是您可以使用的查询。你也可以在这里传递多个条件。谢谢

【讨论】:

这会生成INSERT ... ON DUPLICATE KEY UPDATE ...吗? @RickJames 不。它正在做select * ... where user_id = ?。如果没有返回行 - 插入新行。否则更新该行。并且没有使用任何事务。 False -- 一个单独的线程可能会在SELECTINSERT 之间插入并执行它自己的INSERT,从而把事情搞砸。 SELECT, if, ... 需要一笔交易。 @RickJames 误读了我的评论?我没有写“不需要交易”。 SELECT 也可以在事务内部,FOR UPDATE。否则,当您决定插入/更新时,需要重复 SELECT。我坚持我的声明。【参考方案5】:

你可以使用firstOrCreate/firstOrNew方法(https://laravel.com/docs/5.3/eloquent)

示例(来自文档):

// Retrieve the flight by the attributes, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

// Retrieve the flight by the attributes, or instantiate a new instance...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

【讨论】:

重要的是要知道firstOrCreate 会将信息持久化到数据库并返回实例,firstOrNew 将只返回实例而不持久化 这就是为什么我让 cmets :)【参考方案6】:

使用 `firstOrCreate' 它会和你手动做的一样。

从 Laravel 手册中复制的 FirstOrCreate 的定义。

firstOrCreate 方法将尝试使用给定的列/值对来定位数据库记录。如果在数据库中找不到模型,将插入一条具有给定属性的记录。

所以你应该尝试一下:

$user = User::where( 'id', '=', $otherID )->first();
$friend=Friend::firstOrCreate(['user_id' => $myId], ['user_2' => $otherId]);    

如果不存在,它将检查两个 ID,然后在朋友表中创建记录。

【讨论】:

和我做的不太一样 您的解决方案不能防止重复 firstOrCreate 将根据给定的数组覆盖 你似乎没有理解我想说的 - 它不会。您不能使用 PHP 或任何其他语言来保证唯一性。这不是处理数据库完整性的方法。

以上是关于如何将此 Laravel PHP 代码简化为一个 Eloquent 查询?的主要内容,如果未能解决你的问题,请参考以下文章

SSE2:如何将 _m128 简化为一个单词

如何将查询php转换为laravel框架

如何将 3-SAT 简化为独立集?

如何仅使用货币代码将数字格式化为货币?

如何将此终端语法转换为 OpenSSL 的 PHP 语法 [重复]

如何使用 PHP 在 Laravel 7 中将 PascalCase 字符串转换为可用的 slug?