@@ -14,6 +14,19 @@ class BaseRepository extends EntityRepository
1414 protected $ joins = array ();
1515 protected $ disableJoins = false ;
1616 protected $ setupFunction = null ;
17+ protected $ filterFunctions = array ();
18+
19+ public function addFilterFunction (callable $ func )
20+ {
21+ $ this ->filterFunctions [] = $ func ;
22+
23+ return $ this ;
24+ }
25+
26+ public function getFilterFunctions ()
27+ {
28+ return $ this ->filterFunctions ;
29+ }
1730
1831 public function disableJoins (bool $ disableJoins )
1932 {
@@ -51,6 +64,9 @@ public function findFiltered(array $filters = array(), $order = array(), $limit
5164 // Get the query
5265 $ query = $ queryBuilder ->getQuery ();
5366
67+ // Clear filters
68+ $ this ->filterFunctions = [];
69+
5470 // Execute and return
5571 return $ query ->getResult ();
5672 }
@@ -63,6 +79,9 @@ public function findOneFiltered(array $filters = array(), $order = array(), $off
6379 // Get the query
6480 $ query = $ queryBuilder ->getQuery ();
6581
82+ // Clear filters
83+ $ this ->filterFunctions = [];
84+
6685 // Execute and return
6786 return $ query ->getOneOrNullResult ();
6887 }
@@ -76,7 +95,7 @@ public function buildQuery(array $filters = array(), $order = array(), $limit =
7695 ));
7796
7897 // Got a setup function?
79- if (is_callable ($ this ->setupFunction ))
98+ if (is_callable ($ this ->setupFunction ))
8099 {
81100 // Run it
82101 $ queryBuilder = call_user_func ($ this ->setupFunction , $ this ->alias , $ queryBuilder );
@@ -91,15 +110,15 @@ public function buildQuery(array $filters = array(), $order = array(), $limit =
91110 ), $ opt );
92111
93112 // Any joins?
94- if (count ($ this ->joins ) && !$ opt ['disable_joins ' ] && !$ this ->disableJoins )
113+ if (count ($ this ->joins ) && !$ opt ['disable_joins ' ] && !$ this ->disableJoins )
95114 {
96115 // Loop joins
97- foreach ($ this ->joins AS $ someJoin )
116+ foreach ($ this ->joins as $ someJoin )
98117 {
99118 list ($ joinType , $ joinColumn , $ joinTable ) = $ someJoin ;
100119
101120 // Not got a dot, prefix table alias
102- if (stripos ($ joinColumn , ". " ) === false )
121+ if (stripos ($ joinColumn , ". " ) === false )
103122 $ joinColumn = $ this ->alias . ". " . $ joinColumn ;
104123
105124 // Join
@@ -108,32 +127,42 @@ public function buildQuery(array $filters = array(), $order = array(), $limit =
108127 }
109128
110129 // Order
111- if (count ($ order ))
130+ if (count ($ order ))
112131 {
113132 // Loop columns to order
114- foreach ($ order AS $ key => $ val )
133+ foreach ($ order as $ key => $ val )
115134 {
116135 // Not got a dot, prefix table alias
117- if (is_string ($ key ) && stripos ($ key , ". " ) === false && in_array ($ key , $ this ->getClassMetadata ($ this ->getClassName ())->getColumnNames ()))
136+ if (is_string ($ key ) && stripos ($ key , ". " ) === false && in_array ($ key , $ this ->getClassMetadata ($ this ->getClassName ())->getColumnNames ()))
118137 $ key = $ this ->alias . ". " . $ key ;
119138
120139 $ queryBuilder ->addOrderBy ($ key , $ val );
121140 }
122141 }
123142
124143 // Limit
125- if ($ limit )
144+ if ($ limit )
126145 $ queryBuilder ->setMaxResults ($ limit );
127146
128147 // Offset
129- if ($ offset )
148+ if ($ offset )
130149 $ queryBuilder ->setFirstResult ($ offset );
131150
151+ // Loop the filter functions
152+ foreach ($ this ->getFilterFunctions () as $ someFunc )
153+ {
154+ $ queryBuilder = $ someFunc ($ queryBuilder );
155+ }
156+
157+
158+
159+ $ queryBuilder ->addGroupBy ($ this ->alias . ".id " );
160+
132161 // Got any filters?
133- if (count ($ filters ))
162+ if (count ($ filters ))
134163 {
135164 // Add the where
136- $ queryBuilder ->where ($ this ->addCriteria ($ queryBuilder , $ queryBuilder ->expr ()->andX (), $ filters ));
165+ $ queryBuilder ->andWhere ($ this ->addCriteria ($ queryBuilder , $ queryBuilder ->expr ()->andX (), $ filters ));
137166 }
138167
139168 return $ queryBuilder ;
@@ -142,19 +171,19 @@ public function buildQuery(array $filters = array(), $order = array(), $limit =
142171 public function addCriteria (QueryBuilder $ queryBuilder , Composite $ expr , array $ criteria )
143172 {
144173 // Got criteria
145- if (count ($ criteria ))
174+ if (count ($ criteria ))
146175 {
147- foreach ($ criteria AS $ k => $ v )
176+ foreach ($ criteria as $ k => $ v )
148177 {
149178 // Numeric (i.e. it's being passed in as an operator e.g. ["id", "eq", 999])
150- if (is_numeric ($ k ))
179+ if (is_numeric ($ k ))
151180 {
152181 // Not an array
153- if (!is_array ($ v ))
182+ if (!is_array ($ v ))
154183 throw new \Exception ("Non-indexed criteria must be in array form e.g. ['id', 'eq', 1234] " );
155184
156185 // Extract
157- if (count ($ v ) == 3 )
186+ if (count ($ v ) == 3 )
158187 list ($ field , $ operator , $ value ) = $ v ;
159188 else
160189 {
@@ -165,7 +194,7 @@ public function addCriteria(QueryBuilder $queryBuilder, Composite $expr, array $
165194 }
166195
167196 // Is this a special case i.e. or/and
168- if (in_array ($ field , array ("or " , "and " )))
197+ if (in_array ($ field , array ("or " , "and " )))
169198 {
170199 // Move things around
171200 $ value = $ operator ;
@@ -179,11 +208,11 @@ public function addCriteria(QueryBuilder $queryBuilder, Composite $expr, array $
179208 else
180209 {
181210 // Is the value an array?
182- if (is_array ($ v ))
211+ if (is_array ($ v ))
183212 throw new \Exception ("Indexed criteria does not support array values " );
184213
185214 // Is the value null?
186- if (is_null ($ v ))
215+ if (is_null ($ v ))
187216 {
188217 // Use "is_null" operator
189218 $ field = $ k ;
@@ -200,42 +229,42 @@ public function addCriteria(QueryBuilder $queryBuilder, Composite $expr, array $
200229 }
201230
202231 // Not got a dot, prefix table alias
203- if (stripos ($ field , ". " ) === false )
232+ if (stripos ($ field , ". " ) === false )
204233 $ field = $ this ->alias . ". " . $ field ;
205234
206235 // Raw
207- if ($ operator === 'raw ' )
236+ if ($ operator === 'raw ' )
208237 $ expr ->add ($ value );
209238 // Or
210- elseif ($ operator === 'or ' )
239+ elseif ($ operator === 'or ' )
211240 $ expr ->add ($ this ->addCriteria ($ queryBuilder , $ queryBuilder ->expr ()->orX (), $ value ));
212241 // And
213- elseif ($ operator === 'and ' )
242+ elseif ($ operator === 'and ' )
214243 $ expr ->add ($ this ->addCriteria ($ queryBuilder , $ queryBuilder ->expr ()->andX (), $ value ));
215244 // Basic operators
216- elseif (in_array ($ operator , array ("eq " , "neq " , "gt " , "gte " , "lt " , "lte " , "like " )))
245+ elseif (in_array ($ operator , array ("eq " , "neq " , "gt " , "gte " , "lt " , "lte " , "like " )))
217246 {
218247 // Arrays not supported for this operator
219- if (is_array ($ value ))
248+ if (is_array ($ value ))
220249 throw new \Exception ("Array lookups are not supported for the ' " . $ operator . "' operator " );
221250
222251 // DateTime
223- if (is_object ($ value ) && $ value instanceof \DateTime)
252+ if (is_object ($ value ) && $ value instanceof \DateTime)
224253 {
225254 $ expr ->add ($ queryBuilder ->expr ()->{$ operator }($ field , $ this ->createNamedParameter ($ queryBuilder , $ this ->prepareValue ($ value ))));
226255 }
227256 // Is it a UUID?
228- elseif ($ value instanceof Uuid)
257+ elseif ($ value instanceof Uuid)
229258 {
230259 $ expr ->add ($ queryBuilder ->expr ()->{$ operator }($ field , $ this ->createNamedParameter ($ queryBuilder , $ this ->prepareValue ($ value ->toBinary ()), ParameterType::BINARY )));
231260 }
232261 // Other object (likely an association)
233- elseif (is_object ($ value ))
262+ elseif (is_object ($ value ))
234263 {
235264 $ expr ->add ($ queryBuilder ->expr ()->{$ operator }($ field , $ this ->createNamedParameter ($ queryBuilder , $ this ->prepareValue ($ value ))));
236265 }
237266 // Is it null?
238- elseif (is_null ($ value ))
267+ elseif (is_null ($ value ))
239268 {
240269 $ expr ->add ($ queryBuilder ->expr ()->isNull ($ field ));
241270 }
@@ -246,39 +275,39 @@ public function addCriteria(QueryBuilder $queryBuilder, Composite $expr, array $
246275 }
247276 }
248277 // Null operator
249- elseif (in_array ($ operator , array ("is_null " , "not_null " )))
278+ elseif (in_array ($ operator , array ("is_null " , "not_null " )))
250279 {
251280 // Is null
252- if ($ operator == "is_null " )
281+ if ($ operator == "is_null " )
253282 {
254283 // True or false value?
255- if ($ value )
284+ if ($ value )
256285 $ expr ->add ($ queryBuilder ->expr ()->isNull ($ field ));
257286 else
258287 $ expr ->add ($ queryBuilder ->expr ()->isNotNull ($ field ));
259288 }
260289 // Not null
261- elseif ($ operator == "not_null " )
290+ elseif ($ operator == "not_null " )
262291 {
263292 // True or false value?
264- if ($ value )
293+ if ($ value )
265294 $ expr ->add ($ queryBuilder ->expr ()->isNotNull ($ field ));
266295 else
267296 $ expr ->add ($ queryBuilder ->expr ()->isNull ($ field ));
268297 }
269298 }
270299 // In/NotIn operators
271- elseif (in_array ($ operator , array ("in " , "not_in " )))
300+ elseif (in_array ($ operator , array ("in " , "not_in " )))
272301 {
273302 // Make sure it's an array
274- if (!is_array ($ value ))
303+ if (!is_array ($ value ))
275304 throw new \Exception ("Invalid value for operator: " . $ operator );
276305
277306 // In
278- if ($ operator == "in " )
307+ if ($ operator == "in " )
279308 $ expr ->add ($ queryBuilder ->expr ()->in ($ field , $ this ->createNamedParameter ($ queryBuilder , $ this ->prepareValue ($ value ))));
280309 // Not in
281- elseif ($ operator == "not_in " )
310+ elseif ($ operator == "not_in " )
282311 {
283312 // Need to use multiple != operations because "NOT IN" is not null-safe
284313 // We therefore loop the values and build the SQL string
@@ -287,10 +316,10 @@ public function addCriteria(QueryBuilder $queryBuilder, Composite $expr, array $
287316 $ builtArraySQL = array ();
288317
289318 // Loop the values
290- foreach ($ this ->prepareValue ($ value ) AS $ someValue )
319+ foreach ($ this ->prepareValue ($ value ) as $ someValue )
291320 {
292321 // Is it null?
293- if (is_null ($ someValue ))
322+ if (is_null ($ someValue ))
294323 {
295324 // Make sure we don't return if null
296325 $ builtArraySQL [] = '( ' . $ field . ' IS NOT NULL) ' ;
@@ -304,10 +333,10 @@ public function addCriteria(QueryBuilder $queryBuilder, Composite $expr, array $
304333 }
305334
306335 // Got anything?
307- if (count ($ builtArraySQL ))
336+ if (count ($ builtArraySQL ))
308337 {
309338 // Implode into full array
310- if (phpversion () >= 8 )
339+ if (phpversion () >= 8 )
311340 $ fullSQL = "( " . implode (' AND ' , $ builtArraySQL ) . ") " ;
312341 else
313342 $ fullSQL = "( " . implode ($ builtArraySQL , ' AND ' ) . ") " ;
@@ -345,20 +374,20 @@ public function createNamedParameter(QueryBuilder $queryBuilder, $value)
345374 public function prepareValue ($ value )
346375 {
347376 // DateTime
348- if (is_object ($ value ) && $ value instanceof \DateTime)
377+ if (is_object ($ value ) && $ value instanceof \DateTime)
349378 {
350379 return $ value ->format ('Y-m-d H:i:s ' );
351380 }
352381 // Object
353- elseif (is_object ($ value ))
382+ elseif (is_object ($ value ))
354383 {
355384 return $ value ;
356385 }
357386 // Array
358- elseif (is_array ($ value ))
387+ elseif (is_array ($ value ))
359388 {
360389 // Loop
361- foreach ($ value AS $ k => $ v )
390+ foreach ($ value as $ k => $ v )
362391 {
363392 // Prepare it
364393 $ value [$ k ] = $ this ->prepareValue ($ v );
@@ -374,7 +403,7 @@ public function prepareValue($value)
374403 public function buildSearchCriteria (string $ keywords , array $ searchableColumns = array ())
375404 {
376405 // Got no searchable columns
377- if (!count ($ searchableColumns ))
406+ if (!count ($ searchableColumns ))
378407 throw new \Exception ("No searchable columns specified " );
379408
380409 // Explode individual keywords
@@ -384,13 +413,13 @@ public function buildSearchCriteria(string $keywords, array $searchableColumns =
384413 $ keywordCriteria = array ();
385414
386415 // Loop keywords
387- foreach ($ keywords AS $ someKeyword )
416+ foreach ($ keywords as $ someKeyword )
388417 {
389418 // Grab this group
390419 $ keywordGroup = array ();
391420
392421 // Loop search columns
393- foreach ($ searchableColumns AS $ searchColumn )
422+ foreach ($ searchableColumns as $ searchColumn )
394423 {
395424 // Grab it
396425 $ keywordGroup [] = array ($ searchColumn , "like " , "% " . $ someKeyword . "% " );
0 commit comments