33namespace Ensi \LaravelOpenApiServerGenerator \Generators ;
44
55use cebe \openapi \SpecObjectInterface ;
6+ use Ensi \LaravelOpenApiServerGenerator \Utils \ClassParser ;
67use stdClass ;
78
89class ControllersGenerator extends BaseGenerator implements GeneratorInterface
910{
11+ public const REQUEST_NAMESPACE = 'Illuminate\Http\Request ' ;
12+ public const RESPONSABLE_NAMESPACE = 'Illuminate\Contracts\Support\Responsable ' ;
13+ public const DELIMITER = "\n " ;
14+
1015 private array $ methodsWithRequests = ['PATCH ' , 'POST ' , 'PUT ' , 'DELETE ' ];
1116
17+ private string $ serversUrl ;
18+
1219 public function generate (SpecObjectInterface $ specObject ): void
1320 {
21+ $ openApiData = $ specObject ->getSerializableData ();
22+ $ this ->serversUrl = $ openApiData ?->servers[0 ]?->url ?? '' ;
23+
1424 $ controllers = $ this ->extractControllers ($ specObject );
15- $ this ->createControllersFiles ($ controllers, $ this -> templatesManager -> getTemplate ( ' Controller.template ' ) );
25+ $ this ->createControllersFiles ($ controllers );
1626 }
1727
1828 private function extractControllers (SpecObjectInterface $ specObject ): array
@@ -21,7 +31,7 @@ private function extractControllers(SpecObjectInterface $specObject): array
2131
2232 $ controllers = [];
2333 $ paths = $ openApiData ->paths ?: [];
24- foreach ($ paths as $ routes ) {
34+ foreach ($ paths as $ path => $ routes ) {
2535 foreach ($ routes as $ method => $ route ) {
2636 $ requestClassName = null ;
2737 $ methodWithRequest = in_array (strtoupper ($ method ), $ this ->methodsWithRequests );
@@ -56,14 +66,20 @@ private function extractControllers(SpecObjectInterface $specObject): array
5666 list ($ requestClassName , $ requestNamespace ) = $ this ->getActualClassNameAndNamespace ($ requestClassName , $ requestNamespace );
5767 $ requestNamespace .= '\\' . ucfirst ($ requestClassName );
5868
59- $ controllers [$ fqcn ]['requestsNamespaces ' ][] = $ requestNamespace ;
60- } elseif ($ methodWithRequest ) {
61- $ controllers [$ fqcn ]['requestsNamespaces ' ][] = 'Illuminate\Http\Request ' ;
69+ $ controllers [$ fqcn ]['requestsNamespaces ' ][$ requestNamespace ] = $ requestNamespace ;
6270 }
6371
72+ $ responses = $ route ->responses ?? null ;
6473 $ controllers [$ fqcn ]['actions ' ][] = [
6574 'name ' => $ handler ->method ?: '__invoke ' ,
75+ 'with_request_namespace ' => $ methodWithRequest && !empty ($ route ->{'x-lg-skip-request-generation ' }),
6676 'parameters ' => array_merge ($ this ->extractPathParameters ($ route ), $ this ->getActionExtraParameters ($ methodWithRequest , $ requestClassName )),
77+
78+ 'route ' => [
79+ 'method ' => $ method ,
80+ 'path ' => $ path ,
81+ 'responseCodes ' => $ responses ? array_keys (get_object_vars ($ responses )) : [],
82+ ],
6783 ];
6884 }
6985 }
@@ -73,7 +89,7 @@ private function extractControllers(SpecObjectInterface $specObject): array
7389
7490 private function extractPathParameters (stdClass $ route ): array
7591 {
76- $ oasRoutePath = array_filter ($ route ->parameters ?? [], fn (stdClass $ param ) => $ param ?->in === "path " );
92+ $ oasRoutePath = array_filter ($ route ->parameters ?? [], fn (stdClass $ param ) => $ param ?->in === "path " );
7793
7894 return array_map (fn (stdClass $ param ) => [
7995 'name ' => $ param ->name ,
@@ -93,55 +109,106 @@ private function getActionExtraParameters(bool $methodWithRequest, $requestClass
93109 return [];
94110 }
95111
96- private function createControllersFiles (array $ controllers, string $ template ): void
112+ private function createControllersFiles (array $ controllers ): void
97113 {
98114 foreach ($ controllers as $ controller ) {
99115 $ namespace = $ controller ['namespace ' ];
100116 $ className = $ controller ['className ' ];
101117
102118 $ filePath = $ this ->getNamespacedFilePath ($ className , $ namespace );
103- if ($ this ->filesystem ->exists ($ filePath )) {
119+ $ controllerExists = $ this ->filesystem ->exists ($ filePath );
120+ if (!$ controllerExists ) {
121+ $ this ->createEmptyControllerFile ($ filePath , $ controller );
122+ }
123+
124+ $ class = $ this ->classParser ->parse ("$ namespace \\$ className " );
125+
126+ $ newMethods = $ this ->convertMethodsToString ($ class , $ controller ['actions ' ], $ controller ['requestsNamespaces ' ]);
127+ if (!empty ($ newMethods )) {
128+ $ controller ['requestsNamespaces ' ][static ::RESPONSABLE_NAMESPACE ] = static ::RESPONSABLE_NAMESPACE ;
129+ } elseif ($ controllerExists ) {
104130 continue ;
105131 }
106132
107- $ this ->putWithDirectoryCheck (
108- $ filePath ,
109- $ this ->replacePlaceholders ($ template , [
110- '{{ namespace }} ' => $ namespace ,
111- '{{ className }} ' => $ className ,
112- '{{ requestsNamespaces }} ' => $ this ->formatRequestNamespaces ($ controller ['requestsNamespaces ' ]),
113- '{{ methods }} ' => $ this ->convertMethodsToString ($ controller ['actions ' ]),
114- ])
115- );
133+ $ content = $ class ->getContentWithAdditionalMethods ($ newMethods , $ controller ['requestsNamespaces ' ]);
134+
135+ $ this ->writeControllerFile ($ filePath , $ controller , $ content );
116136 }
117137 }
118138
139+ protected function writeControllerFile (string $ filePath , array $ controller , string $ classContent ): void
140+ {
141+ $ this ->putWithDirectoryCheck (
142+ $ filePath ,
143+ $ this ->replacePlaceholders (
144+ $ this ->templatesManager ->getTemplate ('ControllerExists.template ' ),
145+ [
146+ '{{ namespace }} ' => $ controller ['namespace ' ],
147+ '{{ requestsNamespaces }} ' => $ this ->formatRequestNamespaces ($ controller ['requestsNamespaces ' ]),
148+ '{{ classContent }} ' => $ classContent ,
149+ ]
150+ )
151+ );
152+ }
153+
154+ protected function createEmptyControllerFile (string $ filePath , array $ controller ): void
155+ {
156+ $ this ->putWithDirectoryCheck (
157+ $ filePath ,
158+ $ this ->replacePlaceholders (
159+ $ this ->templatesManager ->getTemplate ('ControllerEmpty.template ' ),
160+ [
161+ '{{ namespace }} ' => $ controller ['namespace ' ],
162+ '{{ requestsNamespaces }} ' => $ this ->formatRequestNamespaces ($ controller ['requestsNamespaces ' ]),
163+ '{{ className }} ' => $ controller ['className ' ],
164+ ]
165+ )
166+ );
167+ }
168+
119169 private function formatActionParamsAsString (array $ params ): string
120170 {
121171 return implode (', ' , array_map (fn (array $ param ) => $ param ['type ' ] . " $ " . $ param ['name ' ], $ params ));
122172 }
123173
124- private function convertMethodsToString (array $ methods ): string
174+ private function convertMethodsToString (ClassParser $ class , array $ methods, array & $ namespaces ): string
125175 {
126176 $ methodsStrings = [];
127177
128178 foreach ($ methods as $ method ) {
179+ if ($ class ->hasMethod ($ method ['name ' ])) {
180+ continue ;
181+ }
182+
183+ if ($ method ['with_request_namespace ' ]) {
184+ $ namespaces [static ::REQUEST_NAMESPACE ] = static ::REQUEST_NAMESPACE ;
185+ }
186+
129187 $ methodsStrings [] = $ this ->replacePlaceholders (
130188 $ this ->templatesManager ->getTemplate ('ControllerMethod.template ' ),
131189 [
132190 '{{ method }} ' => $ method ['name ' ],
133191 '{{ params }} ' => $ this ->formatActionParamsAsString ($ method ['parameters ' ]),
134192 ]
135193 );
194+
195+ $ this ->controllersStorage ->markNewControllerMethod (
196+ serversUrl: $ this ->serversUrl ,
197+ path: $ method ['route ' ]['path ' ],
198+ method: $ method ['route ' ]['method ' ],
199+ responseCodes: $ method ['route ' ]['responseCodes ' ],
200+ );
136201 }
137202
138- return implode ("\n\n " , $ methodsStrings );
203+ $ prefix = !empty ($ methodsStrings ) ? static ::DELIMITER : '' ;
204+
205+ return $ prefix . implode (static ::DELIMITER , $ methodsStrings );
139206 }
140207
141208 protected function formatRequestNamespaces (array $ namespaces ): string
142209 {
143- $ namespaces = array_unique ($ namespaces );
144- sort ($ namespaces );
210+ $ namespaces = array_values ($ namespaces );
211+ sort ($ namespaces, SORT_STRING | SORT_FLAG_CASE );
145212
146213 return implode ("\n" , array_map (fn (string $ namespaces ) => "use {$ namespaces }; " , $ namespaces ));
147214 }
0 commit comments