Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

Commit 0ffb3b7

Browse files
Merge pull request #78 from TheRefraction/test-checkout
Test checkout
2 parents e593756 + d96a6d5 commit 0ffb3b7

8 files changed

Lines changed: 443 additions & 29 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
4+
require __DIR__ .'/../models/Cart.php';
5+
require __DIR__ .'/../models/Payment.php';
6+
require __DIR__ .'/../models/Address.php';
7+
require __DIR__ .'/../models/Invoice.php';
8+
9+
10+
class CheckoutController {
11+
private $cartModel;
12+
private $paymentModel;
13+
private $addressModel;
14+
private $invoiceModel;
15+
16+
public function __construct(PDO $dbConnection) {
17+
$this->cartModel = new Cart();
18+
$this->paymentModel = new Payment($dbConnection);
19+
$this->addressModel = new Address($dbConnection);
20+
$this->invoiceModel = new Invoice($dbConnection);
21+
}
22+
23+
public function viewCheckout() {
24+
$total = $this->cartModel->computeTotal();
25+
require_once __DIR__ . "/../views/checkout.php";
26+
}
27+
28+
public function saveOrder() {
29+
$json = file_get_contents('php://input');
30+
$data = json_decode($json, true);
31+
32+
if (!$data) {
33+
$this->jsonResponse(false, "Données invalides.");
34+
}
35+
36+
$payment_id = $this->paymentModel->createPayment($data['simulate_status']);
37+
38+
$address_id = $this->addressModel->createSafeAddress(
39+
$data["house-number"],
40+
$data["house-suffix"],
41+
$data["street"],
42+
$data["city"],
43+
$data["code-postal"],
44+
$data["country"]
45+
);
46+
/*
47+
$this->invoiceModel->createInvoice(
48+
$_SESSION['user_id'],
49+
$_SESSION['cart'],
50+
3,
51+
$payment_id,
52+
$address_id
53+
);
54+
55+
*/
56+
57+
// TODO: take into account all payment statuses and check in a cleaner way
58+
if($data['simulate_status'] == 3) {
59+
$this->jsonResponse(false, "Paiement refusé par la banque");
60+
} else {
61+
$this->jsonResponse(true, "Commande enregistrée");
62+
}
63+
}
64+
65+
private function jsonResponse($success, $message) {
66+
header('Content-Type: application/json');
67+
echo json_encode([
68+
'success' => $success,
69+
'message' => $message
70+
]);
71+
exit;
72+
}
73+
74+
}
75+
76+
?>

