Návrhový vzor DB pro košík na e-shopu

Dan Hundrt
Člen | 74
+
0
-

Zdravím,

rád bych se Vás zeptal, jak by jste řešili návrhový vzor u košíku. Mám to zatím navržené takto:

1) Mám tabulku ‚products‘, ve které jsou produkty (sloupce: id, title, price, slug, … atd)

  1. Dále tabulku ‚basket‘, do které se zapisují produkty, které si přidám do košíku (sloupce: id, product_id (reference na id produktu), a hash, který vygeneruje eshop pro daného uživatele při vstupu na stránky).

Až sem je to snad ok, nicméně řeším jak dále. Po odeslání objednávky bych nyní vytvořil v tabulce ‚order‘ odběratele a odesílatele + ostatní info a překopíroval produkty z tabulky ‚basket‘, do tabulky ‚order_products‘ s referencní na ‚order‘.

Myslíte, že je to takto ok?

Děkuji

Felix
Nette Core | 1186
+
+2
-

Prijde mi to takto v pohode. Na zacatek si dovedu predstavit, mit misto tabulky basket i cely kosik v session. Ale to zalezi na tobe.

Dan Hundrt
Člen | 74
+
0
-

Zdravím,

postoupil jsem dále a mám vše prakticky hotové a vše funguje. Nyní mám tedy logiku následující:

  1. tabulku order (číslo objednávky, stav objednávky, hash) s vazbami na tabulku subscribers (údaje o zákazníkovi, jméno, tel. …), order_pay (název a cena platby), order_delivery (název a cena dopravy)
  2. tabulku subscribers s vazbou na subscribers_delivery (adresa pro doručení) a ** subscribers_delivery** (adresa pro doručení pro firmu).

Je toho poměrně dost, jenom model má poměrně dost metod (posílám níže aktuální kód). Celé je to v podstatě dělané, aby objednávka byla na 100% neměnná, pokud se mění cena produktů, nastavení plateb a doručení.

Kód logicky postupuje následovně:

  1. v metodě saveSubscriber() se uloží data do tabulky subscribers (jméno, přijímení, email, tel. zákazníka) + z nastavení e-shopu se vytáhne typ platby a doručení a uloží se obojí do (saveSubscriber()), (savePay()).
  2. V metodě saveSubscriber() se poté zavolá metoda saveOrderInterbankTable() a v parametrech se předá ID zákazníka, ID platby a ID doručení a hash (random string).
  3. V metodě saveOrderInterbankTable() se zavolá metoda saveProducts() ve které se vytáhnou produkty z produktové databáze a vloží se do tabulky s produkty pro danou objednávku.

Celé je to poměrně složité, říkám si, že bych to mohl celé zjednodušit a tabulky pro zákazníka spojit (tj. mít to celé v jedné tabulce, to samé u typ platby a doručení, vložit to přímo k objednávce).

Nevíte jaký je správný přístup s ohledem na pozdější škálování?

Děkuji

<?php

namespace App\Model;

use App\Providers\UserProvider;
use Nette\Database\Context;
use Nette\Utils\Random;

class OrderModel
{
  /** Order status */
  const STATUS_NEW = 1;
  const STATUS_SEND = 2;

  /** @var CartModel */
  private $cartModel;

  /** @var ProductModel */
  private $productModel;

  /** @var UserProvider */
  private $userProvider;

  /** @var Context */
  public $database;

  public function __construct(
    CartModel $cartModel,
    ProductModel $productModel,
    Context $database,
    UserProvider $userProvider)
  {
    $this->cartModel = $cartModel;
    $this->productModel = $productModel;
    $this->database = $database;
    $this->userProvider = $userProvider;
  }

  /**
   * @inheritdoc Get all order
   * @return array
   */
  public function getAllOrder($limit = 40): array
  {
    $data = $this->database->table('order')->limit($limit)->order('id DESC')->fetchAll();

    $result = [];
    foreach ($data as $item) {
      $subscribers = $item->ref('subscribers')->toArray();
      $result[] = [
        'order'       => $item->toArray(),
        'subscribers' => $subscribers,
      ];
    }
    return $result;
  }

  /**
   * @inheritdoc Get detail order with all data
   * @param int $id
   * @return array
   */
  public function getDetailOrder(int $id): array
  {
    $order = $this->database->table('order')->get($id);

    $products = $this->database->table('order_products')->where(['order_id' => $order['id']])->fetchAll();
    $subscriber = $this->database->table('subscribers')->where(['id' => $order['subscribers_id']])->fetch();
    $subscriberDelivery = $this->database->table('subscribers_delivery')->where(['id' => $subscriber['delivery_id']])->fetch();
    $company = $this->database->table('subscribers_company')->where(['id' => $subscriber['company_id']])->fetch();
    $delivery = $this->database->table('order_delivery')->where(['id' => $order['delivery_id']])->fetch();
    $pay = $this->database->table('order_pay')->where(['id' => $order['pay_id']])->fetch();

    return [
      'order'              => $order,
      'products'           => $products,
      'subscriber'         => $subscriber,
      'subscriberDelivery' => $subscriberDelivery,
      'delivery'           => $delivery,
      'pay'                => $pay,
      'subscriberCompany'  => $company,
      'totalPrice'         => $this->getTotalPrice($products)
    ];
  }

