mcpField
P
prestashop-module-development

prestashop-module-development

github.com/jeffsenso开发6217 次安装2 天前更新
代码仓库

关于 prestashop-module-development

prestashop-module-development 是一项指导 PrestaShop 模块新建与现代化改造的开发技能,面向模块开发者。内容涵盖 Symfony 架构、配置、数据库、前台集成和安全实践,并强调职责划分与现代模式。

主要功能

  1. 01搭建现代模块结构
  2. 02实现 Symfony 配置页面
  3. 03使用 Doctrine 管理实体
  4. 04集成 Hooks 与前台功能
  5. 05应用安全与输入验证规范

适用场景

  1. 01创建 PrestaShop 新模块
  2. 02将旧模块改造为现代架构
  3. 03开发管理配置与列表页面
  4. 04添加数据库实体和批处理命令

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jeffsenso/prestashop-skills --skill prestashop-module-development

安装后在 Claude Code 中输入/prestashop-module-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CHANGELOG.md
ps9-core-ai/bin/
generate-ai-index.sh
ps9-core-ai/Component/BackOfficeHelp/
CONTEXT.md
ps9-core-ai/Component/Behat/
CONTEXT.md
ps9-core-ai/Component/Configuration/
CONTEXT.md
ps9-core-ai/Component/Console/
CONTEXT.md
ps9-core-ai/Component/Context/
CONTEXT.md
ps9-core-ai/Component/ContextStateManager/
CONTEXT.md
ps9-core-ai/Component/Controller/
CONTEXT.md
ps9-core-ai/Component/Cookie/
CONTEXT.md
ps9-core-ai/Component/CQRS/
CONTEXT.md
ps9-core-ai/Component/Database/
CONTEXT.md
ps9-core-ai/Component/Export/
CONTEXT.md
ps9-core-ai/Component/FacetedSearch/
CONTEXT.md
ps9-core-ai/Component/Forms/
CONTEXT.md
ps9-core-ai/Component/Grid/
CONTEXT.md
ps9-core-ai/Component/Hook/
CONTEXT.md
ps9-core-ai/Component/Import/
CONTEXT.md
ps9-core-ai/Component/Javascript/
CONTEXT.md
ps9-core-ai/Component/Link/
CONTEXT.md
ps9-core-ai/Component/Locale/
CONTEXT.md
ps9-core-ai/Component/MailTemplate/
CONTEXT.md
ps9-core-ai/Component/Migration/
CONTEXT.md
ps9-core-ai/Component/Migration/skills/legacy-to-symfony-migration/
step-00-audit.md
step-01-feature-flag.md
step-02-domain-layer.md
step-03-adapter-layer.md
step-04-behat-tests.md
step-05-listing-page.md
step-06-form-page.md

详细介绍

PrestaShop 模块开发

适用场景

在以下 PrestaShop 模块开发任务中使用此技能:

  • 使用现代架构(Symfony 控制器、服务、实体)创建新模块
  • 重构旧版模块,使其采用现代 PrestaShop 模式
  • 实现钩子、操作和事件监听器
  • 添加配置页面(采用现代 Symfony 表单方式)
  • 创建前台功能和小组件
  • 设置数据库实体和迁移
  • 实施安全措施(CSRF、输入验证、防止 SQL 注入)
  • 添加多语言支持和翻译
  • 将旧版代码模式(HelperForm、jQuery UI sortable、ObjectModel)转换为现代替代方案
  • 使用 PrestaShop Grid 系统构建列表页面(筛选、分页、切换、拖放排序)
  • 创建和管理 PrestaShop 购物车规则(代金券、折扣码、促销码)
  • 实现用于后台任务和批量操作的 Symfony 控制台命令

所需输入

  • PrestaShop 版本(现代开发建议使用 8.x/9.x)
  • 模块范围和功能需求
  • 现有模块路径(如果要更新旧版代码)
  • 数据库架构需求(如适用)
  • 前台集成需求(钩子、小组件、页面)
  • 配置需求(设置、管理界面)
  • 多语言需求和支持的语言