src/app/models/Address.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
require_once __DIR__ . '/BaseModel.php';
3+
4+
class Address extends BaseModel {
5+
6+
7+
public function createSafeAddress($house_num, $house_suf, $street, $city, $postal_code, $country) {
8+
try {
9+
$this->conn->beginTransaction();
10+
11+
// 1. Ensure Country exists
12+
$country_id = $this->getOrCreate('country', 'name', "France");
13+
14+
// 2. Ensure Postal Code exists (linked to country)
15+
// Note: You may need a more complex 'getOrCreate' if unique constraints span multiple columns
16+
$pc_id = $this->getOrCreate('postal_code', 'code', $postal_code, ['country_id' => $country_id]);
17+
18+
// 3. Ensure City exists
19+
$city_id = $this->getOrCreate('city', 'name', $city, ['postal_code_id' => $pc_id]);
20+
21+
// 4. Ensure Street exists
22+
$street_id = $this->getOrCreate('street', 'name', $street, ['city_id' => $city_id]);
23+
24+
// 5. Check if Address exists
25+
$checkStmt = $this->conn->prepare("SELECT id FROM address WHERE house_number = :num AND house_number_suffix <=> :suffix AND street_id = :sid");
26+
$checkStmt->execute([
27+
':num' => $house_num,
28+
':suffix' => !empty($house_suf) ? $house_suf : null,
29+
':sid' => $street_id
30+
]);
31+
$existing = $checkStmt->fetchColumn();
32+
33+
if ($existing) {
34+
$this->conn->commit();
35+
return $existing;
36+
}
37+
38+
// 6. Insert Address
39+
$insertStmt = $this->conn->prepare("INSERT INTO address (house_number, house_number_suffix, street_id) VALUES (:num, :suffix, :sid)");
40+
$insertStmt->execute([
41+
':num' => $house_num,
42+
':suffix' => !empty($house_suf) ? $house_suf : null,
43+
':sid' => $street_id
44+
]);
45+
46+
$id = $this->conn->lastInsertId();
47+
$this->conn->commit();
48+
return $id;
49+
50+
} catch (Exception $e) {
51+
$this->conn->rollBack();
52+
throw $e;
53+
}
54+
}
55+
56+
/**
57+
* Helper to find an ID or create it if missing
58+
*/
59+
private function getOrCreate($table, $column, $value, $extraData = []) {
60+
$sql = "SELECT id FROM $table WHERE $column LIKE :val";
61+
// Add extra conditions for hierarchy (e.g., WHERE name = 'X' AND city_id = 1)
62+
foreach ($extraData as $col => $dat) {
63+
$sql .= " AND $col = :$col";
64+
}
65+
66+
$stmt = $this->conn->prepare($sql);
67+
$params = [':val' => $value];
68+
foreach ($extraData as $col => $dat) { $params[":$col"] = $dat; }
69+
70+
$stmt->execute($params);
71+
$id = $stmt->fetchColumn();
72+
73+
if (!$id) {
74+
$cols = array_merge([$column], array_keys($extraData));
75+
$placeholders = array_map(fn($c) => ":$c", $cols);
76+
77+
$insertSql = "INSERT INTO $table (" . implode(',', $cols) . ") VALUES (" . implode(',', $placeholders) . ")";
78+
$insertStmt = $this->conn->prepare($insertSql);
79+
80+
$insertParams = [":$column" => $value];
81+
foreach ($extraData as $col => $dat) { $insertParams[":$col"] = $dat; }
82+
83+
$insertStmt->execute($insertParams);
84+
$id = $this->conn->lastInsertId();
85+
}
86+
return $id;
87+
}
88+
}
89+
?>

