1

在 Laravel 中有什么方法可以停止同时与同一记录交互。例如,如果用户 A 正在编辑一条记录,那么同时我需要阻止用户 B 编辑同一条记录。

注意:我使用的是 SESSION_DRIVER=file & Laravel 5.2

目前大约有 500 多个用户在使用该系统,它是一种管理面板,管理员可以在其中与记录进行交互,但我需要停止并发访问。什么是最佳解决方案。

4

5 回答 5

5

这是我能够实现这一目标的方法。

1)创建迁移以在模型中添加 2 列is_editingediting_by(如果已经存在则updated_at 无需创建)updated_at

2)制作中间件php artisan make:middleware StopConcurrentOperations

app/Http/Kernel.php3)在下注册中间件$routeMiddleware

4) 将中间件应用到控制器

$this->middleware('concurrent.operations',["only"=>["edit","update"]]);

5) 阻止中间件中的并发编辑

<?php

namespace App\Http\Middleware;

use App\OperationActivity;
use Carbon\Carbon;
use Closure;
use Illuminate\Support\Facades\Auth;

class StopConcurrentOperations
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        $operation_id = current($request->route()->parameters());
        $user_id = Auth::guard('admin')->user()->id;
        if(Auth::guard('admin')->user() && !empty($operation_id)){
            $activity = OperationActivity::firstOrNew(["operation_id" => $operation_id]);
            if($activity->is_editing && $activity->updated_at->diffInMinutes(Carbon::now())<5 && $activity->editing_by!=$user_id)
                abort(409);
            else {
                $activity->is_editing = true;
                $activity->editing_by = $user_id;
                $activity->save();
            }
        }
        return $next($request);
    }
}

在这里,我正在检查是否有人已经在编辑记录并检查执行编辑的时间。

6)如果编辑用户关闭窗口,则可选择在客户端取消阻止记录,例如

  window.onbeforeunload = function() {
    var url = "{{route('unlock_route_name',$operation->id)}}";
    $.ajax({ url: url, method: 'post' })
  };

要从客户端完成该过程,您必须在控制器中创建一个路由和一个方法,然后像这样解锁记录

      $operation = Operation::findOrFail($id);
      $activity = $operation->activities()->where("is_editing",true)->first();
      $activity->is_editing = false;
      $activity->save();

注意:在我的示例中,我有不同的表来记录活动OperationActivity,并且可能的模型名称是Operation

于 2018-01-28T22:49:35.040 回答
3

我最近实现了类似的东西。

为每个相关表添加一个名为“lock”的列,类型为“dateTime”,如下所示:

 $table->dateTime('lock')->nullable();

现在转到控制器并找到“编辑”方法。在这里,您必须检查是否设置了“锁定”。如果是,您必须检查它是否早于 15 分钟(这很重要,因为如果用户在没有更新记录的情况下退出页面,它将永远被锁定)。这可以这样编码:

$sample = $this->sampleRepository->getById($idsample);
if (!$sample->lock || (strtotime($sample->lock) < strtotime('-15minutes'))) {
    // Ok he can access the record
} else {
    // He can not access the record
}

如果用户有权访问记录,您必须更新“锁定”,以便其他人无法访问它。

 if (!$sample->lock || (strtotime($sample->lock) < strtotime('-15minutes'))) {
    // Ok he can access the record
    $sample->lock = \Carbon\Carbon::now();
    $sample->save();
    // Return view, etc.
 } else ...

现在让我们看看“更新”方法。记录更新后,您要重置“锁定”,以便下一个用户可以再次编辑记录。只需添加此行并照常更新记录:

$sample->lock = null;

现在一次只有 1 个用户可以编辑一条记录。

您还可以创建“强制解锁”方法,用户可以使用该方法重置“锁定”,而无需等待 15 分钟。

这是一个简单的实现。它更适用于 javascript,因为您可以实时检查是否有人正在编辑记录。

于 2018-01-27T17:42:21.810 回答
1

您需要在editing表和相应的模型属性中添加一个名为 like 的列。

检查是否editing然后false让用户编辑。

在任何用户编辑中将其值更改为 true,

于 2018-01-27T17:12:34.390 回答
1

并发访问问题可以比锁定字段更容易,但更有效的方式解决:

  1. version默认值为0的字段添加到表迁移中。

    $table->smallInteger('version')->default(0);

  2. version值将在每次成功更新操作时递增。

  3. version如果更新记录的值不等于存储记录的版本值,则更新操作将不会成功。

示例- 两个用户试图更新产品记录,因此他们向您的应用程序发送了一个编辑请求:

  1. 您的应用程序向每个用户发送一个产品实例,其version值为0
  2. 第一个用户首先完成更新,并向您的应用程序发送一个更新请求,该请求包含请求正文中的产品字段,包括version0
  3. 应用程序检查接收到的version值是否等于数据库中存储的值。
  4. 这两个值都是0,所以第一个用户成功更新了产品,并且应用程序增加了版本值,所以现在它是1
  5. 第二个用户完成更新,并发送更新请求。
  6. 应用程序检查接收到的version值 ( 0 ) 是否等于 db ( 1 ) 中存储的值。
  7. 由于值不相等,操作失败,第二个用户必须刷新编辑页面(向您的应用程序发送新的编辑请求)。

更新功能:

public function update(Request $request, $id)
{
    // request validation here  
    $product = Product::find($id);
    if ($request->version == $product->version) {
        // update here
        $product->version = $product->version + 1;
        $product->save();
        return response()->json("product updated successfully!", 200);
    }
    return response()->json("concurrent access: the product has been updated by another user!", 400);
}

这样,记录始终可用于更新,但如果 2 个或更多用户尝试同时更新同一记录,则更新操作会失败。

于 2020-02-09T19:56:39.643 回答
0

我做过类似的事情,但是当用户发送“放置”时,首先检查数据是否没有同时被其他人更改,如果没有 - 更新,否则返回信息和新数据。

您可以仅检查“更新日期”日期,或检查所有数据。

于 2018-01-28T14:35:25.550 回答