Laravel - 停止并发访问记录
Laravel - Stop concurrent access to record
在 Laravel 中有什么方法可以停止同时与同一记录进行交互。例如,如果用户 A 正在编辑一条记录,那么同时我需要阻止用户 B 编辑同一条记录。
注意:我正在使用 SESSION_DRIVER=file & Laravel 5.2
目前大约有 500 多个用户在使用该系统,它是一种管理面板,管理员可以在其中与记录进行交互,但我需要停止并发访问。什么是最佳解决方案。
您需要在您的表和相应的模型属性中添加一个名为 editing
的列。
检查editing
是否为false
然后让用户编辑。
在任何用户编辑中将其值更改为 true,
我最近实现了类似的东西。
对于每个相关的 table 添加一个名为 "lock" 类型 "dateTime" 的列,如下所示:
$table->dateTime('lock')->nullable();
现在转到控制器并找到 "edit" 方法。在这里你必须检查是否设置了 'lock'。如果是,则必须检查它是否早于 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
}
如果用户有权访问该记录,您必须更新 "lock" 以便其他人无法访问它。
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 ...
现在让我们看看 "update" 方法。更新记录后,您希望重置 "lock",以便下一个用户可以再次编辑记录。只需添加此行并像往常一样更新记录:
$sample->lock = null;
现在只有 1 个用户可以同时编辑一条记录。
您还可以创建一个 "force unlock" 方法,用户可以使用该方法重置 "lock" 而无需等待 15 分钟。
这是一个简单的实现。它与 javascript 配合使用效果更好,因为您可以实时检查是否有人正在编辑记录。
我做过类似的事情,但是当用户发送“put”时,首先检查数据是否没有被其他人同时更改,如果没有 - 更新,否则返回信息和新数据。
您可以只检查“updated-at”日期,或检查所有数据。
我是这样实现的。
1) 创建迁移以在模型 is_editing
和 editing_by
和 updated_at
中添加 2 列
(如果已经存在,则无需创建 updated_at
)
2) 制作中间件php artisan make:middleware StopConcurrentOperations
.
3) 在$routeMiddleware
下的app/Http/Kernel.php
中注册中间件。
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) 如果编辑用户关闭 windows,则可选择在客户端取消阻止记录,如
window.onbeforeunload = function() {
var url = "{{route('unlock_route_name',$operation->id)}}";
$.ajax({ url: url, method: 'post' })
};
要从客户端完成该过程,您必须在控制器中创建一个 route 和一个 method 并解锁记录喜欢
$operation = Operation::findOrFail($id);
$activity = $operation->activities()->where("is_editing",true)->first();
$activity->is_editing = false;
$activity->save();
注意:在我的示例中,我有不同的 table 来记录活动 alerady OperationActivity
并且模型名称可能是 Operation
并发访问 问题可以通过比锁定字段更简单、更有效的方式解决:
向您的 table 迁移添加一个 version
字段,默认值为 0.
$table->smallInteger('version')->default(0);
version
值将在每次成功的更新操作时递增。
- 如果更新记录的
version
值不等于存储记录的版本值,更新操作将不会成功。
示例 - 两个用户试图更新产品记录,因此他们发送 edit 请求您的应用程序:
- 您的应用程序向每个用户发送一个产品实例,
version
值为 0。
- 第一个用户先完成更新,然后发送一个update请求,在请求体中保存产品字段,包括
version
0 的值给您的应用程序。
- 应用程序检查收到的
version
值是否等于数据库中存储的值。
- 两个值都是0,所以第一位用户更新产品成功,应用程序增加版本值,所以现在是1.
- 第二个用户更新完成,也发送更新请求
- 应用程序检查收到的
version
值 (0) 是否等于数据库中存储的值 (1 ).
- 操作失败,因为值不相等,第二个用户必须刷新编辑页面(发送新的edit请求您的应用程序)。
更新函数:
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 个或更多用户同时尝试更新同一条记录,更新操作将失败。
在 Laravel 中有什么方法可以停止同时与同一记录进行交互。例如,如果用户 A 正在编辑一条记录,那么同时我需要阻止用户 B 编辑同一条记录。
注意:我正在使用 SESSION_DRIVER=file & Laravel 5.2
目前大约有 500 多个用户在使用该系统,它是一种管理面板,管理员可以在其中与记录进行交互,但我需要停止并发访问。什么是最佳解决方案。
您需要在您的表和相应的模型属性中添加一个名为 editing
的列。
检查editing
是否为false
然后让用户编辑。
在任何用户编辑中将其值更改为 true,
我最近实现了类似的东西。
对于每个相关的 table 添加一个名为 "lock" 类型 "dateTime" 的列,如下所示:
$table->dateTime('lock')->nullable();
现在转到控制器并找到 "edit" 方法。在这里你必须检查是否设置了 'lock'。如果是,则必须检查它是否早于 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
}
如果用户有权访问该记录,您必须更新 "lock" 以便其他人无法访问它。
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 ...
现在让我们看看 "update" 方法。更新记录后,您希望重置 "lock",以便下一个用户可以再次编辑记录。只需添加此行并像往常一样更新记录:
$sample->lock = null;
现在只有 1 个用户可以同时编辑一条记录。
您还可以创建一个 "force unlock" 方法,用户可以使用该方法重置 "lock" 而无需等待 15 分钟。
这是一个简单的实现。它与 javascript 配合使用效果更好,因为您可以实时检查是否有人正在编辑记录。
我做过类似的事情,但是当用户发送“put”时,首先检查数据是否没有被其他人同时更改,如果没有 - 更新,否则返回信息和新数据。
您可以只检查“updated-at”日期,或检查所有数据。
我是这样实现的。
1) 创建迁移以在模型 is_editing
和 editing_by
和 updated_at
中添加 2 列
(如果已经存在,则无需创建 updated_at
)
2) 制作中间件php artisan make:middleware StopConcurrentOperations
.
3) 在$routeMiddleware
下的app/Http/Kernel.php
中注册中间件。
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) 如果编辑用户关闭 windows,则可选择在客户端取消阻止记录,如
window.onbeforeunload = function() {
var url = "{{route('unlock_route_name',$operation->id)}}";
$.ajax({ url: url, method: 'post' })
};
要从客户端完成该过程,您必须在控制器中创建一个 route 和一个 method 并解锁记录喜欢
$operation = Operation::findOrFail($id);
$activity = $operation->activities()->where("is_editing",true)->first();
$activity->is_editing = false;
$activity->save();
注意:在我的示例中,我有不同的 table 来记录活动 alerady OperationActivity
并且模型名称可能是 Operation
并发访问 问题可以通过比锁定字段更简单、更有效的方式解决:
向您的 table 迁移添加一个
version
字段,默认值为 0.$table->smallInteger('version')->default(0);
version
值将在每次成功的更新操作时递增。- 如果更新记录的
version
值不等于存储记录的版本值,更新操作将不会成功。
示例 - 两个用户试图更新产品记录,因此他们发送 edit 请求您的应用程序:
- 您的应用程序向每个用户发送一个产品实例,
version
值为 0。 - 第一个用户先完成更新,然后发送一个update请求,在请求体中保存产品字段,包括
version
0 的值给您的应用程序。 - 应用程序检查收到的
version
值是否等于数据库中存储的值。 - 两个值都是0,所以第一位用户更新产品成功,应用程序增加版本值,所以现在是1.
- 第二个用户更新完成,也发送更新请求
- 应用程序检查收到的
version
值 (0) 是否等于数据库中存储的值 (1 ). - 操作失败,因为值不相等,第二个用户必须刷新编辑页面(发送新的edit请求您的应用程序)。
更新函数:
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 个或更多用户同时尝试更新同一条记录,更新操作将失败。