Skip to content

Commit

Permalink
Merge pull request #164 from Divyesh000/api-products
Browse files Browse the repository at this point in the history
complete api/Products controller
  • Loading branch information
creme332 authored May 16, 2024
2 parents 0d31a95 + 6ac06c7 commit 40cbda1
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 51 deletions.
21 changes: 15 additions & 6 deletions src/controllers/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Steamy\Controller;

use Exception;
use Steamy\Core\Controller;
use Steamy\Core\Utility;

Expand All @@ -26,7 +27,7 @@ public function __construct()
}

/**
* Checks if root relative url starts with /api/v1
* Checks if root relative url starts with api/v1
* @return bool
*/
private function validateURLFormat(): bool
Expand All @@ -37,17 +38,25 @@ private function validateURLFormat(): bool
public function index(): void
{
if (!$this->validateURLFormat()) {
echo "Invalid API URL: " . $_GET["url"];
http_response_code(400);
die();
}

// call appropriate controller to handle resource
$controllerClassName = 'Steamy\\Controller\\API\\' . ucfirst($this->resource);
try {
if (class_exists($controllerClassName)) {
(new $controllerClassName())->index();
} else {
http_response_code(404);
die();
}
} catch (Exception $e) {
http_response_code(500);

// Uncomment line below only when testing API
echo $e->getMessage();

if (class_exists($controllerClassName)) {
(new $controllerClassName())->index();
} else {
echo "Invalid API resource: " . $this->resource;
die();
}
}
Expand Down
247 changes: 224 additions & 23 deletions src/controllers/api/Products.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,255 @@

namespace Steamy\Controller\API;

use Steamy\Core\Utility;
use Steamy\Model\Product;
use Steamy\Core\Model;

/**
* Handles /products route of api
*/
class Products
{
private function getProducts(): void
use Model;

/**
* Get the list of all products available in the store.
*/
private function getAllProducts(): void
{
$all_products = Product::getAll();
// Retrieve all products from the database
$allProducts = Product::getAll();

// Convert products to array format
$result = [];
foreach ($all_products as $product) {
foreach ($allProducts as $product) {
$result[] = $product->toArray();
}

// Return JSON response
echo json_encode($result);
}

private function addProduct(): void
/**
* Get the details of a specific product by its ID.
*/
private function getProductById(): void
{
$productId = (int)Utility::splitURL()[3];

// Retrieve product details from the database
$product = Product::getByID($productId);

// Check if product exists
if ($product === null) {
// Product not found, return 404
http_response_code(404);
echo json_encode(['error' => 'Product not found']);
return;
}

// Return JSON response
echo json_encode($product->toArray());
}

/**
* Get the list of product categories.
*/
private function getProductCategories(): void
{
// Retrieve all product categories from the database
$categories = Product::getCategories();

// Return JSON response
echo json_encode($categories);
}

/**
* Create a new product entry in the database.
*/
private function createProduct(): void
{
// Retrieve POST data
$postData = $_POST;

// TODO : Use json schema validation here
// Check if required fields are present
$requiredFields = [
'name',
'calories',
'img_url',
'img_alt_text',
'category',
'price',
'description'
];

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 Product object
$newProduct = new Product(
$postData['name'],
(int)$postData['calories'],
$postData['img_url'],
$postData['img_alt_text'],
$postData['category'],
(float)$postData['price'],
$postData['description']
);

// Save the new product to the database
if ($newProduct->save()) {
// Product created successfully, return 201 Created
http_response_code(201);
echo json_encode(['message' => 'Product created successfully', 'product_id' => $newProduct->getProductID()]
);
} else {
// Failed to create product, return 500 Internal Server Error
http_response_code(500);
echo json_encode(['error' => 'Failed to create product']);
}
}

/**
* Delete a product with the specified ID.
*/
private function deleteProduct(): void
{
$productId = (int)Utility::splitURL()[3];

// Retrieve the product by ID
$product = Product::getByID($productId);

// Check if product exists
if ($product === null) {
// Product not found, return 404
http_response_code(404);
echo json_encode(['error' => 'Product not found']);
return;
}

// Attempt to delete the product
if ($product->deleteProduct()) {
// Product successfully deleted
http_response_code(204); // No Content
} else {
// Failed to delete the product
http_response_code(500); // Internal Server Error
echo json_encode(['error' => 'Failed to delete product']);
}
}

/**
* Update the details of a product with the specified ID.
*/
private function updateProduct(): void
{
$productId = (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 product
$product = Product::getByID($productId);

// Check if product exists
if ($product === null) {
// Product not found
http_response_code(404); // Not Found
echo json_encode(['error' => 'Product not found']);
return;
}

// Update product in the database
$success = $product->updateProduct($putData);

if ($success) {
// Product updated successfully
http_response_code(200); // OK
echo json_encode(['message' => 'Product updated successfully']);
} else {
// Failed to update product
http_response_code(500); // Internal Server Error
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
{
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
$this->getProducts();
break;
case 'POST':
$this->addProduct();
break;
case 'DELETE':
$this->deleteProduct();
break;
case 'PUT':
$this->updateProduct();
break;
default:
http_response_code(400);
$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();
}
}
}
}
4 changes: 3 additions & 1 deletion src/core/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ class App
*/
public function exception_handler(Throwable $exception): void
{
// echo "Uncaught exception: ", $exception->getMessage(), "\n";
// Uncomment line below only during development
echo "Uncaught exception: ", $exception->getMessage(), "\n";

(new Error())->handleUnknownError();
}

Expand Down
Loading

0 comments on commit 40cbda1

Please sign in to comment.