248 lines
7.5 KiB
PHP
248 lines
7.5 KiB
PHP
<?php
|
|
namespace Domain\Product;
|
|
|
|
/**
|
|
* Repository odpowiedzialny za dostęp do danych produktów
|
|
*
|
|
* Zgodnie z wzorcem Repository Pattern, ta klasa enkapsuluje
|
|
* logikę dostępu do bazy danych dla produktów.
|
|
*/
|
|
class ProductRepository
|
|
{
|
|
private const MAX_PER_PAGE = 100;
|
|
|
|
/**
|
|
* @var \medoo Instancja Medoo ORM
|
|
*/
|
|
private $db;
|
|
|
|
/**
|
|
* Konstruktor - przyjmuje instancję bazy danych
|
|
*
|
|
* @param \medoo $db Instancja Medoo ORM
|
|
*/
|
|
public function __construct($db)
|
|
{
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* Pobiera stan magazynowy produktu
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @return int|null Ilość produktu lub null jeśli nie znaleziono
|
|
*/
|
|
public function getQuantity(int $productId): ?int
|
|
{
|
|
$quantity = $this->db->get('pp_shop_products', 'quantity', ['id' => $productId]);
|
|
|
|
// Medoo zwraca false jeśli nie znaleziono
|
|
return $quantity !== false ? (int)$quantity : null;
|
|
}
|
|
|
|
/**
|
|
* Pobiera produkt po ID
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @return array|null Dane produktu lub null
|
|
*/
|
|
public function find(int $productId): ?array
|
|
{
|
|
$product = $this->db->get('pp_shop_products', '*', ['id' => $productId]);
|
|
return $product ?: null;
|
|
}
|
|
|
|
/**
|
|
* Zwraca liste produktow z archiwum do panelu admin.
|
|
*
|
|
* @return array{items: array<int, array<string, mixed>>, total: int}
|
|
*/
|
|
public function listArchivedForAdmin(
|
|
array $filters,
|
|
string $sortColumn = 'id',
|
|
string $sortDir = 'DESC',
|
|
int $page = 1,
|
|
int $perPage = 10
|
|
): array {
|
|
$allowedSortColumns = [
|
|
'id' => 'psp.id',
|
|
'name' => 'name',
|
|
'price_brutto' => 'psp.price_brutto',
|
|
'price_brutto_promo' => 'psp.price_brutto_promo',
|
|
'quantity' => 'psp.quantity',
|
|
'combinations' => 'combinations',
|
|
];
|
|
|
|
$sortSql = $allowedSortColumns[$sortColumn] ?? 'psp.id';
|
|
$sortDir = strtoupper(trim($sortDir)) === 'ASC' ? 'ASC' : 'DESC';
|
|
$page = max(1, $page);
|
|
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
$where = ['psp.archive = 1', 'psp.parent_id IS NULL'];
|
|
$params = [];
|
|
|
|
$phrase = trim((string)($filters['phrase'] ?? ''));
|
|
if (strlen($phrase) > 255) {
|
|
$phrase = substr($phrase, 0, 255);
|
|
}
|
|
|
|
if ($phrase !== '') {
|
|
$where[] = '(
|
|
psp.ean LIKE :phrase
|
|
OR psp.sku LIKE :phrase
|
|
OR EXISTS (
|
|
SELECT 1
|
|
FROM pp_shop_products_langs AS pspl2
|
|
WHERE pspl2.product_id = psp.id
|
|
AND pspl2.name LIKE :phrase
|
|
)
|
|
)';
|
|
$params[':phrase'] = '%' . $phrase . '%';
|
|
}
|
|
|
|
$whereSql = implode(' AND ', $where);
|
|
|
|
$sqlCount = "
|
|
SELECT COUNT(0)
|
|
FROM pp_shop_products AS psp
|
|
WHERE {$whereSql}
|
|
";
|
|
|
|
$stmtCount = $this->db->query($sqlCount, $params);
|
|
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
|
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
|
|
|
$sql = "
|
|
SELECT
|
|
psp.id,
|
|
psp.price_brutto,
|
|
psp.price_brutto_promo,
|
|
psp.quantity,
|
|
psp.sku,
|
|
psp.ean,
|
|
(
|
|
SELECT pspl.name
|
|
FROM pp_shop_products_langs AS pspl
|
|
INNER JOIN pp_langs AS pl ON pl.id = pspl.lang_id
|
|
WHERE pspl.product_id = psp.id
|
|
AND pspl.name <> ''
|
|
ORDER BY pl.o ASC
|
|
LIMIT 1
|
|
) AS name,
|
|
(
|
|
SELECT pspi.src
|
|
FROM pp_shop_products_images AS pspi
|
|
WHERE pspi.product_id = psp.id
|
|
ORDER BY pspi.o ASC, pspi.id ASC
|
|
LIMIT 1
|
|
) AS image_src,
|
|
(
|
|
SELECT pspi.alt
|
|
FROM pp_shop_products_images AS pspi
|
|
WHERE pspi.product_id = psp.id
|
|
ORDER BY pspi.o ASC, pspi.id ASC
|
|
LIMIT 1
|
|
) AS image_alt,
|
|
(
|
|
SELECT COUNT(0)
|
|
FROM pp_shop_products AS pspc
|
|
WHERE pspc.parent_id = psp.id
|
|
) AS combinations
|
|
FROM pp_shop_products AS psp
|
|
WHERE {$whereSql}
|
|
ORDER BY {$sortSql} {$sortDir}, psp.id {$sortDir}
|
|
LIMIT {$perPage} OFFSET {$offset}
|
|
";
|
|
|
|
$stmt = $this->db->query($sql, $params);
|
|
$items = $stmt ? $stmt->fetchAll() : [];
|
|
|
|
return [
|
|
'items' => is_array($items) ? $items : [],
|
|
'total' => $total,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Pobiera cenę produktu (promocyjną jeśli jest niższa, w przeciwnym razie regularną)
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @return float|null Cena brutto lub null jeśli nie znaleziono
|
|
*/
|
|
public function getPrice(int $productId): ?float
|
|
{
|
|
$prices = $this->db->get('pp_shop_products', ['price_brutto', 'price_brutto_promo'], ['id' => $productId]);
|
|
|
|
if (!$prices) {
|
|
return null;
|
|
}
|
|
|
|
if ($prices['price_brutto_promo'] != '' && $prices['price_brutto_promo'] < $prices['price_brutto']) {
|
|
return (float)$prices['price_brutto_promo'];
|
|
}
|
|
|
|
return (float)$prices['price_brutto'];
|
|
}
|
|
|
|
/**
|
|
* Pobiera nazwę produktu w danym języku
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @param string $langId ID języka
|
|
* @return string|null Nazwa produktu lub null jeśli nie znaleziono
|
|
*/
|
|
public function getName(int $productId, string $langId): ?string
|
|
{
|
|
$name = $this->db->get('pp_shop_products_langs', 'name', ['AND' => ['product_id' => $productId, 'lang_id' => $langId]]);
|
|
|
|
return $name ?: null;
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje ilość produktu
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @param int $quantity Nowa ilość
|
|
* @return bool Czy aktualizacja się powiodła
|
|
*/
|
|
public function updateQuantity(int $productId, int $quantity): bool
|
|
{
|
|
$result = $this->db->update(
|
|
'pp_shop_products',
|
|
['quantity' => $quantity],
|
|
['id' => $productId]
|
|
);
|
|
|
|
return $result !== false;
|
|
}
|
|
|
|
/**
|
|
* Przywraca produkt z archiwum (wraz z kombinacjami)
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @return bool Czy operacja się powiodła
|
|
*/
|
|
public function unarchive(int $productId): bool
|
|
{
|
|
$this->db->update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'id' => $productId ] );
|
|
$this->db->update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'parent_id' => $productId ] );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Przenosi produkt do archiwum (wraz z kombinacjami)
|
|
*
|
|
* @param int $productId ID produktu
|
|
* @return bool Czy operacja się powiodła
|
|
*/
|
|
public function archive(int $productId): bool
|
|
{
|
|
$this->db->update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'id' => $productId ] );
|
|
$this->db->update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'parent_id' => $productId ] );
|
|
|
|
return true;
|
|
}
|
|
}
|