[英]Laravel is a time between a start & end (Prevent double booking)
I'm writing tests to make sure that a new reservation can not be double-booked over another another.我正在编写测试以确保一个新的预订不能重复预订另一个。 I've read though countless other SO threads and now I'm more confused and not sure I'm doing anything right.
我已经阅读了无数其他 SO 线程,现在我更加困惑,不确定我做对了什么。 I'm specifically using Laravel in my project as well as this example.
我在我的项目和这个例子中专门使用了 Laravel。
Migration.php迁移.php
...
$table->date('date'); // 2020-01-01
$table->time('time_start'); // 15:00:00
$table->time('time_end'); // 17:00:00
...
I've worked with dateTime
or timezone
.我使用过
dateTime
或timezone
。 I get into a trap of, "I don't need the date, just the time. I'm saving date somewhere else."我陷入了一个陷阱,“我不需要日期,只需要时间。我正在其他地方保存日期。” I'll then find a thread that suggests saving as
timestamp
and compare the date there as well.然后我会找到一个建议保存为
timestamp
的线程并比较那里的日期。
I have a reservation factory to generate (among other details) date
, time_start
and time_end
:我有一个预订工厂来生成(以及其他细节)
date
、 time_start
和time_end
:
Factory.php工厂.php
'date' => date('Y-m-d'),
'time_start' => '15:00:00',
'time_end' => '17:00:00',
Most threads I've read suggest comparing using strtotime
.我读过的大多数线程都建议使用
strtotime
进行比较。 Something like:就像是:
'time_start' => strtotime('15:00:00'), // 1582210800
This makes sense.这是有道理的。 But then I read that saving as
dateTime
or timezone
is better because of timezone.但是后来我读到保存为
dateTime
或timezone
更好,因为时区。
In my controller I am checking for an existing reservation like this:在我的控制器中,我正在检查这样的现有预订:
Controller.php控制器.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
}
...
Using whereTime looks like exactly what I need:使用whereTime看起来正是我需要的:
->whereTime('created_at', '=', '11:20:45')
It looks like that would be saved as a time
column.看起来这将被保存为
time
列。 In my tests I am checking that I get a 400
back if it can't be created.在我的测试中,我正在检查如果无法创建它是否会返回
400
。
Test.php测试文件
...
$http->assertStatus(400)
->assertJsonStructure([
'type', 'data' => [
'reason'
]])
->assertJson([
'type' => 'reservations',
'data' => [
'reason' => 'Asset is no longer available.',
],
]);
This works great.这很好用。 If I create a reservation that is
15:00:00
to 17:00:00
same date/asset etc. My test passes.如果我创建了一个
15:00:00
到17:00:00
相同日期/资产等的预订。我的测试通过了。 I get the 400 error back exactly as I expect.我完全按照我的预期得到了 400 错误。 However, if I pass
15:01:00
My test fails.但是,如果我通过
15:01:00
我的测试失败。 Not surprised, but that tells me I am not handling the comparison correctly.并不感到惊讶,但这告诉我我没有正确处理比较。 It seems like I am right at the finish line, but then both shoes have come untied.
看起来我就在终点线,但后来两只鞋都解开了。
The UI will just be a drop down with human-readable times. UI 将只是一个带有人类可读时间的下拉菜单。 I was planning on just saving the values as 24 hr time.
我打算只将值保存为 24 小时时间。 For example,
15:00:00
.例如,
15:00:00
。 I'm not sure how else to do that...我不知道该怎么做...
I would be grateful for suggestions to better understand how:我将不胜感激建议以更好地了解如何:
time
, timestamp
, datetime
?time
、 timestamp
、 datetime
time
?strtotime
(or not).strtotime
(或不使用)。 If so, what's the ideal data type?timestamp
, datetime
? timestamp
, datetime
timestamp
? Thank you so much for any thoughts.非常感谢您的任何想法。
UPDATE更新
Following @miken32 suggestion - I did have the relationships already set up that way so it made sense.按照@miken32 的建议 - 我确实已经建立了这种关系,所以这是有道理的。
I am now saving time_start
and time_end
as a dateTime
field in my migration.我现在将
time_start
和time_end
保存为迁移中的dateTime
字段。
Controller.php控制器.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);
Users will only have the option to choose pre-determined time(s).用户只能选择预先确定的时间。 Once a time slot has been reserved for any given asset, that block is un-available.
一旦为任何给定资产预留了一个时间段,该区块将不可用。 I feel confident that I'm in essence making sure that someone can't (somehow) override the
POST
request with a different value.我确信我本质上是在确保某人不能(以某种方式)用不同的值覆盖
POST
请求。
Hopefully this will help someone else.希望这会帮助别人。 If my implementation is off, please let me know so I can correct it for everyone else.
如果我的实施被关闭,请告诉我,以便我可以为其他人纠正它。
You would be better off storing this information as two DATETIME
columns.最好将此信息存储为两个
DATETIME
列。 Advantages include being able to take advantage of Laravel's built-in casting to Carbon dates, and avoiding trouble with booking appointments over midnight.优点包括能够利用 Laravel 内置的 Carbon 日期转换,并避免在午夜预约约会的麻烦。
Then, assuming $request->time_start
and $request->time_end
are full date/times, your query becomes something like this:然后,假设
$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();
You'd also add time_start
and time_end
to your model's $dates
array to take advantage of automatic casting .您还可以将
time_start
和time_end
添加到模型的$dates
数组中以利用自动转换。
And speaking of models, if your relationships are set up correctly, this query could be like this, instead of using the DB
facade:说到模型,如果你的关系设置正确,这个查询可能是这样的,而不是使用
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();
It's no shorter, but IMO it makes it easier to see at a glance what's being searched for.它并不短,但 IMO 可以更容易地一目了然地看到正在搜索的内容。
As for HTTP responses, it doesn't really matter what you use.至于 HTTP 响应,您使用什么并不重要。 It's all your code, so you know what to expect.
这是你的全部代码,所以你知道会发生什么。 But if you want to be pedantic (which I fully support) perhaps 409 might suit your needs?
但是,如果您想学究(我完全支持),也许409可能适合您的需求?
The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.
409(冲突)状态码表示由于与目标资源的当前状态冲突,请求无法完成。 This code is used in situations where the user might be able to resolve the conflict and resubmit the request.
此代码用于用户可能能够解决冲突并重新提交请求的情况。 The server SHOULD generate a payload that includes enough information for a user to recognize the source of the conflict.
服务器应该生成一个负载,其中包含足够的信息让用户识别冲突的来源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.