Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
17 / 17
CRAP
100.00% covered (success)
100.00%
1 / 1
CategoryService
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
17 / 17
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllCategories
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllCategoryIds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCategoriesFromFaq
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryIdFromFaq
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryIdsFromFaq
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryIdFromName
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryData
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 update
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 moveOwnership
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updateParentCategory
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 checkIfCategoryExists
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryLanguagesTranslated
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMissingCategories
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Category service for CRUD and repository operations.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2025 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2025-10-20
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Category;
21
22use phpMyFAQ\Entity\CategoryEntity;
23
24/**
25 * Service for category CRUD and repository operations.
26 * Consolidates all repository delegation methods from the legacy Category class.
27 */
28final readonly class CategoryService
29{
30    public function __construct(
31        private CategoryRepositoryInterface $categoryRepository,
32    ) {
33    }
34
35    /**
36     * Gets all categories.
37     *
38     * @return array<int, array<string, mixed>>
39     */
40    public function getAllCategories(?string $language = null): array
41    {
42        return $this->categoryRepository->findAllCategories($language);
43    }
44
45    /**
46     * Gets all category IDs.
47     *
48     * @return array<int>
49     */
50    public function getAllCategoryIds(?string $language = null): array
51    {
52        return $this->categoryRepository->findAllCategoryIds($language);
53    }
54
55    /**
56     * Gets categories from a FAQ.
57     *
58     * @return array<int, array<string, mixed>>
59     */
60    public function getCategoriesFromFaq(int $faqId, string $language): array
61    {
62        return $this->categoryRepository->findCategoriesFromFaq($faqId, $language);
63    }
64
65    /**
66     * Gets the first category ID from a FAQ.
67     */
68    public function getCategoryIdFromFaq(int $faqId, string $language): int
69    {
70        $categories = $this->getCategoriesFromFaq($faqId, $language);
71        $ids = array_keys($categories);
72        return $ids[0] ?? 0;
73    }
74
75    /**
76     * Gets all category IDs from a FAQ.
77     *
78     * @return array<int>
79     */
80    public function getCategoryIdsFromFaq(int $faqId, string $language): array
81    {
82        $categories = $this->getCategoriesFromFaq($faqId, $language);
83        return array_keys($categories);
84    }
85
86    /**
87     * Gets category ID from name.
88     */
89    public function getCategoryIdFromName(string $categoryName): int|bool
90    {
91        $id = $this->categoryRepository->findCategoryIdByName($categoryName);
92        return $id ?? false;
93    }
94
95    /**
96     * Gets category data by ID and language.
97     */
98    public function getCategoryData(int $categoryId, string $language): CategoryEntity
99    {
100        $entity = $this->categoryRepository->findByIdAndLanguage($categoryId, $language);
101        return $entity ?? new CategoryEntity();
102    }
103
104    /**
105     * Creates a new category.
106     */
107    public function create(CategoryEntity $categoryEntity): ?int
108    {
109        return $this->categoryRepository->create($categoryEntity);
110    }
111
112    /**
113     * Updates an existing category.
114     */
115    public function update(CategoryEntity $categoryEntity): bool
116    {
117        return $this->categoryRepository->update($categoryEntity);
118    }
119
120    /**
121     * Deletes a category.
122     */
123    public function delete(int $categoryId, string $categoryLang): bool
124    {
125        return $this->categoryRepository->delete($categoryId, $categoryLang);
126    }
127
128    /**
129     * Moves ownership from one user to another.
130     */
131    public function moveOwnership(int $currentOwner, int $newOwner): bool
132    {
133        return $this->categoryRepository->moveOwnership($currentOwner, $newOwner);
134    }
135
136    /**
137     * Checks if a category has a specific language.
138     */
139    public function hasLanguage(int $categoryId, string $categoryLanguage): bool
140    {
141        return $this->categoryRepository->hasLanguage($categoryId, $categoryLanguage);
142    }
143
144    /**
145     * Updates the parent category.
146     */
147    public function updateParentCategory(int $categoryId, int $parentId): bool
148    {
149        if ($categoryId === $parentId) {
150            return false;
151        }
152
153        return $this->categoryRepository->updateParentCategory($categoryId, $parentId);
154    }
155
156    /**
157     * Checks if a category exists.
158     */
159    public function checkIfCategoryExists(CategoryEntity $categoryEntity): int
160    {
161        return $this->categoryRepository->countByNameLangParent(
162            $categoryEntity->getName(),
163            $categoryEntity->getLang(),
164            $categoryEntity->getParentId(),
165        );
166    }
167
168    /**
169     * Gets category languages translated.
170     *
171     * @return array<string, string>
172     */
173    public function getCategoryLanguagesTranslated(int $categoryId): array
174    {
175        return $this->categoryRepository->getCategoryLanguagesTranslated($categoryId);
176    }
177
178    /**
179     * Gets missing categories.
180     *
181     * @return array<int, array<string, mixed>>
182     */
183    public function getMissingCategories(?string $language = null): array
184    {
185        return $this->categoryRepository->findMissingCategories($language);
186    }
187}