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();
有两个问题:
- PHP 使用
->调用对象方法,而不是.。 - 创建时的变量名是
$orderState,删除时却写成了$order_state。
最低限度的正确写法是:
$orderState->delete();
删除前的注意事项
如果状态已经被真实订单或订单历史记录引用,卸载模块时直接删除它,可能会导致后台无法正常显示订单历史。支付模块投入使用后,通常更稳妥的处理方式是保留 OrderState,只删除模块自己的 Configuration 项。只有确认没有历史订单引用时,才物理删除该状态。
还应在确认 delete() 成功后,再调用 Configuration::deleteByName()。否则,状态删除失败时配置 ID 却已经丢失,后续清理会更麻烦。
备注:内容仅供参考。