流程

0) 项目结构和命名空间命名

阅读:references/module-structure.md

关键规则:

  • 根据模块名称前缀推导 PSR-4 命名空间 — 切勿使用 PrestaShop\Module\
  • 使用 PrestaShop 模块生成器搭建新模块的脚手架
1) 主模块类和安装程序

阅读:references/module-class-and-installer.md

强制模式:

  • install() 方法:必须始终先执行 if (!parent::install()) { return false; },然后再委托给安装程序
  • uninstall() 方法:必须始终先调用安装程序,然后通过 && 链接调用 parent::uninstall():return $installer->uninstall() && parent::uninstall();
  • 钩子:在 Installer 中定义类属性数组 private array $hooks = [...],使用 $module->registerHook($this->hooks) 注册
  • 选项卡:以结构化数据的形式定义类属性数组 private array $tabs = [...],包含:name、class_name、label、parent_class_name、visible
  • 父级分组选项卡:先检查是否存在;若不存在,则使用 array_unshift($this->tabs, $parentTab) 将其添加到选项卡数组开头
  • 不要使用 getTabs():绝不在模块类中添加 getTabs() 方法 — 所有选项卡管理仅在 Installer 中进行

关键规则:

  • 始终在 _PS_VERSION_ 检查后添加 require_once __DIR__ . '/vendor/autoload.php';
  • 切勿直接在 install() 中注册钩子、查询数据库或调用 Configuration:: — 应委托给 src/Install/Installer.php
  • getContent() 必须仅使用现代 Symfony 路由器重定向到 Symfony 路由:$router = $this->get('router'); $route = $router->generate('route_name'); Tools::redirectAdmin($route); — 绝不要使用 $this->context->link->getAdminLink()
  • 主模块类中不得包含 SQL — 所有数据库访问(包括 hookActionShopDataDuplication 等钩子,以及 getWidgetVariables 等小组件方法中的访问)都必须通过 $this->get('service.id') 委托给 Repository 或 Manager 类
  • 服务访问:在管理端上下文中使用 $this->has() 并检查是否为 null;在前台上下文中使用普通的 $this->get() 并检查是否为 null。绝不要使用 ContainerFinder — 没有必要。请参阅 references/module-class-and-installer.md → Guard patterns 部分。
1b)配置变量管理

阅读:references/configuration-management.md

强制模式:基于 YAML 的配置,并使用 ConfigurationManager 服务

关键规则:

  • 绝不要在业务逻辑中直接使用 Configuration::updateValue() 或 Configuration::get()
  • 始终创建 /config_data/module.yml,定义所有配置键和默认值(唯一真实来源)
  • 始终使用 ConfigurationInstaller 服务执行安装/卸载操作
  • 始终使用 ConfigurationManager 服务读取配置(从数据库或 YAML 回退读取)
  • 结构:
    • config_data/module.yml — 所有键及默认值
    • src/Install/ConfigurationInstaller.php — 安装/卸载配置值
    • src/Configuration/ConfigurationManager.php — 读取配置(数据库或 YAML)
    • config/components/configuration/configuration_manager.yml — 注册服务
  • 优势:唯一真实来源、类型安全的访问方式、无需硬编码键、易于测试和维护
2)现代配置页面

阅读:references/configuration-page.md

强制模式:使用构造函数依赖注入的 FormHandler

