227 lines
6.9 KiB
PHP
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();
|
|
}
|
|
}
|