1
0

SMP all works

This commit is contained in:
Sytnyk Yehor
2025-06-05 12:21:37 +03:00
parent bdcbdb850a
commit d22b7ddcaf
34 changed files with 4932 additions and 0 deletions

View File

@ -0,0 +1,681 @@
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
Кафедра Програмної інженерії
Звіт
з лабораторної роботи №2
з дисципліни: «Скриптові мови програмування»
з теми: «Продовольчий магазин «Весна»»
Виконав: Перевірив:
ст. гр. ПЗПІ-23-2 Старший викладач кафедри ПІ
Ситник Є. С. Сокорчук І. П.
Харків 2025
2
2 ПРОДОВОЛЬЧИЙ МАГАЗИН «ВЕСНА»
2.1 Історія змін
№ Дата Версія звіту Опис змін та виправлень
1 03.03.2025 0.1 Створено звіт
2.2 Мета роботи
Лабораторна робота полягає у розробці консольного застосунку
«Продовольчий магазин «Весна»» засобами мови програмування «PHP».
2.3 Хід роботи
2.3.1 Архітектура програми
Для реалізації консольного застосунку було обрано об’єктно-орієнтований
підхід з використанням наступних компонентів:
2.3.1.1 Класи та їх призначення:
DB клас для роботи з базою даних «SQLite», що забезпечує збереження
даних про товари, кошик користувача та налаштування профілю;
App основний клас застосунку, що реалізує логіку роботи програми та
взаємодію з користувачем;
DbException та AppException класи винятків для обробки помилок.
2.3.1.2 Enum State:
Для управління станами програми використовується перелічення «State» з
наступними значеннями:
Hello привітальний екран;
Menu головне меню;
Items вибір товарів;
Checkout формування рахунку;
Settings налаштування профілю;
Exit вихід з програми.
3
2.3.2 Структура бази даних
Програма використовує базу даних SQLite з трьома таблицями:
settings зберігає інформацію про користувача (ім’я та вік);
items містить каталог товарів з їх назвами та цінами;
cart зберігає товари, додані до кошика, з їх кількістю.
2.3.3 Основний функціонал
2.3.3.1 Головне меню:
Програма відображає заголовок магазину та пропонує користувачу чотири
основні опції:
вибір товарів для покупки;
перегляд підсумкового рахунку;
налаштування профілю користувача;
вихід з програми.
2.3.3.2 Вибір товарів:
При виборі першого пункту меню користувач потрапляє в режим покупки
товарів, де:
відображається список доступних товарів з цінами;
можна вибрати товар за номером та вказати кількість;
товари додаються до кошика;
при введенні кількості «0» товар видаляється з кошика;
реалізована валідація введених даних.
2.3.3.3 Підсумковий рахунок:
Другий пункт меню формує детальний рахунок з інформацією про:
номер, назву та ціну кожного товару;
кількість товару в кошику;
загальну вартість кожного товару;
підсумкову суму до сплати.
4
2.3.3.4 Налаштування профілю:
Третій пункт дозволяє користувачу вказати своє ім’я та вік з валідацією:
ім’я повинно містити хоча б одну літеру;
вік повинен бути в діапазоні від 7 до 150 років.
2.3.4 Технічні деталі
Програма написана з дотриманням сучасних стандартів «PHP»:
використання строгої типізації;
документування методів за допомогою «PHPDoc»;
дотримання принципів «SOLID»;
розділення логіки на окремі методи для кращої читабельності.
2.4 Висновки
Під час даної лабораторної роботи я вивчив основні принципи роботи з «PHP»
для написання консольних програм. Зокрема, було освоєно:
створення об’єктно-орієнтованих консольних застосунків на «PHP»;
роботу з базою даних «SQLite» через «PDO»;
обробку користувацького вводу та валідацію даних;
використання перелічень («enum») для управління станами програми;
реалізацію системи обробки винятків;
форматування виводу в консолі для створення зручного інтерфейсу.
Програмне забезпечення, реалізоване протягом даної лабораторної роботи,
повністю відповідає поставленим вимогам та забезпечує всі необхідні функції для
роботи з продовольчим магазином. Застосунок має зручний інтерфейс, надійну
систему валідації даних та ефективно працює з базою даних для збереження
інформації про товари та користувача.
5
ДОДАТОК А
Відеозапис
Відеозапис презентації результатів лабораторної роботи:
https://youtu.be/CMGeL1OdyvI
Хронологічний опис відеозапису:
00:00 Вступ та опис завдання
00:19 Архітектура програми
00:39 Робота з базою даних
01:43 Реалізація станів програми
02:17 Формування списку товарів
03:13 Додавання товарів до кошика
04:15 Робота з кошиком
05:10 Налаштування профілю користувача
05:38 Демонстрація роботи програми
6
ДОДАТОК Б
Програмний код
Б.1 Скрипт з реалізацією програми
GitHub репозиторій:
https://github.com/NureSytnykYehor/smp-pzpi-23-2-sytnyk-yehor/blob/main/Lab2/smp-pzpi-23-2-sytnyk-yehor-lab2/smp-pzpi-23-2-sytnyk-yehor-lab2-code
1 #!/usr/bin/php
2
3 <?php
4
5 class DbException extends Exception {}
6 class AppException extends Exception {}
7
8 enum State
9 {
10 case Hello;
11 case Menu;
12 case Items;
13 case Checkout;
14 case Settins;
15 case Exit;
16 }
17
18 class DB
19 {
20 private $pdo;
21
22 /**
23 * Initializes database
24 *
25 * @param string $db_path
26 * @throws DbException If there's a database error.
27 */
28 public function __construct($db_path)
29 {
30 try {
31 $this->pdo = new PDO("sqlite:" . $db_path);
32 $this->pdo->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
33 } catch (PDOException $e) {
34 throw new DbException("Connection to DB failed.\nCaused
by: " . $e->getMessage());
35 }
36
37 try {
38 $this->pdo->exec("
39 CREATE TABLE IF NOT EXISTS settings (
40 name TEXT,
41 age TEXT
7
42 );
43 ");
44
45 if ($this->pdo->query("SELECT COUNT(*) FROM settings;")-
>fetchColumn() == 0) {
46 $this->pdo->exec("INSERT INTO settings (name, age)
VALUES ('user', 0);");
47 }
48 } catch (PDOException $e) {
49 throw new DbException("Error initialising settings table.
\nCaused by: " . $e->getMessage());
50 }
51
52 try {
53 $this->pdo->exec("
54 CREATE TABLE IF NOT EXISTS items (
55 id INTEGER PRIMARY KEY AUTOINCREMENT,
56 name TEXT NOT NULL,
57 price REAL NOT NULL
58 );
59 ");
60
61 if ($this->pdo->query("SELECT COUNT(id) FROM items;")-
>fetchColumn() == 0) {
62 $this->pdo->exec("
63 INSERT INTO items (name, price) VALUES ('Молоко
пастеризоване', 12);
64 INSERT INTO items (name, price) VALUES ('Хліб
чорний', 9);
65 INSERT INTO items (name, price) VALUES ('Сир
білий', 21);
66 INSERT INTO items (name, price) VALUES ('Сметана
20%', 25);
67 INSERT INTO items (name, price) VALUES ('Кефір
1%', 19);
68 INSERT INTO items (name, price) VALUES ('Вода
газована', 18);
69 INSERT INTO items (name, price) VALUES ('Печиво
\"Весна\"', 14);
70 ");
71 }
72 } catch (PDOException $e) {
73 throw new DbException("Error initialising items table.
\nCaused by: " . $e->getMessage());
74 }
75
76 try {
77 $this->pdo->exec("
78 CREATE TABLE IF NOT EXISTS cart (
79 id INTEGER NOT NULL UNIQUE,
80 count INTEGER NOT NULL,
81 FOREIGN KEY(id) REFERENCES item(id)
82 );
83 ");
84 } catch (PDOException $e) {
8
85 throw new DbException("Error initialising cart table.
\nCaused by: " . $e->getMessage());
86 }
87 }
88
89 /**
90 * Updates user information in the database
91 *
92 * @param string $name
93 * @param int $age
94 * @return void
95 * @throws DbException If there's a database error.
96 */
97 public function update_user($name, $age): void
98 {
99 try {
100 $stmt = $this->pdo->prepare("UPDATE settings SET name
= :name, age = :age;");
101
102 $stmt->bindParam(':name', $name, PDO::PARAM_STR);
103 $stmt->bindParam(':age', $age, PDO::PARAM_INT);
104
105 $stmt->execute();
106 } catch (PDOException $e) {
107 throw new DbException("Error updating user info.\nCaused
by: " . $e->getMessage());
108 }
109 }
110
111 /**
112 * Fetches all items from the database.
113 *
114 * @return array[]
115 * @throws DbException If there's a database error.
116 */
117 public function get_items(): array
118 {
119 try {
120 $stmt = $this->pdo->query("SELECT id, name, price FROM
items ORDER BY id;");
121 return $stmt->fetchAll(PDO::FETCH_ASSOC);
122 } catch (PDOException $e) {
123 throw new DbException("Error retrieving data from the
items table.\nCaused by: " . $e->getMessage());
124 }
125 }
126
127 /**
128 * Fetches all items in the cart from the database without price
info.
129 *
130 * @return array[]
131 * @throws DbException If there's a database error.
132 */
133 public function get_cart_no_price(): array
134 {
9
135 try {
136 $stmt = $this->pdo->query(
137 "SELECT name, count FROM cart
138 INNER JOIN items ON cart.id = items.id
139 ORDER BY cart.id;"
140 );
141 return $stmt->fetchAll(PDO::FETCH_ASSOC);
142 } catch (PDOException $e) {
143 throw new DbException("Error inserting init data in the
items table.\nCaused by: " . $e->getMessage());
144 }
145 }
146
147 /**
148 * Fetches all items in the cart from the database.
149 *
150 * @return array[]
151 * @throws DbException If there's a database error.
152 */
153 public function get_cart(): array
154 {
155 try {
156 $stmt = $this->pdo->query(
157 "SELECT
158 cart.id, name, price, count, price*count as
total_price
159 FROM cart
160 INNER JOIN items ON cart.id = items.id
161 ORDER BY cart.id;"
162 );
163 return $stmt->fetchAll(PDO::FETCH_ASSOC);
164 } catch (PDOException $e) {
165 throw new DbException("Error inserting init data in the
items table.\nCaused by: " . $e->getMessage());
166 }
167 }
168
169 /**
170 * Add item to the cart
171 *
172 * @param int $id
173 * @param int $count
174 *
175 * @return bool
176 * @throws DbException If there's a database error.
177 */
178 public function add_to_cart($id, $count): bool
179 {
180 try {
181 $stmt = $this->pdo->prepare(
182 "INSERT INTO cart
183 (id, count)
184 VALUES
185 (:id, :count)
186 ON CONFLICT(id)
187 DO UPDATE SET
10
188 count = :count
189 WHERE id = :id;"
190 );
191 return $stmt->execute(['id' => $id, 'count' => $count]);
192 } catch (PDOException $e) {
193 throw new DbException("Error adding item to the cart.
\nCaused by: " . $e->getMessage());
194 }
195 }
196
197 /**
198 * Remove an item from the cart
199 *
200 * @param int $id
201 *
202 * @return bool
203 * @throws DbException If there's a database error.
204 */
205 public function remove_from_cart($id): bool
206 {
207 try {
208 $stmt = $this->pdo->prepare("DELETE FROM cart WHERE id
= :id");
209 return $stmt->execute(['id' => $id]);
210 } catch (PDOException $e) {
211 throw new DbException("Error removimg item from the cart.
\nCaused by: " . $e->getMessage());
212 }
213 }
214 }
215
216 class App
217 {
218 private $db;
219 private $state;
220
221 private $menu_ops = <<<'END'
222 1 Вибрати товари
223 2 Отримати підсумковий рахунок
224 3 Налаштувати свій профіль
225 0 Вийти з програми
226 END;
227 private $hello = <<<'END'
228 ################################
229 # ПРОДОВОЛЬЧИЙ МАГАЗИН "ВЕСНА" #
230 ################################
231 END;
232
233
234 /**
235 * @param string $db_path
236 */
237 public function __construct($db_path)
238 {
239 try {
240 $this->db = new DB($db_path);
11
241 $this->state = State::Hello;
242 } catch (DbException $e) {
243 throw new AppException("Error initializing app.\nCaused
by: " . $e);
244 }
245 }
246
247 public function poll(): void
248 {
249 while ($this->state != State::Exit) {
250 switch ($this->state) {
251 case State::Hello:
252 $this->hello();
253 break;
254 case State::Menu:
255 $this->menu();
256 break;
257 case State::Items:
258 $this->items();
259 break;
260 case State::Checkout:
261 $this->checkout();
262 break;
263 case State::Settins:
264 $this->settings();
265 break;
266
267 default:
268 break;
269 }
270 }
271 }
272
273 private function menu(): void
274 {
275 echo "\n";
276 echo "$this->menu_ops\n";
277
278 $op = readline('Введіть команду: ');
279 switch ($op) {
280 case '1':
281 $this->state = State::Items;
282 break;
283 case '2':
284 $this->state = State::Checkout;
285 break;
286 case '3':
287 $this->state = State::Settins;
288 break;
289 case '0':
290 $this->state = State::Exit;
291 break;
292
293 default:
294 echo "ПОМИЛКА! Введіть правильну команду\n";
295 break;
12
296 }
297
298 echo "\n";
299 }
300 private function hello(): void
301 {
302 echo "$this->hello\n";
303 $this->state = State::Menu;
304 }
305 private function items(): void
306 {
307 $items = $this->db->get_items();
308 array_unshift($items, ['id' => "№", 'name' => "НАЗВА", 'price'
=> "ЦІНА"]);
309 array_push($items, ['id' => " ", 'name' => "-----------",
'price' => ""]);
310 array_push($items, ['id' => "0", 'name' => "ПОВЕРНУТИСЯ",
'price' => ""]);
311 $columns = $this->count_columns($items);
312
313 while (true) {
314 $this->print_lits($items, $columns);
315
316 $id = readline("Виберіть товар: ");
317
318 if ($id == '0') {
319 break;
320 }
321
322 echo "\n";
323
324 $selected = null;
325 foreach ($items as $item) {
326 if ($item['id'] === (int)$id)
327 $selected = $item;
328 }
329
330 if ($selected == null) {
331 echo "ПОМИЛКА! ВКАЗАНО НЕПРАВИЛЬНИЙ НОМЕР ТОВАРУ\n";
332 continue;
333 }
334
335 echo "Вибрано: {$selected['name']}\n";
336
337 $count = (int)readline("Введіть кількість, штук: ");
338
339 if ($count > 100) {
340 echo "ПОМИЛКА! Не можна додати більше 100 одиниць
товару в кошик\n";
341 continue;
342 }
343
344 if ($count < 0) {
345 echo "ПОМИЛКА! Кількість не може бути від'ємною\n";
346 continue;
347 }
13
348
349 if ($count == 0) {
350 echo "ВИДАЛЯЮ З КОШИКА\n";
351 $this->db->remove_from_cart($id);
352 } else {
353 $this->db->add_to_cart($id, $count);
354 }
355
356 $cart = $this->db->get_cart_no_price();
357 if (count($cart) == 0) {
358 echo "КОШИК ПОРОЖНІЙ\n";
359 } else {
360 echo "\nУ КОШИКУ:\n";
361 array_unshift($cart, ['name' => "НАЗВА", 'count' =>
"КІЛЬКІСТЬ"]);
362 $cart_columns = $this->count_columns($cart);
363 $this->print_lits($cart, $cart_columns);
364 echo "\n";
365 }
366 }
367
368 $this->state = State::Menu;
369 }
370 private function checkout(): void
371 {
372 $cart = $this->db->get_cart();
373 if (count($cart) == 0) {
374 echo "КОШИК ПОРОЖНІЙ\n";
375 $this->state = State::Menu;
376 return;
377 } else {
378 echo "У КОШИКУ:\n";
379 array_unshift($cart, ['id' => "№", 'name' => "НАЗВА",
'price' => "ЦІНА", 'count' => "КІЛЬКІСТЬ", 'total_price' =>
"ВАРТІСТЬ"]);
380 $cart_columns = $this->count_columns($cart);
381 $this->print_lits($cart, $cart_columns);
382 }
383
384 $total_price = array_reduce($cart, function ($carry, $item) {
385 return $carry + (int)$item['total_price'];
386 }, 0);
387 echo "РАЗОМ ДО СПЛАТИ: {$total_price}\n";
388
389 $this->state = State::Menu;
390 }
391 private function settings(): void
392 {
393 while (true) {
394 $name = readline("Ваше ім'я: ");
395 if ($name !== "" && preg_match("/[a-zA-Z]+/", $name))
396 break;
397 }
398
399 while (true) {
400 $age = readline("Ваш вік: ");
14
401
402 if (!filter_var($age, FILTER_VALIDATE_INT)) {
403 echo "ПОМИЛКА! Вік користувача потрібно вказати
числом\n\n";
404 continue;
405 }
406
407 if ($age < 7 || $age > 150) {
408 echo "ПОМИЛКА! Користувач повинен мати вік від 7 та до
150 років\n\n";
409 continue;
410 }
411
412 break;
413 }
414
415 echo "\n";
416
417 $this->db->update_user($name, $age);
418
419 $this->state = State::Menu;
420 }
421
422 /**
423 * @param array<array> $items
424 * @return array<int>
425 */
426 private function count_columns($items): array
427 {
428 $columns = [];
429 foreach ($items as $item) {
430 foreach ($item as $field => $value) {
431 if (!key_exists($field, $columns))
432 $columns[$field] = mb_strlen($value);
433 else
434 $columns[$field] = max(mb_strlen($value),
$columns[$field]);
435 }
436 }
437
438 return $columns;
439 }
440 /**
441 * @param array $element
442 * @param array<int> $columns
443 */
444 private function pad_row($element, $columns): string
445 {
446 $result = [];
447 foreach ($element as $field => $value)
448 $result[] = mb_str_pad($value, $columns[$field], ' ',
STR_PAD_RIGHT);
449
450 return implode(" ", $result);
451 }
452 /**
15
453 * @param array<array> $items
454 * @param array<int> $columns
455 */
456 private function print_lits($items, $columns): void
457 {
458 foreach ($items as $item)
459 echo $this->pad_row($item, $columns) . "\n";
460 }
461 }
462
463 try {
464 $app = new App("db.sqlite");
465 } catch (AppException $e) {
466 echo $e;
467 exit(1);
468 }
469
470 $app->poll();