All checks were successful
Create Release Package / build-release (push) Successful in 1m10s
- Add complete REST API infrastructure under src/Api/ - ResponseFormatter for standardized responses - RateLimiter with tiered limits (public 60/min, availability 30/min, booking 10/min, admin 120/min) - AbstractController base class with common functionality - BuildingsController: list, get, rooms endpoints - RoomsController: list, get, availability, calendar, search endpoints - BookingsController: CRUD + confirm/check-in/check-out status transitions - GuestsController: list, get, search, booking history (admin only) - ServicesController: list, get, calculate endpoints - PricingController: calculate, seasons endpoints - API settings tab with enable/disable toggles - Comprehensive API documentation in README Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
324 lines
9.3 KiB
PHP
324 lines
9.3 KiB
PHP
<?php
|
|
/**
|
|
* Buildings REST Controller
|
|
*
|
|
* Handles REST API endpoints for buildings.
|
|
*
|
|
* @package Magdev\WpBnb\Api\Controllers
|
|
*/
|
|
|
|
declare( strict_types=1 );
|
|
|
|
namespace Magdev\WpBnb\Api\Controllers;
|
|
|
|
use Magdev\WpBnb\PostTypes\Building;
|
|
use Magdev\WpBnb\PostTypes\Room;
|
|
use WP_REST_Request;
|
|
use WP_REST_Response;
|
|
use WP_REST_Server;
|
|
use WP_Error;
|
|
|
|
/**
|
|
* Buildings Controller class.
|
|
*/
|
|
final class BuildingsController extends AbstractController {
|
|
|
|
/**
|
|
* Route base.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $rest_base = 'buildings';
|
|
|
|
/**
|
|
* Register routes.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register_routes(): void {
|
|
// GET /buildings - List all buildings.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base,
|
|
array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_items' ),
|
|
'permission_callback' => array( $this, 'public_permission' ),
|
|
'args' => $this->get_collection_params(),
|
|
),
|
|
)
|
|
);
|
|
|
|
// GET /buildings/{id} - Get single building.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/(?P<id>[\d]+)',
|
|
array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_item' ),
|
|
'permission_callback' => array( $this, 'public_permission' ),
|
|
'args' => array(
|
|
'id' => array(
|
|
'description' => __( 'Building ID.', 'wp-bnb' ),
|
|
'type' => 'integer',
|
|
'required' => true,
|
|
'sanitize_callback' => 'absint',
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
// GET /buildings/{id}/rooms - Get rooms in building.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/(?P<id>[\d]+)/rooms',
|
|
array(
|
|
array(
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => array( $this, 'get_building_rooms' ),
|
|
'permission_callback' => array( $this, 'public_permission' ),
|
|
'args' => array(
|
|
'id' => array(
|
|
'description' => __( 'Building ID.', 'wp-bnb' ),
|
|
'type' => 'integer',
|
|
'required' => true,
|
|
'sanitize_callback' => 'absint',
|
|
),
|
|
'status' => array(
|
|
'description' => __( 'Filter by room status.', 'wp-bnb' ),
|
|
'type' => 'string',
|
|
'enum' => array( 'available', 'occupied', 'maintenance', 'blocked' ),
|
|
'sanitize_callback' => 'sanitize_text_field',
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get collection of buildings.
|
|
*
|
|
* @param WP_REST_Request $request Current request.
|
|
* @return WP_REST_Response|WP_Error Response object or error.
|
|
*/
|
|
public function get_items( $request ) {
|
|
// Check rate limit.
|
|
$rate_limit_error = $this->check_rate_limit( $request );
|
|
if ( $rate_limit_error ) {
|
|
return $rate_limit_error;
|
|
}
|
|
|
|
$pagination = $this->get_pagination_params( $request );
|
|
$sorting = $this->get_sorting_params( $request, array( 'title', 'date' ), 'title' );
|
|
|
|
$args = array(
|
|
'post_type' => Building::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => $pagination['per_page'],
|
|
'offset' => $pagination['offset'],
|
|
'orderby' => $sorting['orderby'],
|
|
'order' => $sorting['order'],
|
|
);
|
|
|
|
// Search filter.
|
|
$search = $request->get_param( 'search' );
|
|
if ( $search ) {
|
|
$args['s'] = $search;
|
|
}
|
|
|
|
$query = new \WP_Query( $args );
|
|
$items = array();
|
|
|
|
foreach ( $query->posts as $post ) {
|
|
$items[] = $this->prepare_building_response( $post );
|
|
}
|
|
|
|
$response = $this->formatter->collection(
|
|
$items,
|
|
$query->found_posts,
|
|
$pagination['page'],
|
|
$pagination['per_page']
|
|
);
|
|
|
|
return $this->add_rate_limit_headers( $response, $request );
|
|
}
|
|
|
|
/**
|
|
* Get single building.
|
|
*
|
|
* @param WP_REST_Request $request Current request.
|
|
* @return WP_REST_Response|WP_Error Response object or error.
|
|
*/
|
|
public function get_item( $request ) {
|
|
// Check rate limit.
|
|
$rate_limit_error = $this->check_rate_limit( $request );
|
|
if ( $rate_limit_error ) {
|
|
return $rate_limit_error;
|
|
}
|
|
|
|
$id = $request->get_param( 'id' );
|
|
$post = get_post( $id );
|
|
|
|
if ( ! $post || Building::POST_TYPE !== $post->post_type || 'publish' !== $post->post_status ) {
|
|
return $this->formatter->not_found( __( 'Building', 'wp-bnb' ) );
|
|
}
|
|
|
|
$data = $this->prepare_building_response( $post, true );
|
|
$response = $this->formatter->success( $data );
|
|
|
|
return $this->add_rate_limit_headers( $response, $request );
|
|
}
|
|
|
|
/**
|
|
* Get rooms in a building.
|
|
*
|
|
* @param WP_REST_Request $request Current request.
|
|
* @return WP_REST_Response|WP_Error Response object or error.
|
|
*/
|
|
public function get_building_rooms( $request ) {
|
|
// Check rate limit.
|
|
$rate_limit_error = $this->check_rate_limit( $request );
|
|
if ( $rate_limit_error ) {
|
|
return $rate_limit_error;
|
|
}
|
|
|
|
$building_id = $request->get_param( 'id' );
|
|
$building = get_post( $building_id );
|
|
|
|
if ( ! $building || Building::POST_TYPE !== $building->post_type || 'publish' !== $building->post_status ) {
|
|
return $this->formatter->not_found( __( 'Building', 'wp-bnb' ) );
|
|
}
|
|
|
|
$args = array(
|
|
'post_type' => Room::POST_TYPE,
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'meta_query' => array(
|
|
array(
|
|
'key' => '_bnb_room_building_id',
|
|
'value' => $building_id,
|
|
),
|
|
),
|
|
'orderby' => 'meta_value',
|
|
'meta_key' => '_bnb_room_room_number',
|
|
'order' => 'ASC',
|
|
);
|
|
|
|
// Filter by status.
|
|
$status = $request->get_param( 'status' );
|
|
if ( $status ) {
|
|
$args['meta_query'][] = array(
|
|
'key' => '_bnb_room_status',
|
|
'value' => $status,
|
|
);
|
|
}
|
|
|
|
$rooms = get_posts( $args );
|
|
$items = array();
|
|
|
|
foreach ( $rooms as $room ) {
|
|
$items[] = $this->prepare_room_summary( $room );
|
|
}
|
|
|
|
$response = $this->formatter->success( $items );
|
|
|
|
return $this->add_rate_limit_headers( $response, $request );
|
|
}
|
|
|
|
/**
|
|
* Prepare building data for response.
|
|
*
|
|
* @param \WP_Post $post Building post object.
|
|
* @param bool $full Include full details.
|
|
* @return array Building data.
|
|
*/
|
|
private function prepare_building_response( \WP_Post $post, bool $full = false ): array {
|
|
$data = $this->format_post_base( $post );
|
|
|
|
// Featured image.
|
|
$data['featured_image'] = $this->format_featured_image( $post->ID );
|
|
$data['permalink'] = get_permalink( $post->ID );
|
|
|
|
// Address.
|
|
$data['address'] = array(
|
|
'street' => get_post_meta( $post->ID, '_bnb_building_street', true ),
|
|
'street2' => get_post_meta( $post->ID, '_bnb_building_street2', true ),
|
|
'city' => get_post_meta( $post->ID, '_bnb_building_city', true ),
|
|
'state' => get_post_meta( $post->ID, '_bnb_building_state', true ),
|
|
'postal_code' => get_post_meta( $post->ID, '_bnb_building_zip', true ),
|
|
'country' => get_post_meta( $post->ID, '_bnb_building_country', true ),
|
|
);
|
|
|
|
// Contact.
|
|
$data['contact'] = array(
|
|
'phone' => get_post_meta( $post->ID, '_bnb_building_phone', true ),
|
|
'email' => get_post_meta( $post->ID, '_bnb_building_email', true ),
|
|
'website' => get_post_meta( $post->ID, '_bnb_building_website', true ),
|
|
);
|
|
|
|
// Details.
|
|
$data['details'] = array(
|
|
'rooms_count' => (int) get_post_meta( $post->ID, '_bnb_building_total_rooms', true ),
|
|
'floors' => (int) get_post_meta( $post->ID, '_bnb_building_floors', true ),
|
|
'year_built' => (int) get_post_meta( $post->ID, '_bnb_building_year_built', true ),
|
|
'check_in_time' => get_post_meta( $post->ID, '_bnb_building_check_in_time', true ) ?: '14:00',
|
|
'check_out_time' => get_post_meta( $post->ID, '_bnb_building_check_out_time', true ) ?: '11:00',
|
|
);
|
|
|
|
// Count actual rooms.
|
|
$actual_rooms = Room::get_rooms_for_building( $post->ID );
|
|
$data['details']['actual_rooms_count'] = count( $actual_rooms );
|
|
|
|
// Full address formatted.
|
|
if ( $full ) {
|
|
$data['address']['formatted'] = Building::get_formatted_address( $post->ID );
|
|
|
|
// Country name.
|
|
$countries = Building::get_countries();
|
|
$country_code = $data['address']['country'];
|
|
$data['address']['country_name'] = $countries[ $country_code ] ?? $country_code;
|
|
}
|
|
|
|
// Add HATEOAS links.
|
|
$data['_links'] = array(
|
|
'self' => array(
|
|
array( 'href' => rest_url( $this->namespace . '/buildings/' . $post->ID ) ),
|
|
),
|
|
'rooms' => array(
|
|
array( 'href' => rest_url( $this->namespace . '/buildings/' . $post->ID . '/rooms' ) ),
|
|
),
|
|
);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Prepare room summary for building rooms list.
|
|
*
|
|
* @param \WP_Post $room Room post object.
|
|
* @return array Room summary data.
|
|
*/
|
|
private function prepare_room_summary( \WP_Post $room ): array {
|
|
return array(
|
|
'id' => $room->ID,
|
|
'title' => get_the_title( $room ),
|
|
'slug' => $room->post_name,
|
|
'permalink' => get_permalink( $room->ID ),
|
|
'room_number' => get_post_meta( $room->ID, '_bnb_room_room_number', true ),
|
|
'floor' => (int) get_post_meta( $room->ID, '_bnb_room_floor', true ),
|
|
'capacity' => (int) get_post_meta( $room->ID, '_bnb_room_capacity', true ),
|
|
'status' => get_post_meta( $room->ID, '_bnb_room_status', true ) ?: 'available',
|
|
'thumbnail' => get_the_post_thumbnail_url( $room->ID, 'thumbnail' ) ?: null,
|
|
'_links' => array(
|
|
'self' => array(
|
|
array( 'href' => rest_url( $this->namespace . '/rooms/' . $room->ID ) ),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|