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

请求完善PrestaShop按国家显示/隐藏商品的完整模块代码

PrestaShop 按访客国家过滤商品模块解决方案

原代码问题排查

  • 仅注册了filterProductSearch钩子但未实现实际查询过滤逻辑,无法影响商品显示
  • 缺少商品配送类别的自定义字段,无法标记商品所属配送范围类型
  • 地理编码依赖外部API未做缓存,可能导致性能损耗
  • 未适配PrestaShop多语言、多店铺场景

完整可运行模块代码

1. 主模块文件 myipmodule.php

<?php
if (!defined('_PS_VERSION_')) {
    exit;
}

require_once(dirname(__FILE__) . '/helpers/geoip_helper.php');

class MyIpModule extends Module
{
    public function __construct()
    {
        $this->name = 'myipmodule';
        $this->tab = 'front_office_features';
        $this->version = '1.0.0';
        $this->author = 'Your Name';
        $this->need_instance = 0;
        $this->ps_versions_compliancy = ['min' => '1.7', 'max' => _PS_VERSION_];
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Country-Based Product Filter');
        $this->description = $this->l('Shows or hides products based on visitor\'s country, supporting 3 delivery categories: Global, Selected Countries, Local Only');
        $this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
    }

    public function install()
    {
        return parent::install() &&
            $this->installDB() &&
            $this->registerHook('filterProductSearch') &&
            $this->registerHook('displayAdminProductsExtra') &&
            $this->registerHook('actionProductUpdate') &&
            $this->registerHook('actionProductAdd');
    }

    public function uninstall()
    {
        return parent::uninstall() && $this->uninstallDB();
    }

    private function installDB()
    {
        $sql = [];
        $sql[] = 'ALTER TABLE `' . _DB_PREFIX_ . 'product` ADD `delivery_category` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "0=Global,1=Selected Countries,2=Local Only"';
        $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'product_allowed_countries` (
            `id_product` INT(11) NOT NULL,
            `id_country` INT(11) NOT NULL,
            PRIMARY KEY (`id_product`, `id_country`)
        ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';

        foreach ($sql as $query) {
            if (!Db::getInstance()->execute($query)) {
                return false;
            }
        }
        return true;
    }

    private function uninstallDB()
    {
        $sql = [];
        $sql[] = 'ALTER TABLE `' . _DB_PREFIX_ . 'product` DROP COLUMN `delivery_category`';
        $sql[] = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'product_allowed_countries`';

        foreach ($sql as $query) {
            if (!Db::getInstance()->execute($query)) {
                return false;
            }
        }
        return true;
    }

    public function hookDisplayAdminProductsExtra($params)
    {
        $id_product = (int)$params['id_product'];
        $delivery_category = (int)Db::getInstance()->getValue('SELECT delivery_category FROM ' . _DB_PREFIX_ . 'product WHERE id_product = ' . $id_product);
        $allowed_countries = Db::getInstance()->executeS('SELECT id_country FROM ' . _DB_PREFIX_ . 'product_allowed_countries WHERE id_product = ' . $id_product);
        $selected_countries = array_column($allowed_countries, 'id_country');

        $this->context->smarty->assign([
            'delivery_category' => $delivery_category,
            'countries' => Country::getCountries($this->context->language->id),
            'selected_countries' => $selected_countries,
            'id_product' => $id_product
        ]);

        return $this->display(__FILE__, 'views/templates/admin/product_extra.tpl');
    }

    public function hookActionProductUpdate($params)
    {
        $this->saveProductCountrySettings($params['id_product']);
    }

    public function hookActionProductAdd($params)
    {
        $this->saveProductCountrySettings($params['id_product']);
    }

    private function saveProductCountrySettings($id_product)
    {
        if (!Tools::isSubmit('delivery_category')) {
            return;
        }

        $delivery_category = (int)Tools::getValue('delivery_category');
        Db::getInstance()->update('product', ['delivery_category' => $delivery_category], 'id_product = ' . (int)$id_product);

        Db::getInstance()->delete('product_allowed_countries', 'id_product = ' . (int)$id_product);

        if ($delivery_category === 1 && Tools::isSubmit('allowed_countries')) {
            $allowed_countries = Tools::getValue('allowed_countries');
            foreach ($allowed_countries as $id_country) {
                Db::getInstance()->insert('product_allowed_countries', [
                    'id_product' => (int)$id_product,
                    'id_country' => (int)$id_country
                ]);
            }
        }
    }

    public function hookFilterProductSearch($params)
    {
        $visitor_country_code = GeoIpHelper::getVisitorCountryCode();

        if (!$visitor_country_code) {
            return;
        }

        $visitor_country_id = Country::getByIso($visitor_country_code);
        if (!$visitor_country_id) {
            return;
        }

        $sql_filter = new DbQuery();
        $sql_filter->select('p.id_product');
        $sql_filter->from('product', 'p');
        $sql_filter->where('p.delivery_category = 0');
        $sql_filter->orWhere('p.delivery_category = 2 AND p.id_country = ' . (int)$visitor_country_id);
        $sql_filter->orWhere('p.delivery_category = 1 AND EXISTS (
            SELECT 1 FROM ' . _DB_PREFIX_ . 'product_allowed_countries pac
            WHERE pac.id_product = p.id_product AND pac.id_country = ' . (int)$visitor_country_id . '
        )');

        $allowed_product_ids = Db::getInstance()->executeS($sql_filter);
        $allowed_ids = array_column($allowed_product_ids, 'id_product');

        if (!empty($allowed_ids)) {
            $params['search']->addFilter('id_product', $allowed_ids, 'in');
        } else {
            $params['search']->addFilter('id_product', [-1], 'in');
        }
    }
}

