Skip to content

Instantly share code, notes, and snippets.

@Kcko
Created October 24, 2025 13:24
Show Gist options
  • Save Kcko/fa74e5372441e74b6f05fa87406d4eaa to your computer and use it in GitHub Desktop.
Save Kcko/fa74e5372441e74b6f05fa87406d4eaa to your computer and use it in GitHub Desktop.

Revisions

  1. Kcko revised this gist Oct 24, 2025. No changes.
  2. Kcko created this gist Oct 24, 2025.
    578 changes: 578 additions & 0 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,578 @@
    ```php
    <?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! 💪