PHP 根据数据库中最后一个预订号递增预订号

PHP increment booking number according to the last booking number in database

我正在将 PHP 7 与 Phalcon PHP 一起使用,并且我正在尝试创建一种生成预订编号的方法。这是我目前的方法:

public function generateNumber($company_code) {
    // Build the prefix : COMPANY20190820
    $prefix = $company_code . date('Ymd');

    // It's like SELECT count(*) FROM bookings WHERE number LIKE 'COMPANY20190820%'
    $counter = Bookings::count(array(
        "number LIKE :number:",
        "bind" => array('number' => $prefix.'%')
    )); 

    // Concat prefix with bookings counter with str_pad
    // COMPANY20190820 + 005 (if 4 bookings in DB)
    $booking_number = $prefix . str_pad($counter + 1, 3, 0, STR_PAD_LEFT);

    // Return COMPANY20190820005
    return $booking_number;
}

所以我有一个问题,因为有时我必须删除 1 个或多个预订,这样我才能得到:

COMPANY20190820001
COMPANY20190820002
COMPANY20190820005
COMPANY20190820006
COMPANY20190820007

而且我需要在我的数据库中的最后一个之后添加,所以在这里 007,因为如果我这样算,我可以获得重复的预订号码。

那么如何取最后一个并根据当天的最后一个预订号递增呢?

您可以将数据库字段分成两部分,这样您就可以分别保存前缀和计数器。
然后,您只需select您想要的前缀的最高计数器并增加那个。

如果您不能更改 table 结构,您也可以按 id 降序排序,select 第一。然后你可以手动提取它的计数器。请记住,您应该填充数字,否则即使 #10 存在,您也会得到 #9。

如果填充不是一个选项,您可以指示数据库替换您的前缀。这样,您可以将剩余的字符串转换为一个数字并让数据库排序 - 不过这会降低一些性能,因此请保持较低的记录量。

所以我找到了一个解决方案,也许有更好的方法,但我的功能现在可以工作了:

public function generateNumber($company_code) {
    // Build the prefix : COMPANY20190820
    $prefix = $company_code . date('Ymd');

    // Get the last booking with the today prefix
    // e.g : COMPANY20190820005
    $last_booking = Bookings::maximum(array(
        "column" => "number",
        "number LIKE :number:",
        "bind" => array('number' => $prefix.'%')
    ));

    // Get the last number by removing the prefix (e.g 005)
    $last_number = str_replace($prefix, "", $last_booking);
    // trim left 0 if exist to get only the current number
    // cast to in to increment my counter (e.g 5 + 1 = 6)
    $counter = intval(ltrim($last_number, "0")) + 1;
    // Concat prefix + counter with pad 006
    $booking_number = $prefix . str_pad($counter, 3, 0, STR_PAD_LEFT);

    // Return COMPANY20190820006
    return $booking_number;
}

你需要重新考虑你想在这里做什么,因为它永远不会那样做。

据我所知,您至少有两个选择:

  1. 使用自动递增的 id 并将其与前缀结合使用
  2. 使用随机的相当独特的字符串(例如 UUID4)

您永远不应该手动尝试获取当前的最大 ID,因为这可能并且很可能会在某些时候导致竞争条件和脆弱的代码。

我认为您描述的用例并不能证明在 PHP 中编写自定义序列生成器的麻烦是合理的。此外,在预计会发生预订删除的情况下,ID 重用感觉更像是一个错误而不是一个功能,因此您的系统应该存储一个永久计数器以避免重用,从而降低它的简单性。不要误会我的意思,这是可以做到的,这不是火箭科学,但您不需要花费时间和精力。

你的数据库引擎肯定有一个本地工具来生成自动递增的主键,具有不同的名称和实现(SQL服务器有身份,Oracle有序列和身份,MySQL有auto_increment...)。请改用它。

将内部数据和用户显示分开。更具体地说,不要使用后者来重新生成前者。您的 COMPANY20190820007 示例很容易从各个字段组成,无论是在 PHP:

$booking_number = sprintf('%s%s%03d',
    $company_code,
    $booking_date->format('Ymd'),
    $booking_id
);

... 或 SQL:

-- This is MySQL dialect, other engines use their own variations
SELECT CONCAT(company_code, DATE_FORMAT(booking_date, '%Y%m%d'), LPAD(booking_id, 3, '0')) AS booking_number
FROM ...

您可以(并且可能应该)保存结果 booking_number,但您不能将其用作进一步计算的来源。它与日期的情况完全相同:不需要以纯英文存储日期以便最终将它们显示给最终用户,并且您绝对不想将英文日期解析回实际日期以执行任何其他操作超越印刷。


您还提到了生成长的纯数字标识符的可能性,就像 Bookings.com 那样。有很多方法可以做到这一点,我们不知道他们使用哪一种,但您可能想考虑通过 integer obfuscation.

从自动递增的 PK 中生成数字哈希