js css
This commit is contained in:
@@ -25,6 +25,105 @@ final class ProductRepository extends BaseRepository
|
||||
return $this->preparePreviewProducts($statement->fetchAll());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function promoPreview(int $limit = 10): array
|
||||
{
|
||||
$limit = max(1, min($limit, 10));
|
||||
$products = $this->manualPromoPreview($limit);
|
||||
$usedIds = array_flip(array_map(static fn (array $product): int => (int) ($product['id'] ?? 0), $products));
|
||||
|
||||
$fallbackProducts = $this->catalogPreview(5000);
|
||||
$fallbackProducts = array_values(array_filter(array_map(
|
||||
fn (array $product): ?array => $this->withRetailPreviewVariant($product, 6.0),
|
||||
$fallbackProducts
|
||||
)));
|
||||
|
||||
usort($fallbackProducts, static function (array $left, array $right): int {
|
||||
return (int) sprintf('%u', crc32('promo|' . ($left['id'] ?? 0)))
|
||||
<=> (int) sprintf('%u', crc32('promo|' . ($right['id'] ?? 0)));
|
||||
});
|
||||
|
||||
foreach ($fallbackProducts as $fallbackProduct) {
|
||||
$fallbackId = (int) ($fallbackProduct['id'] ?? 0);
|
||||
if (isset($usedIds[$fallbackId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$products[] = $fallbackProduct;
|
||||
if (count($products) >= $limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return array_map(
|
||||
fn (array $product): array => $this->applyPromoPricing($product),
|
||||
array_slice($products, 0, $limit)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function newPreview(int $limit = 10): array
|
||||
{
|
||||
$limit = max(1, min($limit, 50));
|
||||
$sql = $this->previewSelectSql(
|
||||
'ORDER BY p.created_at DESC, p.id DESC',
|
||||
$limit,
|
||||
0,
|
||||
false
|
||||
);
|
||||
|
||||
$statement = $this->pdo()->query($sql);
|
||||
$products = $this->preparePreviewProducts($statement->fetchAll());
|
||||
|
||||
foreach ($products as &$product) {
|
||||
$product['badge_label'] = 'Новинка';
|
||||
$product['badge_kind'] = 'new';
|
||||
}
|
||||
unset($product);
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function categoryRetailPreview(int $categoryId, int $limit = 10, float $maxPackageQuantity = 6.0): array
|
||||
{
|
||||
return $this->categoryRetailShowcase($categoryId, $limit, $maxPackageQuantity)['products'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{products: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function categoryRetailShowcase(int $categoryId, int $limit = 10, float $maxPackageQuantity = 6.0): array
|
||||
{
|
||||
$products = $this->forCategory($categoryId, 5000, 0, false);
|
||||
$total = count($products);
|
||||
$products = array_values(array_filter(array_map(
|
||||
fn (array $product): ?array => $this->withRetailPreviewVariant($product, $maxPackageQuantity),
|
||||
$products
|
||||
)));
|
||||
|
||||
usort($products, static function (array $left, array $right): int {
|
||||
return [
|
||||
(int) sprintf('%u', crc32('home|' . ($left['id'] ?? 0))),
|
||||
(string) ($left['name'] ?? ''),
|
||||
] <=> [
|
||||
(int) sprintf('%u', crc32('home|' . ($right['id'] ?? 0))),
|
||||
(string) ($right['name'] ?? ''),
|
||||
];
|
||||
});
|
||||
|
||||
return [
|
||||
'products' => array_slice($products, 0, max(1, min($limit, 10))),
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
@@ -479,6 +578,127 @@ final class ProductRepository extends BaseRepository
|
||||
LIMIT ' . $limit . ' OFFSET ' . $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $product
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function withRetailPreviewVariant(array $product, float $maxPackageQuantity): ?array
|
||||
{
|
||||
$variants = array_values(array_filter(
|
||||
$product['preview_variants'] ?? [],
|
||||
static function (array $variant) use ($maxPackageQuantity): bool {
|
||||
$packagePrice = (float) ($variant['package_price'] ?? 0);
|
||||
$packageQuantity = (float) ($variant['package_quantity'] ?? 0);
|
||||
|
||||
return $packagePrice > 0
|
||||
&& $packageQuantity > 0
|
||||
&& $packageQuantity <= $maxPackageQuantity;
|
||||
}
|
||||
));
|
||||
|
||||
if ($variants === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$product['preview_variants'] = $variants;
|
||||
|
||||
return $this->syncProductVariantFields($product, $variants[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function manualPromoPreview(int $limit): array
|
||||
{
|
||||
$statement = $this->pdo()->query(
|
||||
'SELECT product_id
|
||||
FROM promo_block_items
|
||||
WHERE is_active = 1
|
||||
ORDER BY sort_order, id
|
||||
LIMIT ' . max(1, min($limit, 10))
|
||||
);
|
||||
$productIds = array_map('intval', $statement->fetchAll(\PDO::FETCH_COLUMN));
|
||||
|
||||
if ($productIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($productIds), '?'));
|
||||
$sql = $this->previewSelectSql(
|
||||
'AND p.id IN (' . $placeholders . ')
|
||||
ORDER BY FIELD(p.id, ' . $placeholders . ')',
|
||||
count($productIds),
|
||||
0,
|
||||
false
|
||||
);
|
||||
|
||||
$productsStatement = $this->pdo()->prepare($sql);
|
||||
$productsStatement->execute(array_merge($productIds, $productIds));
|
||||
$products = $this->preparePreviewProducts($productsStatement->fetchAll());
|
||||
$productsById = [];
|
||||
|
||||
foreach ($products as $product) {
|
||||
$retailProduct = $this->withRetailPreviewVariant($product, 6.0);
|
||||
if ($retailProduct !== null) {
|
||||
$productsById[(int) ($product['id'] ?? 0)] = $retailProduct;
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($productIds as $productId) {
|
||||
if (isset($productsById[$productId])) {
|
||||
$result[] = $productsById[$productId];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $product
|
||||
* @param array<string, mixed> $variant
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function syncProductVariantFields(array $product, array $variant): array
|
||||
{
|
||||
$product['variant_id'] = $variant['id'] ?? $product['variant_id'] ?? null;
|
||||
$product['variant_name'] = $variant['name'] ?? $product['variant_name'] ?? null;
|
||||
$product['unit'] = $variant['unit'] ?? $product['unit'] ?? null;
|
||||
$product['package_quantity'] = $variant['package_quantity'] ?? $product['package_quantity'] ?? null;
|
||||
$product['price_per_unit'] = $variant['price_per_unit'] ?? $product['price_per_unit'] ?? null;
|
||||
$product['package_price'] = $variant['package_price'] ?? $product['package_price'] ?? null;
|
||||
$product['old_package_price'] = $variant['old_package_price'] ?? $product['old_package_price'] ?? null;
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $product
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function applyPromoPricing(array $product): array
|
||||
{
|
||||
$discountPercent = 5 + ((int) sprintf('%u', crc32('discount|' . ($product['id'] ?? 0))) % 31);
|
||||
$divider = max(0.01, 1 - ($discountPercent / 100));
|
||||
$variants = [];
|
||||
|
||||
foreach (($product['preview_variants'] ?? []) as $variant) {
|
||||
$packagePrice = (float) ($variant['package_price'] ?? 0);
|
||||
if ($packagePrice > 0) {
|
||||
$variant['old_package_price'] = (string) ceil($packagePrice / $divider);
|
||||
}
|
||||
$variants[] = $variant;
|
||||
}
|
||||
|
||||
$product['preview_variants'] = $variants;
|
||||
$product = $variants === [] ? $product : $this->syncProductVariantFields($product, $variants[0]);
|
||||
$product['badge_label'] = '-' . $discountPercent . '%';
|
||||
$product['badge_kind'] = 'promo';
|
||||
$product['promo_discount_percent'] = $discountPercent;
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $products
|
||||
* @return array<int, array<string, mixed>>
|
||||
|
||||
Reference in New Issue
Block a user