php - 周期性日历事件

标签 php mysql

我正在尝试构建自己的日历,并且我有一个用于输入事件的表单,如下所示。

我对如何设计 MySQL 表以及如何以易于提取的方式记录这些信息感到困惑?

calendar event form

以下是我的尝试,它有效但真的很丑,我相信它可以改进:

CREATE TABLE events (
  event_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  title VARCHAR(100) NOT NULL,
  location VARCHAR(100) NOT NULL,
  body TEXT NOT NULL,
  start_ts DATETIME NOT NULL,
  end_ts DATETIME NOT NULL,
  valid ENUM('Y','N') DEFAULT 'Y',
  reoccurring ENUM('Y','N') DEFAULT 'N',
  every ENUM('day','week','month','year',''),
  bymonth ENUM('day','weekday',''),
  end_date DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',

  PRIMARY KEY (event_id)
);

CREATE TABLE events_byweek (
  event_id INTEGER UNSIGNED NOT NULL,
  weekday TINYINT UNSIGNED NOT NULL,

  FOREIGN KEY (event_id)
    REFERENCES events(event_id)
);

-- returns all dates, reoccurring or otherwise within specified time range
-- by month
SELECT * FROM events WHERE (:year = YEAR(start_ts) AND :month = MONTH(start_ts))
OR (reoccurring = 'Y' AND ((YEAR(end_date) >= :year AND MONTH(end_date) >= :month) OR end_date = '0000-00-00 00:00:00')
AND (every != 'year' OR MONTH(start_ts) = :month)) AND valid = 'Y'

-- by day
SELECT * FROM events WHERE DATE(start_ts) = :date
OR (reoccurring = 'Y' AND (DATE(end_date) >= :date OR end_date = '0000-00-00 00:00:00')
AND (every != 'year' OR MONTH(start_ts) = :month)) AND valid = 'Y'

-- by week
SELECT * FROM events_byweek WHERE event_id = :event_id

如果有任何建议,我将不胜感激!

最佳答案

我认为你不需要任何关系数组。

您的数据库表可能是:

event_id UINT(10) auto_increment NOT NULL
/* not important fields omitted */
start DATETIME not null
end DATETIME not null
reoccurring ENUM('NO', 'WEEKDAY', 'MONTHDAY', 'MONTH_REL', 'YEARLY') DEFAULT 'NO';
weekdays UINT(1) DEFAULT 0;
until TIMESTAMP DEFAULT NULL

我使用了DATETIME,因为它更容易查询(见下文),但实际上这无关紧要。如果您愿意,可以保留它 TIMESTAMP

weekdays 中,可以保留一个字节,其位为 2^0:星期日,2^1:星期一,依此类推。因此,如果事件每天都重复发生,您将在那里放置 127

如果 untilNULL,则事件将永远重复。

因为很难通过SQL找到“每月的第三个星期三”是否在特定范围内,我认为没有用户自定义函数即使不是不可能,但很难而且代码不会清晰,我建议您获取所有事件,在 php 中获取它们并在那里过滤。

仅加载必要事件(预过滤)的查询将是:

SELECT ... FROM events
  WHERE 
     /* We take all non-reoccuring events in period */
    ((reoccurring = 'NO') AND (start >= :start) AND (end <= :end))
  OR
     /* We take some part of reoccurring events */
    ((reoccuring <> 'NO') AND ((start <= :end) OR (end >= :start)) AND ((until >= :start) OR until IS NULL)
  ORDER BY start ASC;

因此,在获取记录时,您可以测试一条记录是否满足以下条件:

  1. 没有重复出现,所以保留
  2. 在特定的工作日重复发生(例如每周三和每个周五)- 检查期间之间是否有这样的工作日,如果至少有一个,则事件保留,否则,将其丢弃
  3. 如果重复发生在月日(例如,每年 12 月 21 日),则执行相同的操作。
  4. 如果每月重复一次 - 相对日期(如第三个星期三),则执行相同操作 等

如果你的记录不符合条件,删除它,当然是从数组中,而不是数据库:-)。

一些任务也很容易放入 SQL 查询中。例如,您可以通过 SELECT ... WHERE ... OR ... (start LIKE '%-:month-:day %) 过滤特定月日的重复事件(假设开始和事件结束是相同的,如问题中的图片所示)。这是 DATETIME 字段的一些优势,您可以像搜索字符串一样轻松地搜索它们,因此 %-12-21 % 会找到包含第 12 个月和第 21 天的所有记录(当然,它们应该始终是两位数)。 (TIMESTAMP的优点是很容易计算日期差异等)

如果事件在每个月的每一天都重复,请使用 ... LIKE%-%-:day %` 等等。

所以最后你需要两个返回 boolean 的函数来检查两种情况:

  1. 是特定时期的工作日
  2. 是该期间的第 n 个工作日(请注意,如果第一个失败,则不需要运行第二个)

您甚至可以使用 foreach 或其他东西通过蛮力对它们进行编码。

此外,如果字段值为 127,则无需执行工作日检查 - 因此它每天都会发生。

2013-03-29 编辑 - 说明如何将位用作工作日

在字段 weekdays 中,可以将天数保留为位,因为一个(无符号)INT(1) 数中有 7 天和 8 位。因此,您不必添加更多列,如“occurs_monday”、“occurs_tuesday”等,也不必使用任何关系。之所以这样提出,是因为我觉得“每周一”、“每周五”都有可能发生事件。如果不是,请在此处保留一个数字(0=星期日,1=星期一,依此类推)。

此外,每天发生的事件也是每周 7 天发生的事件的特例,所以我不需要 reoccurring 中的另一个 ENUM 值> 专栏。

现在如何在 PHP 中使用它?

您只需要测试是否设置了特定工作日的位。您可以使用按位 AND 运算符执行此操作。如果您定义一些常量,它会更简单:

define("WEEKDAY_SUNDAY",1); // 2^0
define("WEEKDAY_MONDAY",2); // 2^1
define("WEEKDAY_TUESDAY",4); // 2^2
// ....
define("WEEKDAY_SATURDAY",64); // 2^6

// Does the event occur on Friday, maybe also other weekdays?
if($row['weekdays'] & WEEKDAY_FRIDAY){

// Does the event occurs only on Friday, and no other day?
if($row['weekdays'] == WEEKDAY_FRIDAY){

// Let's make the event occur on Friday and the day(s) that it already occurs
$row['weekdays'] = $row['weekdays'] | WEEKDAY_FRIDAY;

// Make the event occur only on Friday and no other weekday
$row['weekdays'] = WEEKDAY_FRIDAY;

// Let's check if the event occurs today:
if(pow(2,date('w')) & $row['weekdays']){ //...

带“w”参数的 date 函数返回从 0 到 6 的星期几,所以这就是我使用它的原因。

关于php - 周期性日历事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14866343/

相关文章:

php - 在单独的数据透视表中按计数对行进行排序

java - 将JAVA程序转换为PHP代码

javascript - 使用 AJAX 刷新莫里斯图

mysql - 如何从 SQL Server 切换到 MySQL 以与 Excel Pivot 一起使用

php - 使用 readfile 允许用户从我的服务器下载图像而看不到图像的 URL 是否会给服务器带来压力?

PHP gd 库说明

php - 如何在 Laravel 中使用 Left Join 查找匹配记录

php - 在 PHP 中使用从 jQuery 接收的数据

MySQL UPPER() 和 LOWER() 不适用于 utf-8 字符

php - 使用多个 where 子句进行删除非常慢