关于 ddev
面向 Craft CMS 开发者的 DDEV 工作指南,涵盖项目配置、常用命令、插件开发和调试要点。重点说明容器环境中的安全操作及 Craft CLI 优先实践。
主要功能
- 01Craft 项目初始化与配置
- 02DDEV 常用命令指南
- 03Craft CLI 操作建议
- 04插件依赖与测试隔离
- 05浏览器调试工作流
适用场景
- 01启动新的 Craft CMS 项目
- 02在容器内安装依赖和运行测试
- 03调试模板、DOM 与网络请求
MCP 相关
介绍配置 Chrome DevTools MCP,让 Claude Code 检查页面、控制台、网络请求并与 DOM 交互。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ michtio/ craftcms-claude-skills --skill ddev安装后在 Claude Code 中输入/ddev确认已加载。
详细介绍
Craft CMS 开发的 DDEV
配套技能 — 始终一并加载
触发此技能时,也要加载:
craftcms— 插件/模块开发。当 DDEV 命令涉及 Craft CLI(ddev craft make、ddev craft migrate、ddev craft project-config)时必须加载。craft-php-guidelines— PHP 编码规范。当 DDEV 命令涉及代码质量工具(ddev composer check-cs、ddev composer phpstan、ddev exec --dir … vendor/bin/pest)时必须加载。
文档
- DDEV 文档:https://docs.ddev.com/en/stable/
- Craft CMS 快速入门:https://docs.ddev.com/en/stable/users/quickstart/#craft-cms
- 配置参考:https://docs.ddev.com/en/stable/users/configuration/config/
- 自定义命令:https://docs.ddev.com/en/stable/users/extend/custom-commands/
- 附加服务:https://docs.ddev.com/en/stable/users/extend/additional-services/
- Vite 集成:https://docs.ddev.com/en/stable/users/usage/developer-tools/#nodejs
不确定某项 DDEV 功能时,使用 WebFetch 获取相关文档页面。
常见问题
- 使用
ddev exec composer install而不是ddev composer install— DDEV 简写命令会处理路径解析和环境设置。始终使用简写命令。 - 忘记
ddev craft up会同时执行migrate/all和project-config/apply— 拉取或部署后无需分别运行它们。 - 使用
ports而不是web_extra_exposed_ports暴露 Vite 开发服务器 — 运行多个 DDEV 项目时,ports会导致冲突。web_extra_exposed_ports会通过 Traefik 路由,并支持 HTTPS。 - 运行
ddev composer global require— 全局软件包会安装在容器内,并在重启时消失。只安装项目级依赖。 - 设置
nodejs_version,却在主机上运行npm install— 必须通过ddev npm在容器内运行 Node,以匹配已配置的版本。 - 容器运行时编辑
.ddev/config.yaml,却不重启 — 配置变更需要执行ddev restart才会生效。 - 在多数据库配置中使用
ddev import-db,却不加--target-db=db— 默认目标是db,但如果你配置了其他数据库,请明确指定。 - 在自定义命令中添加
#ddev-generated,而这些命令是你自定义的 — 更新时,DDEV 会覆盖带有此注释的文件。仅对由附加组件管理的命令使用此注释。自行维护的自定义命令应省略此注释。 - 在主机上运行
composer install,再运行ddev composer check-cs/ddev composer phpstan— 如果主机 PHP 版本与 DDEV 的版本不同(例如主机 PHP 8.4、DDEV PHP 8.3),vendor/composer/platform_check.php会失败。始终运行ddev composer install,确保vendor/与容器的 PHP 版本一致。
优先使用 Craft CLI,最后才用原始 SQL
始终优先使用 Craft CLI 命令,而不是原始数据库查询:
ddev craft users/list-admins # not: ddev mysql -e "SELECT * FROM users WHERE admin=1"
ddev craft project-config/get system # not: reading project.yaml manually
ddev craft resave/entries # not: UPDATE queries on content tables
ddev craft elements/delete # not: DELETE FROM elements
只有在没有对应 CLI 命令时,才使用 ddev mysql(例如,检查表结构、调试特定行,或对卡住的互斥锁执行 TRUNCATE cache)。Craft CLI 命令会处理项目配置、搜索索引更新和事件触发,而这些都是原始 SQL 会跳过的。
简写命令
始终优先使用 DDEV 简写命令,而不是 ddev exec:
ddev composer install # not ddev exec composer install
ddev craft up # not ddev exec php craft up
ddev npm install # not ddev exec npm install
ddev craft make service # scaffolding
Craft CMS 项目类型
# .ddev/config.yaml
name: my-craft-site
type: craftcms
docroot: web
php_version: "8.3"
database:
type: mysql
version: "8.0"
nodejs_version: "20"
DDEV 会自动注入:CRAFT_DB_SERVER、CRAFT_DB_USER、CRAFT_DB_PASSWORD、CRAFT_DB_DATABASE、PRIMARY_SITE_URL。这些变量通过 .ddev/.env.web 注入容器。你可以在 .ddev/config.yaml 中设置 disable_settings_management: true 来选择不使用这些变量。
新项目初始化
全新 DDEV + Craft 项目的标准流程:
mkdir my-craft-site && cd my-craft-site
ddev config --project-type=craftcms --docroot=web # writes .ddev/config.yaml
ddev start
ddev composer create-project craftcms/craft # Craft's setup wizard runs automatically
ddev composer create-project 完成后会启动 Craft 的交互式安装向导。如果向导没有运行(或你需要重新运行),请使用 ddev craft install。也可以将 craftcms/craft 替换为社区入门项目,从该项目开始初始化。
常用命令
ddev start # Start the project
ddev stop # Stop the project
ddev restart # Restart containers
ddev ssh # SSH into web container
ddev describe # Show project info and URLs
ddev launch # Open the project in a browser
ddev launch -m # Open Mailpit (also --mailpit)
ddev logs # View container logs
ddev import-db --file=dump.sql # Import database
ddev export-db --file=dump.sql # Export database
ddev xdebug on # Enable Xdebug
ddev craft db/backup # Craft database backup
安装后自动运行
Composer 脚本会在安装/更新后自动运行 craft up:
{
"scripts": {
"post-craft-update": [
"@php craft install/check && php craft up --interactive=0 || exit 0"
],
"post-update-cmd": "@post-craft-update",
"post-install-cmd": "@post-craft-update"
}
}
无需手动运行 ddev craft migrate/all 或 ddev craft project-config/apply——ddev craft up 会完成这两项操作,并且会在 ddev composer install/update 后自动运行。
附加组件
ddev add-on get ddev/ddev-redis # Install Redis
ddev add-on list # List installed add-ons
ddev add-on remove ddev/ddev-redis # Remove add-on
Mailpit 已内置于 DDEV 核心中,无需安装附加组件。发出的邮件会自动被捕获。运行 ddev mailpit 即可访问 Web 界面,或者运行 ddev describe 查看其 URL(例如 https://{project}.ddev.site:8026)。
分享本地站点
ddev share # Expose the project on a temporary public URL
ddev share 默认使用 ngrok 提供商,这需要一个免费的 ngrok.com 账户以及已配置的 ngrok 身份验证令牌。(cloudflared 是另一种提供商,可通过 --provider=cloudflared 使用,无需账户,但默认提供商是 ngrok。)
自定义命令
将脚本放在 .ddev/commands/web/(容器)或 .ddev/commands/host/(主机)中:
#!/usr/bin/env bash
## Description: Run ECS code style check
## Usage: check-cs
## Example: ddev check-cs
cd /var/www/html && composer check-cs
注意:对于你维护的自定义命令,请省略 #ddev-generated——DDEV 会在更新期间覆盖带有此注释的文件。只有由附加组件管理的命令才应包含此注释。
Composer Path 仓库与卷挂载
在本地开发插件时,Composer path 仓库会将插件符号链接到 vendor/。要让它在 DDEV 的 Docker 容器内正常工作,必须挂载主机路径,以便解析符号链接。
设置
- composer.json — 使用本地主机路径:
{
"repositories": [
{
"type": "path",
"url": "/Users/Shared/dev/craft-plugins/v5/*"
}
]
}
- docker-compose 覆盖配置 — 将同一路径挂载到容器中。创建
.ddev/docker-compose.mounts.yaml:
services:
web:
volumes:
- /Users/Shared/dev/craft-plugins:/Users/Shared/dev/craft-plugins
容器内的挂载路径必须与主机路径完全一致——Composer 创建的是绝对符号链接,必须能在两种环境中解析。将 /Users/Shared/dev/craft-plugins 替换为你的实际插件目录路径。
- 要求安装插件:
ddev composer require vendor/plugin-handle:@dev
常见错误
- 在
composer.json的url中使用 Docker 内部路径——该路径必须是主机文件系统路径,而不是/var/www/... - 忘记卷挂载——
ddev composer install虽然成功,但符号链接在容器内无处可指 - 在
composer.json的config中设置"platform": {"php": "8.3"}——不要这样做。DDEV 通过.ddev/config.yaml管理 PHP 版本。平台覆盖会导致主机与容器之间的依赖解析不一致,并妨碍 DDEV 顺畅管理版本升级。
使用 Chrome DevTools MCP 调试浏览器
Chrome DevTools MCP 服务可让 Claude Code 直接访问浏览器——检查页面、读取控制台日志、检查网络请求、截取屏幕截图,以及与 DOM 交互。
安装
claude mcp add chrome-devtools -- npx chrome-devtools-mcp@latest
退出并重新打开 Claude Code,以加载新的 MCP 服务。需要 Chrome 或 Chromium 正在运行——MCP 服务会自动处理 DevTools Protocol 连接。
功能
| 能力 | 用例 |
|---|---|
| 页面检查 | 检查渲染后的 HTML、验证模板输出、检查 meta 标签 |
| 控制台日志 | 捕获 Twig 错误、JS 异常、Garnish 初始化失败 |
| 网络请求 | 调试 404 资源、失败的 AJAX 调用、Sprig/htmx 更新 |
| DOM 查询 | 验证表单标记、检查字段渲染、验证 ARIA 属性 |
| 屏幕截图 | 可视化验证 CP 模板、测试响应式布局 |
| 导航与登录 | 登录 CP、导航至插件设置/编辑页面 |
适用场景
- 前端模板调试 — 404、资源缺失、布局异常、验证 SEOmatic 元标签
- CP 模板验证 — 插件设置页面正确渲染、可编辑表格正常工作、滑出式编辑器正常加载
- Garnish/JS 调试 — 模态框、拖动排序、折叠菜单正常初始化,且控制台无错误
- Sprig/htmx 调试 — 监视 htmx 交换的网络请求,验证响应 HTML 片段
- 身份验证流程测试 — 端到端走查登录、注册和密码重置
- 只读模式验证 — 确认关闭
allowAdminChanges后设置页面仍能正常显示 - 视觉回归测试 — 模板更改前后截图对比
CP 身份验证模式
可通过 https://{project}.ddev.site 访问 DDEV 站点。若要检查 CP 页面:
- 访问
https://{project}.ddev.site/{cpTrigger} - 使用管理员凭据登录
- 前往要检查的插件或设置页面
- 检查控制台中的 JS 错误,并检查 DOM 中的标记是否正确
项目设置
craft-project-setup 技能会在脚手架搭建期间提供安装 Chrome DevTools MCP 的选项。若之后安装,请在项目根目录运行 claude mcp add chrome-devtools -- npx chrome-devtools-mcp@latest — 这会写入项目的 .claude.json,将配置保留在项目级别。
运行插件自带的测试套件
将插件符号链接到宿主项目后,请在容器内从插件自己的目录运行其测试套件:
ddev exec --dir /var/www/html/vendor/acme/my-plugin vendor/bin/pest
ddev exec --dir /var/www/html/vendor/acme/my-plugin composer test
--dir 设置工作目录,这是关键所在。markhuot/craft-pest-core 仅从 getcwd().'/phpunit.xml(.dist)' 读取环境覆盖项 — 它不会解析 --configuration= 标记。因此,使用共享根目录的形式:
# UNSAFE — runs the tests, but against the project's live database
ddev craft pest -- --configuration=vendor/acme/my-plugin/phpunit.xml.dist
……会发现并执行插件测试,却悄悄忽略插件自身的 CRAFT_DB_DATABASE 固定设置。如果测试套件还没有绑定 RefreshesDatabase 特征(回滚为可选项),测试数据就会永久写入开发数据库。对于任何 craft-pest-core 测试套件,都应将共享根目录调用视为不安全。
完整机制、隔离检查清单和 CI 等效方案请参见 craft-pest 技能(references/isolation.md)。
对于通过 Composer 路径仓库挂载的插件,--dir 路径是容器内的符号链接目标 — 关于如何解析该路径,请参见Composer 路径仓库和卷挂载。
故障排除
ddev poweroff # Stop all DDEV projects
ddev debug router # Debug router configuration
ddev debug capabilities # Check Docker capabilities
ddev delete --omit-snapshot # Remove project without snapshot
general-config 编辑未生效(ddev 注入的环境变量优先)
DDEV 会通过 .ddev/.env.web 自动向容器注入 Craft 环境变量(包括 PRIMARY_SITE_URL 和 CRAFT_DB_*),并且每次 ddev start/重启时都会重新生成该文件,除非在 .ddev/config.yaml 中设置了 disable_settings_management: true。由于真实的容器环境变量优先于 cms/.env 中的 dotenv 值,而 CRAFT_* 环境变量又优先于 config/general.php(参见 craftcms 技能中的 config-bootstrap.md),因此你在 .env 或 general.php 中编辑的值可能会被 ddev 注入的值悄悄覆盖。
两种症状及其修复方法:
- 站点 URL 或协议不正确。
PRIMARY_SITE_URL会采用 ddev 的主 URL,其协议通常为http;如果 Craft 解析出意外的基础 URL,可能就是该注入变量所致。在站点配置中,将 Craft 的站点 URL 指向一个名称不同的环境变量(例如SITE_URL),避免 ddev 的PRIMARY_SITE_URL覆盖它;或者有意覆盖PRIMARY_SITE_URL。 - 重启后
.env更改被还原。 如果你手动编辑了.ddev/.env.web,它会在重启时重新生成 — 请将持久覆盖项放入.ddev/config.yaml(web_environment),或设置disable_settings_management: true,以便自行管理该文件。
当配置值“无法更改”时,请先检查解析后的环境(ddev exec printenv | grep CRAFT_ 和 .ddev/.env.web),再继续编辑 PHP。