1+ <?php
2+ /*
3+ * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4+ *
5+ * Copyright (C) 2019 - 2024 Jan Böhmer (https://github.com/jbtronics)
6+ *
7+ * This program is free software: you can redistribute it and/or modify
8+ * it under the terms of the GNU Affero General Public License as published
9+ * by the Free Software Foundation, either version 3 of the License, or
10+ * (at your option) any later version.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU Affero General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Affero General Public License
18+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
19+ */
20+
21+ declare (strict_types=1 );
22+
23+
24+ namespace App \ApiPlatform \Filter ;
25+
26+ use ApiPlatform \Doctrine \Orm \Filter \AbstractFilter ;
27+ use ApiPlatform \Doctrine \Orm \Util \QueryNameGeneratorInterface ;
28+ use ApiPlatform \Metadata \Operation ;
29+ use Doctrine \ORM \QueryBuilder ;
30+ use Symfony \Component \PropertyInfo \Type ;
31+
32+ /**
33+ * Due to their nature, tags are stored in a single string, separated by commas, which requires some more complex search logic.
34+ * This filter allows to easily search for tags in a part entity.
35+ */
36+ final class TagFilter extends AbstractFilter
37+ {
38+
39+ protected function filterProperty (
40+ string $ property ,
41+ $ value ,
42+ QueryBuilder $ queryBuilder ,
43+ QueryNameGeneratorInterface $ queryNameGenerator ,
44+ string $ resourceClass ,
45+ ?Operation $ operation = null ,
46+ array $ context = []
47+ ): void {
48+ // Ignore filter if property is not enabled or mapped
49+ if (
50+ !$ this ->isPropertyEnabled ($ property , $ resourceClass ) ||
51+ !$ this ->isPropertyMapped ($ property , $ resourceClass )
52+ ) {
53+ return ;
54+ }
55+
56+ //Escape any %, _ or \ in the tag
57+ $ value = addcslashes ($ value , '%_ \\' );
58+
59+ $ tag_identifier_prefix = $ queryNameGenerator ->generateParameterName ($ property );
60+
61+ $ expr = $ queryBuilder ->expr ();
62+
63+ $ tmp = $ expr ->orX (
64+ $ expr ->like ('o. ' .$ property , ': ' . $ tag_identifier_prefix . '_1 ' ),
65+ $ expr ->like ('o. ' .$ property , ': ' . $ tag_identifier_prefix . '_2 ' ),
66+ $ expr ->like ('o. ' .$ property , ': ' . $ tag_identifier_prefix . '_3 ' ),
67+ $ expr ->eq ('o. ' .$ property , ': ' . $ tag_identifier_prefix . '_4 ' ),
68+ );
69+
70+ $ queryBuilder ->andWhere ($ tmp );
71+
72+ //Set the parameters for the LIKE expression, in each variation of the tag (so with a comma, at the end, at the beginning, and on both ends, and equaling the tag)
73+ $ queryBuilder ->setParameter ($ tag_identifier_prefix . '_1 ' , '%, ' . $ value . ',% ' );
74+ $ queryBuilder ->setParameter ($ tag_identifier_prefix . '_2 ' , '%, ' . $ value );
75+ $ queryBuilder ->setParameter ($ tag_identifier_prefix . '_3 ' , $ value . ',% ' );
76+ $ queryBuilder ->setParameter ($ tag_identifier_prefix . '_4 ' , $ value );
77+ }
78+
79+ public function getDescription (string $ resourceClass ): array
80+ {
81+ if (!$ this ->properties ) {
82+ return [];
83+ }
84+
85+ $ description = [];
86+ foreach (array_keys ($ this ->properties ) as $ property ) {
87+ $ description [(string )$ property ] = [
88+ 'property ' => $ property ,
89+ 'type ' => Type::BUILTIN_TYPE_STRING ,
90+ 'required ' => false ,
91+ 'description ' => 'Filter for tags of a part ' ,
92+ 'openapi ' => [
93+ 'example ' => '' ,
94+ 'allowReserved ' => false ,// if true, query parameters will be not percent-encoded
95+ 'allowEmptyValue ' => true ,
96+ 'explode ' => false , // to be true, the type must be Type::BUILTIN_TYPE_ARRAY, ?product=blue,green will be ?product=blue&product=green
97+ ],
98+ ];
99+ }
100+ return $ description ;
101+ }
102+ }
0 commit comments