PAYATHON 2026

CodeIgniter callback 表单验证中如何正确比较日期

支付小周

结论

CodeIgniter 表单验证 callback 会把当前字段的值作为第一个参数传入,验证规则方括号中的 $projectID 是第二个参数。

因此,callback 应定义为:

public function comparePaymentDate($paymentDate, $projectID)

不能只定义为:

public function comparePaymentDate($projectID)

另外,getLatestPayment() 返回的是数组,比较日期时应使用 $latestPayment['payment_date']。有效日期需要满足:

last payment date < payment date <= today

如果输入日期小于或等于最后付款日期,或者晚于今天,就应返回 FALSE。

正确的 callback 写法

如果表单提交的日期格式是 Y-m-d,可以通过 DateTime::createFromFormat() 严格解析:

public function comparePaymentDate($paymentDate, $projectID)
{
    $latestPayment = $this->payment_model->getLatestPayment($projectID);

    $inputDate = DateTime::createFromFormat('!Y-m-d', $paymentDate);
    $today = new DateTime('today');

    if (!$inputDate) {
        $this->form_validation->set_message(
            'comparePaymentDate',
            'Payment Date is invalid.'
        );

        return FALSE;
    }

    // 如果还没有付款记录,只需要检查输入日期不能晚于今天
    if (empty($latestPayment) || empty($latestPayment['payment_date'])) {
        if ($inputDate > $today) {
            $this->form_validation->set_message(
                'comparePaymentDate',
                'Payment Date should be earlier than or equal to the date today.'
            );

            return FALSE;
        }

        return TRUE;
    }

    $lastPaymentDate = DateTime::createFromFormat(
        '!Y-m-d',
        $latestPayment['payment_date']
    );

    if (!$lastPaymentDate) {
        log_message('error', 'Invalid payment_date stored for project ID: ' . $projectID);

        $this->form_validation->set_message(
            'comparePaymentDate',
            'Unable to validate Payment Date.'
        );

        return FALSE;
    }

    if ($inputDate <= $lastPaymentDate || $inputDate > $today) {
        $this->form_validation->set_message(
            'comparePaymentDate',
            'Payment Date should be later than the latest payment date and earlier than or equal to today.'
        );

        return FALSE;
    }

    return TRUE;
}

验证规则仍可通过方括号传入项目 ID:

array(
    'field' => 'form_payment_date',
    'label' => 'Payment Date',
    'rules' => 'required|callback_comparePaymentDate[' . $projectID . ']'
)

CodeIgniter 执行 callback 时,实际传入的参数相当于:

comparePaymentDate(
    $this->input->post('form_payment_date'),
    $projectID
);

所以,原来的方法只有一个参数 comparePaymentDate($projectID),这个参数实际接收到的是表单日期,而不是项目 ID。

使用时间戳的简化写法

如果数据库和表单中的日期都是明确的 Y-m-d 格式,也可以改用时间戳比较:

public function comparePaymentDate($paymentDate, $projectID)
{
    $latestPayment = $this->payment_model->getLatestPayment($projectID);

    $inputTimestamp = strtotime($paymentDate);
    $todayTimestamp = strtotime(date('Y-m-d'));

    if ($inputTimestamp === FALSE) {
        $this->form_validation->set_message(
            'comparePaymentDate',
            'Payment Date is invalid.'
        );

        return FALSE;
    }

    if (!empty($latestPayment['payment_date'])) {
        $lastPaymentTimestamp = strtotime($latestPayment['payment_date']);

        if (
            $lastPaymentTimestamp === FALSE ||
            $inputTimestamp <= $lastPaymentTimestamp ||
            $inputTimestamp > $todayTimestamp
        ) {
            $this->form_validation->set_message(
                'comparePaymentDate',
                'Payment Date should be later than the latest payment date and earlier than or equal to today.'
            );

            return FALSE;
        }
    } elseif ($inputTimestamp > $todayTimestamp) {
        $this->form_validation->set_message(
            'comparePaymentDate',
            'Payment Date should be earlier than or equal to the date today.'
        );

        return FALSE;
    }

    return TRUE;
}

查询最后付款日期

如果“最后付款”是指 payment_date 最大的记录,应直接按 payment_date 排序,不要按 payment_id 排序:

public function getLatestPayment($projectID)
{
    return $this->db
        ->select('payment_date')
        ->from('payment')
        ->where('project_id', $projectID)
        ->order_by('payment_date', 'DESC')
        ->limit(1)
        ->get()
        ->row_array();
}

只有当 payment_id 的顺序能够可靠反映付款发生顺序时,才适合按 payment_id 排序。

日期格式注意事项

5/2/2019 是有歧义的,既可能表示 2019 年 5 月 2 日,也可能表示 2019 年 2 月 5 日。前端和后端应统一日期格式,最好提交没有歧义的 Y-m-d:

2019-05-02

如果表单提交的确实是 m/d/Y,解析格式也要相应调整:

$inputDate = DateTime::createFromFormat('!m/d/Y', $paymentDate);

不要让 strtotime() 猜测有歧义的日期格式。以输入日期 2019-05-02、最后付款日期 2019-05-05 为例,下面的条件成立:

$inputDate <= $lastPaymentDate

因此,callback 会返回 FALSE。

备注:内容仅供参考。