Laravel - Stop concurrent access to record

天大地大妈咪最大 提交于 2019-12-11 07:54:52

问题


In Laravel is there any way to stop interacting with the same record simultaneously. For example, if User A is editing a record then at the same time I need to stop User B from editing same record.

Note: I am using SESSION_DRIVER=file & Laravel 5.2

There are around 500+ users using the system currently and it is kind of Admin panel where admins interact with the records but I need to stop concurrent access. what would be the optimum solution.


回答1:


I recently implemented something similar.

For each concerned table add a column named "lock" of type "dateTime" like follows:

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

Now go to the controller and find the "edit" method. In here you have to check if 'lock' is set. If it is, you have to check if it is older than, say, 15 minutes (this is important because, if a user quits the page without updating the record, it will be locked forever). This can be coded like this:

$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 the user has access to the record, you have to update "lock" so that no other can access it.

 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 ...

Now lets look at the "update" method. Once the record is updated, you want to reset "lock", so that the next user can edit the record again. Just add this line and update the record as usual:

$sample->lock = null;

Now only 1 user can edit a record at a time.

You can also create a "force unlock" method, with which a user can reset "lock" without having to wait 15 minutes.

This is a simple implementation. It works better with javascript because you can check in real time if somebody is editing the record.




回答2:


Here is how I am able to achieve this.

1) Create a migration to add 2 columns in the model is_editing and editing_by and updated_at (no need to created updated_at if already there)

2) Make middleware php artisan make:middleware StopConcurrentOperations.

3) Register the middleware in app/Http/Kernel.php under $routeMiddleware.

4) Apply middleware to the controller

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

5) Block concurrent edits in middleware

<?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);
    }
}

here I am checking if someone already editing the record and checking the time when was editing performed.

6) Optionally on the client side if the editing user closes the windows then to unblock the record like

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

to complete the process from the client side you have to create a route and a method in the controller and just unlock the record like

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

Note: In my example, I have different table to log activites alerady OperationActivity and likely Model name is Operation




回答3:


You need to add a column named like editing to your tables and corresponding model attribute.

check ifediting is false then let the user to edit.

In any user edit change its value to true,




回答4:


I’ve done sth similar, but when user sending „put”, first checking if data wasn’t changed by someone else at the same time, if not - update, else back with info and new data.

You can check only „updated-at” date, or check all data.



来源:https://stackoverflow.com/questions/48477857/laravel-stop-concurrent-access-to-record

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!