SMP all works
This commit is contained in:
469
semester-4/СмП/lb-3/src/pzpi-23-2-sytnyk-yehor-task3.php
Executable file
469
semester-4/СмП/lb-3/src/pzpi-23-2-sytnyk-yehor-task3.php
Executable 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();
|
Reference in New Issue
Block a user