You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PrestaShop自定义产品文本模块表单未在Hook中显示求助

PrestaShop模块后台表单不显示问题排查与解决建议

我开发了一款PrestaShop模块,可为每个产品添加额外文本,支持在后台编辑并将文本保存至数据库,更新后数据可留存。但目前模块的编辑表单未在任何已安装的Hook中显示,恳请提供解决建议。


模块PHP代码

<?php

if (!defined('_PS_VERSION_')) {
    exit;
}

class MyModuleText extends Module
{
    protected $config_form = false;

    public function __construct()
    {
        $this->name = 'mymoduletext';
        $this->tab = 'administration';
        $this->version = '1.0.0';
        $this->author = 'Jordi Comes';
        $this->need_instance = 0;

        $this->bootstrap = true;
        parent::__construct();

        $this->displayName = $this->l('My_Module_Text');
        $this->description = $this->l('Crea un texto editable desde el backoffice para cualquier producto de forma independiente');
        $this->ps_versions_compliancy = array('min' => '1.6', 'max' => _PS_VERSION_);
    }

    public function install()
    {
        include(dirname(__FILE__).'/sql/install.php');

        $this->registerHook('displayProductTabContent');
        $this->registerHook('actionProductUpdate');
        $this->registerHook('displayProductAdditionalInfo');
        $this->registerHook('header');

        return parent::install()
            && Configuration::updateValue('MYMODULETEXT_MODULE_NAME', 'producttextmodule');
    }

    public function uninstall()
    {
        include(dirname(__FILE__).'/sql/uninstall.php');

        $this->unregisterHook('actionProductUpdate');
        $this->unregisterHook('displayProductAdditionalInfo');
        $this->unregisterHook('header');

        return parent::uninstall()
            && Configuration::deleteByName('MYMODULETEXT_MODULE_NAME');
    }

    public function hookDisplayProductTabContent($params)
    {
        $productText = $this->getProductText((int)$params['product']['id_product']);

        $this->smarty->assign(array(
            'productText' => $productText,
        ));

        return $this->context->smarty->fetch($this->local_path.'../producttabcontent.tpl');
    }

    public function hookActionProductUpdate($params)
    {
        if (Tools::isSubmit('submitProductTextModule')) {
            $id_product = (int)$params['id_product'];
            $text = pSQL(Tools::getValue('mymoduletext'));
            
            if (isset($text)) {
                $sql = 'REPLACE INTO `'._DB_PREFIX_.'mymoduletext` (`id_product`, `text`) 
                VALUES ('.$id_product.', "'.$text.'")';
    
                Db::getInstance()->execute($sql);
            }
        }
    }

    public function hookDisplayProductAdditionalInfo($params)
    {
        $id_product = (int)$params['product']['id_product'];
        $sql = 'SELECT `text` FROM `'._DB_PREFIX_.'mymoduletext` WHERE `id_product` = '.(int)$id_product;
        $text = Db::getInstance()->getValue($sql);
        
        if ($text === false) {
            return '';
        }

        $this->context->smarty->assign(array(
            'mymoduletext' => $text,
        ));

        return $this->context->smarty->fetch($this->local_path.'../producttextmodule.tpl');
    }

    public function hookHeader()
    {
        $this->context->controller->addCSS($this->_path.'views/css/producttextmodule.css');
        $this->context->controller->addJS($this->_path.'views/js/producttextmodule.js');
    }
    
    public function getContent()
    {
        $output = '';
        if (Tools::isSubmit('submitProductTextModule')) {
            $my_module_name = strval(Tools::getValue('MYMODULETEXT_MODULE_NAME'));
            if (!$my_module_name
                || empty($my_module_name)
                || !Validate::isGenericName($my_module_name)
            ) {
                $output .= $this->displayError($this->l('Invalid Configuration value'));
            } else {
                Configuration::updateValue('MYMODULETEXT_MODULE_NAME', $my_module_name);
                $output .= $this->displayConfirmation($this->l('Settings updated'));
            }
        }
    
        return $output.$this->displayForm();
    }
    
    public function displayForm()
    {
        $default_lang = (int)Configuration::get('PS_LANG_DEFAULT');
    
        $fields_form[0]['form'] = array(
            'legend' => array(
                'title' => $this->l('Settings'),
            ),
            'input' => array(
                array(
                    'type' => 'text',
                    'label' => $this->l('Configuration value'),
                    'name' => 'MYMODULETEXT_MODULE_NAME_MODULE_NAME',
                    'size' => 20,
                    'required' => true
                )
            ),
            'submit' => array(
                'title' => $this->l('Save'),
                'class' => 'btn btn-default pull-right'
            )
        );
    
        $helper = new HelperForm();
    
        $helper->module = $this;
        $helper->name_controller = $this->name;
        $helper->token = Tools::getAdminTokenLite('AdminModules');
        $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name;
        
        $languages = Language::getLanguages(false);
        $helper->default_form_language = $default_lang;
        $helper->allow_employee_form_lang = $default_lang;
        $helper->languages = $languages;

        $helper->title = $this->displayName;
        $helper->show_toolbar = true;       
        $helper->toolbar_scroll = true;      
        $helper->submit_action = 'submitProductTextModule';
        $helper->toolbar_btn = array(
            'save' =>
                array(
                    'desc' => $this->l('Save'),
                    'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
                        '&token='.Tools::getAdminTokenLite('AdminModules'),
                ),
            'back' => array(
                'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'),
                'desc' => $this->l('Back to list')
            )
        );

