feat(06-sonarqube-quality): extract long methods to fix S138 violations (06-06)
ShopproOrdersSyncService: sync() 195→44 lines via syncOneIntegration, fetchOrdersPage, processPageCandidates, importOneOrder; mapAddresses() 166→34 lines via buildCustomerAddress, buildDeliveryAddress. OrdersRepository: paginate() 183→69 lines via buildPaginateFilters, buildListSql, transformOrderRow; findDetails() 101→40 lines via loadOrderAddresses/Items/Payments/Shipments/Documents/Notes/StatusHistory. SonarQube S138 violations: 4 → 0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,21 +24,78 @@ final class OrdersRepository
|
||||
$page = max(1, (int) ($filters['page'] ?? 1));
|
||||
$perPage = max(1, min(100, (int) ($filters['per_page'] ?? 20)));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = [];
|
||||
$params = [];
|
||||
$effectiveStatusSql = $this->effectiveStatusSql('o', 'asm');
|
||||
$effectiveOrderedAtSql = $this->effectiveOrderedAtSql('o');
|
||||
|
||||
['where' => $where, 'params' => $params] = $this->buildPaginateFilters($filters, $effectiveStatusSql, $effectiveOrderedAtSql);
|
||||
$whereSql = $where === [] ? '' : (' WHERE ' . implode(' AND ', $where));
|
||||
|
||||
$sort = (string) ($filters['sort'] ?? 'ordered_at');
|
||||
$sortDir = strtoupper((string) ($filters['sort_dir'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
|
||||
$sortColumn = match ($sort) {
|
||||
'source_order_id' => 'o.source_order_id',
|
||||
'external_order_id' => 'o.external_order_id',
|
||||
'external_status_id' => 'o.external_status_id',
|
||||
'payment_status' => 'o.payment_status',
|
||||
'total_with_tax' => 'o.total_with_tax',
|
||||
'total_paid' => 'o.total_paid',
|
||||
'source_updated_at' => 'o.source_updated_at',
|
||||
'fetched_at' => 'o.fetched_at',
|
||||
'id' => 'o.id',
|
||||
default => $effectiveOrderedAtSql,
|
||||
};
|
||||
|
||||
try {
|
||||
$countSql = 'SELECT COUNT(*) FROM orders o '
|
||||
. 'LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer" '
|
||||
. 'LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.external_status_id) = asm.allegro_status_code'
|
||||
. $whereSql;
|
||||
$countStmt = $this->pdo->prepare($countSql);
|
||||
$countStmt->execute($params);
|
||||
$total = (int) $countStmt->fetchColumn();
|
||||
|
||||
$listSql = $this->buildListSql($effectiveStatusSql, $effectiveOrderedAtSql, $whereSql, $sortColumn, $sortDir);
|
||||
$stmt = $this->pdo->prepare($listSql);
|
||||
foreach ($params as $key => $value) {
|
||||
$stmt->bindValue(':' . $key, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
|
||||
}
|
||||
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
$rows = $stmt->fetchAll();
|
||||
if (!is_array($rows)) {
|
||||
$rows = [];
|
||||
}
|
||||
$itemPreviewsByOrderId = $this->loadOrderItemsPreviews(array_map(
|
||||
static fn (array $row): int => (int) ($row['id'] ?? 0),
|
||||
$rows
|
||||
));
|
||||
|
||||
return [
|
||||
'items' => array_map(fn (array $row) => $this->transformOrderRow($row, $itemPreviewsByOrderId), $rows),
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'error' => '',
|
||||
];
|
||||
} catch (Throwable $exception) {
|
||||
return ['items' => [], 'total' => 0, 'page' => $page, 'per_page' => $perPage, 'error' => $exception->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $filters
|
||||
* @return array{where:array<int,string>,params:array<string,mixed>}
|
||||
*/
|
||||
private function buildPaginateFilters(array $filters, string $effectiveStatusSql, string $effectiveOrderedAtSql): array
|
||||
{
|
||||
$where = [];
|
||||
$params = [];
|
||||
|
||||
$search = trim((string) ($filters['search'] ?? ''));
|
||||
if ($search !== '') {
|
||||
$where[] = '('
|
||||
. 'o.source_order_id LIKE :search '
|
||||
. 'OR o.external_order_id LIKE :search '
|
||||
. 'OR o.customer_login LIKE :search '
|
||||
. 'OR a.name LIKE :search '
|
||||
. 'OR a.email LIKE :search'
|
||||
. ')';
|
||||
$where[] = '(o.source_order_id LIKE :search OR o.external_order_id LIKE :search OR o.customer_login LIKE :search OR a.name LIKE :search OR a.email LIKE :search)';
|
||||
$params['search'] = '%' . $search . '%';
|
||||
}
|
||||
|
||||
@@ -72,33 +129,12 @@ final class OrdersRepository
|
||||
$params['date_to'] = $dateTo . ' 23:59:59';
|
||||
}
|
||||
|
||||
$whereSql = $where === [] ? '' : (' WHERE ' . implode(' AND ', $where));
|
||||
return ['where' => $where, 'params' => $params];
|
||||
}
|
||||
|
||||
$sort = (string) ($filters['sort'] ?? 'ordered_at');
|
||||
$sortDir = strtoupper((string) ($filters['sort_dir'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
|
||||
$sortColumn = match ($sort) {
|
||||
'source_order_id' => 'o.source_order_id',
|
||||
'external_order_id' => 'o.external_order_id',
|
||||
'external_status_id' => 'o.external_status_id',
|
||||
'payment_status' => 'o.payment_status',
|
||||
'total_with_tax' => 'o.total_with_tax',
|
||||
'total_paid' => 'o.total_paid',
|
||||
'source_updated_at' => 'o.source_updated_at',
|
||||
'fetched_at' => 'o.fetched_at',
|
||||
'id' => 'o.id',
|
||||
default => $effectiveOrderedAtSql,
|
||||
};
|
||||
|
||||
try {
|
||||
$countSql = 'SELECT COUNT(*) FROM orders o '
|
||||
. 'LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer" '
|
||||
. 'LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.external_status_id) = asm.allegro_status_code'
|
||||
. $whereSql;
|
||||
$countStmt = $this->pdo->prepare($countSql);
|
||||
$countStmt->execute($params);
|
||||
$total = (int) $countStmt->fetchColumn();
|
||||
|
||||
$listSql = 'SELECT
|
||||
private function buildListSql(string $effectiveStatusSql, string $effectiveOrderedAtSql, string $whereSql, string $sortColumn, string $sortDir): string
|
||||
{
|
||||
return 'SELECT
|
||||
o.id,
|
||||
o.internal_order_number,
|
||||
o.source,
|
||||
@@ -129,78 +165,49 @@ final class OrdersRepository
|
||||
FROM orders o
|
||||
LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer"
|
||||
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.external_status_id) = asm.allegro_status_code'
|
||||
. $whereSql
|
||||
. ' ORDER BY ' . $sortColumn . ' ' . $sortDir
|
||||
. ' LIMIT :limit OFFSET :offset';
|
||||
. $whereSql
|
||||
. ' ORDER BY ' . $sortColumn . ' ' . $sortDir
|
||||
. ' LIMIT :limit OFFSET :offset';
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare($listSql);
|
||||
foreach ($params as $key => $value) {
|
||||
if (is_int($value)) {
|
||||
$stmt->bindValue(':' . $key, $value, PDO::PARAM_INT);
|
||||
} else {
|
||||
$stmt->bindValue(':' . $key, $value);
|
||||
}
|
||||
}
|
||||
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int, array<int, array{name:string,quantity:float,media_url:string}>> $itemPreviewsByOrderId
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function transformOrderRow(array $row, array $itemPreviewsByOrderId): array
|
||||
{
|
||||
$orderId = (int) ($row['id'] ?? 0);
|
||||
|
||||
$rows = $stmt->fetchAll();
|
||||
if (!is_array($rows)) {
|
||||
$rows = [];
|
||||
}
|
||||
$itemPreviewsByOrderId = $this->loadOrderItemsPreviews(array_map(
|
||||
static fn (array $row): int => (int) ($row['id'] ?? 0),
|
||||
$rows
|
||||
));
|
||||
|
||||
return [
|
||||
'items' => array_map(static function (array $row) use ($itemPreviewsByOrderId): array {
|
||||
$orderId = (int) ($row['id'] ?? 0);
|
||||
return [
|
||||
'id' => $orderId,
|
||||
'internal_order_number' => (string) ($row['internal_order_number'] ?? ''),
|
||||
'source' => (string) ($row['source'] ?? ''),
|
||||
'source_order_id' => (string) ($row['source_order_id'] ?? ''),
|
||||
'external_order_id' => (string) ($row['external_order_id'] ?? ''),
|
||||
'external_status_id' => (string) ($row['external_status_id'] ?? ''),
|
||||
'effective_status_id' => (string) ($row['effective_status_id'] ?? ''),
|
||||
'payment_status' => isset($row['payment_status']) ? (int) $row['payment_status'] : null,
|
||||
'currency' => (string) ($row['currency'] ?? ''),
|
||||
'total_with_tax' => $row['total_with_tax'] !== null ? (float) $row['total_with_tax'] : null,
|
||||
'total_paid' => $row['total_paid'] !== null ? (float) $row['total_paid'] : null,
|
||||
'ordered_at' => (string) ($row['effective_ordered_at'] ?? ''),
|
||||
'source_created_at' => (string) ($row['source_created_at'] ?? ''),
|
||||
'source_updated_at' => (string) ($row['source_updated_at'] ?? ''),
|
||||
'fetched_at' => (string) ($row['fetched_at'] ?? ''),
|
||||
'is_invoice' => (int) ($row['is_invoice'] ?? 0) === 1,
|
||||
'is_canceled_by_buyer' => (int) ($row['is_canceled_by_buyer'] ?? 0) === 1,
|
||||
'external_carrier_id' => (string) ($row['external_carrier_id'] ?? ''),
|
||||
'external_payment_type_id' => (string) ($row['external_payment_type_id'] ?? ''),
|
||||
'buyer_name' => (string) ($row['buyer_name'] ?? ''),
|
||||
'buyer_email' => (string) ($row['buyer_email'] ?? ''),
|
||||
'buyer_city' => (string) ($row['buyer_city'] ?? ''),
|
||||
'items_count' => (int) ($row['items_count'] ?? 0),
|
||||
'items_qty' => (float) ($row['items_qty'] ?? 0),
|
||||
'shipments_count' => (int) ($row['shipments_count'] ?? 0),
|
||||
'documents_count' => (int) ($row['documents_count'] ?? 0),
|
||||
'items_preview' => (array) ($itemPreviewsByOrderId[$orderId] ?? []),
|
||||
];
|
||||
}, $rows),
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'error' => '',
|
||||
];
|
||||
} catch (Throwable $exception) {
|
||||
return [
|
||||
'items' => [],
|
||||
'total' => 0,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'error' => $exception->getMessage(),
|
||||
];
|
||||
}
|
||||
return [
|
||||
'id' => $orderId,
|
||||
'internal_order_number' => (string) ($row['internal_order_number'] ?? ''),
|
||||
'source' => (string) ($row['source'] ?? ''),
|
||||
'source_order_id' => (string) ($row['source_order_id'] ?? ''),
|
||||
'external_order_id' => (string) ($row['external_order_id'] ?? ''),
|
||||
'external_status_id' => (string) ($row['external_status_id'] ?? ''),
|
||||
'effective_status_id' => (string) ($row['effective_status_id'] ?? ''),
|
||||
'payment_status' => isset($row['payment_status']) ? (int) $row['payment_status'] : null,
|
||||
'currency' => (string) ($row['currency'] ?? ''),
|
||||
'total_with_tax' => $row['total_with_tax'] !== null ? (float) $row['total_with_tax'] : null,
|
||||
'total_paid' => $row['total_paid'] !== null ? (float) $row['total_paid'] : null,
|
||||
'ordered_at' => (string) ($row['effective_ordered_at'] ?? ''),
|
||||
'source_created_at' => (string) ($row['source_created_at'] ?? ''),
|
||||
'source_updated_at' => (string) ($row['source_updated_at'] ?? ''),
|
||||
'fetched_at' => (string) ($row['fetched_at'] ?? ''),
|
||||
'is_invoice' => (int) ($row['is_invoice'] ?? 0) === 1,
|
||||
'is_canceled_by_buyer' => (int) ($row['is_canceled_by_buyer'] ?? 0) === 1,
|
||||
'external_carrier_id' => (string) ($row['external_carrier_id'] ?? ''),
|
||||
'external_payment_type_id' => (string) ($row['external_payment_type_id'] ?? ''),
|
||||
'buyer_name' => (string) ($row['buyer_name'] ?? ''),
|
||||
'buyer_email' => (string) ($row['buyer_email'] ?? ''),
|
||||
'buyer_city' => (string) ($row['buyer_city'] ?? ''),
|
||||
'items_count' => (int) ($row['items_count'] ?? 0),
|
||||
'items_qty' => (float) ($row['items_qty'] ?? 0),
|
||||
'shipments_count' => (int) ($row['shipments_count'] ?? 0),
|
||||
'documents_count' => (int) ($row['documents_count'] ?? 0),
|
||||
'items_preview' => (array) ($itemPreviewsByOrderId[$orderId] ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,87 +421,122 @@ final class OrdersRepository
|
||||
return null;
|
||||
}
|
||||
|
||||
$addressesStmt = $this->pdo->prepare('SELECT * FROM order_addresses WHERE order_id = :order_id ORDER BY FIELD(address_type, "customer", "invoice", "delivery"), id ASC');
|
||||
$addressesStmt->execute(['order_id' => $orderId]);
|
||||
$addresses = $addressesStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($addresses)) {
|
||||
$addresses = [];
|
||||
}
|
||||
|
||||
$itemsMediaSql = $this->resolvedMediaUrlSql('oi', 'o.source');
|
||||
$itemsStmt = $this->pdo->prepare('SELECT oi.*, ' . $itemsMediaSql . ' AS resolved_media_url
|
||||
FROM order_items oi
|
||||
INNER JOIN orders o ON o.id = oi.order_id
|
||||
WHERE oi.order_id = :order_id
|
||||
ORDER BY oi.sort_order ASC, oi.id ASC');
|
||||
$itemsStmt->execute(['order_id' => $orderId]);
|
||||
$items = $itemsStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
$items = array_map(static function (array $row): array {
|
||||
$resolvedMediaUrl = trim((string) ($row['resolved_media_url'] ?? ''));
|
||||
if ($resolvedMediaUrl !== '') {
|
||||
$row['media_url'] = $resolvedMediaUrl;
|
||||
}
|
||||
unset($row['resolved_media_url']);
|
||||
|
||||
return $row;
|
||||
}, $items);
|
||||
|
||||
$paymentsStmt = $this->pdo->prepare('SELECT * FROM order_payments WHERE order_id = :order_id ORDER BY payment_date ASC, id ASC');
|
||||
$paymentsStmt->execute(['order_id' => $orderId]);
|
||||
$payments = $paymentsStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($payments)) {
|
||||
$payments = [];
|
||||
}
|
||||
|
||||
$shipmentsStmt = $this->pdo->prepare('SELECT * FROM order_shipments WHERE order_id = :order_id ORDER BY posted_at ASC, id ASC');
|
||||
$shipmentsStmt->execute(['order_id' => $orderId]);
|
||||
$shipments = $shipmentsStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($shipments)) {
|
||||
$shipments = [];
|
||||
}
|
||||
|
||||
$documentsStmt = $this->pdo->prepare('SELECT * FROM order_documents WHERE order_id = :order_id ORDER BY source_created_at ASC, id ASC');
|
||||
$documentsStmt->execute(['order_id' => $orderId]);
|
||||
$documents = $documentsStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($documents)) {
|
||||
$documents = [];
|
||||
}
|
||||
|
||||
$notesStmt = $this->pdo->prepare('SELECT * FROM order_notes WHERE order_id = :order_id ORDER BY created_at_external DESC, id DESC');
|
||||
$notesStmt->execute(['order_id' => $orderId]);
|
||||
$notes = $notesStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($notes)) {
|
||||
$notes = [];
|
||||
}
|
||||
|
||||
$historyStmt = $this->pdo->prepare('SELECT * FROM order_status_history WHERE order_id = :order_id ORDER BY changed_at DESC, id DESC');
|
||||
$historyStmt->execute(['order_id' => $orderId]);
|
||||
$history = $historyStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($history)) {
|
||||
$history = [];
|
||||
}
|
||||
|
||||
$activityLog = $this->loadActivityLog($orderId);
|
||||
|
||||
return [
|
||||
'order' => $order,
|
||||
'addresses' => $addresses,
|
||||
'items' => $items,
|
||||
'payments' => $payments,
|
||||
'shipments' => $shipments,
|
||||
'documents' => $documents,
|
||||
'notes' => $notes,
|
||||
'status_history' => $history,
|
||||
'activity_log' => $activityLog,
|
||||
'addresses' => $this->loadOrderAddresses($orderId),
|
||||
'items' => $this->loadOrderItems($orderId),
|
||||
'payments' => $this->loadOrderPayments($orderId),
|
||||
'shipments' => $this->loadOrderShipments($orderId),
|
||||
'documents' => $this->loadOrderDocuments($orderId),
|
||||
'notes' => $this->loadOrderNotes($orderId),
|
||||
'status_history' => $this->loadOrderStatusHistory($orderId),
|
||||
'activity_log' => $this->loadActivityLog($orderId),
|
||||
];
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderAddresses(int $orderId): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM order_addresses WHERE order_id = :order_id ORDER BY FIELD(address_type, "customer", "invoice", "delivery"), id ASC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderItems(int $orderId): array
|
||||
{
|
||||
$itemsMediaSql = $this->resolvedMediaUrlSql('oi', 'o.source');
|
||||
$stmt = $this->pdo->prepare('SELECT oi.*, ' . $itemsMediaSql . ' AS resolved_media_url
|
||||
FROM order_items oi
|
||||
INNER JOIN orders o ON o.id = oi.order_id
|
||||
WHERE oi.order_id = :order_id
|
||||
ORDER BY oi.sort_order ASC, oi.id ASC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map(static function (array $row): array {
|
||||
$resolvedMediaUrl = trim((string) ($row['resolved_media_url'] ?? ''));
|
||||
if ($resolvedMediaUrl !== '') {
|
||||
$row['media_url'] = $resolvedMediaUrl;
|
||||
}
|
||||
unset($row['resolved_media_url']);
|
||||
|
||||
return $row;
|
||||
}, $rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderPayments(int $orderId): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM order_payments WHERE order_id = :order_id ORDER BY payment_date ASC, id ASC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderShipments(int $orderId): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM order_shipments WHERE order_id = :order_id ORDER BY posted_at ASC, id ASC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderDocuments(int $orderId): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM order_documents WHERE order_id = :order_id ORDER BY source_created_at ASC, id ASC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderNotes(int $orderId): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM order_notes WHERE order_id = :order_id ORDER BY created_at_external DESC, id DESC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function loadOrderStatusHistory(int $orderId): array
|
||||
{
|
||||
$stmt = $this->pdo->prepare('SELECT * FROM order_status_history WHERE order_id = :order_id ORDER BY changed_at DESC, id DESC');
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, int> $orderIds
|
||||
* @return array<int, array<int, array{name:string, quantity:float, media_url:string}>>
|
||||
|
||||
Reference in New Issue
Block a user