关键规则:

  • 不要使用 HelperForm — 使用 Symfony 表单组件和 FrameworkBundleAdminController
  • 不要在控制器中通过 $this->get() 访问服务 — 控制器的服务定位器功能有限。改用 FormHandler 和构造函数依赖注入
  • 五个类:DataConfiguration、FormDataProvider、FormType、FormHandler(PrestaShop Core)、Controller(使用 DI)
  • 在 config/components/configuration/services.yml(由 config/admin/services.yml 导入)和 config/routes.yml 中配置所有内容
  • FormHandler:使用 PrestaShop\PrestaShop\Core\Form\Handler 服务封装表单创建、验证和保存
  • 控制器注册:必须注册为 public: true 服务,并添加 controller.service_arguments 标签
  • FormType 注册:必须设置 parent: 'form.type.translatable.aware' 和 tags: [{ name: form.type }]
  • Twig 模板的表单包装:始终在 {% extends %} 之前使用 {% form_theme configurationForm '@PrestaShop/Admin/TwigTemplateForm/prestashop_ui_kit.html.twig' %};将 {{ form_start() }} 放在 <div class="card"> 之前,并将 {{ form_end() }} 放在 </div> 之后 — 表单标签必须在相同嵌套层级包裹整个卡片结构,以避免 HTML 损坏
  • 控制器模式:
    private FormHandlerInterface $formHandler;
    
    public function __construct(FormHandlerInterface $formHandler)
    {
        $this->formHandler = $formHandler;
    }
    
    public function indexAction(Request $request): Response
    {
        $form = $this->formHandler->getForm();
        // ... handle request and save via $this->formHandler->save()
    }
    
  • CategoryChoiceTreeType:PrestaShop 提供的层级分类选择树表单类型。需要进行特定设置:
    • 有效的表单选项:多选时使用 'multiple' => true;可选时使用 'required' => false。不要使用 'expanded' 选项 — 此类型不支持该选项
    • 模板要求:在 {% extends %} 指令之前添加 {% form_theme configurationForm '@PrestaShop/Admin/TwigTemplateForm/prestashop_ui_kit.html.twig' %};在 {% block javascripts %} 中包含 JS bundle
    • JavaScript 初始化:创建 views/js/mymodule.form.bundle.js,其中包含 window.prestashop.component.initComponents(['ChoiceTree']) 和 new window.prestashop.component.ChoiceTree('#form_categories').enableAutoCheckChildren() — 选择器必须与 HTML id 属性匹配(通常为 form_<field_name>)
    • 表单数据处理:返回分类 ID 数组;通过 implode(',', $data['categories']) 存储为逗号分隔的字符串;读取时使用 array_filter(array_map('intval', explode(',', $storedValue))) 并转换回数组
    • 常见问题:树渲染为普通复选框 → 缺少表单主题;展开/折叠无效 → 缺少 JS 初始化;出现 "ChoiceTree is not defined" → 使用 window.prestashop.component.ChoiceTree,不要使用普通的 ChoiceTree
3)数据库操作与实体

阅读:references/database-and-entities.md
对于使用 Grid 的可翻译实体:阅读 references/entity-doctrine.md

  • 对于任何具有 Grid 列表页或可翻译字段的实体,始终使用 Doctrine ORM(Entity + LangEntity + Repository + Manager)
  • ObjectModel 已过时;不要在新模块或现代化改造的模块中使用
  • 实体类名 = 不带 _DB_PREFIX_ 的表名——PS 会全局添加前缀;使用不带 name= 参数的 @ORM\Table()
  • 表名使用一致的公司或模块前缀(例如 prefix_mymodule_items、prefix_mymodule_items_lang),以便将相关表归在一起并避免与 PS 核心表冲突
  • Lang 实体的属性类型必须与数据库列的可空性一致:声明为 NOT NULL DEFAULT '' 的列使用 string $field = '';声明为 NULL 的列使用 ?string $field = null
  • 对于用户未填写的语言,TranslatableType 会返回 null——在 Manager 中,始终将 null 转换为 '',用于 NOT NULL 字符串字段:$row->setName((string) ($value ?? ''))。绝不要将原始表单值直接传给 NOT NULL 列对应的 setter
  • 不要为表命名创建 MetadataListener 或 Doctrine 事件监听器
  • 始终清理原始 DBAL SQL:使用 (int) 转换 ID,并使用绑定参数
  • Repository 和 Manager 类之外,不得使用原始 SQL(Db::getInstance()、pSQL()、拼接 _DB_PREFIX_ 字符串)。 此规则适用于所有位置:主模块类、Installer、FixturesInstaller、钩子和小组件方法。唯一例外是 Installer 的 SQL 架构查询(CREATE TABLE、DROP TABLE),因为没有对应的 Repository 实现。
  • FixturesInstaller 必须使用 Db::getInstance() 执行原始 SQL——在 pr:mo Symfony 控制台上下文中,SymfonyContainer::getInstance() 会返回 null(从未设置 global $kernel)。所有 Doctrine ORM 调用在安装时都会静默失效。参见 references/module-class-and-installer.md → FixturesInstaller 部分。
  • Db::getValue() 会在内部追加 LIMIT 1——传给它的 SQL 字符串中绝不要写 LIMIT 1;否则会导致 MariaDB 语法错误。
