|  | 
| 1 | 1 | <?php | 
| 2 | 2 | namespace Vimeo\MysqlEngine\Processor\Expression; | 
| 3 | 3 | 
 | 
|  | 4 | +use Closure; | 
| 4 | 5 | use Vimeo\MysqlEngine\FakePdoInterface; | 
| 5 | 6 | use Vimeo\MysqlEngine\Processor\ProcessorException; | 
| 6 | 7 | use Vimeo\MysqlEngine\Processor\QueryResult; | 
| @@ -938,26 +939,62 @@ private static function sqlConcat( | 
| 938 | 939 |      * @param array<string, mixed> $row | 
| 939 | 940 |      */ | 
| 940 | 941 |     private static function sqlGroupConcat( | 
| 941 |  | -        FakePdoInterface $conn, | 
| 942 |  | -        Scope $scope, | 
|  | 942 | +        FakePdoInterface   $conn, | 
|  | 943 | +        Scope              $scope, | 
| 943 | 944 |         FunctionExpression $expr, | 
| 944 |  | -        QueryResult $result | 
| 945 |  | -    ): string { | 
|  | 945 | +        QueryResult        $result | 
|  | 946 | +    ): string | 
|  | 947 | +    { | 
| 946 | 948 |         $args = $expr->args; | 
| 947 | 949 | 
 | 
| 948 | 950 |         $items = []; | 
| 949 | 951 |         foreach ($result->rows as $row) { | 
| 950 | 952 |             $tmp_str = ""; | 
|  | 953 | +            /** @var Closure(array{direction: "ASC"|"DESC", expression: Expression}): ?scalar $func */ | 
|  | 954 | +            // @phpstan-ignore-next-line | 
|  | 955 | +            $func = function (array $order) use ($result, $row, $scope, $conn) { | 
|  | 956 | +                /** @var array{expression: Expression} $order */ | 
|  | 957 | +                return Evaluator::evaluate($conn, $scope, $order["expression"], $row, $result); | 
|  | 958 | +            }; | 
|  | 959 | +            $orders = array_map( | 
|  | 960 | +                $func, | 
|  | 961 | +                $expr->order ?? [] | 
|  | 962 | +            ); | 
| 951 | 963 |             foreach ($args as $arg) { | 
| 952 |  | -                $val = (string) Evaluator::evaluate($conn, $scope, $arg, $row, $result); | 
|  | 964 | +                $val = (string)Evaluator::evaluate($conn, $scope, $arg, $row, $result); | 
| 953 | 965 |                 $tmp_str .= $val; | 
| 954 | 966 |             } | 
| 955 | 967 |             if ($tmp_str !== "" && (!$expr->distinct || !isset($items[$tmp_str]))) { | 
| 956 |  | -                $items[$tmp_str] = $tmp_str; | 
|  | 968 | +                $items[$tmp_str] = ["val" => $tmp_str, "orders" => $orders]; | 
| 957 | 969 |             } | 
| 958 | 970 |         } | 
| 959 | 971 | 
 | 
| 960 |  | -        return implode(",", array_values($items)); | 
|  | 972 | +        usort($items, function ($a, $b) use ($expr): int { | 
|  | 973 | +            /** | 
|  | 974 | +             * @var array{val: string, orders: array<int, scalar>} $a | 
|  | 975 | +             * @var array{val: string, orders: array<int, scalar>} $b | 
|  | 976 | +             */ | 
|  | 977 | +            for ($i = 0; $i < count($expr->order ?? []); $i++) { | 
|  | 978 | +                $direction = $expr->order[$i]["direction"] ?? 'ASC'; | 
|  | 979 | +                $a_val = $a["orders"][$i]; | 
|  | 980 | +                $b_val = $b["orders"][$i]; | 
|  | 981 | + | 
|  | 982 | +                if ($a_val < $b_val) { | 
|  | 983 | +                    return ($direction === 'ASC') ? -1 : 1; | 
|  | 984 | +                } elseif ($a_val > $b_val) { | 
|  | 985 | +                    return ($direction === 'ASC') ? 1 : -1; | 
|  | 986 | +                } | 
|  | 987 | +            } | 
|  | 988 | +            return 0; | 
|  | 989 | +        }); | 
|  | 990 | + | 
|  | 991 | +        if (isset($expr->separator)) { | 
|  | 992 | +            $separator = (string)(Evaluator::evaluate($conn, $scope, $expr->separator, [], $result)); | 
|  | 993 | +        } else { | 
|  | 994 | +            $separator = ","; | 
|  | 995 | +        } | 
|  | 996 | + | 
|  | 997 | +        return implode($separator, array_column($items, 'val')); | 
| 961 | 998 |     } | 
| 962 | 999 | 
 | 
| 963 | 1000 |     /** | 
|  | 
0 commit comments