From fcefbfe64c070c683e0dd5c98eb6e0e0786c45ec Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Mon, 20 May 2024 21:14:41 +0400 Subject: [PATCH 1/5] add Review model table property, getAll, getAllReviewsForProduct, updateReview, and deleteReview methods --- src/controllers/api/Reviews.php | 236 ++++++++++++++++++++++++++++++++ src/models/Review.php | 70 +++++++++- 2 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/controllers/api/Reviews.php diff --git a/src/controllers/api/Reviews.php b/src/controllers/api/Reviews.php new file mode 100644 index 00000000..015c0aa4 --- /dev/null +++ b/src/controllers/api/Reviews.php @@ -0,0 +1,236 @@ +toArray(); + } + + // Return JSON response + echo json_encode($result); + } + + /** + * Get all reviews for a particular product by its ID. + */ + private 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 + http_response_code(404); + echo json_encode(['error' => 'Review not found']); + return; + } + + // Return JSON response + echo json_encode($reviews); + } + + /** + * Create a new review for a product. + */ + private function createReview(): void + { + // Retrieve POST data + $postData = $_POST; + + // TODO: Implement validation for required fields and data types + // Check if required fields are present + $requiredFields = [ + 'product_id', + 'client_id', + 'text', + 'rating', + ]; + + if (empty($postData)) { + http_response_code(400); + echo json_encode(['error' => "Missing fields: " . implode(', ', $requiredFields)]); + return; + } + + foreach ($requiredFields as $field) { + if (empty($postData[$field])) { + // Required field is missing, return 400 Bad Request + http_response_code(400); + echo json_encode(['error' => "Missing required field: $field"]); + return; + } + } + // Create a new Review object + $newReview = new Review( + null, // review_id will be auto-generated + (int)$postData['product_id'], + (int)$postData['client_id'], + $postData['text'], + (int)$postData['rating'] + ); + + // Save the new review to the database + if ($newReview->save()) { + // Review created successfully, return 201 Created + http_response_code(201); + echo json_encode(['message' => 'Review created successfully', 'review_id' => $newReview->getReviewID()]); + } else { + // Failed to create review, return 500 Internal Server Error + http_response_code(500); + echo json_encode(['error' => 'Failed to create review']); + } + } + + /** + * Update the details of a review with the specified ID. + */ + private function updateReview(): void + { + $reviewId = (int)Utility::splitURL()[3]; + + // Retrieve PUT request data + $putData = json_decode(file_get_contents("php://input"), true); + + // Check if PUT data is valid + if (empty($putData)) { + // Invalid JSON data + http_response_code(400); // Bad Request + echo json_encode(['error' => 'Invalid JSON data']); + return; + } + + // Retrieve existing review + $review = Review::getByID($reviewId); + + // Check if review exists + if ($review === null) { + // Review not found + http_response_code(404); // Not Found + echo json_encode(['error' => 'Review not found']); + return; + } + + // Update review in the database + $success = $review->updateReview($putData); + + if ($success) { + // Review updated successfully + http_response_code(200); // OK + echo json_encode(['message' => 'Review updated successfully']); + } else { + // Failed to update review + http_response_code(500); // Internal Server Error + echo json_encode(['error' => 'Failed to update review']); + } + } + + /** + * Delete a review with the specified ID. + */ + private function deleteReview(): void + { + $reviewId = (int)Utility::splitURL()[3]; + + // Retrieve the review by ID + $review = Review::getByID($reviewId); + + // Check if review exists + if ($review === null) { + // Review not found, return 404 + http_response_code(404); + echo json_encode(['error' => 'Review not found']); + return; + } + + // Attempt to delete the review + if ($review->deleteReview($reviewId)) { + // Review successfully deleted + http_response_code(204); // No Content + } else { + // Failed to delete the review + http_response_code(500); // Internal Server Error + 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/models/Review.php b/src/models/Review.php index fb74c640..0be20ff9 100644 --- a/src/models/Review.php +++ b/src/models/Review.php @@ -12,7 +12,7 @@ class Review { use Model; - + protected string $table = 'review'; private int $review_id; private int $product_id; @@ -88,6 +88,74 @@ public static function getByID(int $review_id): ?Review ); } + /** + * Retrieve all reviews from the database. + * + * @return array Array of Review objects representing all reviews in the database. + */ + public static function getAll(): array + { + // Prepare and execute SQL query to retrieve all reviews + $query = "SELECT * FROM review"; + $results = self::query($query); + + // Fetch all reviews as Review objects + $reviews = []; + foreach ($results as $result) { + $obj = new Review( + product_id: $result->product_id, + client_id: $result->client_id, + text: $result->text, + rating: $result->rating, + created_date: Utility::stringToDate($result->created_date) + ); + $obj->setReviewID($result->review_id); + $reviews[] = $obj; + } + + return $reviews; + } + + /** + * Retrieves all reviews for a particular product from the database. + * + * @param int $productId The ID of the product. + * @return array An array containing all reviews for the specified product. + */ + public static function getAllReviewsForProduct(int $productId): array + { + $query = "SELECT * FROM review WHERE product_id = :product_id"; + $params = ['product_id' => $productId]; + return self::query($query, $params); + } + + /** + * Updates review record in database but does not update the object itself. + * @param array $newReviewData Associative array indexed by attribute name. + * The values are the new review data. + * @return bool Success or not + */ + public function updateReview (array $newReviewData): bool + { + // remove review_id (if present) from user data + unset($newReviewData['review_id']); + + return $this->update($newReviewData, ['review_id' => $this->review_id], $this->table); + } + + /** + * Deletes a review with the specified ID from the database. + * + * @param int $reviewId The ID of the review to delete. + * @return bool True if the deletion was successful, false otherwise. + */ + public static function deleteReview(int $reviewId): bool + { + $query = "DELETE FROM review WHERE review_id = :review_id"; + $params = ['review_id' => $reviewId]; + return self::query($query, $params); + } + public function getReviewID(): int { return $this->review_id; From 9fb7fae97317467c4bd1fc626ec02da8571b65c8 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Tue, 21 May 2024 13:11:08 +0400 Subject: [PATCH 2/5] rewrite logic for the API to make errors clearer and to reduce code duplication (e.g. `getHandler` was being duplicated in all api controllers) --- src/controllers/API.php | 77 ++++++++++++++++++---- src/controllers/api/Districts.php | 62 +++--------------- src/controllers/api/Products.php | 90 +++++++------------------- src/controllers/api/Reviews.php | 103 +++++++++--------------------- src/controllers/api/Sessions.php | 21 +++--- 5 files changed, 136 insertions(+), 217 deletions(-) diff --git a/src/controllers/API.php b/src/controllers/API.php index d5ea393e..84f387f9 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 7d3ed3a4..ea0f6105 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 978005d5..dc87b101 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 015c0aa4..bfbcc9f3 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 48d559c4..537c7c34 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 From 274350da8da7330209bd0376aa3867a556317ff1 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Tue, 21 May 2024 19:42:49 +0400 Subject: [PATCH 3/5] add new API endpoint to retrieve all reviews for a product by ID and implemente corresponding controller logic in Products and Reviews controllers --- src/controllers/api/Products.php | 20 ++++++++++++++++++++ src/controllers/api/Reviews.php | 3 +-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/controllers/api/Products.php b/src/controllers/api/Products.php index dc87b101..c973359f 100644 --- a/src/controllers/api/Products.php +++ b/src/controllers/api/Products.php @@ -17,6 +17,7 @@ class Products '/api/v1/products' => 'getAllProducts', '/api/v1/products/categories' => 'getProductCategories', '/api/v1/products/{id}' => 'getProductById', + '/api/v1/products/{id}/reviews' => 'getAllReviewsForProduct', ], 'POST' => [ '/api/v1/products' => 'createProduct', @@ -211,4 +212,23 @@ public function updateProduct(): void echo json_encode(['error' => 'Failed to update product']); } } + + /** + * Get all reviews for a particular product by its ID. + */ + public function getAllReviewsForProduct(): void + { + // Get product ID from URL + $productId = (int)Utility::splitURL()[3]; + + // Instantiate the Reviews controller + $reviewsController = new Reviews(); + + // Call the method to get all reviews for the specified product + // Since the Reviews controller method expects the ID to be in the URL, we'll set it directly + $_SERVER['REQUEST_URI'] = "/api/v1/products/$productId/reviews"; + + // Call the method from the Reviews controller + $reviewsController->getAllReviewsForProduct(); + } } diff --git a/src/controllers/api/Reviews.php b/src/controllers/api/Reviews.php index bfbcc9f3..3b1608d6 100644 --- a/src/controllers/api/Reviews.php +++ b/src/controllers/api/Reviews.php @@ -50,8 +50,7 @@ public function getAllReviews(): void */ public function getAllReviewsForProduct(): void { - $productId = (int)Utility::splitURL()[4]; - + $productId = (int)Utility::splitURL()[3]; // Check if product exists if (ProductModel::getById($productId) === null) { // product not found, return 404 From 2d97d7f19530da8eda877459a936bf4f6612aaf3 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Wed, 22 May 2024 20:57:01 +0400 Subject: [PATCH 4/5] ensure that getAll() and getAllReviewsForProduct() always return array --- src/models/Review.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/models/Review.php b/src/models/Review.php index 0be20ff9..f45d3e9b 100644 --- a/src/models/Review.php +++ b/src/models/Review.php @@ -12,6 +12,7 @@ class Review { use Model; + protected string $table = 'review'; private int $review_id; private int $product_id; @@ -99,6 +100,10 @@ public static function getAll(): array $query = "SELECT * FROM review"; $results = self::query($query); + if (!$results) { + return []; + } + // Fetch all reviews as Review objects $reviews = []; foreach ($results as $result) { @@ -126,7 +131,8 @@ public static function getAllReviewsForProduct(int $productId): array { $query = "SELECT * FROM review WHERE product_id = :product_id"; $params = ['product_id' => $productId]; - return self::query($query, $params); + $result = self::query($query, $params); + return $result ?: []; } /** @@ -135,7 +141,7 @@ public static function getAllReviewsForProduct(int $productId): array * The values are the new review data. * @return bool Success or not */ - public function updateReview (array $newReviewData): bool + public function updateReview(array $newReviewData): bool { // remove review_id (if present) from user data unset($newReviewData['review_id']); @@ -340,7 +346,7 @@ public function getNestedComments(): array // Order the children array of each comment by created_date foreach ($commentMap as $comment) { - usort($comment->children, function($a, $b) { + usort($comment->children, function ($a, $b) { return strtotime($a->created_date) - strtotime($b->created_date); }); } From 2d7ec814677c04b8eca0b57ae50f9d1b628e4f2f Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Wed, 22 May 2024 21:01:37 +0400 Subject: [PATCH 5/5] add getReviewByID, send appropriate errors when review attributes are invalid in createReview --- src/controllers/api/Reviews.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/controllers/api/Reviews.php b/src/controllers/api/Reviews.php index 3b1608d6..892735fb 100644 --- a/src/controllers/api/Reviews.php +++ b/src/controllers/api/Reviews.php @@ -14,6 +14,7 @@ class Reviews public static array $routes = [ 'GET' => [ '/api/v1/reviews' => 'getAllReviews', + '/api/v1/reviews/{id}' => 'getReviewByID', '/api/v1/products/{id}/reviews' => 'getAllReviewsForProduct', ], 'POST' => [ @@ -45,6 +46,26 @@ public function getAllReviews(): void echo json_encode($result); } + public function getReviewByID(): void + { + $id = (int)Utility::splitURL()[3]; + + // Retrieve all reviews from the database + $review = Review::getByID($id); + + // Check if product exists + if ($review === null) { + // review not found, return 404 + http_response_code(404); + echo json_encode(['error' => 'Review not found']); + return; + } + + // Return JSON response + echo json_encode($review->toArray()); + } + + /** * Get all reviews for a particular product by its ID. */ @@ -106,6 +127,14 @@ public function createReview(): void (int)$postData['rating'] ); + $errors = $newReview->validate(); + + if (!empty($errors)) { + http_response_code(400); + echo json_encode(['error' => ($errors)]); + return; + } + // Save the new review to the database if ($newReview->save()) { // Review created successfully, return 201 Created