1
0
Files
nure/semester-4/СмП/lb-3/src/pzpi-23-2-sytnyk-yehor-task3.php
Sytnyk Yehor d22b7ddcaf SMP all works
2025-06-05 12:21:37 +03:00

470 lines
14 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

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.

#!/usr/bin/php
<?php
class DbException extends Exception {}
class AppException extends Exception {}
enum State
{
case Hello;
case Menu;
case Items;
case Checkout;
case Settins;
case Exit;
}
class DB
{
private $pdo;
/**
* Initializes database
*
* @param string $db_path
* @throws DbException If there's a database error.
*/
public function __construct($db_path)
{
try {
$this->pdo = new PDO("sqlite:" . $db_path);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
throw new DbException("Connection to DB failed.\nCaused by: " . $e->getMessage());
}
try {
$this->pdo->exec("
CREATE TABLE IF NOT EXISTS settings (
name TEXT,
age TEXT
);
");
if ($this->pdo->query("SELECT COUNT(*) FROM settings;")->fetchColumn() == 0) {
$this->pdo->exec("INSERT INTO settings (name, age) VALUES ('user', 0);");
}
} catch (PDOException $e) {
throw new DbException("Error initialising settings table.\nCaused by: " . $e->getMessage());
}
try {
$this->pdo->exec("
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL NOT NULL
);
");
if ($this->pdo->query("SELECT COUNT(id) FROM items;")->fetchColumn() == 0) {
$this->pdo->exec("
INSERT INTO items (name, price) VALUES ('Молоко пастеризоване', 12);
INSERT INTO items (name, price) VALUES ('Хліб чорний', 9);
INSERT INTO items (name, price) VALUES ('Сир білий', 21);
INSERT INTO items (name, price) VALUES ('Сметана 20%', 25);
INSERT INTO items (name, price) VALUES ('Кефір 1%', 19);
INSERT INTO items (name, price) VALUES ('Вода газована', 18);
INSERT INTO items (name, price) VALUES ('Печиво \"Весна\"', 14);
");
}
} catch (PDOException $e) {
throw new DbException("Error initialising items table.\nCaused by: " . $e->getMessage());
}
try {
$this->pdo->exec("
CREATE TABLE IF NOT EXISTS cart (
id INTEGER NOT NULL UNIQUE,
count INTEGER NOT NULL,
FOREIGN KEY(id) REFERENCES item(id)
);
");
} catch (PDOException $e) {
throw new DbException("Error initialising cart table.\nCaused by: " . $e->getMessage());
}
}
/**
* Updates user information in the database
*
* @param string $name
* @param int $age
* @return void
* @throws DbException If there's a database error.
*/
public function update_user($name, $age): void
{
try {
$stmt = $this->pdo->prepare("UPDATE settings SET name = :name, age = :age;");
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
$stmt->execute();
} catch (PDOException $e) {
throw new DbException("Error updating user info.\nCaused by: " . $e->getMessage());
}
}
/**
* Fetches all items from the database.
*
* @return array[]
* @throws DbException If there's a database error.
*/
public function get_items(): array
{
try {
$stmt = $this->pdo->query("SELECT id, name, price FROM items ORDER BY id;");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new DbException("Error retrieving data from the items table.\nCaused by: " . $e->getMessage());
}
}
/**
* Fetches all items in the cart from the database without price info.
*
* @return array[]
* @throws DbException If there's a database error.
*/
public function get_cart_no_price(): array
{
try {
$stmt = $this->pdo->query(
"SELECT name, count FROM cart
INNER JOIN items ON cart.id = items.id
ORDER BY cart.id;"
);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
}
}
/**
* Fetches all items in the cart from the database.
*
* @return array[]
* @throws DbException If there's a database error.
*/
public function get_cart(): array
{
try {
$stmt = $this->pdo->query(
"SELECT
cart.id, name, price, count, price*count as total_price
FROM cart
INNER JOIN items ON cart.id = items.id
ORDER BY cart.id;"
);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
throw new DbException("Error inserting init data in the items table.\nCaused by: " . $e->getMessage());
}
}
/**
* Add item to the cart
*
* @param int $id
* @param int $count
*
* @return bool
* @throws DbException If there's a database error.
*/
public function add_to_cart($id, $count): bool
{
try {
$stmt = $this->pdo->prepare(
"INSERT INTO cart
(id, count)
VALUES
(:id, :count)
ON CONFLICT(id)
DO UPDATE SET
count = :count
WHERE id = :id;"
);
return $stmt->execute(['id' => $id, 'count' => $count]);
} catch (PDOException $e) {
throw new DbException("Error adding item to the cart.\nCaused by: " . $e->getMessage());
}
}
/**
* Remove an item from the cart
*
* @param int $id
*
* @return bool
* @throws DbException If there's a database error.
*/
public function remove_from_cart($id): bool
{
try {
$stmt = $this->pdo->prepare("DELETE FROM cart WHERE id = :id");
return $stmt->execute(['id' => $id]);
} catch (PDOException $e) {
throw new DbException("Error removimg item from the cart.\nCaused by: " . $e->getMessage());
}
}
}
class App
{
private $db;
private $state;
private $menu_ops = <<<'END'
1 Вибрати товари
2 Отримати підсумковий рахунок
3 Налаштувати свій профіль
0 Вийти з програми
END;
private $hello = <<<'END'
################################
# ПРОДОВОЛЬЧИЙ МАГАЗИН "ВЕСНА" #
################################
END;
/**
* @param string $db_path
*/
public function __construct($db_path)
{
try {
$this->db = new DB($db_path);
$this->state = State::Hello;
} catch (DbException $e) {
throw new AppException("Error initializing app.\nCaused by: " . $e);
}
}
public function poll(): void
{
while ($this->state != State::Exit) {
switch ($this->state) {
case State::Hello:
$this->hello();
break;
case State::Menu:
$this->menu();
break;
case State::Items:
$this->items();
break;
case State::Checkout:
$this->checkout();
break;
case State::Settins:
$this->settings();
break;
default:
break;
}
}
}
private function menu(): void
{
echo "\n";
echo "$this->menu_ops\n";
$op = readline('Введіть команду: ');
switch ($op) {
case '1':
$this->state = State::Items;
break;
case '2':
$this->state = State::Checkout;
break;
case '3':
$this->state = State::Settins;
break;
case '0':
$this->state = State::Exit;
break;
default:
echo "ПОМИЛКА! Введіть правильну команду\n";
break;
}
echo "\n";
}
private function hello(): void
{
echo "$this->hello\n";
$this->state = State::Menu;
}
private function items(): void
{
$items = $this->db->get_items();
array_unshift($items, ['id' => "", 'name' => "НАЗВА", 'price' => "ЦІНА"]);
array_push($items, ['id' => " ", 'name' => "-----------", 'price' => ""]);
array_push($items, ['id' => "0", 'name' => "ПОВЕРНУТИСЯ", 'price' => ""]);
$columns = $this->count_columns($items);
while (true) {
$this->print_lits($items, $columns);
$id = readline("Виберіть товар: ");
if ($id == '0') {
break;
}
echo "\n";
$selected = null;
foreach ($items as $item) {
if ($item['id'] === (int)$id) $selected = $item;
}
if ($selected == null) {
echo "ПОМИЛКА! ВКАЗАНО НЕПРАВИЛЬНИЙ НОМЕР ТОВАРУ\n";
continue;
}
echo "Вибрано: {$selected['name']}\n";
$count = (int)readline("Введіть кількість, штук: ");
if ($count > 100) {
echo "ПОМИЛКА! Не можна додати більше 100 одиниць товару в кошик\n";
continue;
}
if ($count < 0) {
echo "ПОМИЛКА! Кількість не може бути від'ємною\n";
continue;
}
if ($count == 0) {
echo "ВИДАЛЯЮ З КОШИКА\n";
$this->db->remove_from_cart($id);
} else {
$this->db->add_to_cart($id, $count);
}
$cart = $this->db->get_cart_no_price();
if (count($cart) == 0) {
echo "КОШИК ПОРОЖНІЙ\n";
} else {
echo "\nУ КОШИКУ:\n";
array_unshift($cart, ['name' => "НАЗВА", 'count' => "КІЛЬКІСТЬ"]);
$cart_columns = $this->count_columns($cart);
$this->print_lits($cart, $cart_columns);
echo "\n";
}
}
$this->state = State::Menu;
}
private function checkout(): void
{
$cart = $this->db->get_cart();
if (count($cart) == 0) {
echo "КОШИК ПОРОЖНІЙ\n";
$this->state = State::Menu;
return;
} else {
echo "У КОШИКУ:\n";
array_unshift($cart, ['id' => "", 'name' => "НАЗВА", 'price' => "ЦІНА", 'count' => "КІЛЬКІСТЬ", 'total_price' => "ВАРТІСТЬ"]);
$cart_columns = $this->count_columns($cart);
$this->print_lits($cart, $cart_columns);
}
$total_price = array_reduce($cart, function ($carry, $item) {
return $carry + (int)$item['total_price'];
}, 0);
echo "РАЗОМ ДО СПЛАТИ: {$total_price}\n";
$this->state = State::Menu;
}
private function settings(): void
{
while (true) {
$name = readline("Ваше ім'я: ");
if ($name !== "" && preg_match("/[a-zA-Z]+/", $name))
break;
}
while (true) {
$age = readline("Ваш вік: ");
if (!filter_var($age, FILTER_VALIDATE_INT)) {
echo "ПОМИЛКА! Вік користувача потрібно вказати числом\n\n";
continue;
}
if ($age < 7 || $age > 150) {
echo "ПОМИЛКА! Користувач повинен мати вік від 7 та до 150 років\n\n";
continue;
}
break;
}
echo "\n";
$this->db->update_user($name, $age);
$this->state = State::Menu;
}
/**
* @param array<array> $items
* @return array<int>
*/
private function count_columns($items): array
{
$columns = [];
foreach ($items as $item) {
foreach ($item as $field => $value) {
if (!key_exists($field, $columns))
$columns[$field] = mb_strlen($value);
else
$columns[$field] = max(mb_strlen($value), $columns[$field]);
}
}
return $columns;
}
/**
* @param array $element
* @param array<int> $columns
*/
private function pad_row($element, $columns): string
{
$result = [];
foreach ($element as $field => $value)
$result[] = mb_str_pad($value, $columns[$field], ' ', STR_PAD_RIGHT);
return implode(" ", $result);
}
/**
* @param array<array> $items
* @param array<int> $columns
*/
private function print_lits($items, $columns): void
{
foreach ($items as $item)
echo $this->pad_row($item, $columns) . "\n";
}
}
try {
$app = new App("db.sqlite");
} catch (AppException $e) {
echo $e;
exit(1);
}
$app->poll();