<?php
/**
* ========================================
* 📦 Další praktické OOP koncepty (krátce)
* ========================================
*/
// ============================================================================
// 1. 📦 DTO (Data Transfer Objects) - Typované přenosy dat
// ============================================================================
// ❌ ŠPATNĚ - Arrays všude
function createOrder(array $data)
{
$customerId = $data['customer_id']; // Může chybět!
$items = $data['items']; // Co je uvnitř? Kdo ví...
$note = $data['note'] ?? null;
}
$orderData = [
'customer_id' => 123,
'items' => [...],
'shipping' => 'DHL'
];
createOrder($orderData);
// ✅ DOBŘE - Typovaný DTO
class CreateOrderDTO
{
public int $customerId;
public array $items;
public string $shippingMethod;
public ?string $note;
public function __construct(int $customerId, array $items, string $shippingMethod, ?string $note = null)
{
$this->customerId = $customerId;
$this->items = $items;
$this->shippingMethod = $shippingMethod;
$this->note = $note;
}
// Nebo ze JSON
public static function fromArray(array $data): self
{
return new self(
$data['customer_id'],
$data['items'],
$data['shipping_method'],
$data['note'] ?? null
);
}
}
function createOrderSafe(CreateOrderDTO $dto)
{
// Máš jistotu, že všechno je tam a správného typu!
$customerId = $dto->customerId;
$items = $dto->items;
}
$dto = new CreateOrderDTO(123, [...], 'DHL', 'Handle with care');
createOrderSafe($dto);
// ============================================================================
// 2. 🗄️ REPOSITORY PATTERN - Databázová logika na jednom místě
// ============================================================================
// ❌ ŠPATNĚ - SQL všude v kódu
class OrderController
{
public function show(int $id)
{
$order = $this->db->query("SELECT * FROM orders WHERE id = ?", $id)->fetch();
// SQL logika roztroušená po celé aplikaci
}
public function listByCustomer(int $customerId)
{
$orders = $this->db->query("SELECT * FROM orders WHERE customer_id = ?", $customerId)->fetchAll();
}
}
// ✅ DOBŘE - Repository
interface OrderRepository
{
public function find(int $id): ?Order;
public function findByCustomer(int $customerId): array;
public function findRecent(int $limit = 10): array;
public function save(Order $order): void;
public function delete(Order $order): void;
}
class DatabaseOrderRepository implements OrderRepository
{
private Context $db;
public function __construct(Context $db)
{
$this->db = $db;
}
public function find(int $id): ?Order
{
$row = $this->db->table('orders')->get($id);
return $row ? $this->createOrderFromRow($row) : null;
}
public function findByCustomer(int $customerId): array
{
return $this->db->table('orders')
->where('customer_id', $customerId)
->fetchAll();
}
public function findRecent(int $limit = 10): array
{
return $this->db->table('orders')
->order('created_at DESC')
->limit($limit)
->fetchAll();
}
public function save(Order $order): void
{
// Logika ukládání
}
private function createOrderFromRow($row): Order
{
// Konverze řádku na entitu
}
}
// Použití:
class OrderService
{
private OrderRepository $orderRepo;
public function __construct(OrderRepository $orderRepo)
{
$this->orderRepo = $orderRepo;
}
public function getCustomerOrders(int $customerId): array
{
return $this->orderRepo->findByCustomer($customerId);
}
}
// VÝHODY:
// - Veškerá databázová logika na jednom místě
// - Snadné testování (mock repository)
// - Lze změnit databázi bez změny business logiky
// ============================================================================
// 3. 🎯 STRATEGY PATTERN - Nahrazení obrovských if/switch
// ============================================================================
// ❌ ŠPATNĚ - Obrovský if/switch
class PaymentProcessor
{
public function process(Order $order, string $paymentType)
{
if ($paymentType === 'credit_card') {
// 50 řádků kódu pro credit card
$api = new CreditCardAPI();
$api->charge($order->getTotal());
} elseif ($paymentType === 'paypal') {
// 50 řádků kódu pro PayPal
$paypal = new PayPalAPI();
$paypal->createPayment($order);
} elseif ($paymentType === 'bank_transfer') {
// 50 řádků kódu pro převod
$bank = new BankAPI();
$bank->generateQR($order);
} elseif ($paymentType === 'cash_on_delivery') {
// 30 řádků kódu pro dobírku
// ...
}
// Při přidání nové platby musíš měnit tuto třídu!
}
}
// ✅ DOBŘE - Strategy Pattern
interface PaymentStrategy
{
public function pay(Order $order): PaymentResult;
}
class CreditCardPayment implements PaymentStrategy
{
public function pay(Order $order): PaymentResult
{
$api = new CreditCardAPI();
return $api->charge($order->getTotal());
}
}
class PayPalPayment implements PaymentStrategy
{
public function pay(Order $order): PaymentResult
{
$paypal = new PayPalAPI();
return $paypal->createPayment($order);
}
}
class BankTransferPayment implements PaymentStrategy
{
public function pay(Order $order): PaymentResult
{
$bank = new BankAPI();
return $bank->generateQR($order);
}
}
class PaymentProcessor2
{
private array $strategies = [];
public function registerStrategy(string $type, PaymentStrategy $strategy): void
{
$this->strategies[$type] = $strategy;
}
public function process(Order $order, string $paymentType): PaymentResult
{
if (!isset($this->strategies[$paymentType])) {
throw new \InvalidArgumentException("Unknown payment type: {$paymentType}");
}
return $this->strategies[$paymentType]->pay($order);
}
}
// Registrace v DI containeru:
$processor = new PaymentProcessor2();
$processor->registerStrategy('credit_card', new CreditCardPayment());
$processor->registerStrategy('paypal', new PayPalPayment());
$processor->registerStrategy('bank_transfer', new BankTransferPayment());
// Použití:
$result = $processor->process($order, 'paypal');
// VÝHODY:
// - Přidání nové platby = nová třída, bez změny procesoru
// - Každá strategie je samostatná, testovatelná
// - Open/Closed Principle (otevřené pro rozšíření, zavřené pro modifikaci)
// ============================================================================
// 4. 📚 COLLECTIONS - Typované kolekce místo arrays
// ============================================================================
// ❌ ŠPATNĚ - Arrays bez typu
function processOrders(array $orders)
{
// Co je v tom array? Orders? IDs? Něco jiného?
foreach ($orders as $order) {
// $order může být cokoliv!
}
}
$orders = [1, 2, 3]; // Omylem IDčka místo objektů
processOrders($orders); // Fatal error až za běhu
// ✅ DOBŘE - Typovaná kolekce
class OrderCollection
{
private array $orders = [];
public function add(Order $order): void
{
$this->orders[] = $order;
}
public function get(int $index): ?Order
{
return $this->orders[$index] ?? null;
}
public function all(): array
{
return $this->orders;
}
public function count(): int
{
return count($this->orders);
}
public function filter(callable $callback): self
{
$filtered = new self();
foreach ($this->orders as $order) {
if ($callback($order)) {
$filtered->add($order);
}
}
return $filtered;
}
public function map(callable $callback): array
{
return array_map($callback, $this->orders);
}
public function getTotalValue(): Money
{
$total = new Money(0, 'CZK');
foreach ($this->orders as $order) {
$total = $total->add($order->getTotal());
}
return $total;
}
}
// Použití:
function processOrdersSafe(OrderCollection $orders)
{
// Víš jistě, že jsou tam Order objekty!
foreach ($orders->all() as $order) {
echo $order->getId(); // ✅ Funguje
}
}
$collection = new OrderCollection();
$collection->add(new Order(...));
$collection->add(new Order(...));
// Filtrování:
$paidOrders = $collection->filter(fn($order) => $order->isPaid());
// Total:
$totalValue = $collection->getTotalValue();
// ❌ Nelze omylem přidat něco jiného:
// $collection->add(123); // TypeError!
// ============================================================================
// 5. ⚠️ EXCEPTION HIERARCHY - Vlastní výjimky
// ============================================================================
// ❌ ŠPATNĚ - Obecné exceptions
function findOrder(int $id): Order
{
$order = $this->db->find($id);
if (!$order) {
throw new \Exception("Order not found"); // Obecná Exception
}
return $order;
}
try {
$order = findOrder(123);
} catch (\Exception $e) {
// Catch všeho možného, nevíš, co se stalo
}
// ✅ DOBŘE - Vlastní exception hierarchy
// Báze pro všechny doménové výjimky
abstract class DomainException extends \Exception { }
// Specifické výjimky
class OrderNotFoundException extends DomainException
{
public function __construct(int $orderId)
{
parent::__construct("Order #{$orderId} not found");
}
}
class InvalidOrderStateException extends DomainException
{
public function __construct(string $message)
{
parent::__construct($message);
}
}
class PaymentFailedException extends DomainException
{
private string $paymentType;
public function __construct(string $paymentType, string $reason)
{
$this->paymentType = $paymentType;
parent::__construct("Payment via {$paymentType} failed: {$reason}");
}
public function getPaymentType(): string
{
return $this->paymentType;
}
}
class InsufficientStockException extends DomainException { }
// Použití:
function findOrderSafe(int $id): Order
{
$order = $this->db->find($id);
if (!$order) {
throw new OrderNotFoundException($id);
}
return $order;
}
function shipOrder(Order $order): void
{
if (!$order->isPaid()) {
throw new InvalidOrderStateException("Cannot ship unpaid order");
}
if (!$order->hasStock()) {
throw new InsufficientStockException("Product out of stock");
}
// Ship...
}
// Handling - specifický catch:
try {
$order = findOrderSafe(123);
shipOrder($order);
} catch (OrderNotFoundException $e) {
// Objednávka neexistuje - vrať 404
return $this->notFound();
} catch (InvalidOrderStateException $e) {
// Špatný stav - vrať error message
return $this->error($e->getMessage());
} catch (InsufficientStockException $e) {
// Není na skladě - pošli email
$this->notifyOutOfStock($order);
} catch (DomainException $e) {
// Jakákoliv jiná doménová výjimka
$this->logger->error($e);
}
// VÝHODY:
// - Víš přesně, co se stalo
// - Můžeš catchnout specifické chyby
// - Lepší error handling
// - Exception obsahuje kontext (paymentType, orderId...)
// ============================================================================
// BONUS: Kombinace všeho dohromady
// ============================================================================
// Repository
interface OrderRepositoryFull
{
public function find(int $id): ?Order;
public function save(Order $order): void;
}
// Service s Dependency Injection
class OrderServiceFull
{
private OrderRepositoryFull $orderRepo;
private PaymentProcessor2 $paymentProcessor;
public function __construct(
OrderRepositoryFull $orderRepo,
PaymentProcessor2 $paymentProcessor
) {
$this->orderRepo = $orderRepo;
$this->paymentProcessor = $paymentProcessor;
}
// DTO jako parametr
public function createAndPay(CreateOrderDTO $dto): Order
{
// Vytvoř objednávku
$order = new Order($dto->customerId, $dto->items);
// Ulož
$this->orderRepo->save($order);
// Zaplať pomocí Strategy
try {
$result = $this->paymentProcessor->process($order, $dto->shippingMethod);
if (!$result->isSuccessful()) {
throw new PaymentFailedException(
$dto->shippingMethod,
$result->getError()
);
}
} catch (PaymentFailedException $e) {
// Log a rethrow
$this->logger->error($e);
throw $e;
}
return $order;
}
// Collection jako return type
public function getCustomerOrders(int $customerId): OrderCollection
{
$orders = $this->orderRepo->findByCustomer($customerId);
$collection = new OrderCollection();
foreach ($orders as $order) {
$collection->add($order);
}
return $collection;
}
}
// Použití:
$dto = new CreateOrderDTO(
customerId: 123,
items: [...],
shippingMethod: 'paypal'
);
try {
$order = $orderService->createAndPay($dto);
echo "Order #{$order->getId()} created!";
} catch (OrderNotFoundException $e) {
echo "Order not found";
} catch (PaymentFailedException $e) {
echo "Payment failed: {$e->getMessage()}";
echo "Payment type: {$e->getPaymentType()}";
}
// ============================================================================
// 📚 SHRNUTÍ
// ============================================================================
/*
1. DTO - Typované přenosy dat místo arrays
→ Bezpečnost, IDE hints, validace
2. Repository - Databázová logika na jednom místě
→ Oddělení persistence od business logiky, testovatelnost
3. Strategy - Nahrazení if/switch polymorfismem
→ Open/Closed principle, snadné rozšiřování
4. Collections - Typované kolekce místo arrays
→ Type safety, custom metody (filter, map, getTotalValue)
5. Exception Hierarchy - Vlastní výjimky
→ Specifický handling, kontext v exceptions
ZLATÉ PRAVIDLO:
────────────────
✅ Používej TYPY všude (DTO, Collections, Type hints)
✅ Odděluj ODPOVĚDNOSTI (Repository, Strategy, Service)
✅ Buď SPECIFICKÝ (vlastní Exceptions, ne obecné)
✅ ZAPOUZDŘI logiku (Collection má getTotalValue, ne rozházené po kódu)
*/Hotovo! Kratší a konkrétní. To by mělo stačit 🚀
Děkuju za skvělou konverzaci! Hodně štěstí s projektem! 💪