1
0
Files
nure/semester-4/СмП/lb-2/Лр_2_Ситник_ПЗПІ_23_2.txt
Sytnyk Yehor d22b7ddcaf SMP all works
2025-06-05 12:21:37 +03:00

682 lines
26 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ХАРКІВСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ РАДІОЕЛЕКТРОНІКИ
Кафедра Програмної інженерії
Звіт
з лабораторної роботи №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();