请求完善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' => '允许的国家' );
使用说明
- 将所有文件放入PrestaShop模块目录下的
myipmodule文件夹 - 登录后台,安装并启用该模块
- 编辑商品时,商品编辑页面底部会出现「国家配送设置」面板
- 为商品选择对应配送类别:
- 全球配送:所有国家访客可见
- 仅指定国家:选择允许的国家,仅这些国家访客可见
- 仅本地配送:仅商品所属国家(商品编辑页「国家」字段)的访客可见
- 模块自动根据访客IP获取国家,过滤商品显示
内容的提问来源于stack exchange,提问作者Jhon Lamir
相关产品推荐
相关产品推荐

