All checks were successful
CI / Coding Standards (push) Successful in 43s
CI / PHPStan (push) Successful in 52s
CI / Tests (PHP 8.1) (push) Successful in 47s
CI / Tests (PHP 8.2) (push) Successful in 49s
CI / Tests (PHP 8.3) (push) Successful in 37s
CI / No Debug Code (push) Successful in 2s
All classes are now organised by domain (Availability, Booking, Auth). Each domain package contains its value object, repository, admin controller, REST endpoint, and any shortcode pages under a matching sub-namespace. Cross-cutting wiring (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) lives at src/ root. Tests mirror the domain structure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
6.1 KiB
Markdown
110 lines
6.1 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
composer install # Install all dependencies
|
|
|
|
composer test # Run the full test suite (required after every change)
|
|
composer lint # PHPStan static analysis
|
|
composer cs # PHPCS coding standards check
|
|
composer cs:fix # Auto-fix coding standards
|
|
|
|
# Run a single test file
|
|
./vendor/bin/phpunit tests/Unit/Availability/AvailabilityRepositoryTest.php
|
|
|
|
# Run a single test by name
|
|
./vendor/bin/phpunit --filter testInsertCallsWpdbInsertAndReturnsId
|
|
```
|
|
|
|
**Run `composer test` after every code change before considering a task complete.**
|
|
|
|
## Architecture
|
|
|
|
### Plugin Bootstrap
|
|
`unsupervised-schedular.php` defines constants (`USC_VERSION`, `USC_PLUGIN_DIR`, `USC_PLUGIN_URL`), registers activation/deactivation hooks, then calls `Plugin::boot()` on `plugins_loaded`. No logic lives in the root file.
|
|
|
|
### Directory Structure
|
|
```
|
|
src/ — All plugin PHP (PSR-4 namespace: Unsupervised\Schedular\)
|
|
Availability/ — Availability slots: value object, repository, controller, REST endpoint
|
|
Booking/ — Lessons/bookings: value object, repository, controller, REST endpoint, shortcode page
|
|
Auth/ — Roles, capabilities, login page
|
|
Plugin.php — Wires all components together on plugins_loaded
|
|
Installer.php — Creates DB tables and roles on activation
|
|
Schema.php — CREATE TABLE SQL for dbDelta
|
|
AdminMenu.php — Registers wp-admin menu pages
|
|
RestRegistrar.php — Registers all REST routes under us-scheduler/v1
|
|
ShortcodeRegistrar.php — Registers [us_booking] and [us_student_login] shortcodes
|
|
templates/ — PHP view files included by controllers/shortcodes
|
|
assets/ — CSS and JS (vanilla JS, no build step)
|
|
tests/Unit/ — PHPUnit unit tests (PSR-4: Unsupervised\Schedular\Tests\)
|
|
Availability/ — Tests for src/Availability/
|
|
Booking/ — Tests for src/Booking/
|
|
Auth/ — Tests for src/Auth/
|
|
docs/features/ — One markdown file per feature describing data model, API, and test locations
|
|
```
|
|
|
|
**Code is organised package-by-domain** (Availability, Booking, Auth). Each domain package contains everything related to that domain: value objects, repositories, controllers, REST endpoints, and shortcode pages. Cross-cutting wiring classes (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) live directly under `src/`.
|
|
|
|
### Data Storage
|
|
Two custom database tables (created via `dbDelta` on activation):
|
|
- `{prefix}us_availability` — instructor availability windows
|
|
- `{prefix}us_lessons` — booked lessons
|
|
|
|
All database access goes through repository classes within their domain package. No direct `$wpdb` calls outside repositories.
|
|
|
|
### Key Classes
|
|
|
|
| Class | Responsibility |
|
|
|---|---|
|
|
| `Plugin` | Wires all components together on `plugins_loaded` |
|
|
| `Installer` | Creates DB tables and roles on activation |
|
|
| `Schema` | CREATE TABLE SQL strings for dbDelta |
|
|
| `AdminMenu` | Registers wp-admin menu pages |
|
|
| `RestRegistrar` | Registers all REST routes under `us-scheduler/v1` |
|
|
| `ShortcodeRegistrar` | Registers `[us_booking]` and `[us_student_login]` shortcodes |
|
|
| `Auth\RoleManager` | Registers `us_instructor` and `us_student` roles with custom caps |
|
|
| `Auth\LoginPage` | Renders front-end student login form |
|
|
| `Availability\AvailabilitySlot` | Immutable value object for a slot row |
|
|
| `Availability\AvailabilityRepository` | CRUD for availability slots |
|
|
| `Availability\AvailabilityController` | Instructor availability management page |
|
|
| `Availability\AvailabilityEndpoint` | REST handlers for availability CRUD |
|
|
| `Booking\Lesson` | Immutable value object for a lesson row |
|
|
| `Booking\BookingRepository` | CRUD for lesson bookings |
|
|
| `Booking\BookingEndpoint` | REST handlers for booking and status updates |
|
|
| `Booking\BookingPage` | Renders student booking UI shell (JS takes over) |
|
|
| `Booking\LessonController` | Admin and instructor lesson list pages |
|
|
|
|
### REST API Namespace
|
|
All endpoints live under `/wp-json/us-scheduler/v1/`. Permissions are enforced via `permission_callback` using capability checks (`manage_availability`, `book_lesson`), never role name checks.
|
|
|
|
### Testing Approach
|
|
Tests use [Brain\Monkey](https://brain-wp.github.io/BrainMonkey/) to stub WordPress functions without a full WP installation, and Mockery to mock `$wpdb` and other dependencies.
|
|
|
|
All test classes extend `tests/Unit/TestCase.php`, which handles `Monkey\setUp()` / `Monkey\tearDown()` and stubs all WP translation/escape functions automatically.
|
|
|
|
**Brain\Monkey API notes:**
|
|
- `Functions\when('fn')->alias(fn() => ...)` — stub with a closure (NOT `returnUsing()`)
|
|
- `Functions\when('fn')->justReturn($val)` — stub returning a fixed value
|
|
- `Functions\expect('fn')->once()->with(...)` — assert call count and arguments
|
|
- Use `Functions\when()` (not `Functions\expect()`) when you need argument-routing (e.g. `get_role` returning different values per argument) to avoid chaining ambiguity
|
|
- Mockery matchers (e.g. `\Mockery::type()`) inside plain PHP arrays do not work with `with()` — use `\Mockery::on(fn($arr) => ...)` or `\Mockery::any()` instead
|
|
- When mocking `$wpdb`, set `$mock->prefix = 'wp_'` explicitly — it is a public property, not a method
|
|
|
|
### Adding a Feature
|
|
1. Write the feature doc in `docs/features/<feature-name>.md` (data model, API, classes, test paths).
|
|
2. Create a domain package under `src/<Domain>/` containing all classes for that feature.
|
|
3. Add template(s) under `templates/` if needed.
|
|
4. Write unit tests under `tests/Unit/<Domain>/` mirroring the `src/<Domain>/` structure.
|
|
5. Run `composer test` — all tests must pass before the feature is complete.
|
|
|
|
### CI
|
|
Gitea Actions (`.gitea/workflows/ci.yml`) runs on every push and pull request:
|
|
- **lint** — PHPCS WordPress coding standards
|
|
- **static-analysis** — PHPStan level 6
|
|
- **test** — PHPUnit on PHP 8.1, 8.2, 8.3
|
|
- **no-debug** — rejects commits with `var_dump`, `error_log`, etc. in `src/`
|