Ver Fonte

Add support for factory states in transformermodel and apiresourcemodel

shalvah há 5 anos atrás
pai
commit
b345bae747

+ 20 - 14
src/Extracting/Strategies/Responses/UseApiResourceTags.php

@@ -10,6 +10,7 @@ use Illuminate\Http\Resources\Json\ResourceCollection;
 use Illuminate\Http\Response;
 use Illuminate\Routing\Route;
 use Illuminate\Support\Arr;
+use Knuckles\Scribe\Tools\AnnotationParser;
 use League\Fractal\Resource\Collection;
 use Knuckles\Scribe\Extracting\RouteDocBlocker;
 use Knuckles\Scribe\Extracting\Strategies\Strategy;
@@ -71,8 +72,8 @@ class UseApiResourceTags extends Strategy
         }
 
         list($statusCode, $apiResourceClass) = $this->getStatusCodeAndApiResourceClass($apiResourceTag);
-        $model = $this->getClassToBeTransformed($tags);
-        $modelInstance = $this->instantiateApiResourceModel($model);
+        [$model, $factoryStates] = $this->getClassToBeTransformed($tags);
+        $modelInstance = $this->instantiateApiResourceModel($model, $factoryStates);
 
         try {
             $resource = new $apiResourceClass($modelInstance);
@@ -85,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)];
+            $models = [$modelInstance, $this->instantiateApiResourceModel($model, $factoryStates)];
             $resource = $resource instanceof ResourceCollection
                 ? new $apiResourceClass(collect($models))
                 : $apiResourceClass::collection(collect($models));
@@ -117,33 +118,34 @@ class UseApiResourceTags extends Strategy
         return [$status, $apiResourceClass];
     }
 
-    /**
-     * @param array $tags
-     *
-     * @return string
-     * @throws Exception
-     */
-    private function getClassToBeTransformed(array $tags): string
+    private function getClassToBeTransformed(array $tags): array
     {
         $modelTag = Arr::first(array_filter($tags, function ($tag) {
             return ($tag instanceof Tag) && strtolower($tag->getName()) == 'apiresourcemodel';
         }));
 
-        $type = $modelTag ? $modelTag->getContent() : null;
+        $type = null;
+        $states = [];
+        if ($modelTag) {
+            ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states']);
+            $states = explode(',', $attributes['states'] ?? '');
+        }
 
         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;
+        return [$type, $states];
     }
 
     /**
      * @param string $type
      *
+     * @param array $factoryStates
+     *
      * @return Model|object
      */