服务拆分与组件架构

阅读:references/services-split.md

关键规则:

  • 不要创建 config/services.yml——只需要 config/admin/services.yml 和 config/front/services.yml;根目录下的 config/services.yml 从来都不是必需的,也不应存在
  • config/admin/services.yml 仅由后台内核加载——导入 ../common.yml 和后台组件(绝不要在没有 ../ 前缀的情况下使用 common.yml)
  • Repository 服务只能放在 config/common.yml 的组件中(Doctrine 层级,不依赖 PrestaShopBundle)
  • 所有依赖 PrestaShopBundle 的服务都放在 config/admin/services.yml 中
  • 始终在 config/components/ 下拆分为组件子文件夹——绝不要使用单一的扁平 services.yml
  • 只有通过 $this->get('service.id') 访问的服务才需要 public: true——对每个此类服务显式声明;所有其他服务保持私有(省略 public:)。避免将 _defaults: public: true 用作全局设置:这违反 Symfony 最佳实践,并且与使用 parent: 的服务搭配时,会在 PrestaShop 的 Symfony 版本中导致致命错误
  • 使用 parent: 且需要公开访问的服务,必须在服务定义中显式声明 public: true——设置 parent 时无法从 _defaults 继承 public(PrestaShop 的 Symfony 版本对此有强制要求)
  • 绝不要让 config/services.yml 指向 admin/services.yml——这会将仅限后台的服务加载到前台内核,导致容器编译失败,并使所有 $this->get() 调用静默失败
4)安全(必需)

阅读:references/security.md

  • CSRF:Symfony 表单会自动处理;对于原始 AJAX 端点,需手动验证
  • SQL 注入:对每个值使用 (int) + pSQL(),或使用 DbQuery 构建器
  • 文件上传:将完整的、兼容 $_FILES 的数组(包括 type、size、error)传给 ImageManager::validateUpload()
5)Hook 与前台集成

阅读:references/hooks-and-front-office.md

  • 重要:注册前台 CSS/JS 时,始终使用 actionFrontControllerSetMedia hook,绝不要使用 header 或 displayHeader——在该 hook 中使用 $this->context->controller->registerStylesheet() 和 ->registerJavascript()
  • 在 Installer 中注册 hooks,不要直接在 install() 中注册
  • 实现 WidgetInterface 时,不要定义显式的 hook 方法,例如仅调用 renderWidget() 的 hookDisplayHome()——PrestaShop 会自动为所有已注册的显示 hook 调用 renderWidget()
  • 对于前台 Smarty 模板:直接在模板中使用 {l s='Text' d='Modules.Modulename.Front'},绝不要从 getWidgetVariables() 将预翻译字符串作为 Smarty 变量传入——只传递动态数据(URL、ID 等),不要传递静态的可翻译 UI 文本
5b)主题模板注入(安装时调用 widget)

阅读:references/theme-template-injection.md

  • PS8 不支持模块主题覆盖——应改用基于标记的文件补丁
  • 双类设计:ThemeTemplateInjector(服务,可复用)+ ThemeTemplateInstaller(安装协调器)
  • 安装时绝不要使用 Theme::getThemes()——这是一个旧版类,在 Symfony 控制台中不会自动加载;改用 scandir(_PS_ALL_THEMES_DIR_)
  • 将安装/卸载调用包装在 try/catch 中并返回 true——主题注入绝不能阻止模块安装
