Skip to content

Commit 6decd3e

Browse files
committed
Merge branch 'feature/shopping' into v2.0
2 parents 5a5a7c6 + b3eaf3d commit 6decd3e

File tree

12 files changed

+688
-3
lines changed

12 files changed

+688
-3
lines changed

db.sql

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,41 @@ CREATE TABLE FcmToken
115115
ON UPDATE CASCADE ON DELETE CASCADE
116116
);
117117

118+
DROP TABLE IF EXISTS Product;
119+
CREATE TABLE Product
120+
(
121+
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
122+
name VARCHAR(255) NOT NULL,
123+
ean VARCHAR(13) NOT NULL,
124+
price FLOAT(10, 2) UNSIGNED NOT NULL,
125+
shopId INT NOT NULL,
126+
-- A shop can only use an EAN once
127+
UNIQUE (shopId, ean),
128+
FOREIGN KEY (shopId) REFERENCES Shop (id)
129+
ON UPDATE CASCADE ON DELETE CASCADE
130+
);
131+
132+
DROP TABLE IF EXISTS ShoppingList;
133+
CREATE TABLE ShoppingList
134+
(
135+
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
136+
userId INT NOT NULL,
137+
createdAt DATETIME NOT NULL DEFAULT NOW(),
138+
isReady BOOLEAN NOT NULL DEFAULT FALSE
139+
);
140+
141+
DROP TABLE IF EXISTS ShoppingList_Products;
142+
CREATE TABLE ShoppingList_Products
143+
(
144+
shoppingListId INT NOT NULL,
145+
productId INT NOT NULL,
146+
PRIMARY KEY (shoppingListId, productId),
147+
FOREIGN KEY (shoppingListId) REFERENCES ShoppingList (id)
148+
ON UPDATE CASCADE ON DELETE CASCADE,
149+
FOREIGN KEY (productId) REFERENCES Product (id)
150+
ON UPDATE CASCADE ON DELETE CASCADE
151+
);
152+
118153
SET FOREIGN_KEY_CHECKS = 1;
119154

120155
DROP VIEW IF EXISTS PendingBooking;
@@ -194,6 +229,25 @@ SELECT Session.id AS sessionId,
194229
FROM Session
195230
JOIN UserDetails ON Session.userId = UserDetails.id;
196231

