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,469 @@
#!/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();