6)翻译

阅读:references/translations.md

  • FrameworkBundleAdminController::trans() 的签名是 trans($id, $domain, $parameters = [])——与 Symfony 的参数顺序不同——务必调用 $this->trans('Text', 'Modules.Mymodule.Admin')(不要使用 $this->trans('Text', [], 'Domain')——将 [] 作为 domain、将字符串作为 $parameters 传入会导致致命类型错误)
  • 在 Twig 中使用 'Text'|trans({}, 'Modules.Mymodule.Admin')
  • 在模块类中声明 isUsingNewTranslationSystem(): true
7)旧版代码转换

阅读:references/legacy-conversion.md

常见转换:HelperForm → Symfony form,jQuery UI sortable → Grid PositionColumn,ModuleAdminController → FrameworkBundleAdminController。

8)服务与依赖注入

阅读:references/services-and-di.md

重要:不要在服务/控制器中使用旧版静态调用:

  • ❌ Context::getContext()——改为注入 $context: "@=service('prestashop.adapter.legacy.context').getContext()"
  • ❌ Configuration::get() / updateValue()——改为注入 @prestashop.adapter.legacy.configuration
  • ❌ Context::getContext()->getTranslator()——改为注入 @translator

关键规则:

  • 在 config/components/ 子目录中定义服务(由 config/admin/services.yml 导入)
  • 在 Symfony 控制器中使用 $this->get('service.id')
  • 对计算得出的构造函数参数(context、language ID、shop ID)使用 Expression Language(@=)
  • 始终通过构造函数注入依赖项,绝不要使用静态访问器
Symfony 控制台命令

阅读:references/services-and-di.md → Symfony 控制台命令部分

重要:命令必须在 config/admin/services.yml 中注册(不要在 common.yml 或 front/services.yml 中注册):

# config/admin/services.yml
mymodule.command.my_command:
  class: Vendor\MyModule\Command\MyCommand
  arguments: ["@mymodule.service.my_service"]
  tags:
    - { name: console.command, command: modulename:action }

关键规则:

  • 命令命名:采用 modulename:action 格式(例如,wsautocartrules:create、ws_keepdblight:cleandb)
  • 返回码:成功时为 0,失败时为 1(不要使用 Command::SUCCESS/FAILURE 常量)
  • 使用 SymfonyStyle 输出丰富的控制台内容(表格、进度条、样式化消息)
  • 为批量操作添加 --limit 选项,以便使用小型数据集进行测试
  • 始终通过构造函数注入服务,绝不要使用静态调用或 $this->get()
9) 购物车规则与优惠券

阅读:references/cart-rules.md

以编程方式创建和管理 PrestaShop 购物车规则(折扣优惠券)的完整指南:

关键模式:

  • 代码生成:使用字符集 123456789ABCDEFGHIJKLMNPQRSTUVWXYZ(不包含 O/0,以免混淆;与 PrestaShop admin.js 相同)
  • 唯一性检查:创建前使用 CartRule::getIdByCode($code) 验证
  • 多语言名称:通过 Language::getLanguages(true) 为所有启用的语言设置 name
  • 客户限制:使用 $cartRule->id_customer 创建特定客户的优惠券
  • 日期范围:date_from 和 date_to 均为必填项(格式:Y-m-d H:i:s)
  • 折扣类型:reduction_percent、reduction_amount、free_shipping 或 gift_product
  • 必填字段:quantity、quantity_per_user、priority、highlight、partial_use、active
  • 限制条件:默认均为 false(无任何限制);根据需要设置具体限制

文档中介绍的常见用例:

  • 针对特定客户的百分比折扣优惠券
  • 自动应用购物车规则(无需代码)
  • 带高亮显示的限时闪购
  • 免运费优惠券
  • 赠品优惠券
10) 网格系统(支持拖放排序的列表页)

阅读:references/grid-system.md