-    protected function instantiateApiResourceModel(string $type)
+    protected function instantiateApiResourceModel(string $type, array $factoryStates = [])
     {
         try {
             // Try Eloquent model factory
@@ -152,7 +154,11 @@ class UseApiResourceTags extends Strategy
             // but the user might write it that way in a comment. Let's be safe.
             $type = ltrim($type, '\\');
 
-            return factory($type)->make();
+            $factory = factory($type);
+            if (count($factoryStates)) {
+                $factory->states($factoryStates);
+            }
+            return $factory->make();
         } catch (Exception $e) {
             if (Flags::$shouldBeVerbose) {
                 clara('knuckleswtf/scribe')->warn("Eloquent model factory failed to instantiate {$type}; trying to fetch from database.");

+ 16 - 14
src/Extracting/Strategies/Responses/UseTransformerTags.php

@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Model as IlluminateModel;
 use Illuminate\Routing\Route;
 use Illuminate\Support\Arr;
+use Knuckles\Scribe\Tools\AnnotationParser;
 use League\Fractal\Manager;
 use League\Fractal\Resource\Collection;
 use League\Fractal\Resource\Item;
@@ -70,8 +71,8 @@ class UseTransformerTags extends Strategy
             }
 
             [$statusCode, $transformer] = $this->getStatusCodeAndTransformerClass($transformerTag);
-            $model = $this->getClassToBeTransformed($tags, (new ReflectionClass($transformer))->getMethod('transform'));
-            $modelInstance = $this->instantiateTransformerModel($model);
+            [$model, $factoryStates] = $this->getClassToBeTransformed($tags, (new ReflectionClass($transformer))->getMethod('transform'));
+            $modelInstance = $this->instantiateTransformerModel($model, $factoryStates);
 
             $fractal = new Manager();
 
@@ -81,7 +82,7 @@ class UseTransformerTags extends Strategy
 
             $resource = (strtolower($transformerTag->getName()) == 'transformercollection')
                 ? new Collection(
-                    [$modelInstance, $this->instantiateTransformerModel($model)],
+                    [$modelInstance, $this->instantiateTransformerModel($model, $factoryStates)],
                     new $transformer()
                 )
                 : new Item($modelInstance, new $transformer());
@@ -117,17 +118,19 @@ class UseTransformerTags extends Strategy
      *
      * @throws Exception
      *
-     * @return string
+     * @return array
      */
-    private function getClassToBeTransformed(array $tags, ReflectionFunctionAbstract $transformerMethod): string
+    private function getClassToBeTransformed(array $tags, ReflectionFunctionAbstract $transformerMethod): array
     {
         $modelTag = Arr::first(array_filter($tags, function ($tag) {
             return ($tag instanceof Tag) && strtolower($tag->getName()) == 'transformermodel';
         }));
 
         $type = null;
+        $states = [];
         if ($modelTag) {
-            $type = $modelTag->getContent();
+            ['content' => $type, 'attributes' => $attributes] = AnnotationParser::parseIntoContentAndAttributes($modelTag->getContent(), ['states']);
+            $states = explode(',', $attributes['states'] ?? '');
         } else {
             $parameter = Arr::first($transformerMethod->getParameters());
             if ($parameter->hasType() && ! $parameter->getType()->isBuiltin() && class_exists($parameter->getType()->getName())) {
@@ -140,15 +143,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;
+        return [$type, $states];
     }
 
-    /**
-     * @param string $type
-     *
-     * @return Model|object
-     */
-    protected function instantiateTransformerModel(string $type)
+    protected function instantiateTransformerModel(string $type, array $factoryStates = [])
     {
         try {
             // try Eloquent model factory
@@ -157,7 +155,11 @@ class UseTransformerTags extends Strategy
             // but the user might write it that way in a comment. Let's be safe.
             $type = ltrim($type, '\\');
 
-            return factory($type)->make();
+            $factory = factory($type);
+            if (count($factoryStates)) {
+                $factory->states($factoryStates);
+            }
+            return $factory->make();
         } catch (Exception $e) {
             if (Flags::$shouldBeVerbose) {
                 clara('knuckleswtf/scribe')->warn("Eloquent model factory failed to instantiate {$type}; trying to fetch from database.");

+ 1 - 1
src/Tools/AnnotationParser.php

@@ -25,7 +25,7 @@ class AnnotationParser
      */
     public static function parseIntoContentAndAttributes(string $annotationContent, array $allowedAttributes): array
     {
-        $result = preg_split('/(\w+)=(\w+|".+?"|\'.+?\')\s*/', trim($annotationContent), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+        $result = preg_split('/(\w+)=([\S]+|".+?"|\'.+?\')\s*/', trim($annotationContent), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
 
         $defaults = array_fill_keys($allowedAttributes, null);
         if (count($result) == 1) { // No key-value pairs

+ 45 - 1
tests/Extracting/Strategies/Responses/UseApiResourceTagsTest.php

@@ -36,8 +36,11 @@ class UseApiResourceTagsTest extends TestCase
                 'email' => 'a@b.com',
             ];
         });
+        $factory->state(TestUser::class, 'state1', ["state1" => true]);
+        $factory->state(TestUser::class, 'random-state', ["random-state" => true]);
     }
-        /** @test */
+
+    /** @test */
     public function can_parse_apiresource_tags()
     {
         $config = new DocumentationConfig([]);
@@ -63,6 +66,34 @@ class UseApiResourceTagsTest extends TestCase
         ], $results);
     }
 
+    /** @test */
+    public function can_parse_apiresource_tags_with_model_factory_states()
+    {
+        $config = new DocumentationConfig([]);
+
+        $strategy = new UseApiResourceTags($config);
+        $tags = [
+            new Tag('apiResource', '\Knuckles\Scribe\Tests\Fixtures\TestUserApiResource'),
+            new Tag('apiResourceModel', '\Knuckles\Scribe\Tests\Fixtures\TestUser states=state1,random-state'),
+        ];
+        $results = $strategy->getApiResourceResponse($tags);
+
+        $this->assertArraySubset([
+            [
+                'status' => 200,
+                'content' => json_encode([
+                    'data' => [
+                        'id' => 4,
+                        'name' => 'Tested Again',
+                        'email' => 'a@b.com',
+                        'state1' => true,
+                        'random-state' => true,
+                    ],
+                ]),
+            ],
+        ], $results);
+    }
+
     /** @test */
     public function can_parse_apiresourcecollection_tags()
     {
@@ -132,4 +163,17 @@ class UseApiResourceTagsTest extends TestCase
         ], $results);
     }
 
+    public function dataResources()
+    {
+        return [
+            [
+                null,
+                '{"data":{"id":1,"description":"Welcome on this test versions","name":"TestName"}}',
+            ],
+            [
+                'League\Fractal\Serializer\JsonApiSerializer',
+                '{"data":{"type":null,"id":"1","attributes":{"description":"Welcome on this test versions","name":"TestName"}}}',
+            ],
+        ];
+    }
 }

+ 25 - 1
tests/Extracting/Strategies/Responses/UseTransformerTagsTest.php

@@ -4,6 +4,8 @@ namespace Knuckles\Scribe\Tests\Extracting\Strategies\Responses;
 
 use Knuckles\Scribe\Extracting\Strategies\Responses\UseTransformerTags;
 use Knuckles\Scribe\ScribeServiceProvider;
+use Knuckles\Scribe\Tests\Fixtures\TestModel;
+use Knuckles\Scribe\Tests\Fixtures\TestUser;
 use Knuckles\Scribe\Tools\DocumentationConfig;
 use Mpociot\Reflection\DocBlock\Tag;
 use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
@@ -63,6 +65,29 @@ class UseTransformerTagsTest extends TestCase
         ], $results);
     }
 
+    /** @test */
+    public function can_parse_transformer_tag_with_model_and_factory_states()
+    {
+        $factory = app(\Illuminate\Database\Eloquent\Factory::class);
+        $factory->define(TestUser::class, function () { return ['id' => 3, 'name' => 'myname']; });
+        $factory->state(TestUser::class, 'state1', ["state1" => true]);
+        $factory->state(TestUser::class, 'random-state', ["random-state" => true]);
+
+        $strategy = new UseTransformerTags(new DocumentationConfig([]));
+        $tags = [
+            new Tag('transformer', '\Knuckles\Scribe\Tests\Fixtures\TestEloquentTransformer'),
+            new Tag('transformermodel', '\Knuckles\Scribe\Tests\Fixtures\TestUser states=state1,random-state'),
+        ];
+        $results = $strategy->getTransformerResponse($tags);
+
+        $this->assertArraySubset([
+            [
+                'status' => 200,
+                'content' => '{"data":{"id":3,"name":"myname","state1":true,"random-state":true}}',
+            ],
+        ], $results);
+    }
+
     /** @test */
     public function can_parse_transformer_tag_with_status_code()
     {
@@ -120,7 +145,6 @@ class UseTransformerTagsTest extends TestCase
         ], $results);
     }
 
-
     public function dataResources()
     {
         return [

+ 24 - 0
tests/Fixtures/TestEloquentTransformer.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures;
+
+use Illuminate\Database\Eloquent\Model;
+use League\Fractal\TransformerAbstract;
+
+class TestEloquentTransformer extends TransformerAbstract
+{
+    /**
+     * A Fractal transformer.
+     *
+     * @return array
+     */
+    public function transform(Model $model)
+    {
+        return [
+            'id' => $model->id,
+            'name' => $model->name,
+            'state1' => $model['state1'],
+            'random-state' => $model['random-state'],
+        ];
+    }
+}

+ 2 - 0
tests/Fixtures/TestUserApiResource.php

@@ -19,6 +19,8 @@ class TestUserApiResource extends JsonResource
             'id' => $this->id,
             'name' => $this->first_name . ' ' . $this->last_name,
             'email' => $this->email,
+            'state1' => $this['state1'],
+            'random-state' => $this['random-state'],
         ];
     }
 }