我正在使用Laravel 5.1,并试图实现记录锁,当用户打开记录编辑视图,以便任何其他用户不能打开同一记录的编辑视图,直到锁被释放。
很明显,有时候用户永远不会完成编辑,所以我需要能够自动解锁记录(当用户进行任何其他事务时,或者在超时事件之后,或者使用ajax后台keep-alive调用)。
我已经研究了lockForUpdate()
,并阅读了InnoDB锁定功能,但我无法获得有关它的真正信息(搜索了20多个帖子,他们似乎都在背诵彼此,没有真正的信息)。文档也没有提供太多的信息,探索Laravel查询类代码让我取得了以下进展:
- 头痛。
- 我意识到它只在查询生成器中可用(我需要它for Eloquent)。
- 似乎这只是一个跨国锁定,而不是一个签入/签出记录锁定。
似乎有一个很大的混淆术语和mysql (innodb)锁实际做什么,或者它可能只是我试图弄清楚这个
长话短说,innodb/Laravel锁定(对于我的需求)的主要警告是,它不是持久的,因此它会在php脚本终止时重置自己,用户仍在编辑表单。它只用于事务。
我需要实现上述功能(编辑签入/签出),在我重新发明轮子之前,我想知道是否已经有一个内置的Laravel/PHP功能。
如果没有,我正在考虑以下方法,并希望得到关于哪一个是最好的输入。
。创建一个包含record_id, user_id,时间戳的edit_checkins
表
B。在记录表中添加一个locked_at
列,它将保存一个时间戳或null(类似于deleted_at
列)。
我主要关心的是性能&'垃圾收集',因为有时记录被锁定但从未主动解锁。使用方法A,我可以在一个简单的查询中从edit_checkins
表中删除所有用户锁。然而,当检查记录是否被锁定时,它会慢一点,因为我将不得不做一个表连接(我认为它应该可以忽略不计,因为编辑事件比其他事件更少)。与方法B,它是更快的检查,但我没有得到所有的信息(如user_id),这是很难到几乎不可能实现解锁所有事件不知道user_id(我可能需要添加一个locked_by
列以及记录)。
您要做的是对记录进行应用程序级别的锁定。您有一个业务级需求,一次只能有一个用户查看记录的编辑视图。这个"锁"可以持续几秒、几分钟、几小时,或者您希望允许的任何最大超时。
这与数据库级别的记录锁完全不同。需要数据库级锁来确保两个更新语句不会同时在同一条记录上运行。这个锁只会持续事务所需的时间,通常只有几毫秒。长时间运行或开放的数据库事务不是一个好主意。
你需要设计你自己的应用逻辑来做你想做的事情。我没有看到任何现有的Laravel包。有一种叫做larvel -record-lock,但是它不能做你想做的事情,而且它不会在多个请求之间持久化锁。
我认为最灵活的设计是创建一个record_locks
表,然后创建一个多态关系与任何模型,你想是lockable
。多态关系文档。让你开始:
DB表:
record_locks
- id
- timestamps (if you want)
- lockable_id - integer
- lockable_type - string
- user_id - integer
- locked_at - datetime/timestamp
模型class RecordLock extends Model
{
/**
* Polymorphic relationship. Name of the relationship should be
* the same as the prefix for the *_id/*_type fields.
*/
public function lockable()
{
return $this->morphTo();
}
/**
* Relationship to user.
*/
public function user()
{
return $this->belongsTo('App'User');
}
// additional functionality
}
现在你可以添加多态关系到任何你想要锁定的模型:
class Book extends Model
{
/**
* Polymorphic relationship. Second parameter to morphOne/morphMany
* should be the same as the prefix for the *_id/*_type fields.
*/
public function recordLock()
{
return $this->morphOne('App'RecordLock', 'lockable');
}
}
class Car extends Model
{
/**
* Polymorphic relationship. Second parameter to morphOne/morphMany
* should be the same as the prefix for the *_id/*_type fields.
*/
public function recordLock()
{
return $this->morphOne('App'RecordLock', 'lockable');
}
}
最后,作为规则关系使用:
$book = 'App'Book::first();
$lock = $book->recordLock; // RecordLock object or null
$car = 'App'Car::first();
$lock = $car->recordLock; // RecordLock object or null
/**
* Accessing the relationship from the RecordLock object will
* dynamically return the type of object that was locked.
*/
$lock = 'App'RecordLock::find(1);
$lockedObject = $lock->lockable; // 'App'Book object
$lock = 'App'RecordLock::find(2);
$lockedObject = $lock->lockable; // 'App'Car object
最后一个旁注来解决你对查询生成器和雄辩的关注:雄辩模型回落到雄辩的查询生成器,雄辩的查询生成器回落到普通的查询生成器。如果在Eloquent Model上调用方法,它会尝试在Eloquent Query Builder上调用该方法。如果它不存在,它将尝试在普通Query Builder上调用它。如果它不存在,你会得到一个错误。
因此,如果您要执行'App'User::lockForUpdate()
,则方法调用最终将过滤到普通查询生成器(因为它不存在于雄辩模型或雄辩查询生成器上)。