13
13
14
14
namespace ApiPlatform \Core \Bridge \Symfony \Routing ;
15
15
16
- use ApiPlatform \Core \Api \OperationType ;
17
16
use ApiPlatform \Core \Metadata \Property \Factory \PropertyMetadataFactoryInterface ;
18
17
use ApiPlatform \Core \Metadata \Property \Factory \PropertyNameCollectionFactoryInterface ;
19
18
use ApiPlatform \Core \Metadata \Resource \Factory \ResourceMetadataFactoryInterface ;
20
- use ApiPlatform \Core \Metadata \Resource \Factory \ResourceNameCollectionFactoryInterface ;
21
- use ApiPlatform \Core \PathResolver \OperationPathResolverInterface ;
19
+ use ApiPlatform \Core \PathResolver \PathNameGeneratorInterface ;
22
20
23
21
/**
24
22
* @internal
25
23
*/
26
24
final class SubresourceOperationFactory
27
25
{
28
26
const SUBRESOURCE_SUFFIX = '_subresource ' ;
27
+ const FORMAT_SUFFIX = '.{_format} ' ;
29
28
30
- public function __construct (ResourceNameCollectionFactoryInterface $ resourceNameCollectionFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , OperationPathResolverInterface $ operationPathResolver )
29
+ private $ resourceMetadataFactory ;
30
+ private $ propertyNameCollectionFactory ;
31
+ private $ propertyMetadataFactory ;
32
+ private $ pathNameGenerator ;
33
+
34
+ public function __construct (ResourceMetadataFactoryInterface $ resourceMetadataFactory , PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , PathNameGeneratorInterface $ pathNameGenerator )
31
35
{
32
- $ this ->resourceNameCollectionFactory = $ resourceNameCollectionFactory ;
33
36
$ this ->resourceMetadataFactory = $ resourceMetadataFactory ;
34
37
$ this ->propertyNameCollectionFactory = $ propertyNameCollectionFactory ;
35
38
$ this ->propertyMetadataFactory = $ propertyMetadataFactory ;
36
- $ this ->operationPathResolver = $ operationPathResolver ;
39
+ $ this ->pathNameGenerator = $ pathNameGenerator ;
37
40
}
38
41
39
42
public function create (string $ resourceClass ): array
40
43
{
41
44
$ tree = [];
42
45
$ this ->computeSubresourceOperations ($ resourceClass , $ tree );
46
+
43
47
return $ tree ;
44
48
}
45
49
46
50
/**
47
51
* Handles subresource operations recursively and declare their corresponding routes.
48
52
*
49
- * @param string $resourceClass
50
- * @param array $tree
51
- * @param string $rootResourceClass null on the first iteration, it then keeps track of the origin resource class
52
- * @param array $parentOperation the previous call operation
53
+ * @param string $resourceClass
54
+ * @param array $tree
55
+ * @param string $rootResourceClass null on the first iteration, it then keeps track of the origin resource class
56
+ * @param array $parentOperation the previous call operation
53
57
*/
54
- private function computeSubresourceOperations (string $ resourceClass , array &$ tree , string $ rootResourceClass = null , array $ parentOperation = null , array $ visited = [] )
58
+ private function computeSubresourceOperations (string $ resourceClass , array &$ tree , string $ rootResourceClass = null , array $ parentOperation = null )
55
59
{
56
60
if (null === $ rootResourceClass ) {
57
61
$ rootResourceClass = $ resourceClass ;
@@ -65,29 +69,37 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
65
69
}
66
70
67
71
$ subresource = $ propertyMetadata ->getSubresource ();
68
- $ subresourceMetadata = $ this ->resourceMetadataFactory ->create ($ subresource ->getResourceClass ());
72
+ $ subresourceClass = $ subresource ->getResourceClass ();
73
+ $ subresourceMetadata = $ this ->resourceMetadataFactory ->create ($ subresourceClass );
69
74
70
- $ operation = [
71
- 'property ' => $ property ,
72
- 'collection ' => $ subresource ->isCollection (),
73
- 'resource_class ' => $ subresource ->getResourceClass (),
74
- 'shortnames ' => [$ subresourceMetadata ->getShortName ()],
75
- ];
75
+ if (null === $ parentOperation ) {
76
+ $ visiting = "$ rootResourceClass- $ property- $ subresourceClass " ;
77
+ } else {
78
+ $ prefix = '' ;
79
+ $ visiting = "{$ parentOperation ['property ' ]}- {$ parentOperation ['resource_class ' ]}- $ property- $ subresourceClass " ;
76
80
77
- $ visiting = "$ rootResourceClass $ resourceClass $ property {$ subresource ->getResourceClass ()}" ;
81
+ foreach ($ parentOperation ['identifiers ' ] as $ key => list ($ param , $ class )) {
82
+ $ prefix .= 0 === $ key ? "$ class " : "- $ param- $ class " ;
83
+ }
78
84
79
- if (in_array ($ visiting , $ visited , true )) {
80
- continue ;
85
+ if (false !== strpos ($ prefix , $ visiting )) {
86
+ continue ;
87
+ }
88
+
89
+ $ visiting = $ prefix .'- ' .$ visiting ;
81
90
}
82
91
83
- $ visited [] = $ visiting ;
84
92
$ operationName = 'get ' ;
85
-
93
+ $ operation = [
94
+ 'property ' => $ property ,
95
+ 'collection ' => $ subresource ->isCollection (),
96
+ 'resource_class ' => $ subresourceClass ,
97
+ 'shortNames ' => [$ subresourceMetadata ->getShortName ()],
98
+ ];
86
99
87
100
if (null === $ parentOperation ) {
88
101
$ rootResourceMetadata = $ this ->resourceMetadataFactory ->create ($ rootResourceClass );
89
102
$ rootShortname = $ rootResourceMetadata ->getShortName ();
90
-
91
103
$ operation ['identifiers ' ] = [['id ' , $ rootResourceClass ]];
92
104
$ operation ['route_name ' ] = sprintf (
93
105
'%s%s_%s_%s%s ' ,
@@ -97,20 +109,29 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
97
109
$ operationName ,
98
110
self ::SUBRESOURCE_SUFFIX
99
111
);
100
- $ operation ['path ' ] = $ this ->operationPathResolver ->resolveOperationPath ($ rootShortname , $ operation , OperationType::SUBRESOURCE , $ operation ['route_name ' ]);
101
- $ operation ['shortnames ' ][] = $ rootShortname ;
112
+
113
+ $ operation ['path ' ] = sprintf (
114
+ '/%s/{id}/%s%s ' ,
115
+ $ this ->pathNameGenerator ->getPathName ($ rootShortname , true ),
116
+ $ this ->pathNameGenerator ->getPathName ($ operation ['property ' ], $ operation ['collection ' ]),
117
+ self ::FORMAT_SUFFIX
118
+ );
119
+
120
+ $ operation ['shortNames ' ][] = $ rootShortname ;
102
121
} else {
103
122
$ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ resourceClass );
104
123
$ operation ['identifiers ' ] = $ parentOperation ['identifiers ' ];
105
124
$ operation ['identifiers ' ][] = [$ parentOperation ['property ' ], $ resourceClass ];
106
-
107
- $ operation ['shortnames ' ][] = $ resourceMetadata ->getShortName ();
108
125
$ operation ['route_name ' ] = str_replace ('get ' .self ::SUBRESOURCE_SUFFIX , RouteNameGenerator::inflector ($ property , $ operation ['collection ' ]).'_get ' .self ::SUBRESOURCE_SUFFIX , $ parentOperation ['route_name ' ]);
109
- $ operation ['path ' ] = $ this ->operationPathResolver ->resolveOperationPath ($ parentOperation ['path ' ], $ operation , OperationType::SUBRESOURCE , $ operation ['route_name ' ]);
126
+ $ operation ['shortNames ' ][] = $ resourceMetadata ->getShortName ();
127
+
128
+ $ operation ['path ' ] = str_replace (self ::FORMAT_SUFFIX , '' , $ parentOperation ['path ' ]);
129
+ list ($ key ) = end ($ operation ['identifiers ' ]);
130
+ $ operation ['path ' ] .= sprintf ('/{%s}/%s%s ' , $ key , $ this ->pathNameGenerator ->getPathName ($ property , $ operation ['collection ' ]), self ::FORMAT_SUFFIX );
110
131
}
111
132
112
133
$ tree [$ visiting ] = $ operation ;
113
- $ this ->computeSubresourceOperations ($ subresource -> getResourceClass () , $ tree , $ rootResourceClass , $ operation, $ visited );
134
+ $ this ->computeSubresourceOperations ($ subresourceClass , $ tree , $ rootResourceClass , $ operation );
114
135
}
115
136
}
116
137
}
0 commit comments