# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is IvyForms is a WordPress form builder plugin with three distinct areas: - `backend/src/` — PHP 7.4+ with dependency injection (PHP-DI) - `frontend/src/` — Vue 3 + TypeScript, built with Vite - `e2e/` — Playwright end-to-end tests (see `e2e/CLAUDE.md` for details) ## Commands All commands run from the repo root. PHP quality checks and `composer install` run **inside the Docker container**. ### Setup ```bash make setup # Install all dependencies (PHP + Node + E2E) make install # Composer install only (inside container) ``` ### Frontend development ```bash make dev # Vite dev server with HMR make build # Production build + type check make build-only # Production build only make type # TypeScript type check only make lint # oxlint make format # oxfmt ``` ### PHP quality checks ```bash make quality-check # All three checks in sequence (aliases: make q) make phpcs # PSR12 style check (aliases: make cs) make phpcs-fix # Auto-fix style issues make phpstan # Static analysis at level 6 (aliases: make stan) make phpmd # Mess detector ``` ### E2E tests (run from repo root) ```bash make test # Headless — builds frontend + sets up wp-docker environment make test-remote # Headless, skips build + setup (remote env) make test-open # Same as test but with visible browser make test-open-remote # Visible browser, skips build + setup make test-ui # Interactive Playwright UI mode make test-ui-remote # UI mode, skips build + setup ``` ### Docker / WordPress ```bash make ssh # Shell into WordPress container make debug-log # Tail wp-content/debug.log make error-log # Tail PHP error log make dev-on / dev-off # Toggle IVYFORMS_DEV constant in ivyforms.php make dev-status # Check current dev mode ``` ## Architecture ### PHP backend Follows a strict layered architecture: **Routes → Controllers → Services → Repositories → Entities/ValueObjects** - **`Plugin.php`** — singleton entry point; builds the PHP-DI container, registers all WordPress hooks - **`Config/`** — DI wiring: `container.php`, `services.php`, `repository.php` - **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. - **Services** — business logic, injected into controllers via constructor - **Repositories** — data access only; always bound via interface in `Config/repository.php` - **Entities** — wrap ValueObjects and expose domain methods (e.g. `Form`, `Field`) - **ValueObjects** — immutable data holders; all have a `toArray()` method with properties aligned for readability - **Factories** — construct Entities from raw arrays ### Frontend Vue 3 Composition API throughout. Key entry points: `frontend/src/assets/js/admin/admin.ts` and `public/public.ts`. - **Stores** (`stores/`) — Pinia, one store per domain - **Composables** (`composables/`) — shared reactive logic - **Router** (`router/index.ts`) — routes are conditionally loaded based on the WordPress `page` query param (e.g. `ivyforms-builder`) - **Components** — auto-imported; all use `