From 129248ef4a86354bd18ddce0af176eb8585673c1 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Fri, 17 May 2024 20:10:48 +0400 Subject: [PATCH 01/19] add Order model method to fetch orders by client ID and update Profile controller to use it --- src/controllers/Profile.php | 16 +++++----------- src/models/Order.php | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/controllers/Profile.php b/src/controllers/Profile.php index f7eca13..24151ca 100644 --- a/src/controllers/Profile.php +++ b/src/controllers/Profile.php @@ -9,6 +9,7 @@ use Steamy\Model\Client; use Steamy\Model\District; use Steamy\Model\Location; +use Steamy\Model\Order; class Profile { @@ -197,17 +198,10 @@ public function index(): void return; } - // TODO: fetch 5 latest orders - $this->view_data["orders"] = array_fill( - 0, - 5, - (object)[ - 'date' => '16/01/2024', - 'id' => 4343, - 'cost' => 100.00, - 'status' => 'Completed' - ] - ); + // Fetch orders for the signed-in client + $orders = Order::getOrdersByClientId($this->signed_client->getUserID()); + + $this->view_data["orders"] = $orders; // initialize user details for template $this->view_data["name"] = $this->signed_client->getFirstName() . " " . $this->signed_client->getLastName(); diff --git a/src/models/Order.php b/src/models/Order.php index 5a88b5d..c4b6145 100644 --- a/src/models/Order.php +++ b/src/models/Order.php @@ -9,6 +9,7 @@ use PDOException; use Steamy\Core\Model; use Steamy\Core\Utility; +use Steamy\Core\Database; class Order { @@ -264,6 +265,23 @@ private static function getOrderProducts(int $order_id): array return $order_products_arr; } + public static function getOrdersByClientId(int $client_id, int $limit = 5): array { + // Perform database query to fetch orders by client ID + $query = "SELECT * FROM `order` WHERE `client_id` = :client_id ORDER BY `created_date` DESC LIMIT :limit"; + $params = [':client_id' => $client_id, ':limit' => $limit]; + // Execute the query + $orders = self::query($query, $params); + + // Check if orders were found + if ($orders === false) { + // No orders found, return an empty array + return []; + } + + // Orders found, return them + return $orders; + } + public function getOrderID(): int { From 925386d9fc41b7ba3707709b34a55516896f78f0 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sat, 18 May 2024 17:30:58 +0400 Subject: [PATCH 02/19] resolve the problem with sql but when orders are ordered the tab 'orders' and 'setting' got disable --- src/models/Order.php | 31 ++++++++++++++++--------------- src/views/Profile.php | 6 +----- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/models/Order.php b/src/models/Order.php index c4b6145..fbeddf2 100644 --- a/src/models/Order.php +++ b/src/models/Order.php @@ -4,6 +4,7 @@ namespace Steamy\Model; +use PDO; use DateTime; use Exception; use PDOException; @@ -265,21 +266,21 @@ private static function getOrderProducts(int $order_id): array return $order_products_arr; } - public static function getOrdersByClientId(int $client_id, int $limit = 5): array { - // Perform database query to fetch orders by client ID - $query = "SELECT * FROM `order` WHERE `client_id` = :client_id ORDER BY `created_date` DESC LIMIT :limit"; - $params = [':client_id' => $client_id, ':limit' => $limit]; - // Execute the query - $orders = self::query($query, $params); - - // Check if orders were found - if ($orders === false) { - // No orders found, return an empty array - return []; - } - - // Orders found, return them - return $orders; + public static function getOrdersByClientId(int $client_id, int $limit = 5): array + { + $db = self::connect(); + $stmt = $db->prepare(' + SELECT order_id, created_date, status + FROM `order` + WHERE client_id = :client_id + ORDER BY created_date DESC + LIMIT :limit + '); + $stmt->bindParam(':client_id', $client_id, PDO::PARAM_INT); + $stmt->bindParam(':limit', $limit, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt->fetchAll(PDO::FETCH_OBJ); } diff --git a/src/views/Profile.php b/src/views/Profile.php index 10d5ad1..d30a50b 100644 --- a/src/views/Profile.php +++ b/src/views/Profile.php @@ -86,7 +86,6 @@ Date Order ID - Total cost Status Actions @@ -95,13 +94,11 @@ foreach ($orders as $order) { $date = htmlspecialchars($order->date); $id = filter_var($order->id, FILTER_SANITIZE_NUMBER_INT); - $cost = filter_var($order->cost, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); $status = htmlspecialchars($order->status); echo <<< EOL $date $id - $cost $status @@ -201,5 +198,4 @@ function openTab(evt, tabName) { }); - - + \ No newline at end of file From d5ee3f167f71be069e1736bb96df40422d11bb41 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sat, 18 May 2024 21:38:34 +0400 Subject: [PATCH 03/19] update variable names in Profile.php: $order->date to $order->created_date and $order->id to $order->order_id --- src/views/Profile.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/Profile.php b/src/views/Profile.php index d30a50b..625c195 100644 --- a/src/views/Profile.php +++ b/src/views/Profile.php @@ -92,8 +92,8 @@ date); - $id = filter_var($order->id, FILTER_SANITIZE_NUMBER_INT); + $date = htmlspecialchars($order->created_date); + $id = filter_var($order->order_id, FILTER_SANITIZE_NUMBER_INT); $status = htmlspecialchars($order->status); echo <<< EOL From 4daee5a30b315b0afe3500b4b6f43871c572600e Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sun, 19 May 2024 09:49:15 +0400 Subject: [PATCH 04/19] tests for Order and OrderProduct models --- tests/OrderProductTest.php | 132 ++++++++++++++++++++++++ tests/OrderTest.php | 206 +++++++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 tests/OrderProductTest.php create mode 100644 tests/OrderTest.php diff --git a/tests/OrderProductTest.php b/tests/OrderProductTest.php new file mode 100644 index 0000000..cd31dd6 --- /dev/null +++ b/tests/OrderProductTest.php @@ -0,0 +1,132 @@ +dummy_store = new Store( + phone_no: "987654321", + address: new Location( + street: "Augus", + city: "Flacq", + district_id: 2, + latitude: 60, + longitude: 60 + ) + ); + + $success = $this->dummy_store->save(); + if (!$success) { + $errors = $this->dummy_store->validate(); + $error_msg = "Unable to save store to database. "; + if (!empty($errors)) { + $error_msg .= "Errors: " . implode(',', $errors); + } else { + $error_msg .= "Attributes seem to be ok as per validate()."; + } + + throw new Exception($error_msg); + } + + // Create a dummy client + $this->client = new Client("john@example.com", "John", "Doe", "john_doe", "password", new Location("Royal", "Curepipe", 1, 50, 50)); + $success = $this->client->save(); + if (!$success) { + throw new Exception('Unable to save client'); + } + + // Create dummy products + $this->dummy_product = new Product("Latte", 50, "latte.jpeg", "A delicious latte", "Beverage", 5.0, "A cup of latte", new DateTime()); + $success = $this->dummy_product->save(); + if (!$success) { + throw new Exception('Unable to save product'); + } + + // Create a dummy order + $this->dummy_order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID()); + $success = $this->dummy_order->save(); + if (!$success) { + throw new Exception('Unable to save order'); + } + + // Create dummy orderProduct + $this->orderProduct = new OrderProduct( + product_id: $this->dummy_product->getProductID(), + cup_size: "medium", + milk_type: "oat", + quantity: 2, + unit_price: 2.99, + order_id: $this->dummy_order->getOrderID() + ); + $success = $this->orderProduct->save(); + if (!$success) { + throw new Exception('Unable to save order product'); + } + } + + public function tearDown(): void + { + $this->dummy_order = null; + $this->client = null; + $this->dummy_store = null; + $this->dummy_product = null; + $this->orderProduct = null; + + // Clear all data from relevant tables + self::query('DELETE FROM order_product; DELETE FROM `order`; DELETE FROM client; DELETE FROM user; DELETE FROM store_product; DELETE FROM product; DELETE FROM store;'); + } + + public function testValidate(): void + { + $invalidOrderProduct = new OrderProduct( + product_id: $this->dummy_product->getProductID(), + cup_size: "extra large", // Invalid cup size + milk_type: "cow", // Invalid milk type + quantity: -1, // Invalid quantity + unit_price: -2.99, // Invalid unit price + order_id: $this->dummy_order->getOrderID() + ); + + $errors = $invalidOrderProduct->validate(); + + $this->assertArrayHasKey('quantity', $errors); + $this->assertArrayHasKey('cup_size', $errors); + $this->assertArrayHasKey('milk_type', $errors); + $this->assertArrayHasKey('unit_price', $errors); + } + + public function testGetByID(): void + { + $retrievedOrderProduct = OrderProduct::getByID($this->dummy_order->getOrderID(), $this->dummy_product->getProductID()); + + $this->assertNotNull($retrievedOrderProduct); + $this->assertEquals($this->dummy_order->getOrderID(), $retrievedOrderProduct->getOrderID()); + $this->assertEquals($this->dummy_product->getProductID(), $retrievedOrderProduct->getProductID()); + $this->assertEquals("medium", $retrievedOrderProduct->getCupSize()); + $this->assertEquals("oat", $retrievedOrderProduct->getMilkType()); + $this->assertEquals(2, $retrievedOrderProduct->getQuantity()); + $this->assertEquals(2.99, $retrievedOrderProduct->getUnitPrice()); + } +} diff --git a/tests/OrderTest.php b/tests/OrderTest.php new file mode 100644 index 0000000..ddade17 --- /dev/null +++ b/tests/OrderTest.php @@ -0,0 +1,206 @@ +dummy_store = new Store( + phone_no: "987654321", // Phone number + address: new Location( + street: "Augus", + city: "Flacq", + district_id: 2, + latitude: 60, + longitude: 60 + ) + ); + + $success = $this->dummy_store->save(); + if (!$success) { + $errors = $this->dummy_store->validate(); + $error_msg = "Unable to save store to database. "; + if (!empty($errors)) { + $error_msg .= "Errors: " . implode(',', $errors); + } else { + $error_msg .= "Attributes seem to be ok as per validate()."; + } + + throw new Exception($error_msg); + } + + // Create a dummy client + $this->client = new Client("john@example.com", "John", "Doe", "john_doe", "password", new Location( "Royal", "Curepipe", 1, 50, 50)); + $success = $this->client->save(); + if (!$success) { + throw new Exception('Unable to save client'); + } + + // Create dummy products + $product1 = new Product("Latte", 50, "latte.jpeg", "A delicious latte", "Beverage", 5.0, "A cup of latte", new DateTime()); + $success = $product1->save(); + if (!$success) { + throw new Exception('Unable to save product 1'); + } + + $product2 = new Product("Espresso", 30, "espresso.jpeg", "A strong espresso", "Beverage", 3.0, "A cup of espresso", new DateTime()); + $success = $product2->save(); + if (!$success) { + throw new Exception('Unable to save product 2'); + } + + // Create dummy order line items + $this->line_items = [ + new OrderProduct($product1->getProductID(), "medium", "oat", 2, 2.99), + new OrderProduct($product2->getProductID(), "small", "almond", 1, 4.99) + ]; + + // Create a dummy order + $this->dummy_order = new Order( + $this->dummy_store->getStoreID(), + $this->client->getUserID(), + $this->line_items + ); + } + + /** + * Tear down test data + */ + public function tearDown(): void + { + $this->dummy_order = null; + $this->client = null; + $this->dummy_store = null; + $this->line_items = []; + + // Clear all data from relevant tables + self::query('DELETE FROM order_product; DELETE FROM `order`; DELETE FROM client; DELETE FROM user; DELETE FROM store_product; DELETE FROM product; DELETE FROM store;'); + } + + public function testConstructor(): void + { + $new_order = new Order( + $this->dummy_store->getStoreID(), + $this->client->getUserID(), + $this->line_items + ); + + self::assertEquals($this->dummy_store->getStoreID(), $new_order->getStoreID()); + self::assertEquals($this->client->getUserID(), $new_order->getClientID()); + self::assertEquals(OrderStatus::PENDING, $new_order->getStatus()); + self::assertEquals($this->line_items, $new_order->getLineItems()); + } + + public function testToArray(): void + { + $result = $this->dummy_order->toArray(); + + self::assertArrayHasKey('order_id', $result); + self::assertArrayHasKey('status', $result); + self::assertArrayHasKey('created_date', $result); + self::assertArrayHasKey('pickup_date', $result); + self::assertArrayHasKey('client_id', $result); + self::assertArrayHasKey('store_id', $result); + + self::assertEquals($this->dummy_order->getOrderID(), $result['order_id']); + self::assertEquals($this->dummy_order->getStatus()->value, $result['status']); + self::assertEquals($this->dummy_order->getCreatedDate()->format('Y-m-d H:i:s'), $result['created_date']); + self::assertEquals($this->dummy_order->getPickupDate()?->format('Y-m-d H:i:s'), $result['pickup_date']); + self::assertEquals($this->dummy_order->getClientID(), $result['client_id']); + self::assertEquals($this->dummy_order->getStoreID(), $result['store_id']); + } + + public function testSave(): void + { + $success = $this->dummy_order->save(); + self::assertTrue($success); + + $order_id = $this->dummy_order->getOrderID(); + self::assertGreaterThan(0, $order_id); + + // Verify order in database + $saved_order = Order::getByID($order_id); + self::assertNotNull($saved_order); + self::assertEquals($this->dummy_order->getStoreID(), $saved_order->getStoreID()); + self::assertEquals($this->dummy_order->getClientID(), $saved_order->getClientID()); + } + + public function testSaveWithEmptyLineItems(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Cart cannot be empty'); + + $order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID(), []); + $order->save(); + } + + public function testAddLineItem(): void + { + $order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID()); + $order->addLineItem(new OrderProduct(1, "medium", "whole", 1)); + self::assertCount(1, $order->getLineItems()); + } + + public function testGetByID(): void + { + $this->dummy_order->save(); + $order_id = $this->dummy_order->getOrderID(); + + $fetched_order = Order::getByID($order_id); + self::assertNotNull($fetched_order); + + self::assertEquals($this->dummy_order->getStoreID(), $fetched_order->getStoreID()); + self::assertEquals($this->dummy_order->getClientID(), $fetched_order->getClientID()); + self::assertEquals($this->dummy_order->getStatus(), $fetched_order->getStatus()); + + // Test getByID with invalid ID + self::assertNull(Order::getByID(-1)); + } + + public function testCalculateTotalPrice(): void + { + $this->dummy_order->save(); + $total_price = $this->dummy_order->calculateTotalPrice(); + + $expected_price = array_reduce($this->line_items, function($carry, $item) { + return $carry + $item->getQuantity() * $item->getUnitPrice(); + }, 0); + + self::assertEquals($expected_price, $total_price); + } + + public function testValidate(): void + { + $errors = $this->dummy_order->validate(); + self::assertEmpty($errors); + + // Test invalid status + $invalid_order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID(), $this->line_items, null, null, OrderStatus::from('INVALID')); + $errors = $invalid_order->validate(); + self::assertNotEmpty($errors); + self::assertArrayHasKey('status', $errors); + self::assertEquals('Status must be one of: pending, cancelled, completed', $errors['status']); + } + +} From 085f1854fa70cc1dec73ee7ad39d9f7470ef3a56 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sun, 19 May 2024 17:46:13 +0400 Subject: [PATCH 05/19] update Order model to include total price in getOrdersByClientId method and modified Profile view to display additional order details. --- src/models/Order.php | 29 ++++++++++++++++++++--------- src/views/Profile.php | 11 +++++++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/models/Order.php b/src/models/Order.php index fbeddf2..79d9ebc 100644 --- a/src/models/Order.php +++ b/src/models/Order.php @@ -266,15 +266,25 @@ private static function getOrderProducts(int $order_id): array return $order_products_arr; } - public static function getOrdersByClientId(int $client_id, int $limit = 5): array - { - $db = self::connect(); - $stmt = $db->prepare(' - SELECT order_id, created_date, status - FROM `order` - WHERE client_id = :client_id - ORDER BY created_date DESC - LIMIT :limit + /** + * Retrieves a list of orders for a specific client, including the total price of products in each order. + * + * @param int $client_id The ID of the client whose orders are to be retrieved. + * @param int $limit The maximum number of orders to retrieve. Defaults to 5. + * @return array An array of stdClass objects, each containing order details and the total price. + * @throws PDOException If there is an error executing the database query. + */ + public static function getOrdersByClientId(int $client_id, int $limit = 5): array + { + $db = self::connect(); + $stmt = $db->prepare(' + SELECT o.order_id, o.created_date, o.status, o.store_id, SUM(op.unit_price * op.quantity) AS total_price + FROM `order` o + JOIN order_product op ON o.order_id = op.order_id + WHERE o.client_id = :client_id + GROUP BY o.order_id, o.created_date, o.status, o.store_id + ORDER BY o.created_date DESC + LIMIT :limit; '); $stmt->bindParam(':client_id', $client_id, PDO::PARAM_INT); $stmt->bindParam(':limit', $limit, PDO::PARAM_INT); @@ -284,6 +294,7 @@ public static function getOrdersByClientId(int $client_id, int $limit = 5): arra } + public function getOrderID(): int { return $this->order_id; diff --git a/src/views/Profile.php b/src/views/Profile.php index 625c195..ede7891 100644 --- a/src/views/Profile.php +++ b/src/views/Profile.php @@ -10,6 +10,7 @@ */ use Steamy\Model\Client; +use Steamy\Model\Order; ?> @@ -84,9 +85,11 @@
- + + + @@ -94,12 +97,16 @@ foreach ($orders as $order) { $date = htmlspecialchars($order->created_date); $id = filter_var($order->order_id, FILTER_SANITIZE_NUMBER_INT); + $storeid = filter_var($order->store_id, FILTER_SANITIZE_NUMBER_INT); $status = htmlspecialchars($order->status); + $totalPrice = $order->total_price; echo <<< EOL - + + + From 809bb3c1adab8e4b2557eccb1ffb51a4d5d0ee7b Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Sun, 19 May 2024 22:56:40 +0400 Subject: [PATCH 06/19] try to solve the errors getting --- src/models/Order.php | 4 ++++ tests/OrderProductTest.php | 45 +++++++++++++++++++------------------- tests/OrderTest.php | 33 ++++++++++------------------ 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/models/Order.php b/src/models/Order.php index 5a88b5d..9b389a1 100644 --- a/src/models/Order.php +++ b/src/models/Order.php @@ -194,6 +194,10 @@ public function save(): bool */ public function addLineItem(OrderProduct $orderProduct): void { + $errors = $orderProduct->validate(); + if (!empty($errors)) { + throw new Exception("Invalid line item: " . json_encode($errors)); + } $this->line_items[] = $orderProduct; } diff --git a/tests/OrderProductTest.php b/tests/OrderProductTest.php index cd31dd6..aecc090 100644 --- a/tests/OrderProductTest.php +++ b/tests/OrderProductTest.php @@ -19,15 +19,15 @@ class OrderProductTest extends TestCase private ?Client $client; private ?Store $dummy_store; private ?Product $dummy_product; - private ?OrderProduct $orderProduct; + private array $line_items = []; public function setUp(): void { parent::setUp(); - + // Initialize a dummy store object for testing $this->dummy_store = new Store( - phone_no: "987654321", + phone_no: "987654321", // Phone number address: new Location( street: "Augus", city: "Flacq", @@ -57,32 +57,32 @@ public function setUp(): void throw new Exception('Unable to save client'); } - // Create dummy products + // Create a dummy product $this->dummy_product = new Product("Latte", 50, "latte.jpeg", "A delicious latte", "Beverage", 5.0, "A cup of latte", new DateTime()); $success = $this->dummy_product->save(); if (!$success) { throw new Exception('Unable to save product'); } + // Create dummy order line items + $this->line_items = [ + new OrderProduct($this->dummy_product->getProductID(), "medium", "oat", 2, 5.0) + ]; + // Create a dummy order - $this->dummy_order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID()); - $success = $this->dummy_order->save(); - if (!$success) { - throw new Exception('Unable to save order'); + $this->dummy_order = new Order( + $this->dummy_store->getStoreID(), + $this->client->getUserID() + ); + + // Add line items to the order + foreach ($this->line_items as $line_item) { + $this->dummy_order->addLineItem($line_item); } - // Create dummy orderProduct - $this->orderProduct = new OrderProduct( - product_id: $this->dummy_product->getProductID(), - cup_size: "medium", - milk_type: "oat", - quantity: 2, - unit_price: 2.99, - order_id: $this->dummy_order->getOrderID() - ); - $success = $this->orderProduct->save(); + $success = $this->dummy_order->save(); if (!$success) { - throw new Exception('Unable to save order product'); + throw new Exception('Unable to save order'); } } @@ -92,7 +92,7 @@ public function tearDown(): void $this->client = null; $this->dummy_store = null; $this->dummy_product = null; - $this->orderProduct = null; + $this->line_items = []; // Clear all data from relevant tables self::query('DELETE FROM order_product; DELETE FROM `order`; DELETE FROM client; DELETE FROM user; DELETE FROM store_product; DELETE FROM product; DELETE FROM store;'); @@ -103,7 +103,7 @@ public function testValidate(): void $invalidOrderProduct = new OrderProduct( product_id: $this->dummy_product->getProductID(), cup_size: "extra large", // Invalid cup size - milk_type: "cow", // Invalid milk type + milk_type: "invalid milk", // Invalid milk type quantity: -1, // Invalid quantity unit_price: -2.99, // Invalid unit price order_id: $this->dummy_order->getOrderID() @@ -119,6 +119,7 @@ public function testValidate(): void public function testGetByID(): void { + // Assuming getByID is a method that retrieves an OrderProduct by order ID and product ID $retrievedOrderProduct = OrderProduct::getByID($this->dummy_order->getOrderID(), $this->dummy_product->getProductID()); $this->assertNotNull($retrievedOrderProduct); @@ -127,6 +128,6 @@ public function testGetByID(): void $this->assertEquals("medium", $retrievedOrderProduct->getCupSize()); $this->assertEquals("oat", $retrievedOrderProduct->getMilkType()); $this->assertEquals(2, $retrievedOrderProduct->getQuantity()); - $this->assertEquals(2.99, $retrievedOrderProduct->getUnitPrice()); + $this->assertEquals(5.0, $retrievedOrderProduct->getUnitPrice()); } } diff --git a/tests/OrderTest.php b/tests/OrderTest.php index ddade17..1f41efc 100644 --- a/tests/OrderTest.php +++ b/tests/OrderTest.php @@ -8,18 +8,18 @@ use Steamy\Model\OrderStatus; use Steamy\Model\Store; use Steamy\Model\Client; -use Steamy\Model\Product; use Steamy\Core\Database; use Steamy\Model\Location; +use Steamy\Model\Product; + class OrderTest extends TestCase { use Database; - private ?Order $dummy_order; - private ?Client $client; - private ?Store $dummy_store; - private array $line_items; - + private ?Order $dummy_order = null; + private ?Client $client = null; + private ?Store $dummy_store = null; + private array $line_items = []; public function setUp(): void { @@ -51,7 +51,7 @@ public function setUp(): void } // Create a dummy client - $this->client = new Client("john@example.com", "John", "Doe", "john_doe", "password", new Location( "Royal", "Curepipe", 1, 50, 50)); + $this->client = new Client("john@example.com", "John", "Doe", "john_doe", "password", new Location("Royal", "Curepipe", 1, 50, 50)); $success = $this->client->save(); if (!$success) { throw new Exception('Unable to save client'); @@ -72,8 +72,8 @@ public function setUp(): void // Create dummy order line items $this->line_items = [ - new OrderProduct($product1->getProductID(), "medium", "oat", 2, 2.99), - new OrderProduct($product2->getProductID(), "small", "almond", 1, 4.99) + new OrderProduct($product1->getProductID(), "medium", "oat", 2, 5.0), + new OrderProduct($product2->getProductID(), "small", "almond", 1, 3.0) ]; // Create a dummy order @@ -84,9 +84,6 @@ public function setUp(): void ); } - /** - * Tear down test data - */ public function tearDown(): void { $this->dummy_order = null; @@ -158,7 +155,7 @@ public function testSaveWithEmptyLineItems(): void public function testAddLineItem(): void { $order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID()); - $order->addLineItem(new OrderProduct(1, "medium", "whole", 1)); + $order->addLineItem(new OrderProduct(1, "medium", "oat", 1, 5.0)); self::assertCount(1, $order->getLineItems()); } @@ -183,7 +180,7 @@ public function testCalculateTotalPrice(): void $this->dummy_order->save(); $total_price = $this->dummy_order->calculateTotalPrice(); - $expected_price = array_reduce($this->line_items, function($carry, $item) { + $expected_price = array_reduce($this->line_items, function ($carry, $item) { return $carry + $item->getQuantity() * $item->getUnitPrice(); }, 0); @@ -194,13 +191,5 @@ public function testValidate(): void { $errors = $this->dummy_order->validate(); self::assertEmpty($errors); - - // Test invalid status - $invalid_order = new Order($this->dummy_store->getStoreID(), $this->client->getUserID(), $this->line_items, null, null, OrderStatus::from('INVALID')); - $errors = $invalid_order->validate(); - self::assertNotEmpty($errors); - self::assertArrayHasKey('status', $errors); - self::assertEquals('Status must be one of: pending, cancelled, completed', $errors['status']); } - } From 9327d5fc41ac720fa416368c88ab3d4a9cd3dde7 Mon Sep 17 00:00:00 2001 From: creme332 <65414576+creme332@users.noreply.github.com> Date: Mon, 20 May 2024 10:42:03 +0400 Subject: [PATCH 07/19] use empty() to check if there are no errors with line item --- src/models/Order.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/Order.php b/src/models/Order.php index 9b389a1..9c92240 100644 --- a/src/models/Order.php +++ b/src/models/Order.php @@ -121,7 +121,7 @@ public function save(): bool $update_stock_stm = $conn->prepare($query); foreach ($this->line_items as $line_item) { - if (!$line_item->validate()) { + if (!empty($line_item->validate())) { // line item contains invalid attributes $conn->rollBack(); $conn = null; @@ -191,6 +191,7 @@ public function save(): bool * * @param OrderProduct $orderProduct * @return void + * @throws Exception */ public function addLineItem(OrderProduct $orderProduct): void { From ca690c334942109909ba72f23af8d10f23788335 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Mon, 20 May 2024 19:10:42 +0400 Subject: [PATCH 08/19] add `addProductStock` method to `Store` model and update `OrderProductTest` and `OrderTest` to use it --- src/models/Store.php | 10 ++++++++++ tests/OrderProductTest.php | 3 +++ tests/OrderTest.php | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/src/models/Store.php b/src/models/Store.php index e230aa8..18a96b9 100644 --- a/src/models/Store.php +++ b/src/models/Store.php @@ -146,6 +146,16 @@ public function validate(): array return $errors; } + public function addProductStock(int $product_id, int $quantity): bool + { + $query = "INSERT INTO store_product (store_id, product_id, stock_level) VALUES (:store_id, :product_id, :quantity) + ON DUPLICATE KEY UPDATE stock_level = stock_level + :quantity"; + $params = ['store_id' => $this->store_id, 'product_id' => $product_id, 'quantity' => $quantity]; + $result = self::query($query, $params); + + return $result; + } + public function getProductStock(int $product_id): int { $query = "SELECT stock_level FROM store_product WHERE store_id = :store_id AND product_id = :product_id;"; diff --git a/tests/OrderProductTest.php b/tests/OrderProductTest.php index aecc090..7104b4b 100644 --- a/tests/OrderProductTest.php +++ b/tests/OrderProductTest.php @@ -64,6 +64,9 @@ public function setUp(): void throw new Exception('Unable to save product'); } + // Update stock level for the product + $this->dummy_store->addProductStock($this->dummy_product->getProductID(), 10); + // Create dummy order line items $this->line_items = [ new OrderProduct($this->dummy_product->getProductID(), "medium", "oat", 2, 5.0) diff --git a/tests/OrderTest.php b/tests/OrderTest.php index 1f41efc..d825c10 100644 --- a/tests/OrderTest.php +++ b/tests/OrderTest.php @@ -70,6 +70,10 @@ public function setUp(): void throw new Exception('Unable to save product 2'); } + // Add stock to the store for the products + $this->dummy_store->addProductStock($product1->getProductID(), 10); + $this->dummy_store->addProductStock($product2->getProductID(), 10); + // Create dummy order line items $this->line_items = [ new OrderProduct($product1->getProductID(), "medium", "oat", 2, 5.0), From b7ee6657874468964ac000294cbc80c7b729f458 Mon Sep 17 00:00:00 2001 From: divyesh000 Date: Mon, 20 May 2024 19:26:40 +0400 Subject: [PATCH 09/19] refactore Order model to return array of Order objects instead of stdClass objects, and update Profile view to accommodate changes --- src/models/Order.php | 30 +++++++++++++++++++++++------- src/views/Profile.php | 17 +++++++---------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/models/Order.php b/src/models/Order.php index 79d9ebc..e991f04 100644 --- a/src/models/Order.php +++ b/src/models/Order.php @@ -10,7 +10,6 @@ use PDOException; use Steamy\Core\Model; use Steamy\Core\Utility; -use Steamy\Core\Database; class Order { @@ -267,22 +266,20 @@ private static function getOrderProducts(int $order_id): array } /** - * Retrieves a list of orders for a specific client, including the total price of products in each order. + * Retrieves a list of orders for a specific client. * * @param int $client_id The ID of the client whose orders are to be retrieved. * @param int $limit The maximum number of orders to retrieve. Defaults to 5. - * @return array An array of stdClass objects, each containing order details and the total price. + * @return Order[] An array of Order objects. * @throws PDOException If there is an error executing the database query. */ public static function getOrdersByClientId(int $client_id, int $limit = 5): array { $db = self::connect(); $stmt = $db->prepare(' - SELECT o.order_id, o.created_date, o.status, o.store_id, SUM(op.unit_price * op.quantity) AS total_price + SELECT o.order_id, o.created_date, o.status, o.store_id, o.pickup_date, o.client_id FROM `order` o - JOIN order_product op ON o.order_id = op.order_id WHERE o.client_id = :client_id - GROUP BY o.order_id, o.created_date, o.status, o.store_id ORDER BY o.created_date DESC LIMIT :limit; '); @@ -290,7 +287,26 @@ public static function getOrdersByClientId(int $client_id, int $limit = 5): arra $stmt->bindParam(':limit', $limit, PDO::PARAM_INT); $stmt->execute(); - return $stmt->fetchAll(PDO::FETCH_OBJ); + $orderDataArray = $stmt->fetchAll(PDO::FETCH_OBJ); + $orders = []; + + foreach ($orderDataArray as $orderData) { + // Get the line items for this order + $lineItems = self::getOrderProducts((int)$orderData->order_id); + + // Create an Order object with the retrieved data + $orders[] = new Order( + store_id: (int)$orderData->store_id, + client_id: (int)$orderData->client_id, + line_items: $lineItems, + order_id: (int)$orderData->order_id, + pickup_date: $orderData->pickup_date ? Utility::stringToDate($orderData->pickup_date) : null, + status: OrderStatus::from($orderData->status), + created_date: Utility::stringToDate($orderData->created_date), + ); + } + + return $orders; } diff --git a/src/views/Profile.php b/src/views/Profile.php index ede7891..a58016b 100644 --- a/src/views/Profile.php +++ b/src/views/Profile.php @@ -6,12 +6,9 @@ * The following attributes are defined in controllers/Profile.php * * @var $client Client signed in client - * @var $orders array array of orders + * @var $orders Order[] array of orders */ -use Steamy\Model\Client; -use Steamy\Model\Order; - ?>
Date Order IDStore IDDate StatusTotal Price Actions
$date $id$storeid$date $status$totalPrice