我想了解如何使用Doctrine和Symfony进行安全的“测试和设置”操作。
我正在建立会议安排系统。我有一个名为
的实体MeetingTime,老师和学生
除其他事项外,MeetingTime与学生之间的多对一关系与教师之间存在多对一的关系。
我想强制说,只有当正确的当前为null时,才能保存meetingTime的student属性。即,一名学生不应该覆盖另一名学生。第一个预订时间的学生获胜。似乎交易会这样做。
我这样做:
$saved = true;
try
{
$em->transactional(function($em) use($mtId, $student) {
$mt = $em->getRepository('MyBundle:MeetingTime')->find($mtId);
if (is_null($mt->getStudent()))
{
$mt->setStudent($student);
$em->persist($mt);
}
else
{
throw new Exception();
}
});
}
catch(Exception $e)
{
$saved = false;
}
if (!$saved)
{
// flash "Sorry,, somebody else just grabbed that time".
}
我的用户界面将通过在页面上显示“预订此时间”按钮来处理大部分内容,如果该时间已经完成,但当然如果有两个人同时执行此操作,则数据库具有最终决定权。
这似乎有效,但这是正确的方法吗?交易安全吗?
我首先使用额外的“预订”实体实现了它,其中一对一到MeetingTime,多对一的学生和MeetingTime的唯一约束。这项工作没有问题,但将Student作为MeetingTime的财产更好,因为我还希望MeetingTime对学生和教师有一个独特的约束,因为学生只能与老师预约一次会议。
我知道我应该创建自己的Exception类,以便我知道异常的类型。如果已经设置了正确的学生,我也应该让它感到高兴。
如果重要,数据库是PostgreSQL。我确信这可以通过触发器非常有效地完成但是...这是一个我没有进入的整个世界,并且保持这个数据库不可知是很好的。
由于
答案 0 :(得分:0)
交易在这里没有帮助。所有它确保冲洗成功。它没有锁定任何东西。
您可能希望使用乐观锁定,只需要在表格中添加版本号并使用@Version标记标记它。而且中提琴!虽然您需要准备好在刷新时捕获可能的异常,但不会出现并发问题。
考虑将空检查放在$ mt-> setStudent中,以便它始终发生。
答案 1 :(得分:0)
让存储库查找具有ID和null学生的MeetingTime实体。如果找到一个,那么你可以设置学生。否则告诉学生他们运气不好。
如果你还没有,你可能必须为此创建自己的存储库类。
除非你有很多学生同时预订,否则我怀疑这是一个并发问题。
Doctrine也有一个悲观的锁定选项,但有一些警告:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html#pessimistic-locking