@@ -39,7 +39,7 @@ public function __construct(private readonly NormalizerInterface $decorated)
39
39
*/
40
40
public function normalize (mixed $ object , ?string $ format = null , array $ context = []): array
41
41
{
42
- $ pathsCallback = static fn ( $ decoratedObject ): array => $ decoratedObject instanceof Paths ? $ decoratedObject -> getPaths () : [] ;
42
+ $ pathsCallback = $ this -> getPathsCallBack () ;
43
43
$ context [AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS ] = true ;
44
44
$ context [AbstractObjectNormalizer::SKIP_NULL_VALUES ] = true ;
45
45
$ context [AbstractNormalizer::CALLBACKS ] = [
@@ -95,4 +95,46 @@ public function hasCacheableSupportsMethod(): bool
95
95
96
96
return true ;
97
97
}
98
+
99
+ private function getPathsCallBack (): \Closure
100
+ {
101
+ return static function ($ decoratedObject ): array {
102
+ if ($ decoratedObject instanceof Paths) {
103
+ $ paths = $ decoratedObject ->getPaths ();
104
+
105
+ // sort paths by tags, then by path for each tag
106
+ uksort ($ paths , function ($ keyA , $ keyB ) use ($ paths ) {
107
+ $ a = $ paths [$ keyA ];
108
+ $ b = $ paths [$ keyB ];
109
+
110
+ $ tagsA = [
111
+ ...($ a ->getGet ()?->getTags() ?? []),
112
+ ...($ a ->getPost ()?->getTags() ?? []),
113
+ ...($ a ->getPatch ()?->getTags() ?? []),
114
+ ...($ a ->getPut ()?->getTags() ?? []),
115
+ ...($ a ->getDelete ()?->getTags() ?? []),
116
+ ];
117
+ sort ($ tagsA );
118
+
119
+ $ tagsB = [
120
+ ...($ b ->getGet ()?->getTags() ?? []),
121
+ ...($ b ->getPost ()?->getTags() ?? []),
122
+ ...($ b ->getPatch ()?->getTags() ?? []),
123
+ ...($ b ->getPut ()?->getTags() ?? []),
124
+ ...($ b ->getDelete ()?->getTags() ?? []),
125
+ ];
126
+ sort ($ tagsB );
127
+
128
+ return match (true ) {
129
+ current ($ tagsA ) === current ($ tagsB ) => $ keyA <=> $ keyB ,
130
+ default => current ($ tagsA ) <=> current ($ tagsB ),
131
+ };
132
+ });
133
+
134
+ return $ paths ;
135
+ }
136
+
137
+ return [];
138
+ };
139
+ }
98
140
}
0 commit comments