        $helper->fields_value['MYMODULETEXT_MODULE_NAME'] = Configuration::get('MYMODULETEXT_MODULE_NAME');

        return $helper->generateForm($fields_form);
    }
}

?>

模板文件1:producttabcontent.tpl

<div class="product-text-module">
    <form action="{$link->getAdminLink('AdminProducts')}&id_product={$product.id_product}&updateproduct" method="post">
        <textarea name="mymoduletext" id="mymoduletext">{$productText}</textarea>
        <button type="submit" name="submitProductTextModule" class="btn btn-default">{l s='Save'}</button>
    </form>
</div>

{literal}
<style>
.product-text-module {
    padding: 20px;
    background-color: #f5f5f5;
    border-radius: 5px;
    margin-bottom: 20px;
}
</style>
{/literal}

模板文件2:producttextmodule.tpl

<div class="product-text-module">
    <p>{$mymoduletext}</p>
</div>

{literal}
<style>
.product-text-module {
    padding: 20px;
    background-color: #f5f5f5;
    border-radius: 5px;
    margin-bottom: 20px;
}
</style>
{/literal}

解决建议

1. 注册后台产品编辑页专属Hook

当前注册的Hook均为前台产品页Hook,后台编辑产品的表单需要绑定displayAdminProductsExtra——这个Hook会在后台产品编辑页面底部渲染内容。

修改install()方法,添加后台Hook注册:

public function install()
{
    include(dirname(__FILE__).'/sql/install.php');

    $this->registerHook('displayProductTabContent');
    $this->registerHook('actionProductUpdate');
    $this->registerHook('displayProductAdditionalInfo');
    $this->registerHook('header');
    // 新增后台Hook
    $this->registerHook('displayAdminProductsExtra');

    return parent::install()
        && Configuration::updateValue('MYMODULETEXT_MODULE_NAME', 'producttextmodule');
}

同时在uninstall()方法中添加取消注册:

$this->unregisterHook('displayAdminProductsExtra');

然后实现后台Hook的渲染方法:

public function hookDisplayAdminProductsExtra($params)
{
    $id_product = (int)$params['id_product'];
    $productText = $this->getProductText($id_product);

    $this->context->smarty->assign(array(
        'productText' => $productText,
        'product' => $params['product'],
        'token' => Tools::getAdminTokenLite('AdminProducts')
    ));

    // 按规范指向后台模板路径
    return $this->context->smarty->fetch($this->_path.'views/templates/admin/producttabcontent.tpl');
}

2. 修复模板路径错误

当前代码中$this->local_path.'../producttabcontent.tpl'路径逻辑错误,local_path已指向模块根目录,无需../。建议按PrestaShop规范组织文件:

  • 后台模板:/views/templates/admin/producttabcontent.tpl
  • 前台模板:/views/templates/front/producttextmodule.tpl

修改Hook中的模板路径为:

// 前台Hook示例
return $this->context->smarty->fetch($this->_path.'views/templates/front/producttextmodule.tpl');

// 后台Hook示例
return $this->context->smarty->fetch($this->_path.'views/templates/admin/producttabcontent.tpl');

3. 实现缺失的getProductText方法

代码中调用了$this->getProductText但未实现,会触发PHP错误导致Hook无法渲染,添加该方法:

private function getProductText($id_product)
{
    $sql = 'SELECT `text` FROM `'._DB_PREFIX_.'mymoduletext` WHERE `id_product` = '.(int)$id_product;
    return Db::getInstance()->getValue($sql) ?: '';
}

4. 添加后台表单安全Token

后台表单必须包含PrestaShop安全Token,否则提交会被拦截。修改producttabcontent.tpl的form:

<form action="{$link->getAdminLink('AdminProducts')}&id_product={$product.id_product}&updateproduct" method="post">
    <input type="hidden" name="token" value="{$token}">
    <textarea name="mymoduletext" id="mymoduletext">{$productText}</textarea>
    <button type="submit" name="submitProductTextModule" class="btn btn-default">{l s='Save'}</button>
</form>

5. 检查Hook注册状态

安装模块后,进入后台模块管理->位置页面,确认displayAdminProductsExtra已关联到你的模块。如果未显示,可尝试卸载重装模块,或手动在ps_hook_module数据库表中添加关联记录。


内容的提问来源于stack exchange,提问作者Jordi Comes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 07:25:25