2. 地理编码辅助文件 helpers/geoip_helper.php

<?php
if (!defined('_PS_VERSION_')) {
    exit;
}

class GeoIpHelper
{
    public static function getVisitorCountryCode()
    {
        $cache_key = 'myipmodule_country_' . Tools::getRemoteAddr();
        $country_code = Cache::get($cache_key);

        if ($country_code) {
            return $country_code;
        }

        $ip = Tools::getRemoteAddr();
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP)) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP)) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        }

        $response = @file_get_contents('http://www.geoplugin.net/json.gp?ip=' . urlencode($ip));
        if (!$response) {
            return false;
        }

        $data = json_decode($response, true);
        $country_code = isset($data['geoplugin_countryCode']) ? strtoupper($data['geoplugin_countryCode']) : false;

        if ($country_code) {
            Cache::set($cache_key, $country_code, 86400);
        }

        return $country_code;
    }
}

3. 后台商品模板文件 views/templates/admin/product_extra.tpl

<div class="panel">
    <div class="panel-heading">
        <i class="icon-globe"></i> {$l->l('Country Delivery Settings')}
    </div>
    <div class="panel-body">
        <div class="form-group">
            <label>{$l->l('Delivery Category')}</label>
            <select name="delivery_category" id="delivery_category" class="form-control">
                <option value="0" {if $delivery_category == 0}selected{/if}>{$l->l('Global Delivery')}</option>
                <option value="1" {if $delivery_category == 1}selected{/if}>{$l->l('Selected Countries Only')}</option>
                <option value="2" {if $delivery_category == 2}selected{/if}>{$l->l('Local Delivery Only')}</option>
            </select>
        </div>

        <div id="allowed_countries_container" class="form-group" {if $delivery_category != 1}style="display:none"{/if}>
            <label>{$l->l('Allowed Countries')}</label>
            <select name="allowed_countries[]" id="allowed_countries" multiple class="form-control">
                {foreach from=$countries item=country}
                    <option value="{$country.id_country}" {if in_array($country.id_country, $selected_countries)}selected{/if}>{$country.name}</option>
                {/foreach}
            </select>
        </div>
    </div>
</div>

<script>
    document.getElementById('delivery_category').addEventListener('change', function() {
        const container = document.getElementById('allowed_countries_container');
        container.style.display = this.value == 1 ? 'block' : 'none';
    });
</script>

4. 英文翻译文件 translations/en.php

<?php
$_MODULE = array(
    'myipmodule' => 'Country-Based Product Filter',
    'Country-Based Product Filter' => 'Country-Based Product Filter',
    'Shows or hides products based on visitor\'s country, supporting 3 delivery categories: Global, Selected Countries, Local Only' => 'Shows or hides products based on visitor\'s country, supporting 3 delivery categories: Global, Selected Countries, Local Only',
    'Are you sure you want to uninstall?' => 'Are you sure you want to uninstall?',
    'Country Delivery Settings' => 'Country Delivery Settings',
    'Delivery Category' => 'Delivery Category',
    'Global Delivery' => 'Global Delivery',
    'Selected Countries Only' => 'Selected Countries Only',
    'Local Delivery Only' => 'Local Delivery Only',
    'Allowed Countries' => 'Allowed Countries'
);

5. 中文翻译文件 translations/zh.php

<?php
$_MODULE = array(
    'myipmodule' => '按国家过滤商品',
    'Country-Based Product Filter' => '按国家过滤商品',
    'Shows or hides products based on visitor\'s country, supporting 3 delivery categories: Global, Selected Countries, Local Only' => '根据访客所在国家显示或隐藏商品,支持三类配送:全球配送、指定国家配送、仅本地配送',
    'Are you sure you want to uninstall?' => '确定要卸载吗?',
    'Country Delivery Settings' => '国家配送设置',
    'Delivery Category' => '配送类别',
    'Global Delivery' => '全球配送',
    'Selected Countries Only' => '仅指定国家',
    'Local Delivery Only' => '仅本地配送',
    'Allowed Countries' => '允许的国家'
);

使用说明

  1. 将所有文件放入PrestaShop模块目录下的myipmodule文件夹
  2. 登录后台,安装并启用该模块
  3. 编辑商品时,商品编辑页面底部会出现「国家配送设置」面板
  4. 为商品选择对应配送类别:
    • 全球配送:所有国家访客可见
    • 仅指定国家:选择允许的国家,仅这些国家访客可见
    • 仅本地配送:仅商品所属国家(商品编辑页「国家」字段)的访客可见
  5. 模块自动根据访客IP获取国家,过滤商品显示

内容的提问来源于stack exchange,提问作者Jhon Lamir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 06:14:56