232+
DROP VIEW IF EXISTS ShoppingListDetail;
233+
CREATE VIEW ShoppingListDetail AS
234+
SELECT ShoppingList.id AS shoppingListId,
235+
ShoppingList.createdAt,
236+
ShoppingList.userId,
237+
ShoppingList.isReady,
238+
Product.shopId,
239+
Product.id AS productId,
240+
Product.price,
241+
Product.ean,
242+
Product.name AS productName,
243+
ShopWithCount.*,
244+
UserWithRole.name AS userName
245+
FROM ShoppingList
246+
JOIN ShoppingList_Products ON ShoppingList.id = ShoppingList_Products.shoppingListId
247+
JOIN Product ON ShoppingList_Products.productId = Product.id
248+
JOIN ShopWithCount ON ShopWithCount.id = Product.shopId
249+
JOIN UserWithRole ON UserWithRole.id = ShoppingList.userId;
250+
197251
-- Apply the haversine formula to calculate
198252
-- the distance between 2 points on Earth in KMs
199253
DROP FUNCTION IF EXISTS DISTANCE_KM;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
/**
3+
* Copyright 2020 Simone Sestito
4+
* This file is part of Shops Queue.
5+
*
6+
* Shops Queue is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Shops Queue is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with Shops Queue. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
class ProductController extends BaseController {
21+
private $productDao;
22+
23+
public function __construct(ProductDao $productDao) {
24+
$this->productDao = $productDao;
25+
$this->registerRoute('/shops/:id/products', 'GET', '*', 'getShopProducts');
26+
$this->registerRoute('/products', 'POST', 'OWNER', 'addShopProduct');
27+
$this->registerRoute('/products/:id', 'GET', '*', 'getProduct');
28+
$this->registerRoute('/products/:id', 'PUT', 'OWNER', 'editProduct');
29+
$this->registerRoute('/products/:id', 'DELETE', 'OWNER', 'deleteProduct');
30+
}
31+
32+
/**
33+
* @param int $shopId
34+
* @return Product[]
35+
*/
36+
public function getShopProducts(int $shopId): array {
37+
$entities = $this->productDao->getProductsByShopId($shopId);
38+
return array_map(function ($entity) {
39+
return new Product($entity);
40+
}, $entities);
41+
}
42+
43+
/**
44+
* @param NewProduct $newProduct
45+
* @return Product
46+
*/
47+
public function addShopProduct(NewProduct $newProduct): Product {
48+
$shopId = AuthService::getAuthContext()['shopId'];
49+
$id = $this->productDao->addProduct($shopId, $newProduct);
50+
$entity = $this->productDao->getProductById($id);
51+
return new Product($entity);
52+
}
53+
54+
/**
55+
* @param int $id
56+
* @return Product
57+
* @throws AppHttpException
58+
*/
59+
public function getProduct(int $id) {
60+
$entity = $this->productDao->getProductById($id);
61+
if ($entity == null)
62+
throw new AppHttpException(HTTP_NOT_FOUND);
63+
return new Product($entity);
64+
}
65+
66+
/**
67+
* @param int $id
68+
* @param NewProduct $newProduct
69+
* @return Product
70+
* @throws AppHttpException
71+
*/
72+
public function editProduct(int $id, NewProduct $newProduct): Product {
73+
$shopId = AuthService::getAuthContext()['shopId'];
74+
$this->productDao->editProduct($id, $shopId, $newProduct);
75+
$newEntity = $this->productDao->getProductById($id);
76+
if ($newEntity == null) {
77+
throw new AppHttpException(HTTP_NOT_FOUND);
78+
}
79+
return new Product($newEntity);
80+
}
81+
82+
/**
83+
* @param int $id
84+
*/
85+
public function deleteProduct(int $id) {
86+
$shopId = AuthService::getAuthContext()['shopId'];
87+
$this->productDao->deleteProduct($id, $shopId);
88+
}
89+
}
90+
91+
onInit(function () {
92+
registerController(ProductController::class);
93+
});
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
/**
3+
* Copyright 2020 Simone Sestito
4+
* This file is part of Shops Queue.
5+
*
6+
* Shops Queue is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU Affero General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Shops Queue is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU Affero General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with Shops Queue. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
class ShoppingListController extends BaseController {
21+
private $shoppingListDao;
22+
private $productDao;
23+
private $fcmService;
24+
25+
public function __construct(ShoppingListDao $shoppingListDao, ProductDao $productDao, FcmService $fcmService) {
26+
$this->shoppingListDao = $shoppingListDao;
27+
$this->productDao = $productDao;
28+
$this->fcmService = $fcmService;
29+
$this->registerRoute('/users/me/lists', 'GET', 'USER', 'getMyLists');
30+
$this->registerRoute('/shops/me/lists', 'GET', 'OWNER', 'getMyShopLists');
31+
$this->registerRoute('/lists', 'POST', 'USER', 'addList');
32+
$this->registerRoute('/lists/:id', 'DELETE', '*', 'deleteList');
33+
$this->registerRoute('/lists/:id', 'POST', 'OWNER', 'prepareList');
34+
}
35+
36+
/**
37+
* Get the lists of the current user
38+
* @return ShoppingList[]
39+
*/
40+
public function getMyLists() {
41+
$userId = AuthService::getAuthContext()['id'];
42+
$lists = $this->shoppingListDao->getListsByUserId($userId);
43+
return array_map(function ($list) {
44+
return new ShoppingList($list);
45+
}, $lists);
46+
}
47+
48+
/**
49+
* Get the lists of the current shop owner
50+
* @return ShoppingList[]
51+
*/
52+
public function getMyShopLists() {
53+
$shopId = AuthService::getAuthContext()['shopId'];
54+
$lists = $this->shoppingListDao->getListsByShopId($shopId);
55+
return array_map(function ($list) {
56+
return new ShoppingList($list);
57+
}, $lists);
58+
}
59+
60+
/**
61+
* Add a new list
62+
* @param NewShoppingList $newShoppingList
63+
* @return ShoppingList
64+
* @throws AppHttpException
65+
*/
66+
public function addList(NewShoppingList $newShoppingList) {
67+
// Check that every product is sold by the same shop
68+
$products = $this->productDao->getProductsByIds($newShoppingList->productIds);
69+
if (count($products) != count($newShoppingList->productIds)) {
70+
// Some product IDs aren't known
71+
throw new AppHttpException(HTTP_NOT_FOUND);
72+
}
73+
$shopId = $products[0]['shopId'];
74+
foreach ($products as $product) {
75+
if ($product['shopId'] !== $shopId)
76+
throw new AppHttpException(HTTP_BAD_REQUEST);
77+
}
78+
79+
$userId = AuthService::getAuthContext()['id'];
80+
$id = $this->shoppingListDao->addUserShoppingList($userId, $newShoppingList);
81+
$entity = $this->shoppingListDao->getListById($id);
82+
return new ShoppingList($entity);
83+
}
84+
85+
/**
86+
* Delete a list created by the current user
87+
* @param int $listId
88+
* @throws AppHttpException
89+
*/
90+
public function deleteList(int $listId) {
91+
$authContext = AuthService::getAuthContext();
92+
$userId = $authContext['id'];
93+
$userRole = $authContext['role'];
94+
$userShopId = $authContext['shopId'];
95+
96+
$entity = $this->shoppingListDao->getListById($listId);
97+
if ($entity == null)
98+
throw new AppHttpException(HTTP_NOT_FOUND);
99+
$shoppingList = new ShoppingList($entity);
100+
101+
if ($shoppingList->userId == $userId) {
102+
$this->shoppingListDao->deleteShoppingList($listId);
103+
} elseif ($userRole == 'OWNER' && $shoppingList->shop->id == $userShopId) {
104+
if (!$shoppingList->isReady) {
105+
// Send push notification
106+
$this->fcmService->sendPayloadToUser(
107+
$shoppingList->userId,
108+
FCM_TYPE_ORDER_CANCELLED,
109+
$shoppingList
110+
);
111+
}
112+
$this->shoppingListDao->deleteShoppingList($listId);
113+
} else {
114+
throw new AppHttpException(HTTP_NOT_AUTHORIZED);
115+
}
116+
}
117+
118+
/**
119+
* Set a shopping list as ready to be retired
120+
* @param int $listId
121+
* @return ShoppingList
122+
* @throws AppHttpException
123+
*/
124+
public function prepareList(int $listId) {
125+
$this->shoppingListDao->prepareShoppingList($listId);
126+
127+
$entity = $this->shoppingListDao->getListById($listId);
128+
if ($entity == null)
129+
throw new AppHttpException(HTTP_NOT_FOUND);
130+
131+
$shoppingList = new ShoppingList($entity);
132+
if ($shoppingList->shop->id != AuthService::getAuthContext()['shopId'])
133+
throw new AppHttpException(HTTP_NOT_AUTHORIZED);
134+
135+
// Send push notification
136+
$this->fcmService->sendPayloadToUser(
137+
$shoppingList->userId,
138+
FCM_TYPE_ORDER_READY,
139+
$shoppingList
140+
);
141+
142+
return new ShoppingList($entity);
143+
}
144+
}
145+
146+
onInit(function () {
147+
registerController(ShoppingListController::class);
148+
});

0 commit comments

Comments
 (0)