diff --git a/src/controllers/API.php b/src/controllers/API.php index d5ea393..84f387f 100644 --- a/src/controllers/API.php +++ b/src/controllers/API.php @@ -21,8 +21,12 @@ class API public function __construct() { + // Set the Content-Type header to application/json header("Content-Type:application/json"); + // Allow access from any origin (CORS) + header('Access-Control-Allow-Origin: *'); + $this->resource = Utility::splitURL()[2] ?? ""; } @@ -32,32 +36,83 @@ public function __construct() */ private function validateURLFormat(): bool { - return preg_match("/^api\/v1/", $_GET["url"]) > 0; + return preg_match("/^api\/v1/", Utility::getURL()) > 0; + } + + + /** + * Returns the name of function responsible for handling the current request, as defined by the $routes variable. + * @param string $controllerName class name of controller + * @return string|null + */ + private function getHandler(string $controllerName): ?string + { + $all_routes = $controllerName::$routes; + + // check if there are handlers defined for current request method + $my_routes = $all_routes[$_SERVER['REQUEST_METHOD']] ?? ""; + if (empty($my_routes)) { + return null; + } + + foreach ($my_routes as $route => $handler) { + $pattern = str_replace('/', '\/', $route); // Convert to regex pattern + $pattern = preg_replace( + '/\{([a-zA-Z0-9_]+)\}/', + '(?P<$1>[^\/]+)', + $pattern + ); // Replace placeholders with regex capture groups + $pattern = '/^' . $pattern . '$/'; + + if (preg_match($pattern, '/' . Utility::getURL(), $matches)) { + return $handler; + } + } + return null; } public function index(): void { if (!$this->validateURLFormat()) { http_response_code(400); - die(); + return; } - // call appropriate controller to handle resource + // check if there is a controller to handle resource $controllerClassName = 'Steamy\\Controller\\API\\' . ucfirst($this->resource); + if (!class_exists($controllerClassName)) { + // no controller available + http_response_code(404); + echo 'Invalid resource: ' . $this->resource; // comment this line for production + return; + } + + // determine which function to call in the controller to handle route + $functionName = $this->getHandler($controllerClassName); + if ($functionName === null) { + // Controller does not have any method defined for route + http_response_code(404); + echo "Request has not been defined in \$routes for " . $controllerClassName; + return; + } + + $controller = new $controllerClassName(); + + if (!method_exists($controller, $functionName)) { + // handle function not found in controller + http_response_code(500); + echo $controllerClassName . ' does not have a public method ' . $functionName; + return; + } + + // call function in controller for handling request try { - if (class_exists($controllerClassName)) { - (new $controllerClassName())->index(); - } else { - http_response_code(404); - die(); - } + call_user_func(array($controller, $functionName)); } catch (Exception $e) { http_response_code(500); // Uncomment line below only when testing API echo $e->getMessage(); - - die(); } } } diff --git a/src/controllers/api/Districts.php b/src/controllers/api/Districts.php index 7d3ed3a..ea0f610 100644 --- a/src/controllers/api/Districts.php +++ b/src/controllers/api/Districts.php @@ -12,10 +12,17 @@ class Districts { use Model; + public static array $routes = [ + 'GET' => [ + '/api/v1/districts' => 'getAllDistricts', + '/api/v1/districts/{id}' => 'getDistrictById', + ] + ]; + /** * Get the list of all districts available. */ - private function getAllDistricts(): void + public function getAllDistricts(): void { // Retrieve all districts from the database $allDistricts = District::getAll(); @@ -36,7 +43,7 @@ private function getAllDistricts(): void /** * Get the details of a specific district by its ID. */ - private function getDistrictById(): void + public function getDistrictById(): void { $districtId = (int)Utility::splitURL()[3]; @@ -57,55 +64,4 @@ private function getDistrictById(): void 'name' => $district->getName() ]); } - - private function getHandler($routes): ?string - { - foreach ($routes[$_SERVER['REQUEST_METHOD']] as $route => $handler) { - $pattern = str_replace('/', '\/', $route); // Convert to regex pattern - $pattern = preg_replace( - '/\{([a-zA-Z0-9_]+)\}/', - '(?P<$1>[^\/]+)', - $pattern - ); // Replace placeholders with regex capture groups - $pattern = '/^' . $pattern . '$/'; - - if (preg_match($pattern, '/' . Utility::getURL(), $matches)) { - return $handler; - } - } - return null; - } - - /** - * Main entry point for the Districts API. - */ - public function index(): void - { - $routes = [ - 'GET' => [ - '/api/v1/districts' => 'getAllDistricts', - '/api/v1/districts/{id}' => 'getDistrictById', - ] - ]; - - // Handle the request - $handler = $this->getHandler($routes); - - if ($handler !== null) { - $functionName = $handler; - if (method_exists($this, $functionName)) { - call_user_func(array($this, $functionName)); - } else { - // Handle function not found - http_response_code(404); - echo "Function Not Found"; - die(); - } - } else { - // Handle route not found - http_response_code(404); - echo "Route Not Found"; - die(); - } - } } diff --git a/src/controllers/api/Products.php b/src/controllers/api/Products.php index 978005d..dc87b10 100644 --- a/src/controllers/api/Products.php +++ b/src/controllers/api/Products.php @@ -12,10 +12,27 @@ class Products { use Model; + public static array $routes = [ + 'GET' => [ + '/api/v1/products' => 'getAllProducts', + '/api/v1/products/categories' => 'getProductCategories', + '/api/v1/products/{id}' => 'getProductById', + ], + 'POST' => [ + '/api/v1/products' => 'createProduct', + ], + 'PUT' => [ + '/api/v1/products/{id}' => 'updateProduct', + ], + 'DELETE' => [ + '/api/v1/products/{id}' => 'deleteProduct', + ] + ]; + /** * Get the list of all products available in the store. */ - private function getAllProducts(): void + public function getAllProducts(): void { // Retrieve all products from the database $allProducts = Product::getAll(); @@ -33,7 +50,7 @@ private function getAllProducts(): void /** * Get the details of a specific product by its ID. */ - private function getProductById(): void + public function getProductById(): void { $productId = (int)Utility::splitURL()[3]; @@ -55,7 +72,7 @@ private function getProductById(): void /** * Get the list of product categories. */ - private function getProductCategories(): void + public function getProductCategories(): void { // Retrieve all product categories from the database $categories = Product::getCategories(); @@ -67,7 +84,7 @@ private function getProductCategories(): void /** * Create a new product entry in the database. */ - private function createProduct(): void + public function createProduct(): void { // Retrieve POST data $postData = $_POST; @@ -126,7 +143,7 @@ private function createProduct(): void /** * Delete a product with the specified ID. */ - private function deleteProduct(): void + public function deleteProduct(): void { $productId = (int)Utility::splitURL()[3]; @@ -155,7 +172,7 @@ private function deleteProduct(): void /** * Update the details of a product with the specified ID. */ - private function updateProduct(): void + public function updateProduct(): void { $productId = (int)Utility::splitURL()[3]; @@ -194,65 +211,4 @@ private function updateProduct(): void echo json_encode(['error' => 'Failed to update product']); } } - - private function getHandler($routes): ?string - { - foreach ($routes[$_SERVER['REQUEST_METHOD']] as $route => $handler) { - $pattern = str_replace('/', '\/', $route); // Convert to regex pattern - $pattern = preg_replace( - '/\{([a-zA-Z0-9_]+)\}/', - '(?P<$1>[^\/]+)', - $pattern - ); // Replace placeholders with regex capture groups - $pattern = '/^' . $pattern . '$/'; - - if (preg_match($pattern, '/' . Utility::getURL(), $matches)) { - return $handler; - } - } - return null; - } - - /** - * Main entry point for the Products API. - */ - public function index(): void - { - $routes = [ - 'GET' => [ - '/api/v1/products' => 'getAllProducts', - '/api/v1/products/categories' => 'getProductCategories', - '/api/v1/products/{id}' => 'getProductById', - ], - 'POST' => [ - '/api/v1/products' => 'createProduct', - ], - 'PUT' => [ - '/api/v1/products/{id}' => 'updateProduct', - ], - 'DELETE' => [ - '/api/v1/products/{id}' => 'deleteProduct', - ] - ]; - - // Handle the request - $handler = $this->getHandler($routes); - - if ($handler !== null) { - $functionName = $handler; - if (method_exists($this, $functionName)) { - call_user_func(array($this, $functionName)); - } else { - // Handle function not found - http_response_code(404); - echo "Function Not Found"; - die(); - } - } else { - // Handle route not found - http_response_code(404); - echo "Route Not Found"; - die(); - } - } } diff --git a/src/controllers/api/Reviews.php b/src/controllers/api/Reviews.php index 015c0aa..bfbcc9f 100644 --- a/src/controllers/api/Reviews.php +++ b/src/controllers/api/Reviews.php @@ -6,14 +6,31 @@ use Steamy\Core\Utility; use Steamy\Model\Review; +use \Steamy\Model\Product as ProductModel; class Reviews { + public static array $routes = [ + 'GET' => [ + '/api/v1/reviews' => 'getAllReviews', + '/api/v1/products/{id}/reviews' => 'getAllReviewsForProduct', + ], + 'POST' => [ + '/api/v1/reviews' => 'createReview', + ], + 'PUT' => [ + '/api/v1/reviews/{id}' => 'updateReview', + ], + 'DELETE' => [ + '/api/v1/reviews/{id}' => 'deleteReview', + ] + ]; + /** * Get the list of all reviews available. */ - private function getAllReviews(): void + public function getAllReviews(): void { // Retrieve all reviews from the database $allReviews = Review::getAll(); @@ -31,21 +48,21 @@ private function getAllReviews(): void /** * Get all reviews for a particular product by its ID. */ - private function getAllReviewsForProduct(): void + public function getAllReviewsForProduct(): void { $productId = (int)Utility::splitURL()[4]; - // Retrieve all reviews for the specified product from the database - $reviews = Review::getAllReviewsForProduct($productId); - - // Check if Review exists - if ($reviews === null) { - // Review not found, return 404 + // Check if product exists + if (ProductModel::getById($productId) === null) { + // product not found, return 404 http_response_code(404); - echo json_encode(['error' => 'Review not found']); + echo json_encode(['error' => 'Product not found']); return; } + // Retrieve all reviews for the specified product from the database + $reviews = Review::getAllReviewsForProduct($productId); + // Return JSON response echo json_encode($reviews); } @@ -53,13 +70,13 @@ private function getAllReviewsForProduct(): void /** * Create a new review for a product. */ - private function createReview(): void + public function createReview(): void { // Retrieve POST data $postData = $_POST; // TODO: Implement validation for required fields and data types - // Check if required fields are present + // Check if required fields are present $requiredFields = [ 'product_id', 'client_id', @@ -105,7 +122,7 @@ private function createReview(): void /** * Update the details of a review with the specified ID. */ - private function updateReview(): void + public function updateReview(): void { $reviewId = (int)Utility::splitURL()[3]; @@ -148,7 +165,7 @@ private function updateReview(): void /** * Delete a review with the specified ID. */ - private function deleteReview(): void + public function deleteReview(): void { $reviewId = (int)Utility::splitURL()[3]; @@ -173,64 +190,4 @@ private function deleteReview(): void echo json_encode(['error' => 'Failed to delete review']); } } - - private function getHandler($routes): ?string - { - foreach ($routes[$_SERVER['REQUEST_METHOD']] as $route => $handler) { - $pattern = str_replace('/', '\/', $route); // Convert to regex pattern - $pattern = preg_replace( - '/\{([a-zA-Z0-9_]+)\}/', - '(?P<$1>[^\/]+)', - $pattern - ); // Replace placeholders with regex capture groups - $pattern = '/^' . $pattern . '$/'; - - if (preg_match($pattern, '/' . Utility::getURL(), $matches)) { - return $handler; - } - } - return null; - } - - /** - * Main entry point for the Reviews API. - */ - public function index(): void - { - $routes = [ - 'GET' => [ - '/api/v1/reviews' => 'getAllReviews', - '/api/v1/products/{id}/reviews' => 'getAllReviewsForProduct', - ], - 'POST' => [ - '/api/v1/reviews' => 'createReview', - ], - 'PUT' => [ - '/api/v1/reviews/{id}' => 'updateReview', - ], - 'DELETE' => [ - '/api/v1/reviews/{id}' => 'deleteReview', - ] - ]; - - // Handle the request - $handler = $this->getHandler($routes); - - if ($handler !== null) { - $functionName = $handler; - if (method_exists($this, $functionName)) { - call_user_func([$this, $functionName]); - } else { - // Handle function not found - http_response_code(404); - echo "Function Not Found"; - die(); - } - } else { - // Handle route not found - http_response_code(404); - echo "Route Not Found"; - die(); - } - } } diff --git a/src/controllers/api/Sessions.php b/src/controllers/api/Sessions.php index 48d559c..537c7c3 100644 --- a/src/controllers/api/Sessions.php +++ b/src/controllers/api/Sessions.php @@ -11,7 +11,14 @@ */ class Sessions { - private function handleLogin(): void + + public static array $routes = [ + 'POST' => [ + '/api/v1/products' => 'handleLogin', + ] + ]; + + public function handleLogin(): void { $email = trim($_POST['email'] ?? ""); $password = trim($_POST['password'] ?? ""); @@ -39,16 +46,4 @@ private function handleLogin(): void $_SESSION['admin_email'] = $email; session_regenerate_id(); } - - public function index(): void - { - switch ($_SERVER['REQUEST_METHOD']) { - case 'POST': - $this->handleLogin(); - break; - default: - http_response_code(400); - die(); - } - } } \ No newline at end of file