Dam*_*mon 2 php mysql datetime laravel
我正在编写测试以确保新的预订不会与另一个预订重复预订。我已经阅读了无数其他的 SO 线程,现在我更加困惑,不确定我做的事情是否正确。我在我的项目以及这个示例中专门使用了 Laravel。
迁移.php
...
$table->date('date'); // 2020-01-01
$table->time('time_start'); // 15:00:00
$table->time('time_end'); // 17:00:00
...
Run Code Online (Sandbox Code Playgroud)
我曾与dateTime或合作过timezone。我陷入了这样的陷阱:“我不需要日期,只需要时间。我把日期保存在其他地方。” 然后我会找到一个建议另存为的线程timestamp并比较那里的日期。
我有一个预订工厂要生成(以及其他详细信息)date,time_start并且time_end:
工厂.php
'date' => date('Y-m-d'),
'time_start' => '15:00:00',
'time_end' => '17:00:00',
Run Code Online (Sandbox Code Playgroud)
我读过的大多数线程建议比较使用strtotime. 就像是:
'time_start' => strtotime('15:00:00'), // 1582210800
Run Code Online (Sandbox Code Playgroud)
这是有道理的。但后来我读到,由于时区的原因,保存为dateTime或timezone更好。
在我的控制器中,我正在检查现有的预订,如下所示:
控制器.php
...
$existing = DB::table('reservations')
->where('asset_id', '=', $request->asset_id)
->whereDate('date', '=', $request->date)
->whereTime('time_start', '>=', $request->time_start) // or use $request->strtotime('time_start')
->whereTime('time_end', '<=', $request->time_end)
->where(function ($query) {
$query
->where('status', '=', 'created')
->orWhere('status', '=', 'pending')
->orWhere('status', '=', 'completed');
})
->get();
if ($existing->count() > 0) {
// Not allowed
} else {
// OK to proceed
}
...
Run Code Online (Sandbox Code Playgroud)
使用whereTime看起来正是我所需要的:
->whereTime('created_at', '=', '11:20:45')
Run Code Online (Sandbox Code Playgroud)
看起来会被保存为一time列。在我的测试中,我检查400如果无法创建它是否会得到返回。
测试.php
...
$http->assertStatus(400)
->assertJsonStructure([
'type', 'data' => [
'reason'
]])
->assertJson([
'type' => 'reservations',
'data' => [
'reason' => 'Asset is no longer available.',
],
]);
Run Code Online (Sandbox Code Playgroud)
这很好用。15:00:00如果我创建相同17:00:00日期/资产等的预订。我的测试通过。我完全按照我的预期返回了 400 错误。但是,如果我通过了,15:01:00我的测试就会失败。并不感到惊讶,但这告诉我我没有正确处理比较。看起来我已经快到终点了,但随后两只鞋都解开了。
用户界面将只是一个带有人类可读时间的下拉菜单。我原本打算将这些值保存为 24 小时时间。例如,15:00:00。我不知道还能怎么做......
我将不胜感激您的建议,以更好地理解如何:
time,,timestamp?datetimestrtotime(或不使用)。如果是这样,理想的数据类型是什么?timestamp, datetime?非常感谢您的任何想法。
更新
遵循@miken32的建议 - 我确实已经以这种方式建立了关系,所以这是有道理的。
我现在正在储蓄time_start并time_end作为dateTime我迁移中的一个领域。
控制器.php
$asset = Asset::find($request->asset_id);
$existing = $asset->reservations()
->where(function ($query) use ($request) {
$start_dt = new Carbon($request->time_start);
$end_dt = new Carbon($request->time_end);
$query->where('time_start', '>=', $start_dt)
->where('time_end', '<=', $end_dt);
})
->whereIn('status', ['created', 'pending', 'completed'])
->get();
if ($existing->count() > 0) {
// Log::info('CANNOT MAKE RESERVATION FOR: ' . $request->first_name . ' ' . $request->last_name);
return response()->json(['type' => 'reservations', 'data' => ['reason' => 'Asset is no longer available.']], 409);
} else {
$reservation = new Reservation();
...
// Log::info('RESERVATION MADE FOR: ' . $reservation->first_name . ' ' . $reservation->last_name);
Run Code Online (Sandbox Code Playgroud)
用户只能选择预先确定的时间。一旦为任何给定资产保留了时隙,该块就不再可用。我确信我本质上是在确保某人无法(以某种方式)POST用不同的值覆盖请求。
希望这对其他人有帮助。如果我的实施失败,请告诉我,以便我可以为其他人纠正它。
您最好将此信息存储为两DATETIME列。优点包括能够利用 Laravel 内置的 Carbon 日期转换,并避免在午夜预订约会的麻烦。
然后,假设$request->time_start和$request->time_end是完整的日期/时间,您的查询将变成这样:
$existing = DB::table('reservations')
->where('asset_id', $request->asset_id)
->where(
fn ($q) => $q->whereBetween('time_start', [$request->time_start, $request->time_end])
->orWhereBetween('time_end', [$request->time_start, $request->time_end])
->orWhere(
fn ($q) => $q->where('time_start', '<', $request->time_start)
->where('time_end', '>', $request->time_end);
)
)
->whereIn('status', ['created', 'pending', 'completed'])
->get();
Run Code Online (Sandbox Code Playgroud)
您还可以将time_start和添加time_end到模型的$dates数组中以利用自动转换。
说到模型,如果你的关系设置正确,这个查询可能像这样,而不是使用外观DB:
$asset = Asset::find($request->asset_id);
$existing = $asset
->reservations()
->where(
fn ($q) => $q->whereBetween('time_start', [$request->time_start, $request->time_end])
->orWhereBetween('time_end', [$request->time_start, $request->time_end])
->orWhere(
fn ($q) => $q->where('time_start', '<', $request->time_start)
->where('time_end', '>', $request->time_end)
)
)
->whereIn('status', ['created', 'pending', 'completed'])
->get();
Run Code Online (Sandbox Code Playgroud)
它并不短,但在我看来,它让人们更容易一目了然地看到正在搜索的内容。
至于 HTTP 响应,使用什么并不重要。这都是您的代码,因此您知道会发生什么。但如果你想学究气(我完全支持),也许409可能适合你的需要?
409(冲突)状态代码表示由于与目标资源的当前状态发生冲突,请求无法完成。此代码用于用户可能能够解决冲突并重新提交请求的情况。服务器应该生成一个有效负载,其中包含足够的信息,以便用户识别冲突的来源。