Implement Phase 10: REST API Endpoints (v0.10.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m10s
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>
This commit is contained in:
323
src/Api/Controllers/BuildingsController.php
Normal file
323
src/Api/Controllers/BuildingsController.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?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 ) ),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user