src/app/models/Invoice.php

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,146 @@ public function getInvoicesByAccountId($accountId) {
2626

2727
$stmt = $this->conn->prepare($query);
2828
$stmt->bindValue(':accountId', $accountId, PDO::PARAM_INT);
29+
$stmt->bindValue(':total', 0.0);
30+
$stmt->bindValue(':statusId', $statusId, PDO::PARAM_INT);
31+
$stmt->bindValue(':paymentId', $paymentId, PDO::PARAM_INT);
32+
$stmt->bindValue(':addressId', $addressId, PDO::PARAM_INT);
2933
$stmt->execute();
3034

31-
return $stmt->fetchAll(PDO::FETCH_OBJ);
35+
$invoiceId = $this->conn->lastInsertId();
36+
$totalFacture = 0.0;
37+
38+
$products = $cart['products'] ?? [];
39+
foreach ($products as $lineKey => $item) {
40+
41+
$basePrice = (float)$item['price'];
42+
$optionsDeltaSum = 0.0;
43+
44+
if (!empty($item['options'])) {
45+
foreach ($item['options'] as $slot) {
46+
foreach ($slot['choices'] as $choice) {
47+
$optionsDeltaSum += (float)($choice['priceDelta'] ?? 0);
48+
}
49+
}
50+
}
51+
52+
$finalUnitPrice = $basePrice + $optionsDeltaSum;
53+
$quantity = (int)$item['quantity'];
54+
55+
$queryLine = "INSERT INTO invoice_line (unit_price, quantity, invoice_id, product_id, menu_id)
56+
VALUES (:unitPrice, :quantity, :invoiceId, :productId, NULL)";
57+
58+
$stmtLine = $this->conn->prepare($queryLine);
59+
$stmtLine->bindValue(':unitPrice', $finalUnitPrice);
60+
$stmtLine->bindValue(':quantity', $quantity, PDO::PARAM_INT);
61+
$stmtLine->bindValue(':invoiceId', $invoiceId, PDO::PARAM_INT);
62+
$stmtLine->bindValue(':productId', $item['product_id'], PDO::PARAM_INT);
63+
$stmtLine->execute();
64+
65+
$invoiceLineId = $this->conn->lastInsertId();
66+
67+
if (!empty($item['options'])) {
68+
foreach ($item['options'] as $slot) {
69+
foreach ($slot['choices'] as $choice) {
70+
$queryOpt = "INSERT INTO invoice_line_product_option (invoice_line_id, product_option_id, unit_price_delta, quantity)
71+
VALUES (:invoiceLineId, :optionId, :unitPriceDelta, :quantity)";
72+
73+
$stmtOpt = $this->conn->prepare($queryOpt);
74+
$stmtOpt->bindValue(':invoiceLineId', $invoiceLineId, PDO::PARAM_INT);
75+
$stmtOpt->bindValue(':optionId', $choice['optionProductId'], PDO::PARAM_INT);
76+
$stmtOpt->bindValue(':unitPriceDelta', $choice['priceDelta']);
77+
$stmtOpt->bindValue(':quantity', $quantity, PDO::PARAM_INT); // Quantité calquée sur le produit
78+
$stmtOpt->execute();
79+
}
80+
}
81+
}
82+
83+
$totalFacture += ($finalUnitPrice * $quantity);
84+
}
85+
86+
/*
87+
// Add menu items to the invoice
88+
$menus = $cart['menus'] ?? [];
89+
foreach ($menus as $menuId => $menu) {
90+
$query = "INSERT INTO invoice_line (unit_price, quantity, invoice_id, product_id, menu_id)
91+
VALUES (:unitPrice, :quantity, :invoiceId, NULL, :menuId)";
92+
93+
$menuPrice = $menu['price'];
94+
$menuQuantity = $menu['quantity'];
95+
$menuItems = $menu['items'] ?? [];
96+
97+
$stmt = $this->conn->prepare($query);
98+
99+
$stmt->bindValue(':unitPrice', $menuPrice);
100+
$stmt->bindValue(':invoiceId', $invoiceId, PDO::PARAM_INT);
101+
$stmt->bindValue(':menuId', $menuId, PDO::PARAM_INT);
102+
$stmt->bindValue(':quantity', $menuQuantity, PDO::PARAM_INT);
103+
104+
$stmt->execute();
105+
106+
$invoiceLineId = $this->conn->lastInsertId();
107+
108+
// Insert menu items for the invoice line
109+
foreach ($menuItems as $item) {
110+
// price and price_delta are used for redundancy
111+
// $menuPrice should already include the price of the items,
112+
// but we store it for easier retrieval when displaying the invoice
113+
$itemId = $item['id'];
114+
$itemPrice = $item['price'];
115+
$itemDelta = $item['price_delta'];
116+
$itemQuantity = $item['quantity'];
117+
118+
$query = "INSERT INTO invoice_line_menu_item (invoice_line_id, product_id, unit_price, unit_price_delta, quantity)
119+
VALUES (:invoiceLineId, :itemId, :unitPrice, :unitPriceDelta, :quantity)";
120+
121+
$stmt = $this->conn->prepare($query);
122+
123+
$stmt->bindValue(':invoiceLineId', $invoiceLineId, PDO::PARAM_INT);
124+
$stmt->bindValue(':itemId', $itemId, PDO::PARAM_INT);
125+
$stmt->bindValue(':unitPrice', $itemPrice);
126+
$stmt->bindValue(':unitPriceDelta', $itemDelta);
127+
$stmt->bindValue(':quantity', $itemQuantity, PDO::PARAM_INT);
128+
129+
$stmt->execute();
130+
131+
// Insert product options for the menu item
132+
$menuOptions = $item['options'] ?? [];
133+
$optionsTotal = 0.0;
134+
135+
foreach ($menuOptions as $option) {
136+
$query = "INSERT INTO invoice_line_product_option (invoice_line_id, product_option_id, unit_price_delta, quantity)
137+
VALUES (:invoiceLineId, :optionId, :unitPriceDelta, :quantity)";
138+
139+
$optionId = $option['id'];
140+
$optionPriceDelta = $option['price_delta'];
141+
$optionQuantity = $option['quantity'];
142+
143+
$stmt = $this->conn->prepare($query);
144+
145+
$stmt->bindValue(':invoiceLineId', $invoiceLineId, PDO::PARAM_INT);
146+
$stmt->bindValue(':optionId', $optionId, PDO::PARAM_INT);
147+
$stmt->bindValue(':unitPriceDelta', $optionPriceDelta);
148+
$stmt->bindValue(':quantity', $optionQuantity, PDO::PARAM_INT);
149+
150+
$stmt->execute();
151+
152+
$optionsTotal += $optionPriceDelta * $optionQuantity;
153+
}
154+
}
155+
156+
$total += ($menuPrice + $optionsTotal) * $menuQuantity;
157+
}
158+
159+
*/
160+
161+
// Update the invoice total
162+
$queryUpdate = "UPDATE invoice SET total = :total WHERE id = :invoiceId";
163+
$stmtUpdate = $this->conn->prepare($queryUpdate);
164+
$stmtUpdate->bindValue(':total', $totalFacture);
165+
$stmtUpdate->bindValue(':invoiceId', $invoiceId, PDO::PARAM_INT);
166+
$stmtUpdate->execute();
167+
168+
return $invoiceId;
32169
}
33170

