PAYATHON 2026

PrestaShop 1.5 中 OrderState 的创建与删除问题

支付阿杰

结论

array_fill(0, 10, $name) 中的 10 表示生成 10 个数组元素,与系统安装了多少种语言无关。OrderState::$name 这类多语言字段还必须以 id_lang 作为数组键,因此不能直接写死为 10。

每调用一次 $orderState->add(),只会创建一条 order_state 主记录。如果数据库中出现了 14~18 这五条记录,说明创建函数被执行了多次,或者安装流程中还有其他 OrderState->add() 调用。array_fill() 本身不会创建五个状态。

通过保存的 ID 就能加载已有状态:

$orderState = new OrderState($id);

删除时调用对象方法:

$orderState->delete();

原代码中的 $order_state.delete() 不是正确的 PHP 语法,变量名也与前面的 $orderState 不一致。

正确设置多语言名称

array_fill(0, 10, $name) 生成的数组如下:

array(
    0 => $name,
    1 => $name,
    // ...
    9 => $name,
);

PrestaShop 的多语言字段需要使用下面这样的结构:

array(
    1 => $name, // id_lang = 1
    3 => $name, // id_lang = 3
);

语言 ID 不一定从 0 开始,也不一定连续。所以,即使把 10 换成已安装语言的数量,仍然可能得到错误的数组键。

应该根据实际语言列表构造数组:

private function getMultilangValue($value)
{
    $result = array();

    foreach (Language::getLanguages(false) as $language) {
        $result[(int) $language['id_lang']] = $value;
    }

    return $result;
}

创建 OrderState

创建前可以检查 Configuration 中保存的 ID 是否仍然对应有效对象。这样能处理配置项仍然存在,但状态已被手动删除的情况:

private function createOrderState($configurationKey, $name)
{
    $idOrderState = (int) Configuration::get($configurationKey);

    if ($idOrderState) {
        $existingOrderState = new OrderState($idOrderState);

        if (Validate::isLoadedObject($existingOrderState)) {
            return true;
        }
    }

    $orderState = new OrderState();
    $orderState->name = $this->getMultilangValue($name);
    $orderState->send_email = false;
    $orderState->color = '#4169E1';
    $orderState->hidden = false;
    $orderState->delivery = false;
    $orderState->logable = false;
    $orderState->invoice = false;
    $orderState->unremovable = false;

    if (!$orderState->add()) {
        return false;
    }

    return Configuration::updateValue(
        $configurationKey,
        (int) $orderState->id
    );
}

如果启用了邮件通知,模板名称也要按相同方式设置为多语言字段:

$orderState->send_email = true;
$orderState->template = $this->getMultilangValue('SB24');

模板名需要与模块实际提供的邮件模板文件对应。

为什么会出现四个额外的空状态

多语言数组中的十个元素只会影响 order_state_lang 表里的语言记录,不会让 ObjectModel::add() 向 order_state 表插入十条主记录。

出现五个不同的 OrderState ID,通常说明创建逻辑执行了五次。排查时可以重点查看:

  • install() 是否直接或间接多次调用了 createOrderState()。
  • 是否在语言循环、商店循环或订单状态定义循环中调用了这个函数。
  • 每次调用是否传入了不同的 $db_name。
  • 安装失败并重试后,之前创建的状态是否没有被清理。
  • 模块的其他安装函数中是否也调用了 new OrderState() 和 add()。

可以临时记录调用次数和参数:

PrestaShopLogger::addLog(
    'createOrderState called: ' . $configurationKey,
    1
);

修改模块代码不会自动删除数据库中已经生成的空状态。清理这些状态前,需要先确认没有订单引用它们。

根据 Configuration 获取 OrderState

这里不需要自行查询数据库,也不必单独编写 getOrderState():

$idOrderState = (int) Configuration::get($configurationKey);
$orderState = new OrderState($idOrderState);

if (!Validate::isLoadedObject($orderState)) {
    // Configuration 中的 ID 无效,或者该状态已经被删除
}

PrestaShop 的 ObjectModel 会根据传入的 ID 加载对应记录。

正确删除 OrderState

删除函数只需要状态 ID,无须重新设置名称、颜色等字段:

private function deleteOrderState($configurationKey)
{
    $idOrderState = (int) Configuration::get($configurationKey);

    if (!$idOrderState) {
        return true;
    }

    $orderState = new OrderState($idOrderState);

    if (Validate::isLoadedObject($orderState)) {
        if (!$orderState->delete()) {
            return false;
        }
    }

    return Configuration::deleteByName($configurationKey);
}

在模块的 uninstall() 中检查返回值:

public function uninstall()
{
    if (!$this->deleteOrderState('MY_MODULE_ORDER_STATE')) {
        return false;
    }

    return parent::uninstall();
}

原来的写法:

$order_state.delete();

有两个问题:

  1. PHP 使用 -> 调用对象方法,而不是 .。
  2. 创建时的变量名是 $orderState,删除时却写成了 $order_state。

最低限度的正确写法是:

$orderState->delete();

删除前的注意事项

如果状态已经被真实订单或订单历史记录引用,卸载模块时直接删除它,可能会导致后台无法正常显示订单历史。支付模块投入使用后,通常更稳妥的处理方式是保留 OrderState,只删除模块自己的 Configuration 项。只有确认没有历史订单引用时,才物理删除该状态。

还应在确认 delete() 成功后,再调用 Configuration::deleteByName()。否则,状态删除失败时配置 ID 却已经丢失,后续清理会更麻烦。

备注:内容仅供参考。