TGViewer
Библиотека собеса по PHP | вопросы с собеседований Библиотека собеса по PHP | вопросы с собеседований @php_interview_lib · 3.1K subscribers
Post #1276 2.44K
✔️ PHP-тест: клон, который мутирует оригинал

Пользователь нажал «Повторить заказ». Цены в его старом заказе изменились.

📦 Задание

Фича: кнопка «Повторить заказ» в личном кабинете. Копирует предыдущий заказ, применяет промокод, создаёт новый черновик. Оригинал помечается флагом repeated для аналитики.

Через неделю — тикет от бухгалтерии: суммы в старых заказах не сходятся с тем, что было при оплате. Проблема только у заказов, которые хотя бы раз «повторяли». Суммы занижены ровно на размер скидки по промокоду.

// src/Order/Order.php
class Order
{
public function __construct(
private ?int $id,
private int $userId,
private array $items,
private string $status,
private bool $repeated = false,
) {}

public function getId(): ?int { return $this->id; }
public function getItems(): array { return $this->items; }
public function getStatus(): string { return $this->status; }

public function resetForRepeat(): void
{
$this->id = null;
$this->status = 'draft';
$this->repeated = false;
}

public function markAsRepeated(): void
{
$this->repeated = true;
}

public function calculateTotal(): int
{
return array_sum(array_map(
fn(OrderItem $i) => $i->getSubtotal(),
$this->items
));
}
}

// src/Order/OrderItem.php
class OrderItem
{
public function __construct(
private int $productId,
private int $qty,
private int $price,
) {}

public function getProductId(): int { return $this->productId; }
public function getQty(): int { return $this->qty; }
public function getPrice(): int { return $this->price; }

public function getSubtotal(): int
{
return $this->qty * $this->price;
}

public function applyDiscount(int $percent): void
{
$this->price = (int) round(
$this->price * (1 - $percent / 100)
);
}
}

// src/Order/RepeatOrderHandler.php
class RepeatOrderHandler
{
public function __construct(
private OrderRepository $orders,
private PromoService $promo,
) {}

public function handle(int $originalId, ?string $promoCode): Order
{
$original = $this->orders->findById($originalId);

$copy = clone $original;
$copy->resetForRepeat();

if ($promoCode !== null) {
$discount = $this->promo->resolve($promoCode);

foreach ($copy->getItems() as $item) {
$item->applyDiscount($discount->percent);
}
}

$this->orders->save($copy);

$original->markAsRepeated();
$this->orders->save($original);

return $copy;
}
}

// src/Repository/OrderRepository.php
class OrderRepository
{
public function __construct(private PDO $pdo) {}

public function save(Order $order): void
{
if ($order->getId() === null) {
$this->insert($order);
} else {
$this->update($order);
}
}

private function update(Order $order): void
{
$this->pdo->prepare(
'UPDATE orders SET status = ?, repeated = ? WHERE id = ?'
)->execute([$order->getStatus(), (int) $order->isRepeated(), $order->getId()]);

$this->pdo->prepare('DELETE FROM order_items WHERE order_id = ?')
->execute([$order->getId()]);

foreach ($order->getItems() as $item) {
$this->pdo->prepare(
'INSERT INTO order_items (order_id, product_id, qty, price) VALUES (?, ?, ?, ?)'
)->execute([$order->getId(), $item->getProductId(), $item->getQty(), $item->getPrice()]);
}
}
}


🔹 Задачи

— Объяснить, каким образом цены в оригинальном заказе оказались изменены в базе
— Исправить код так, чтобы оригинал гарантированно не мутировал

Ставьте → 🔥 если нравится формат. Если нет → 🌚

💬 Решения пишите в комменты под спойлер — сравним подходы.
  • 🔥 4
  • 👍 2
  • ❤ 1
  • 🌚 1
More from @php_interview_lib
  1. Sep 15, 2026❓ Расскажите о паттерне Bridge Bridge — это структурный паттерн, который разделяет абстрак…
  2. Sep 15, 2026😭 Как не потратить недельный лимит AI-кодинга за три дня? Разберём на вебинаре, как трати…
  3. Aug 4, 2026Какова разница между «cookie» и «session»? В PHP «cookie» и «session» — это механизмы упра…
  4. Aug 4, 2026👅 Самое сложное — выбрать не курс, а направление Сегодня хочется разобраться в AI-агентах…
  5. Aug 4, 2026Один доступ вместо вечного выбора между «нужно для работы» и «давно хотелось изучить» 👇
  6. Jul 31, 2026❓ Объясни разницу между Gate и Policy в Laravel? Оба инструмента для авторизации, но с раз…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →