Pārlūkot izejas kodu

Add ability to load Eloquent relations when fetching model

shalvah 5 gadi atpakaļ
vecāks
revīzija
51e28014b2

+ 18 - 12
src/Commands/GenerateDocumentation.php

@@ -60,18 +60,7 @@ class GenerateDocumentation extends Command
      */
     public function handle(RouteMatcherInterface $routeMatcher)
     {
-        // Using a global static variable here, so fuck off if you don't like it.
-        // Also, the --verbose option is included with all Artisan commands.
-        Flags::$shouldBeVerbose = $this->option('verbose');
-
-        $this->clara = clara('knuckleswtf/scribe', Flags::$shouldBeVerbose)
-            ->useOutput($this->output)
-            ->only();
-
-        $this->docConfig = new DocumentationConfig(config('scribe'));
-        $this->baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
-
-        URL::forceRootUrl($this->baseUrl);
+        $this->bootstrap();
 
         $routes = $routeMatcher->getRoutes($this->docConfig->get('routes'), $this->docConfig->get('router'));
 
@@ -197,4 +186,21 @@ class GenerateDocumentation extends Command
 
         return true;
     }
+
+    public function bootstrap(): void
+    {
+        // Using a global static variable here, so fuck off if you don't like it.
+        // Also, the --verbose option is included with all Artisan commands.
+        Flags::$shouldBeVerbose = $this->option('verbose');
+
+        $this->clara = clara('knuckleswtf/scribe', Flags::$shouldBeVerbose)
+            ->useOutput($this->output)
+            ->only();
+
+        $this->docConfig = new DocumentationConfig(config('scribe'));
+        $this->baseUrl = $this->docConfig->get('base_url') ?? config('app.url');
+
+        // Force root URL so it works in Postman collection
+        URL::forceRootUrl($this->baseUrl);
+    }
 }

+ 5 - 1
src/Extracting/Strategies/ResponseFields/GetFromResponseFieldTag.php

@@ -70,7 +70,11 @@ class GetFromResponseFieldTag extends Strategy
                         $type = '';
                     } else {
                         $nonexistent = new \stdClass();
-                        $value = $validResponse[$name] ?? $validResponse[0][$name] ?? $nonexistent;
+                        $value = $validResponse[$name]
+                            ?? $validResponse['data'][$name] // Maybe it's a Laravel ApiResource
+                            ?? $validResponse[0][$name] // Maybe it's a list
+                            ?? $validResponse['data'][0][$name] // Maybe an Api Resource Collection
+                            ?? $nonexistent;
                         if ($value !== $nonexistent) {
                             $type =  $this->normalizeParameterType(gettype($value));
                         } else {

+ 9 - 7
src/Extracting/Strategies/Responses/UseApiResourceTags.php

@@ -72,8 +72,8 @@ class UseApiResourceTags extends Strategy
         }
 
         list($statusCode, $apiResourceClass) = $this->getStatusCodeAndApiResourceClass($apiResourceTag);
-        [$model, $factoryStates] = $this->getClassToBeTransformed($tags);
-        $modelInstance = $this->instantiateApiResourceModel($model, $factoryStates);
+        [$model, $factoryStates, $relations] = $this->getClassToBeTransformed($tags);
+        $modelInstance = $this->instantiateApiResourceModel($model, $factoryStates, $relations);
 
         try {
             $resource = new $apiResourceClass($modelInstance);
@@ -86,7 +86,7 @@ class UseApiResourceTags extends Strategy
             // Collections can either use the regular JsonResource class (via `::collection()`,
             // or a ResourceCollection (via `new`)
             // See https://laravel.com/docs/5.8/eloquent-resources
-            $models = [$modelInstance, $this->instantiateApiResourceModel($model, $factoryStates)];
+            $models = [$modelInstance, $this->instantiateApiResourceModel($model, $factoryStates, $relations)];
             $resource = $resource instanceof ResourceCollection
                 ? new $apiResourceClass(collect($models))
                 : $apiResourceClass::collection(collect($models));
@@ -127,25 +127,27 @@ class UseApiResourceTags extends Strategy
         $type = null;
         $states = [];
         if ($modelTag) {
-            ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states']);
+            ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states', 'with']);
             $states = $attributes['states'] ? explode(',', $attributes['states']) : [];
+            $relations = $attributes['with'] ? explode(',', $attributes['with']) : [];
         }
 
         if (empty($type)) {
             throw new Exception("Couldn't detect an Eloquent API resource model from your docblock. Did you remember to specify a model using @apiResourceModel?");
         }
 
-        return [$type, $states];
+        return [$type, $states, $relations];
     }
 
     /**
      * @param string $type
      *
+     * @param array $relations
      * @param array $factoryStates
      *
      * @return Model|object
      */
-    protected function instantiateApiResourceModel(string $type, array $factoryStates = [])
+    protected function instantiateApiResourceModel(string $type, array $factoryStates = [], array $relations = [])
     {
         try {
             // Try Eloquent model factory
@@ -168,7 +170,7 @@ class UseApiResourceTags extends Strategy
             if ($instance instanceof \Illuminate\Database\Eloquent\Model) {
                 try {
                     // we can't use a factory but can try to get one from the database
-                    $firstInstance = $type::first();
+                    $firstInstance = $type::with($relations)->first();
                     if ($firstInstance) {
                         return $firstInstance;
                     }

+ 8 - 7
src/Extracting/Strategies/Responses/UseTransformerTags.php

@@ -71,8 +71,8 @@ class UseTransformerTags extends Strategy
             }
 
             [$statusCode, $transformer] = $this->getStatusCodeAndTransformerClass($transformerTag);
-            [$model, $factoryStates] = $this->getClassToBeTransformed($tags, (new ReflectionClass($transformer))->getMethod('transform'));
-            $modelInstance = $this->instantiateTransformerModel($model, $factoryStates);
+            [$model, $factoryStates, $relations] = $this->getClassToBeTransformed($tags, (new ReflectionClass($transformer))->getMethod('transform'));
+            $modelInstance = $this->instantiateTransformerModel($model, $factoryStates, $relations);
 
             $fractal = new Manager();
 
@@ -82,7 +82,7 @@ class UseTransformerTags extends Strategy
 
             $resource = (strtolower($transformerTag->getName()) == 'transformercollection')
                 ? new Collection(
-                    [$modelInstance, $this->instantiateTransformerModel($model, $factoryStates)],
+                    [$modelInstance, $this->instantiateTransformerModel($model, $factoryStates, $relations)],
                     new $transformer()
                 )
                 : new Item($modelInstance, new $transformer());
@@ -129,8 +129,9 @@ class UseTransformerTags extends Strategy
         $type = null;
         $states = [];
         if ($modelTag) {
-            ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states']);
+            ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states', 'with']);
             $states = $attributes['states'] ? explode(',', $attributes['states']) : [];
+            $relations = $attributes['with'] ? explode(',', $attributes['with']) : [];
         } else {
             $parameter = Arr::first($transformerMethod->getParameters());
             if ($parameter->hasType() && ! $parameter->getType()->isBuiltin() && class_exists($parameter->getType()->getName())) {
@@ -143,10 +144,10 @@ class UseTransformerTags extends Strategy
             throw new Exception("Couldn't detect a transformer model from your docblock. Did you remember to specify a model using @transformerModel?");
         }
 
-        return [$type, $states];
+        return [$type, $states, $relations];
     }
 
-    protected function instantiateTransformerModel(string $type, array $factoryStates = [])
+    protected function instantiateTransformerModel(string $type, array $factoryStates = [], array $relations = [])
     {
         try {
             // try Eloquent model factory
@@ -169,7 +170,7 @@ class UseTransformerTags extends Strategy
             if ($instance instanceof IlluminateModel) {
                 try {
                     // we can't use a factory but can try to get one from the database
-                    $firstInstance = $type::first();
+                    $firstInstance = $type::with($relations)->first();
                     if ($firstInstance) {
                         return $firstInstance;
                     }

+ 1 - 3
src/Writing/Writer.php

@@ -299,11 +299,9 @@ class Writer
         $parsedRoutesWithOutput->each(function ($routesInGroup, $groupName) use (
             $parsedRoutesWithOutput
         ) {
-            static $counter = 0;
-            $groupId = "$counter-" . Str::slug($groupName);
+            $groupId = Str::slug($groupName);
             $routeGroupMarkdownFile = $this->sourceOutputPath . "/source/groups/$groupId.md";
 
-            $counter++;
             if ($this->hasFileBeenModified($routeGroupMarkdownFile)) {
                 if ($this->forceIt) {
                     $this->clara->warn("Discarded manual changes for file $routeGroupMarkdownFile");