Files
jachty.pkmp.com.pl/.paul/codebase/architecture.md
2026-05-06 00:18:37 +02:00

131 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture
**Analysis Date:** 2026-05-05
## Pattern Overview
WordPress plugin with layered architecture: CPT-backed data model, REST API surface, Singleton orchestrators, static utility classes.
- Entry point bootstraps a single Singleton (`Yacht_Booking`) via `plugins_loaded`
- All public-facing state changes go through REST API (`/wp-json/yacht-booking/v1/`)
- Admin state changes use WordPress PRG pattern (form POST → redirect)
- External calendar sync handled by separate Integration namespaces
## Class Inventory
| Class | File | Pattern | Responsibility |
|-------|------|---------|---------------|
| `Yacht_Booking` | `includes/class-yacht-booking.php` | Singleton | Master orchestrator — wires all subsystems |
| `Yacht` | `includes/class-yacht.php` | Static utility | `yacht` CPT registration + meta accessors |
| `Booking` | `includes/class-booking.php` | Static utility | `yacht_booking` CPT + `create()` + status |
| `Inquiry` | `includes/class-inquiry.php` | Static utility | `yacht_inquiry` CPT + `send_emails()` |
| `Availability` | `includes/class-availability.php` | Static utility | All `wp_yacht_availability` table operations |
| `Rest_Controller` | `api/class-rest-controller.php` | Extends `WP_REST_Controller` | 7 REST endpoints, booking orchestration |
| `Settings` | `includes/class-settings.php` | Static utility | Typed `wp_options` accessors + formatting helpers |
| `Installer` | `includes/class-installer.php` | Plain class | DB table creation + default options on activation |
| `Email_Templates` | `includes/class-email-templates.php` | Static utility | Template storage, compilation, tag replacement |
| `Admin` | `admin/class-admin.php` | Singleton (admin-only) | Menu, form processing, CSV export, customer emails |
| `Booking_List_Table` | `admin/class-booking-list-table.php` | Extends `WP_List_Table` | Bookings admin table |
| `Yacht_List_Table` | `admin/class-yacht-list-table.php` | Extends `WP_List_Table` | Yachts admin table |
| `Inquiry_List_Table` | `admin/class-inquiry-list-table.php` | Extends `WP_List_Table` | Inquiries admin table |
| `Sync_Controller` | `integrations/google-calendar/class-sync-controller.php` | Singleton | GCal cron sync orchestration |
| `GCal_Service` | `integrations/google-calendar/class-gcal-service.php` | Plain class | Google Calendar API calls |
| `OAuth_Handler` | `integrations/google-calendar/class-oauth-handler.php` | Plain class | OAuth 2.0 token storage + refresh |
| `ICal_Feed` | `integrations/ical/class-ical-feed.php` | Static/plain | iCal feed generation + rewrite rule |
| `ICal_Import` | `integrations/ical/class-ical-import.php` | Static/plain | External iCal URL import + cron |
| `Shortcode` | `frontend/class-shortcode.php` | Singleton | `[yacht_calendar]` shortcode |
| `Calendar_Widget` | `frontend/class-calendar-widget.php` | Extends Elementor `Widget_Base` | Elementor `yacht-calendar` widget |
## Dependency Graph
```
yacht-booking-system.php
└── Yacht_Booking (Singleton)
├── Yacht (static)
├── Booking (static)
│ └── fires: yacht_booking_created
│ yacht_booking_status_changed
├── Availability (static)
├── Settings (static)
├── Email_Templates (static)
├── Rest_Controller
│ ├── Yacht, Booking, Availability, Settings, Inquiry
│ ├── Email_Templates
│ └── listens: yacht_booking_created → send_booking_notification (admin email)
├── Admin (Singleton, admin-only)
│ ├── Booking, Availability, Yacht, Settings, Email_Templates, Inquiry
│ ├── Booking_List_Table → Booking, Availability, Settings
│ ├── Yacht_List_Table, Inquiry_List_Table
│ └── listens: yacht_booking_status_changed → send_customer_notification
├── Sync_Controller (Singleton)
│ ├── GCal_Service → OAuth_Handler
│ └── writes: Availability::mark_as_blocked
├── ICal_Import → Availability::mark_as_blocked
└── ICal_Feed (feed output)
```
## WP Hook Map
| Hook | Type | Registered by | Handler |
|------|------|---------------|---------|
| `plugins_loaded` (p10) | action | bootstrap | `yacht_booking_init()``Yacht_Booking::get_instance()` |
| `init` (p10) | action | Yacht_Booking | `register_post_types()` |
| `init` (p15) | action | Yacht_Booking | `register_shortcodes()` |
| `init` | action | bootstrap | `yacht_booking_load_textdomain()` |
| `wp_enqueue_scripts` | action | Yacht_Booking | `enqueue_frontend_assets()` (conditional) |
| `admin_enqueue_scripts` | action | Yacht_Booking | `enqueue_admin_assets()` (conditional) |
| `rest_api_init` | action | Yacht_Booking | `register_rest_routes()` |
| `elementor/widgets/register` | action | Yacht_Booking | `register_elementor_widgets()` |
| `admin_init` | action | Yacht_Booking | `add_custom_capabilities()` |
| `admin_menu` (p9) | action | Admin | `register_admin_menu()` |
| `admin_init` | action | Admin | `process_bulk_actions()`, `process_booking_actions()`, `process_yacht_save()`, `process_settings_save()`, `process_export_download()` |
| `admin_notices` | action | Admin | `display_admin_notices()` |
| `yacht_booking_created` | custom action | Rest_Controller | `send_booking_notification()` (admin email) |
| `yacht_booking_status_changed` | custom action | Admin | `send_customer_notification()` |
| `before_delete_post` | action | Sync_Controller | `on_booking_deleted()` (GCal event delete) |
| `wp_ajax_yacht_booking_manual_sync` | action | Sync_Controller | `ajax_manual_sync()` |
| `yacht_booking_sync_to_gcal` | cron action | Sync_Controller | `sync_booking_to_gcal()` |
| `yacht_booking_pull_from_gcal` | cron action (hourly) | Sync_Controller | `pull_from_gcal()` |
| `yacht_booking_ical_import` | cron action (hourly) | ICal_Import | `run_import()` |
| `init` | action | ICal_Feed | `add_rewrite_rules()` |
| `template_redirect` | action | ICal_Feed | `handle_feed_request()` |
## Key Data Flows
### Booking Creation (Frontend → REST → DB)
1. User picks dates on FullCalendar → `GET /availability/{yacht_id}``Availability::get_availability_calendar()`
2. User submits form → `POST /bookings` with `X-WP-Nonce` header
3. `Rest_Controller::create_booking()`: nonce check → `is_booking_enabled()``Availability::is_available()`
4. Price: `count_days() × get_price_per_day()`
5. `Booking::create()` → inserts CPT post + meta → fires `yacht_booking_created`
6. `Availability::mark_as_booked()` → inserts rows in `wp_yacht_availability`
7. Admin email sent via `Email_Templates`; JSON response to frontend
### Admin Booking Management
1. Bookings list → `Booking_List_Table::prepare_items()` (WP_Query)
2. Row action URLs: nonce-protected GET `?action=approve_booking_{id}`
3. `Admin::process_booking_actions()``Booking::update_status()` → fires `yacht_booking_status_changed`
4. `Admin::send_customer_notification()` → customer email
5. On cancel/delete: `Availability::clear_booking_availability($booking_id)` deletes rows by booking_id
6. PRG redirect back to list
### GCal Sync
1. Hourly WP Cron: `pull_from_gcal()``GCal_Service` → Google API → `Availability::mark_as_blocked()`
2. On booking create: scheduled single cron `yacht_booking_sync_to_gcal` → push to GCal
3. OAuth token refresh: `OAuth_Handler::get_access_token()` → auto-refresh if expired
## Error Handling Strategy
- REST endpoints return `WP_Error` with HTTP status codes
- Admin operations: `wp_die()` on capability failure; `?error=1` redirect on soft errors
- `Booking::create()` returns `false` on failure — caller checks
- GCal API: `is_wp_error()` check only; HTTP 4xx/5xx responses silently ignored
- No exceptions used anywhere
## Custom Capabilities
Added once to `administrator` role via `Yacht_Booking::add_custom_capabilities()`:
- `yacht_booking_manage_yachts`
- `yacht_booking_manage_bookings`
- `yacht_booking_manage_settings`