44
55namespace Williarin \WordpressInterop \Bridge \Repository ;
66
7+ use Doctrine \DBAL \Query \Expression \CompositeExpression ;
78use Doctrine \DBAL \Query \QueryBuilder ;
89use JetBrains \PhpStorm \ArrayShape ;
910use Symfony \Component \OptionsResolver \OptionsResolver ;
2930
3031abstract class AbstractEntityRepository implements EntityRepositoryInterface
3132{
32- use NestedCriteriaTrait;
3333 use FindByTrait;
3434 use EntityPropertiesTrait;
3535
@@ -326,7 +326,7 @@ protected function handleSpecialCriteria(
326326 }
327327
328328 if ($ value instanceof NestedCondition) {
329- $ this ->createNestedCriteria ($ queryBuilder , $ criteria [$ field ]-> getCriteria (), $ value );
329+ $ this ->createNestedCriteria ($ queryBuilder , $ criteria [$ field ]);
330330
331331 return self ::IS_SPECIAL_CRITERIA ;
332332 }
@@ -360,46 +360,55 @@ protected function handleRegularCriteria(
360360 string $ entityClassName = null ,
361361 int $ aliasNumber = null ,
362362 ): void {
363+ $ queryBuilder ->andWhere ($ this ->getWhereExpressionFromCriteriaField (
364+ $ queryBuilder ,
365+ $ criteria ,
366+ $ field ,
367+ $ value ,
368+ $ entityClassName ,
369+ $ aliasNumber ,
370+ ));
371+ }
372+
373+ protected function addOrderByClause (QueryBuilder $ queryBuilder , ?array $ orderBy ): void
374+ {
375+ if (!empty ($ orderBy )) {
376+ foreach ($ orderBy as $ field => $ orientation ) {
377+ $ this ->validateFieldName ($ field , static ::FALLBACK_ENTITY , static ::TABLE_NAME );
378+
379+ if (!in_array (strtolower ($ orientation ), ['asc ' , 'desc ' ], true )) {
380+ throw new InvalidOrderByOrientationException ($ orientation );
381+ }
382+
383+ $ queryBuilder ->addOrderBy ($ field , $ orientation );
384+ }
385+ }
386+ }
387+
388+ private function getWhereExpressionFromCriteriaField (
389+ QueryBuilder $ queryBuilder ,
390+ array $ criteria ,
391+ string $ field ,
392+ mixed $ value ,
393+ string $ entityClassName = null ,
394+ int $ aliasNumber = null ,
395+ ): CompositeExpression {
363396 $ snakeField = u ($ field )
364397 ->snake ()
365398 ->toString ()
366399 ;
367400 $ parameter = ": {$ snakeField }" ;
368401 $ operator = '= ' ;
402+ $ prefixedField = $ field ;
403+ $ expression = $ queryBuilder ->expr ();
369404
370405 if ($ criteria [$ field ] instanceof Operand) {
371- $ operator = $ criteria [$ field ]->getOperator ();
372-
373- if (
374- in_array (
375- $ operator ,
376- [Operand::OPERATOR_IN , Operand::OPERATOR_NOT_IN , Operand::OPERATOR_IN_ALL ],
377- true
378- )
379- ) {
380- if (count ($ value ) === 0 ) {
381- $ value [] = md5 ((string ) time ());
382- }
383-
384- $ parameters = array_map (
385- static fn (int $ number ) => "{$ snakeField }_ {$ number }" ,
386- range (0 , count ($ value ) - 1 ),
387- );
388- $ parameter = sprintf ('(:%s) ' , implode (', : ' , $ parameters ));
389- $ listValue = array_values ($ value );
390-
391- foreach ($ parameters as $ index => $ name ) {
392- $ queryBuilder ->setParameter ($ name , $ listValue [$ index ]);
393- }
394- } else {
395- $ queryBuilder ->setParameter ($ snakeField , $ criteria [$ field ]->getOperand ());
396- }
406+ ['operator ' => $ operator , 'parameter ' => $ parameter ] =
407+ $ this ->flattenOperand ($ queryBuilder , $ criteria [$ field ], $ field , $ value );
397408 } else {
398409 $ queryBuilder ->setParameter ($ snakeField , $ value );
399410 }
400411
401- $ prefixedField = $ field ;
402-
403412 foreach ($ this ->tableAliases as $ alias => $ fields ) {
404413 if (in_array ($ field , $ fields , true )) {
405414 $ prefixedField = sprintf ('%s.%s ' , $ alias , $ field );
@@ -422,46 +431,82 @@ protected function handleRegularCriteria(
422431
423432 if ($ this ->isFieldMapped ($ field , $ entityClassName )) {
424433 $ exprKey = sprintf ('%s.meta_key = :%s_key ' , $ metaAlias , $ snakeField );
425- $ queryBuilder ->andWhere ($ exprKey )
426- ->setParameter (
427- sprintf ('%s_key ' , $ snakeField ),
428- $ this ->getMappedMetaKey ($ field , $ entityClassName ),
429- )
430- ;
434+ $ queryBuilder ->setParameter (
435+ sprintf ('%s_key ' , $ snakeField ),
436+ $ this ->getMappedMetaKey ($ field , $ entityClassName ),
437+ );
431438 } else {
432439 $ exprKey = sprintf ("TRIM(LEADING '_' FROM %s.meta_key) = :%s_key " , $ metaAlias , $ snakeField );
433- $ queryBuilder ->andWhere ($ exprKey )
434- ->setParameter (sprintf ('%s_key ' , $ snakeField ), $ field )
435- ;
440+ $ queryBuilder ->setParameter (sprintf ('%s_key ' , $ snakeField ), $ field );
436441 }
437442
438443 $ exprValue = sprintf ('%s.meta_value %s %s ' , $ metaAlias , $ operator , $ parameter );
439- $ queryBuilder ->andWhere ($ exprValue );
440- } else {
441- if ($ operator === Operand::OPERATOR_IN_ALL ) {
442- $ operator = 'IN ' ;
443- $ queryBuilder ->andHaving (sprintf ('COUNT(DISTINCT %s) = :%s_count ' , $ prefixedField , $ snakeField ))
444- ->setParameter (sprintf ('%s_count ' , $ snakeField ), count ($ value ))
445- ;
446- }
447- $ expr = sprintf ('%s %s %s ' , $ prefixedField , $ operator , $ parameter );
448- $ queryBuilder ->andWhere ($ expr );
444+
445+ return $ expression ->and ($ exprKey , $ exprValue );
446+ }
447+
448+ if ($ operator === Operand::OPERATOR_IN_ALL ) {
449+ $ operator = 'IN ' ;
450+ $ queryBuilder ->andHaving (sprintf ('COUNT(DISTINCT %s) = :%s_count ' , $ prefixedField , $ snakeField ))
451+ ->setParameter (sprintf ('%s_count ' , $ snakeField ), count ($ value ))
452+ ;
449453 }
454+
455+ return $ expression ->and (sprintf ('%s %s %s ' , $ prefixedField , $ operator , $ parameter ));
450456 }
451457
452- protected function addOrderByClause (QueryBuilder $ queryBuilder , ?array $ orderBy ): void
458+ #[ArrayShape([
459+ 'operator ' => 'string ' ,
460+ 'parameter ' => 'string ' ,
461+ ])]
462+ private function flattenOperand (QueryBuilder $ queryBuilder , Operand $ operand , string $ field , mixed $ value ): array
453463 {
454- if (!empty ($ orderBy )) {
455- foreach ($ orderBy as $ field => $ orientation ) {
456- $ this ->validateFieldName ($ field , static ::FALLBACK_ENTITY , static ::TABLE_NAME );
464+ $ snakeField = u ($ field )
465+ ->snake ()
466+ ->toString ()
467+ ;
468+ $ parameter = ": {$ snakeField }" ;
469+ $ operator = $ operand ->getOperator ();
457470
458- if (!in_array (strtolower ($ orientation ), ['asc ' , 'desc ' ], true )) {
459- throw new InvalidOrderByOrientationException ($ orientation );
460- }
471+ if (
472+ in_array ($ operator , [Operand::OPERATOR_IN , Operand::OPERATOR_NOT_IN , Operand::OPERATOR_IN_ALL ], true )
473+ ) {
474+ if (count ($ value ) === 0 ) {
475+ $ value [] = md5 ((string ) time ());
476+ }
461477
462- $ queryBuilder ->addOrderBy ($ field , $ orientation );
478+ $ parameters = array_map (
479+ static fn (int $ number ) => "{$ snakeField }_ {$ number }" ,
480+ range (0 , count ($ value ) - 1 ),
481+ );
482+
483+ $ parameter = sprintf ('(:%s) ' , implode (', : ' , $ parameters ));
484+ $ listValue = array_values ($ value );
485+
486+ foreach ($ parameters as $ index => $ name ) {
487+ $ queryBuilder ->setParameter ($ name , $ listValue [$ index ]);
463488 }
489+ } else {
490+ $ queryBuilder ->setParameter ($ snakeField , $ operand ->getOperand ());
491+ }
492+
493+ return [
494+ 'operator ' => $ operator ,
495+ 'parameter ' => $ parameter ,
496+ ];
497+ }
498+
499+ private function createNestedCriteria (QueryBuilder $ queryBuilder , NestedCondition $ condition ): void
500+ {
501+ $ criteria = $ condition ->getCriteria ();
502+ $ normalizedCriteria = $ this ->normalizeCriteria ($ condition ->getCriteria ());
503+ $ expressions = [];
504+
505+ foreach ($ normalizedCriteria as $ field => $ value ) {
506+ $ expressions [] = $ this ->getWhereExpressionFromCriteriaField ($ queryBuilder , $ criteria , $ field , $ value );
464507 }
508+
509+ $ queryBuilder ->andWhere ($ queryBuilder ->expr ()->{$ condition ->getOperator ()}(...$ expressions ));
465510 }
466511
467512 private function joinSelfMetaTable (QueryBuilder $ queryBuilder , bool $ incrementIfNecessary = false ): string
0 commit comments