如何在 laravel 中处理同时保存 API 请求?
Posted
技术标签:
【中文标题】如何在 laravel 中处理同时保存 API 请求?【英文标题】:How to handle simultaneous save API requests in laravel? 【发布时间】:2021-08-17 12:58:41 【问题描述】:我正在尝试通过 API 保存新预订。下面是保存预订功能的示例代码。
public function save_booking(Request $request)
$booking = new Booking;
$booking->booking_number = generate_booking_id();
$booking->booking_type = $request->input('bookingType');
$booking->booking_date = $request->input('bookingDate');
$booking->booking_time = $request->input('bookingTime');
$booking->booking_status = $request->input('bookingStatus');
$booking->save();
注意 - generate_booking_id() 是生成预订的辅助函数,例如 BN-202105-0001(最后四位数字 0001 是通过将总记录数加上预订表中的 1 来计算的)。
当两个或多个同时保存 API 请求尝试保存新预订时,会为这两个请求分配相同的预订编号。 (例如,请求 1 = BN-202105-0001,请求 2 = BN-202105-0001 等等。)
我想为每个同时发生的 API 请求分配不同的预订编号。 (例如,有 4 个同时请求尝试保存新预订,然后请求 1 = BN-202105-0001、请求 2 = BN-202105-0002、请求 3 = BN-202105-0003、请求 4 = BN-202105-0004)
如何在 Laravel 中解决这个并发请求问题?
提前致谢!
【问题讨论】:
为了确保我正确理解您的问题:您有自己的增量功能,但有时会生成重复的密钥,因此您最终会得到重复的预订号码? @RobBiermann 实际上我的函数会生成正确的预订号,但是当 2 个请求同时保存记录时,会为这两个请求分配相同的预订号。 好的,那么这是一个经典的并发问题。我首先将数据库中的 booking_number 字段设置为唯一的,因此不可能存储重复项。然后,当您由于重复而无法保存时,您只需增加数字并重试 @RobBiermann 谢谢你的好建议。 我还添加了一些更详细的答案,这在我看来是最理想的。使用这种方法,您不需要进行错误处理,这可能会很痛苦 【参考方案1】:为简单起见,您可以尝试将 booking_number 设为模型的属性。然后,此属性将给定前缀与预订的实际 id 结合起来。这样您就无需创建自己的增量函数,只需将已有的内容组合起来即可。
属性示例:
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Booking extends Model
use HasFactory;
function getBookingNumberAttribute()
$date = Carbon::createFromFormat('Y-m-d', $this->attributes['booking_date']);
$year = $date->year;
$month = $date->format('m');
$fourDigitId = sprintf('%04d', $this->attributes['id']);
return "BN$year$month-$fourDigitId";
static function findByBookingNumber($booking_number)
$splitBookingNumber = explode('-', $booking_number);
return self::where('id', intval($splitBookingNumber[1]));
函数 getBookingNumberAttribute 用作属性的accessor:booking_number
,所以当你运行时:
$now = Carbon::now();
$booking = Booking::create([
'booking_date' => $now->format('Y-m-d'),
'booking_time' => $now->format('H:i:s'),
'booking_status' => 'new',
]);
dump($booking->booking_number);
您应该始终获得一个新 ID,因为创建唯一 ID 的逻辑由 mysql 数据库处理。
我还添加了一种方法来显示如何通过预订编号查找预订。
【讨论】:
【参考方案2】:发生这种情况是因为您的 generate_booking_id()
正在同时从两个不同的请求中读取数据库并为您生成预订 ID。
您可以避免这种情况,方法是在数据库级别创建ON INSERT
触发器,如下所示为您更新booking_number
,而不是手动执行。触发器将确保在创建预订之后而不是之前自动更新booking_number
。
假设您使用的是 MySQL:(尽管如此,您可以在其他数据库管理系统中使用相同的方法)
CREATE TRIGGER 'bookings'.'update_booking_number'
AFTER INSERT ON 'bookings' FOR EACH ROW
BEGIN
UPDATE bookings b
SET booking_number = (SELECT CONCAT('BN-202105-000',COUNT(*) + 1) FROM bookings)
WHERE b.id = NEW.id;
END;
替代解决方案可以是先将预订记录插入数据库,然后调用generate_booking_id()
函数正确计算预订号码,然后将记录更新到数据库:
public function save_booking(Request $request)
$booking = new Booking;
$booking->booking_type = $request->input('bookingType');
$booking->booking_date = $request->input('bookingDate');
$booking->booking_time = $request->input('bookingTime');
$booking->booking_status = $request->input('bookingStatus');
$booking->save();
$booking->booking_number = generate_booking_id();
$booking->save();
【讨论】:
Dhairyashil Nalawadeyashil 我认为您应该尝试一下触发器,在这种情况下您不会必须修改代码。以上是关于如何在 laravel 中处理同时保存 API 请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 laravel 项目中同时使用 web 的守卫和 api 的守卫?