34171

@@ -183,4 +320,4 @@ public function createInvoice($accountId, $cart, $statusId, $paymentId, $address
183320
}
184321

185322

186-
}
323+
}

src/app/models/Payment.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
require_once __DIR__ . '/BaseModel.php';
4+
5+
/**
6+
* Product model that handles all database interactions related to payments.
7+
* This includes CRUD operations and any payment-specific queries.
8+
*/
9+
class Payment extends BaseModel {
10+
/**
11+
* Creates a payment record
12+
* @param int $status Whether the payment failed, succeeded, is pending or was refunded
13+
* @return int last id inserted in the payment table
14+
*/
15+
public function createPayment($status) {
16+
$query = "INSERT INTO payment (date, mode_id, status_id)
17+
VALUES (:date, :mode_id, :status_id)";
18+
19+
20+
$stmt = $this->conn->prepare($query);
21+
22+
$stmt->bindValue(":date", date("Y-m-d"), PDO::PARAM_STR);
23+
$stmt->bindValue(":mode_id", 2, PDO::PARAM_INT);
24+
$stmt->bindValue(":status_id", $status, PDO::PARAM_INT);
25+
26+
$stmt->execute();
27+
return $this->conn->lastInsertId();
28+
}
29+
}
30+
?>

src/app/models/Product.php

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,7 @@
88
*/
99
class Product extends BaseModel {
1010

11-
/**
12-
* Creates a new product in the database.
13-
* @param int $id The unique identifier for the product.
14-
* @param string $name The name of the product.
15-
* @param string|null $description A description of the product.
16-
* @param float $price The price of the product.
17-
* @param int $supplier_id The ID of the supplier providing the product.
18-
* @return bool Returns true on successful creation, false otherwise.
19-
*/
20-
public function createProduct($id, $name, $description, $price, $supplier_id) {
21-
$query = "INSERT INTO product (id, name, description, price, supplier_id)
22-
VALUES (:id, :name, :description, :price, :supplier_id);";
23-
24-
$stmt = $this->conn->prepare($query);
25-
26-
$stmt->bindValue(":id", $id, PDO::PARAM_INT);
27-
$stmt->bindValue(":name", $name, PDO::PARAM_STR);
28-
$stmt->bindValue(":description", $description, PDO::PARAM_STR);
29-
$stmt->bindValue(":price", $price);
30-
$stmt->bindValue(":supplier_id", $supplier_id, PDO::PARAM_INT);
31-
32-
return $stmt->execute();
33-
}
34-
11+
/*
3512
/**
3613
* Retrieves all products from the database.
3714
* @param bool $showHidden Whether to include hidden products in the results. Defaults to true.

src/app/views/cart.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@
4444
</li>
4545
<?php endforeach; ?>
4646
</ul>
47-
<!-- TODO: Display Menus -->
48-
</ul>
47+
48+
<a href="/checkout">Checkout</a>
49+
<!-- TODO: Display Menus -->
4950
<?php } ?>
5051

51-
<?php include 'partials/footer.php'; ?>
52+
<?php include 'partials/footer.php'; ?>

0 commit comments

Comments
 (0)