使用 PS Grid 系统构建 CRUD 列表页的完整模式:

  • GridDefinitionFactory — 列(PositionColumn、ToggleColumn、ActionColumn)、筛选器、行操作
  • QueryBuilder — 支持排序、分页和筛选的 Doctrine DBAL 查询
  • Filters — 默认排序和限制设置
  • config/components/grid/ 中的 5 个服务定义(factory、query、data、grid、position)+ 1 个 Twig FilesystemLoader
  • Twig FilesystemLoader 路径为 %kernel.project_dir%/modules/mymodule/views — %kernel.project_dir% 是 PS 根目录(app/ 的父目录),因此 modules/ 是其直接子目录。切勿使用 %kernel.project_dir%/../modules/
  • routes.yml 中的 4 个路由(index、search、toggle、update-position)
  • 4 个控制器操作(indexAction、searchAction、toggleStatus、updatePositionAction)
  • 通用网格脚本 scripts/grid.bundle.js 复制为 views/js/{modulename}.bundle.js — 不做编辑,不设置网格 ID(核心 window.prestashop.component Grid + 扩展,PS 8.0+ / 9.x)

验证

  • 模块安装时没有 PHP 错误:php bin/console pr:mo install mymodule
  • 配置能正确保存,并经过适当验证
  • 前台功能显示正常且运行正常
  • 翻译在所有已配置的语言中均可正常使用

校验

AI agent 规则——绝不可跳过任何一个步骤。完整说明请阅读 references/validation.md。

步骤 1 — lotr(在模块根目录运行)
vendor/websenso/prestashop-module-devtools/bin/lotr

预期结果:🎉 All commands completed successfully! Executed: 6/6

步骤 2 — 安装测试(在 PS 根目录运行)
php bin/console pr:mo install mymodule

预期结果:L'action Install sur le module … a réussi.

失败情况 / 调试

阅读:references/debugging.md

常见故障领域:

  • references/debugging.md — 所有症状/原因/修复表(安装、配置页面、Grid、InputBag、ImageManager、lotr 步骤)

PrestaShop 9 核心文档

以下文件随此技能一同打包在 ps9-core-ai/ 目录中。 阅读这些文件,深入了解 PS9 核心架构、约定和模式。

  • ps9-core-ai/CONTEXT.md — PS9 代码库的 AI 根上下文:项目级编码 标准、架构层(Core/Adapter/Bundle/Legacy)、CQRS 模式、分支策略,以及领域和组件上下文的完整索引。
  • ps9-core-ai/STRUCTURE.md — .ai/ 文件夹的架构:上下文、 技能和指针文件的组织方式,以及 AI 工具如何发现它们。

技能维护者须知:这些文件是来自官方 PrestaShop 仓库的静态快照。若要更新,技能维护者可以在模块目录中运行 lotr --install。这不是运行时操作——这些文件已预先打包,并与技能一同进行版本控制。

Steering

如果你的项目或组织定义了 steering 层(用于编码标准、架构约定和项目专属覆盖规则的分层上下文规则),请在开始任何任务前加载 steering 文件。

查找解析器——按以下顺序搜索:

  1. 模块根目录中的 steering/resolver.md(自定义安装)
  2. vendor/websenso/prestashop-module-devtools/steering/resolver.md——当 devtools 作为 Composer 包安装时的规范路径(与提供 vendor/websenso/prestashop-module-devtools/bin/lotr 的包相同)
  3. 任意 vendor/*/*/steering/resolver.md——扫描所有 vendor 子目录,查找 steering/resolver.md 文件(使用找到的第一个匹配项)

如果不存在,则静默跳过 steering,仅应用技能默认规则。

典型的 steering 结构(路径相对于找到解析器的位置):

steering/resolver.md                          ← load order and conflict rules
steering/company/                             ← organisation-wide standards
steering/languages/php/coding-standards.md    ← PHP conventions
steering/frameworks/prestashop/               ← PrestaShop-specific rules

按优先级从低到高加载 steering 文件(公司 → 语言 → 框架 → 项目)。后续层级会覆盖较早的层级。

升级处理

评论

支持 Markdown