  /**
   * @inheritdoc Get
   * @param string $hash
   * @return mixed
   */
  public function getOrderIDFromHash(string $hash)
  {
    return $this->database->table('order')->where(['hash' => $hash])->fetch();
  }

  /**
   * @inheritdoc Generate hash order
   * @return string
   */
  public function generateHash(): string
  {
    return Random::generate(65);
  }


  /**
   * @inheritdoc Get order total price
   * @param array $products
   * @return bool|float|int
   */
  public function getTotalPrice(array $products)
  {
    if (!isset($products)) {
      return FALSE;
    }

    $sum = [];
    foreach ($products as $item) {
      $sum[] += $item['price'] * $item['stock'];
    }

    return array_sum($sum);
  }

  /**
   * @inheritdoc Save order
   * @param $data
   * @param string $hash
   * @return int
   */
  public function saveOrder($data, string $hash)
  {
    if ($this->saveSubscriber($data, $hash)) {
      return TRUE; //$this->cartModel->flushCart();
    }
  }


  /**
   * @param $value
   * @param string $hash
   * @return bool|int|\Nette\Database\Table\ActiveRow
   * @throws \Exception
   */
  public function saveSubscriber($value, string $hash)
  {
    $subscriber = [
      'date'        => new \DateTime(),
      'name'        => $value['name'],
      'surname'     => $value['surname'],
      'email'       => $value['email'],
      'phone'       => $value['phone'],
      'delivery_id' => $this->saveSubscriberDelivery($value),
      'company_id'  => $this->saveCompany($value)
    ];

    $subscribersId = $this->database->table('subscribers')->insert($subscriber)->id;
    $deliveryId = $this->saveDelivery($value);
    $payId = $this->savePay($value);

    return $this->saveOrderInterbankTable($subscribersId, $deliveryId, $payId, $hash);

  }

  /**
   * @inheritdoc Save delivery
   * @param $value
   * @return mixed|\Nette\Database\Table\ActiveRow
   */
  public function saveDelivery($value)
  {
    $data = $this->database->table('delivery')->get($value['delivery']);

    $insert = [
      'title' => $data['title'],
      'price' => $data['price'],
    ];

    return $this->database->table('order_delivery')->insert($insert)->id;
  }

  /**
   * @inheritdoc Save pay
   * @param $value
   * @return mixed|\Nette\Database\Table\ActiveRow
   */
  public function savePay($value)
  {
    $data = $this->database->table('pay')->get($value['pay']);

    $insert = [
      'title' => $data['title'],
      'price' => $data['price'],
    ];

    return $this->database->table('order_pay')->insert($insert)->id;
  }

  /**
   * @inheritdoc Save subscriber delivery
   * @param $value
   * @return mixed|\Nette\Database\Table\ActiveRow
   * @throws \Exception
   */
  public function saveSubscriberDelivery($value)
  {
    $subscriberDelivery = [
      'date'       => new \DateTime(),
      'city'       => $value['city'],
      'street'     => $value['street'],
      'postalcode' => $value['postalcode'],
    ];

    return $this->database->table('subscribers_delivery')->insert($subscriberDelivery)->id;
  }


  /**
   * @inheritdoc Save company to table
   * @param $value
   * @return mixed|\Nette\Database\Table\ActiveRow|null
   * @throws \Exception
   */
  public function saveCompany($value)
  {
    if (!$value['use_company']) {
      return NULL;
    }

    $company = [
      'date'    => new \DateTime(),
      'company' => $value['company'],
      'ic'      => $value['ic'],
      'dic'     => $value['dic'],
    ];

    return $this->database->table('subscribers_company')->insert($company)->id;
  }


  /**
   * @inheritdoc Insert data to interbank table (order)
   * @param int $id
   * @param int $delivery
   * @param int $pay
   * @param string $hash
   * @return bool|int|\Nette\Database\Table\ActiveRow
   * @throws \Exception
   */
  public function saveOrderInterbankTable(int $id, ?int $delivery, ?int $pay, string $hash)
  {
    $data = [
      'subscribers_id' => $id,
      'delivery_id'    => $delivery,
      'pay_id'         => $pay,
      'status'         => self::STATUS_NEW,
      'hash'           => $hash
    ];

    return $this->saveProducts($this->database->table('order')->insert($data)->id);
  }


  /**
   * @inheritdoc Save product to table
   * @param $id
   * @return bool|int|\Nette\Database\Table\ActiveRow
   * @throws \Exception
   */
  public function saveProducts($id)
  {
    $user = $this->userProvider->getUserCookie();
    $data = $this->database->table('cart')->where(['user' => $user])->fetchAll();

    $result = [];
    foreach ($data as $product) {
      $cart = $product->toArray();
      $product = $product->ref('product')->toArray();

      $result[] = [
        'stock'      => $cart['count'],
        'price'      => $product['price'],
        'title'      => $product['title'],
        'product_id' => $product['id'],
        'date'       => new \DateTime(),
        'order_id'   => $id,
      ];
    }

    return $this->database->table('order_products')->insert($result);
  }
}