- Custom DB tables for availability slots and lesson bookings - Instructor (wp-admin) and student (front-end) roles with custom capabilities - REST API under us-scheduler/v1 for availability CRUD and booking - [us_booking] and [us_student_login] shortcodes for student front end - PHPUnit + Brain\Monkey unit test suite (29 tests) - Gitea Actions CI: lint, PHPStan, tests on PHP 8.1/8.2/8.3, no-debug check - Feature docs under docs/features/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
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/Data/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\)
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\)
docs/features/— One markdown file per feature describing data model, API, and test locations
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 in src/Data/. 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 |
Roles\RoleManager |
Registers us_instructor and us_student roles with custom caps |
Data\AvailabilityRepository |
CRUD for availability slots |
Data\BookingRepository |
CRUD for lesson bookings |
Model\AvailabilitySlot |
Immutable value object for a slot row |
Model\Lesson |
Immutable value object for a lesson row |
Admin\AdminMenu |
Registers wp-admin menu pages |
Admin\AvailabilityController |
Instructor availability management page |
Admin\LessonController |
Admin and instructor lesson list pages |
Api\RestRegistrar |
Registers all REST routes under us-scheduler/v1 |
Api\AvailabilityEndpoint |
REST handlers for availability CRUD |
Api\BookingEndpoint |
REST handlers for booking and status updates |
Frontend\ShortcodeRegistrar |
Registers [us_booking] and [us_student_login] shortcodes |
Frontend\BookingPage |
Renders student booking UI shell (JS takes over) |
Frontend\LoginPage |
Renders front-end student login form |
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 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 (NOTreturnUsing())Functions\when('fn')->justReturn($val)— stub returning a fixed valueFunctions\expect('fn')->once()->with(...)— assert call count and arguments- Use
Functions\when()(notFunctions\expect()) when you need argument-routing (e.g.get_rolereturning different values per argument) to avoid chaining ambiguity - Mockery matchers (e.g.
\Mockery::type()) inside plain PHP arrays do not work withwith()— 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
- Write the feature doc in
docs/features/<feature-name>.md(data model, API, classes, test paths). - Implement the classes under
src/. - Add template(s) under
templates/if needed. - Write unit tests under
tests/Unit/mirroring thesrc/directory structure. - 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. insrc/