为商店创建营业时间模型的最佳方法是什么 [已关闭]

What's the best approach to create opening hours model for a store [closed]

提问人:Ben 提问时间:8/19/2022 最后编辑:Ben 更新时间:8/19/2022 访问量:270

问:


想改进这个问题吗?更新问题,以便可以通过编辑这篇文章来用事实和引文来回答。

去年关闭。

这篇文章是去年编辑并提交审查的,但未能重新打开该帖子:

原始关闭原因未解决

我想为商店存储一个显示 OpeningHours,例如以下插槽示例:

星期一 = 9h00 => 12h00, 14h30 => 20h00

星期二 = 9H00 => 20H00

我们 = 14h15 => 20h00

THU = 关闭

我需要将它用于 OpeningOursSpecifications Schema.org

Ruby-on-Rails PostgreSQL 日期时间

评论

0赞 Schwern 8/19/2022
您使用的是哪个数据库?Postgres 为此提供了非常有用的范围类型。
0赞 Ben 8/19/2022
是的,我正在使用 postgresql,但我是初学者;)

答:

0赞 Les Nightingill 8/19/2022 #1

有趣的问题...

我会按如下方式创建模型:

class Store
  has_many :day_hours
end

class DayHour
  #columns day, am_open, am_closed, pm_open, pm_closed, store_id

  belongs_to :store

  enum day: Date::ABBR_DAYNAMES
end

我绝对不会在几个小时内使用 jsonb,为什么不使用列?根据我的经验,jsonb 总是有缺点,以后会出现(搜索记录、更改值、排序......等)

枚举列是可选的,它为您提供了一些不错的使用方法。

2赞 Schwern 8/19/2022 #2

Rails 不能很好地支持 JSONB,这非常适合传统关系。

但是,不要创建包含 28 列的表。如果您发现自己将列表制作为列,则可能存在设计问题。相反,为一周中的每一天/开盘/收盘时间制作一个一行的表格。

create_table :store_hours do |t|
  t.references :store, null: false

  # Store the day of week using an integer and then
  # Use ActiveRecord::Enum to turn it into strings.
  t.day_of_week :integer, null: false

  # Use the Time and Interval types so you can do time math easily.
  t.open_at :time, null: false
  t.close_at :time, null: false
end

class StoreHour < ApplicationRecord do
  belongs_to :store

  enum :day_of_week, monday: 0, tuesday: 1, wednesday: 2, thursday: 3, friday: 4, saturday: 5, sunday: 6

  # Check if new hours overlap with exisiting hours.
  validates :open_at,
    scope: [:store_id, :day_of_week]
    conditions: ->(store_hour) {
      where(
        "open_at <= ? and close_at => ?",
        store_hour.close_at, store_hour.open_at
      ) 
    }
end

class Store < ApplicationRecord do
  # A store has many store hours. They will be deleted when the store is.
  # Deleting is ok here, not destroy, because there's nothing special to do
  # when the StoreHours are deleted.
  has_many :store_hours, dependent: :delete
end

这让商店可以随心所欲地拥有复杂的开门/关门时间。

请参阅构建复杂窗体,了解如何编写控制器和视图以添加、编辑和删除 StoreHours。

一些评论:

  • 请参阅活动记录关联,了解依赖项的工作原理以及如何使用关联的 StoreHours。
  • 我添加了一个独特的验证,以确保商店没有任何重叠的开门/关门时间。请参阅validates_uniqueness_of活动记录验证
  • 星期几存储为整数。该类使用 ActiveRecord::Enum 在整数和字符串之间映射。这样一来,您仍然可以将值称为,但它存储为数字以节省空间和时间并防止拼写错误。我已经明确了字符串和数字之间的映射,以防需要添加更多日期(例如:周末、节假日)。"monday"
  • 在这种模式下,商店必须在午夜之前关闭。
  • 如果您改为存储商店的营业时间和营业时间,则商店将在午夜之后营业。例如,表示从中午到第二天凌晨 1 点。这更复杂,但也更灵活。open_at: 12pm, open_for: 13 hours
  • 或者,您可以将 open_at/closed_at 存储为时间戳,但始终使用相同的日期:2000-01-01。午夜后关闭的商店将是 2000-01-02。
  • Postgres 时间类型映射到 Ruby Time 对象,但日期为 2000-01-01。
  • 我们用来确保当商店被销毁时,其 StoreHours 也会被删除。has_many :store_hours, dependent: :delete
  • 下面是时间重叠验证逻辑的演示

评论

0赞 Ben 8/19/2022
有趣的方法,但是如果商店有午休时间,该怎么办?
1赞 Schwern 8/19/2022
@Ben同一天为同一家商店提供两个 StoreHours。
0赞 Ben 8/19/2022
是的,但是你如何做到这一点,用表格??
0赞 Schwern 8/19/2022
@Ben请参阅构建复杂表单
0赞 Ben 8/20/2022
问题不在于如何构建表单,而在于 ux 方法;)
0赞 gapsf 8/19/2022 #3

对于关系数据库,请使用 5 列表:

store_id
dayofweek    (enumerated type: 1-Sun..7-Sat)
period_begin (time type, including)
period_end   (time type, excluding)
period_type  (enumerated type: 1-opened, 2-whatever_state1, ...)

默认情况下,将存储视为已关闭,并仅存储具有其他类型(如已打开)的记录。

1 2 9:30  12:00 1 (opened)
1 2 14:00 18:00 1 (opened)
1 3 9:00  20:00 1 (opened)
1 4 0:00  24:00 2 (whatever_state1)
1 5 0:00  24:00 3 (whatever_state2)

如果商店和用户位于不同的时区,您可以以 UTC 存储period_begin/结束,并相应地计算和显示当地时间和星期几。

评论

0赞 Schwern 8/19/2022
“关闭”状态是多余的,并会引发错误:打开和关闭时间重叠,缺少关闭时间。有必要吗?