| 1 |
# CLAUDE.md |
| 2 |
|
| 3 |
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 |
|
| 5 |
## What this is |
| 6 |
|
| 7 |
IvyForms is a WordPress form builder plugin with three distinct areas: |
| 8 |
- `backend/src/` — PHP 7.4+ with dependency injection (PHP-DI) |
| 9 |
- `frontend/src/` — Vue 3 + TypeScript, built with Vite |
| 10 |
- `e2e/` — Playwright end-to-end tests (see `e2e/CLAUDE.md` for details) |
| 11 |
|
| 12 |
## Commands |
| 13 |
|
| 14 |
All commands run from the repo root. PHP quality checks and `composer install` run **inside the Docker container**. |
| 15 |
|
| 16 |
### Setup |
| 17 |
```bash |
| 18 |
make setup # Install all dependencies (PHP + Node + E2E) |
| 19 |
make install # Composer install only (inside container) |
| 20 |
``` |
| 21 |
|
| 22 |
### Frontend development |
| 23 |
```bash |
| 24 |
make dev # Vite dev server with HMR |
| 25 |
make build # Production build + type check |
| 26 |
make build-only # Production build only |
| 27 |
make type # TypeScript type check only |
| 28 |
make lint # oxlint |
| 29 |
make format # oxfmt |
| 30 |
``` |
| 31 |
|
| 32 |
### PHP quality checks |
| 33 |
```bash |
| 34 |
make quality-check # All three checks in sequence (aliases: make q) |
| 35 |
make phpcs # PSR12 style check (aliases: make cs) |
| 36 |
make phpcs-fix # Auto-fix style issues |
| 37 |
make phpstan # Static analysis at level 6 (aliases: make stan) |
| 38 |
make phpmd # Mess detector |
| 39 |
``` |
| 40 |
|
| 41 |
### E2E tests (run from repo root) |
| 42 |
```bash |
| 43 |
make test # Headless — builds frontend + sets up wp-docker environment |
| 44 |
make test-remote # Headless, skips build + setup (remote env) |
| 45 |
make test-open # Same as test but with visible browser |
| 46 |
make test-open-remote # Visible browser, skips build + setup |
| 47 |
make test-ui # Interactive Playwright UI mode |
| 48 |
make test-ui-remote # UI mode, skips build + setup |
| 49 |
``` |
| 50 |
|
| 51 |
### Docker / WordPress |
| 52 |
```bash |
| 53 |
make ssh # Shell into WordPress container |
| 54 |
make debug-log # Tail wp-content/debug.log |
| 55 |
make error-log # Tail PHP error log |
| 56 |
make dev-on / dev-off # Toggle IVYFORMS_DEV constant in ivyforms.php |
| 57 |
make dev-status # Check current dev mode |
| 58 |
``` |
| 59 |
|
| 60 |
## Architecture |
| 61 |
|
| 62 |
### PHP backend |
| 63 |
|
| 64 |
Follows a strict layered architecture: **Routes → Controllers → Services → Repositories → Entities/ValueObjects** |
| 65 |
|
| 66 |
- **`Plugin.php`** — singleton entry point; builds the PHP-DI container, registers all WordPress hooks |
| 67 |
- **`Config/`** — DI wiring: `container.php`, `services.php`, `repository.php` |
| 68 |
- **Controllers** — one class per action, always named `handle()`, always return `WP_REST_Response`. Must verify nonce first via `Sanitizer::verifyNonce()`, then sanitize all input via `Sanitizer::` methods before any processing. |
| 69 |
- **Services** — business logic, injected into controllers via constructor |
| 70 |
- **Repositories** — data access only; always bound via interface in `Config/repository.php` |
| 71 |
- **Entities** — wrap ValueObjects and expose domain methods (e.g. `Form`, `Field`) |
| 72 |
- **ValueObjects** — immutable data holders; all have a `toArray()` method with properties aligned for readability |
| 73 |
- **Factories** — construct Entities from raw arrays |
| 74 |
|
| 75 |
### Frontend |
| 76 |
|
| 77 |
Vue 3 Composition API throughout. Key entry points: `frontend/src/assets/js/admin/admin.ts` and `public/public.ts`. |
| 78 |
|
| 79 |
- **Stores** (`stores/`) — Pinia, one store per domain |
| 80 |
- **Composables** (`composables/`) — shared reactive logic |
| 81 |
- **Router** (`router/index.ts`) — routes are conditionally loaded based on the WordPress `page` query param (e.g. `ivyforms-builder`) |
| 82 |
- **Components** — auto-imported; all use `<script setup lang="ts">` (never Options API) |
| 83 |
- **SCSS** — design token system: primitives → themes → maps. Always use `--map-*` CSS variables in components (supports light/dark mode); never hardcode colors. |
| 84 |
|
| 85 |
## Key conventions |
| 86 |
|
| 87 |
### PHP |
| 88 |
|
| 89 |
- **No `ABSPATH` guard** in class files — only in non-class files (interfaces, traits, config) |
| 90 |
- **Settings**: always use `SettingsService` — never `get_option('ivyforms_settings')` directly |
| 91 |
- **Strings**: use `BackendStrings::get*Strings()['key']` — check the right method before adding a new key; never create duplicates across methods |
| 92 |
- **Hook naming**: `ivyforms/category/action_name` (e.g. `ivyforms/form/after_submission`) |
| 93 |
- **Variable names**: descriptive (`$formSettings`, not `$arr`; `$serviceClass`, not `$fqcn`) |
| 94 |
- **WP_REST_Request param**: name it `$data`, not `$request` |
| 95 |
|
| 96 |
### Frontend |
| 97 |
|
| 98 |
- **No `any` type** — use specific types or `unknown` |
| 99 |
- **Translatable strings**: `getLabel('key')` from `useLabels()` composable — never hardcoded UI strings |
| 100 |
- **External links**: use `openPricingPage()` from `useProFeatureUpgrade.ts` — never `window.open(url, '_blank')` directly |
| 101 |
- **Colors**: use `--map-base-*` CSS variables from the design token system |
| 102 |
|
| 103 |
### Global |
| 104 |
|
| 105 |
- Keep changes small and scoped to the area being touched |
| 106 |
- Run the relevant quality checks for the changed area before finalizing |
| 107 |
- Preserve architecture boundaries — don't reach across layers |
| 108 |
|