Files
new.rybstock.ru/app/Repositories/CategoryRepository.php
T

227 lines
6.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Repositories;
final class CategoryRepository extends BaseRepository
{
/**
* @return array<int, array<string, mixed>>
*/
public function active(): array
{
$statement = $this->pdo()->query(
'SELECT id, parent_id, name, slug, legacy_path, h1, seo_title, seo_description, seo_keywords, image_path, sort_order
FROM categories
WHERE is_active = 1
ORDER BY parent_id IS NOT NULL, sort_order, name'
);
return $statement->fetchAll();
}
/**
* @return array<int, array<string, mixed>>
*/
public function flatActive(): array
{
$statement = $this->pdo()->query(
'SELECT id, parent_id, name, slug, sort_order
FROM categories
WHERE is_active = 1
ORDER BY parent_id IS NOT NULL, sort_order, name'
);
return $statement->fetchAll();
}
/**
* @return array<int, array<string, mixed>>
*/
public function tree(): array
{
$items = $this->active();
$childrenByParent = [];
foreach ($items as $item) {
$parentKey = $item['parent_id'] === null ? 0 : (int) $item['parent_id'];
$childrenByParent[$parentKey][] = $item;
}
$childrenByParent[0] = $this->sortRootCategories($childrenByParent[0] ?? []);
return $this->buildTree($childrenByParent, 0);
}
/**
* @param array<int, array<string, mixed>> $categories
* @return array<int, array<string, mixed>>
*/
private function sortRootCategories(array $categories): array
{
usort($categories, function (array $left, array $right): int {
return [
$this->rootCategoryWeight((string) ($left['name'] ?? '')),
(int) ($left['sort_order'] ?? 100),
(string) ($left['name'] ?? ''),
] <=> [
$this->rootCategoryWeight((string) ($right['name'] ?? '')),
(int) ($right['sort_order'] ?? 100),
(string) ($right['name'] ?? ''),
];
});
return $categories;
}
private function rootCategoryWeight(string $name): int
{
$lowerName = trim(mb_strtolower($name));
return match (true) {
str_contains($lowerName, 'рыб') => 10,
str_contains($lowerName, 'морепроду') => 20,
str_contains($lowerName, 'икра') => 30,
$lowerName === 'мясо' => 40,
str_contains($lowerName, 'сыр') => 50,
str_contains($lowerName, 'полуфаб') => 60,
str_contains($lowerName, 'фрукт') || str_contains($lowerName, 'овощ') || str_contains($lowerName, 'ягод') || str_contains($lowerName, 'гриб') => 70,
str_contains($lowerName, 'бакале') => 80,
str_contains($lowerName, 'мясная гастрономия') => 90,
str_contains($lowerName, 'молоч') => 100,
default => 1000,
};
}
/**
* @param array<int, array<int, array<string, mixed>>> $childrenByParent
* @return array<int, array<string, mixed>>
*/
private function buildTree(array $childrenByParent, int $parentId): array
{
$branch = [];
foreach ($childrenByParent[$parentId] ?? [] as $item) {
$item['children'] = $this->buildTree($childrenByParent, (int) $item['id']);
$branch[] = $item;
}
return $branch;
}
/**
* @return array<string, mixed>|null
*/
public function findBySlug(string $slug): ?array
{
$statement = $this->pdo()->prepare(
'SELECT id, parent_id, name, slug, legacy_path, description, h1, seo_title, seo_description, seo_keywords, image_path
FROM categories
WHERE slug = :slug AND is_active = 1
LIMIT 1'
);
$statement->execute(['slug' => $slug]);
$category = $statement->fetch();
return $category ?: null;
}
/**
* @return array<int, array<string, string>>
*/
public function breadcrumbs(int $categoryId): array
{
$items = [];
$currentId = $categoryId;
while ($currentId > 0) {
$statement = $this->pdo()->prepare(
'SELECT id, parent_id, name, slug
FROM categories
WHERE id = :id
LIMIT 1'
);
$statement->execute(['id' => $currentId]);
$category = $statement->fetch();
if (!$category) {
break;
}
array_unshift($items, [
'title' => $category['name'],
'url' => '/catalog/' . $category['slug'],
]);
$currentId = $category['parent_id'] === null ? 0 : (int) $category['parent_id'];
}
array_unshift($items, [
'title' => 'Главная',
'url' => '/',
]);
return $items;
}
/**
* @return array<int, int>
*/
public function descendantIds(int $categoryId): array
{
$items = $this->flatActive();
$childrenByParent = [];
foreach ($items as $item) {
$parentId = $item['parent_id'] === null ? 0 : (int) $item['parent_id'];
$childrenByParent[$parentId][] = (int) $item['id'];
}
$ids = [$categoryId];
$queue = [$categoryId];
while ($queue !== []) {
$currentId = array_shift($queue);
foreach ($childrenByParent[$currentId] ?? [] as $childId) {
$ids[] = $childId;
$queue[] = $childId;
}
}
return array_values(array_unique($ids));
}
/**
* @param array<string, mixed> $data
*/
public function create(array $data): int
{
if (($data['name'] ?? '') === '' || ($data['slug'] ?? '') === '') {
throw new \InvalidArgumentException('Название и slug обязательны.');
}
$statement = $this->pdo()->prepare(
'INSERT INTO categories
(parent_id, name, slug, h1, seo_title, seo_description, seo_keywords, sort_order, is_active)
VALUES
(:parent_id, :name, :slug, :h1, :seo_title, :seo_description, :seo_keywords, :sort_order, :is_active)'
);
$statement->execute([
'parent_id' => $data['parent_id'],
'name' => $data['name'],
'slug' => $data['slug'],
'h1' => $data['h1'] ?: null,
'seo_title' => $data['seo_title'] ?: null,
'seo_description' => $data['seo_description'] ?: null,
'seo_keywords' => $data['seo_keywords'] ?: null,
'sort_order' => $data['sort_order'] ?? 100,
'is_active' => $data['is_active'] ?? 1,
]);
return (int) $this->pdo()->lastInsertId();
}
}