Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
SearchEntitiesEndpoint
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
42
0.00% covered (danger)
0.00%
0 / 1
 handle
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace Olz\Api\Endpoints;
4
5use Doctrine\Common\Collections\Criteria;
6use Olz\Api\OlzTypedEndpoint;
7use Olz\Entity\Common\OlzEntity;
8use Olz\Entity\Faq\Question;
9use Olz\Entity\Faq\QuestionCategory;
10use Olz\Entity\Roles\Role;
11use Olz\Entity\Service\Download;
12use Olz\Entity\Service\Link;
13use Olz\Entity\SolvEvent;
14use Olz\Entity\Termine\TerminLabel;
15use Olz\Entity\Termine\TerminLocation;
16use Olz\Entity\Termine\TerminTemplate;
17use Olz\Entity\Users\User;
18use PhpTypeScriptApi\HttpError;
19
20/**
21 * TODO: Support key-of<self::SUPPORTED_ENTITY_TYPES> in php-typescript-api.
22 *
23 * @phpstan-type OlzEntityResult array{
24 *   id: int<1, max>,
25 *   title: non-empty-string,
26 * }
27 * @phpstan-type OlzSearchableEntityType 'Download'|'Link'|'Question'|'QuestionCategory'|'SolvEvent'|'TerminLabel'|'TerminLocation'|'TerminTemplate'|'Role'|'User'
28 *
29 * @extends OlzTypedEndpoint<
30 *   array{
31 *     entityType: OlzSearchableEntityType,
32 *     query?: ?string,
33 *     id?: ?int<1, max>,
34 *     filter?: ?array<string, string>,
35 *   },
36 *   array{
37 *     result: array<OlzEntityResult>
38 *   }
39 * >
40 */
41class SearchEntitiesEndpoint extends OlzTypedEndpoint {
42    public const SUPPORTED_ENTITY_TYPES = [
43        'Download' => Download::class,
44        'Link' => Link::class,
45        'Question' => Question::class,
46        'QuestionCategory' => QuestionCategory::class,
47        'SolvEvent' => SolvEvent::class,
48        'TerminLabel' => TerminLabel::class,
49        'TerminLocation' => TerminLocation::class,
50        'TerminTemplate' => TerminTemplate::class,
51        'Role' => Role::class,
52        'User' => User::class,
53    ];
54
55    protected function handle(mixed $input): mixed {
56        $this->checkPermission('any');
57
58        $entity_type = $input['entityType'];
59        $entity_class = self::SUPPORTED_ENTITY_TYPES[$entity_type];
60
61        $filter_criteria = [];
62        foreach ($input['filter'] ?? [] as $key => $value) {
63            try {
64                $filter_criteria[] = $entity_class::getCriteriaForFilter($key, $value);
65            } catch (\Throwable $th) {
66                throw new HttpError(400, "Invalid filter {$key} => {$value} for entity {$entity_type}{$th->getMessage()}");
67            }
68        }
69
70        $search_terms = preg_split('/\s+/', $input['query'] ?? '') ?: [];
71        $matching_criterium = Criteria::expr()->andX(
72            ...array_map(function ($search_term) use ($entity_class) {
73                return $entity_class::getCriteriaForQuery($search_term);
74            }, $search_terms),
75        );
76
77        $id = $input['id'] ?? null;
78        $id_field_name = $entity_class::getIdFieldNameForSearch();
79        $id_criteria = $id ? [Criteria::expr()->eq($id_field_name, $id)] : [];
80
81        $repo = $this->entityManager()->getRepository($entity_class);
82        $on_off_criteria = is_subclass_of($entity_class, OlzEntity::class) ? [
83            Criteria::expr()->eq('on_off', 1),
84        ] : [];
85        $criteria = Criteria::create()
86            ->where(Criteria::expr()->andX(
87                $matching_criterium,
88                ...$on_off_criteria,
89                ...$filter_criteria,
90                ...$id_criteria,
91            ))
92            ->setFirstResult(0)
93            ->setMaxResults(10)
94        ;
95        $matching_entities = $repo->matching($criteria);
96
97        return [
98            'result' => array_map(function ($entity) {
99                return [
100                    'id' => $entity->getIdForSearch(),
101                    'title' => $entity->getTitleForSearch(),
102                ];
103            }, [...$matching_entities]),
104        ];
105    }
106}