ver. 0.297: REST API products endpoint — list, get, create, update
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -442,6 +442,260 @@ class ProductRepository
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista produktów dla REST API z filtrowaniem, sortowaniem i paginacją.
|
||||
*
|
||||
* @param array $filters Filtry: search, status, promoted
|
||||
* @param string $sortColumn Kolumna sortowania
|
||||
* @param string $sortDir Kierunek: ASC|DESC
|
||||
* @param int $page Numer strony
|
||||
* @param int $perPage Wyników na stronę (max 100)
|
||||
* @return array{items: array, total: int, page: int, per_page: int}
|
||||
*/
|
||||
public function listForApi(
|
||||
array $filters = [],
|
||||
string $sortColumn = 'id',
|
||||
string $sortDir = 'DESC',
|
||||
int $page = 1,
|
||||
int $perPage = 50
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'psp.id',
|
||||
'name' => 'name',
|
||||
'price_brutto' => 'psp.price_brutto',
|
||||
'status' => 'psp.status',
|
||||
'promoted' => 'psp.promoted',
|
||||
'quantity' => 'psp.quantity',
|
||||
];
|
||||
|
||||
$sortSql = isset($allowedSortColumns[$sortColumn]) ? $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 = 0', 'psp.parent_id IS NULL'];
|
||||
$params = [];
|
||||
|
||||
$search = trim((string)($filters['search'] ?? ''));
|
||||
if (strlen($search) > 255) {
|
||||
$search = substr($search, 0, 255);
|
||||
}
|
||||
|
||||
if ($search !== '') {
|
||||
$where[] = '(
|
||||
psp.ean LIKE :search
|
||||
OR psp.sku LIKE :search
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM pp_shop_products_langs AS pspl2
|
||||
WHERE pspl2.product_id = psp.id
|
||||
AND pspl2.name LIKE :search
|
||||
)
|
||||
)';
|
||||
$params[':search'] = '%' . $search . '%';
|
||||
}
|
||||
|
||||
$statusFilter = (string)($filters['status'] ?? '');
|
||||
if ($statusFilter === '1' || $statusFilter === '0') {
|
||||
$where[] = 'psp.status = :status';
|
||||
$params[':status'] = (int)$statusFilter;
|
||||
}
|
||||
|
||||
$promotedFilter = (string)($filters['promoted'] ?? '');
|
||||
if ($promotedFilter === '1' || $promotedFilter === '0') {
|
||||
$where[] = 'psp.promoted = :promoted';
|
||||
$params[':promoted'] = (int)$promotedFilter;
|
||||
}
|
||||
|
||||
$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.sku,
|
||||
psp.ean,
|
||||
psp.price_brutto,
|
||||
psp.price_brutto_promo,
|
||||
psp.price_netto,
|
||||
psp.price_netto_promo,
|
||||
psp.quantity,
|
||||
psp.status,
|
||||
psp.promoted,
|
||||
psp.vat,
|
||||
psp.weight,
|
||||
psp.date_add,
|
||||
psp.date_modify,
|
||||
(
|
||||
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 main_image
|
||||
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);
|
||||
$rows = $stmt ? $stmt->fetchAll(\PDO::FETCH_ASSOC) : [];
|
||||
|
||||
$items = [];
|
||||
if (is_array($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$items[] = [
|
||||
'id' => (int)$row['id'],
|
||||
'sku' => $row['sku'],
|
||||
'ean' => $row['ean'],
|
||||
'name' => $row['name'],
|
||||
'price_brutto' => $row['price_brutto'] !== null ? (float)$row['price_brutto'] : null,
|
||||
'price_brutto_promo' => $row['price_brutto_promo'] !== null ? (float)$row['price_brutto_promo'] : null,
|
||||
'price_netto' => $row['price_netto'] !== null ? (float)$row['price_netto'] : null,
|
||||
'price_netto_promo' => $row['price_netto_promo'] !== null ? (float)$row['price_netto_promo'] : null,
|
||||
'quantity' => (int)$row['quantity'],
|
||||
'status' => (int)$row['status'],
|
||||
'promoted' => (int)$row['promoted'],
|
||||
'vat' => (int)$row['vat'],
|
||||
'weight' => $row['weight'] !== null ? (float)$row['weight'] : null,
|
||||
'main_image' => $row['main_image'],
|
||||
'date_add' => $row['date_add'],
|
||||
'date_modify' => $row['date_modify'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Szczegóły produktu dla REST API.
|
||||
*
|
||||
* @param int $id ID produktu
|
||||
* @return array|null Dane produktu lub null
|
||||
*/
|
||||
public function findForApi(int $id): ?array
|
||||
{
|
||||
$product = $this->db->get('pp_shop_products', '*', ['id' => $id]);
|
||||
if (!$product) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!empty($product['archive'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'id' => (int)$product['id'],
|
||||
'sku' => $product['sku'],
|
||||
'ean' => $product['ean'],
|
||||
'price_brutto' => $product['price_brutto'] !== null ? (float)$product['price_brutto'] : null,
|
||||
'price_brutto_promo' => $product['price_brutto_promo'] !== null ? (float)$product['price_brutto_promo'] : null,
|
||||
'price_netto' => $product['price_netto'] !== null ? (float)$product['price_netto'] : null,
|
||||
'price_netto_promo' => $product['price_netto_promo'] !== null ? (float)$product['price_netto_promo'] : null,
|
||||
'quantity' => (int)$product['quantity'],
|
||||
'status' => (int)$product['status'],
|
||||
'promoted' => (int)$product['promoted'],
|
||||
'vat' => (int)$product['vat'],
|
||||
'weight' => $product['weight'] !== null ? (float)$product['weight'] : null,
|
||||
'stock_0_buy' => (int)($product['stock_0_buy'] ?? 0),
|
||||
'custom_label_0' => $product['custom_label_0'],
|
||||
'custom_label_1' => $product['custom_label_1'],
|
||||
'custom_label_2' => $product['custom_label_2'],
|
||||
'custom_label_3' => $product['custom_label_3'],
|
||||
'custom_label_4' => $product['custom_label_4'],
|
||||
'set_id' => $product['set_id'] !== null ? (int)$product['set_id'] : null,
|
||||
'product_unit_id' => $product['product_unit_id'] !== null ? (int)$product['product_unit_id'] : null,
|
||||
'producer_id' => $product['producer_id'] !== null ? (int)$product['producer_id'] : null,
|
||||
'date_add' => $product['date_add'],
|
||||
'date_modify' => $product['date_modify'],
|
||||
];
|
||||
|
||||
// Languages
|
||||
$langs = $this->db->select('pp_shop_products_langs', '*', ['product_id' => $id]);
|
||||
$result['languages'] = [];
|
||||
if (is_array($langs)) {
|
||||
foreach ($langs as $lang) {
|
||||
$result['languages'][$lang['lang_id']] = [
|
||||
'name' => $lang['name'],
|
||||
'short_description' => $lang['short_description'],
|
||||
'description' => $lang['description'],
|
||||
'meta_description' => $lang['meta_description'],
|
||||
'meta_keywords' => $lang['meta_keywords'],
|
||||
'meta_title' => $lang['meta_title'],
|
||||
'seo_link' => $lang['seo_link'],
|
||||
'copy_from' => $lang['copy_from'],
|
||||
'warehouse_message_zero' => $lang['warehouse_message_zero'],
|
||||
'warehouse_message_nonzero' => $lang['warehouse_message_nonzero'],
|
||||
'tab_name_1' => $lang['tab_name_1'],
|
||||
'tab_description_1' => $lang['tab_description_1'],
|
||||
'tab_name_2' => $lang['tab_name_2'],
|
||||
'tab_description_2' => $lang['tab_description_2'],
|
||||
'canonical' => $lang['canonical'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Images
|
||||
$images = $this->db->select('pp_shop_products_images', ['id', 'src', 'alt'], [
|
||||
'product_id' => $id,
|
||||
'ORDER' => ['o' => 'ASC', 'id' => 'ASC'],
|
||||
]);
|
||||
$result['images'] = is_array($images) ? $images : [];
|
||||
foreach ($result['images'] as &$img) {
|
||||
$img['id'] = (int)$img['id'];
|
||||
}
|
||||
unset($img);
|
||||
|
||||
// Categories
|
||||
$categories = $this->db->select('pp_shop_products_categories', 'category_id', ['product_id' => $id]);
|
||||
$result['categories'] = [];
|
||||
if (is_array($categories)) {
|
||||
foreach ($categories as $catId) {
|
||||
$result['categories'][] = (int)$catId;
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes
|
||||
$attributes = $this->db->select('pp_shop_products_attributes', ['attribute_id', 'value_id'], ['product_id' => $id]);
|
||||
$result['attributes'] = [];
|
||||
if (is_array($attributes)) {
|
||||
foreach ($attributes as $attr) {
|
||||
$result['attributes'][] = [
|
||||
'attribute_id' => (int)$attr['attribute_id'],
|
||||
'value_id' => (int)$attr['value_id'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Szczegóły produktu (admin) — zastępuje factory product_details().
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user