浏览代码

fix(apiResourceModel/relations)[ISSUE-415]: added handler for creating BelongsTo relationship correctly

pallam 3 年之前
父节点
当前提交
bffe76249a

+ 3 - 0
src/Tools/Utils.php

@@ -6,6 +6,7 @@ use Closure;
 use DirectoryIterator;
 use Exception;
 use FastRoute\RouteParser\Std;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 use Illuminate\Routing\Route;
 use Illuminate\Support\Str;
@@ -251,6 +252,8 @@ class Utils
                         : [];
 
                     $factory = $factory->hasAttached($factoryChain, $pivot, $relationVector);
+                } else if ($relationType === BelongsTo::class) {
+                    $factory = $factory->for($factoryChain, $relationVector);
                 } else {
                     $factory = $factory->has($factoryChain, $relationVector);
                 }

+ 21 - 0
tests/Fixtures/inmemory/TestDepartment.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class TestDepartment extends Model
+{
+    use HasFactory;
+
+    protected static function newFactory()
+    {
+        return TestDepartmentFactory::new();
+    }
+
+    public function work()
+    {
+        return $this->belongsTo(TestWork::class);
+    }
+}

+ 23 - 0
tests/Fixtures/inmemory/TestDepartmentApiResource.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class TestDepartmentApiResource extends JsonResource
+{
+    /**
+     * Transform the resource into an array.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     *
+     * @return array
+     */
+    public function toArray($request)
+    {
+        return [
+            'id' => $this->id,
+            'name' => $this->name,
+        ];
+    }
+}

+ 21 - 0
tests/Fixtures/inmemory/TestDepartmentFactory.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\Factory;
+
+class TestDepartmentFactory extends Factory
+{
+    protected $model = TestDepartment::class;
+
+    /**
+     * @inheritDoc
+     */
+    public function definition()
+    {
+        return [
+            'name' => 'My best department',
+            'test_work_id' => TestWork::factory(),
+        ];
+    }
+}

+ 21 - 0
tests/Fixtures/inmemory/TestPet.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class TestPet extends Model
+{
+    use HasFactory;
+
+    protected static function newFactory()
+    {
+        return TestPetFactory::new();
+    }
+
+    public function owners()
+    {
+        return $this->belongsToMany(TestUser::class)->withPivot('duration');
+    }
+}

+ 32 - 0
tests/Fixtures/inmemory/TestPetApiResource.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class TestPetApiResource extends JsonResource
+{
+    /**
+     * Transform the resource into an array.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     *
+     * @return array
+     */
+    public function toArray($request)
+    {
+        $result = [
+            'id' => $this->id,
+            'name' => $this->name,
+            'species' => $this->species,
+            'owners' => $this->whenLoaded('owners', function () {
+                return TestUserApiResource::collection($this->owners);
+            }),
+            'ownership' => $this->whenPivotLoaded('pet_user', function () {
+                return $this->pivot;
+            })
+        ];
+
+        return $result;
+    }
+}

+ 18 - 0
tests/Fixtures/inmemory/TestPetFactory.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\Factory;
+
+class TestPetFactory extends Factory
+{
+    protected $model = TestPet::class;
+
+    public function definition()
+    {
+        return [
+            'name' => 'Mephistopheles',
+            'species' => 'dog',
+        ];
+    }
+}

+ 32 - 0
tests/Fixtures/inmemory/TestUser.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use \Illuminate\Database\Eloquent\Factory;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class TestUser extends Model
+{
+    use HasFactory;
+
+    protected static function newFactory()
+    {
+        return TestUserFactory::new();
+    }
+
+    public function children()
+    {
+        return $this->hasMany(TestUser::class, 'parent_id');
+    }
+
+    public function pets()
+    {
+        return $this->belongsToMany(TestPet::class)->withPivot('duration');
+    }
+
+    public function work()
+    {
+        return $this->belongsTo(TestWork::class, 'test_work_id');
+    }
+}

+ 44 - 0
tests/Fixtures/inmemory/TestUserApiResource.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class TestUserApiResource extends JsonResource
+{
+    /**
+     * Transform the resource into an array.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     *
+     * @return array
+     */
+    public function toArray($request)
+    {
+        $result = [
+            'id' => $this->id,
+            'name' => $this->first_name . ' ' . $this->last_name,
+            'email' => $this->email,
+            'children' => $this->whenLoaded('children', function () {
+                return TestUserApiResource::collection($this->children);
+            }),
+            'pets' => $this->whenLoaded('pets', function () {
+                return TestPetApiResource::collection($this->pets);
+            }),
+            'work' => $this->whenLoaded('work', function () {
+                return TestWorkApiResource::make($this->work);
+            })
+        ];
+
+        if($request->route()->named('someone')) {
+            return ['someone' => true];
+        }
+
+        if ($this['state1'] && $this['random-state']) {
+            $result['state1'] = $this['state1'];
+            $result['random-state'] = $this['random-state'];
+        }
+
+        return $result;
+    }
+}

+ 33 - 0
tests/Fixtures/inmemory/TestUserFactory.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\Factory;
+
+class TestUserFactory extends Factory
+{
+    protected $model = TestUser::class;
+
+    public function configure()
+    {
+        return $this->afterCreating(function (TestUser $user) {
+            $user->load([
+                'children.pets',
+                'work.departments',
+            ]);
+        });
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function definition()
+    {
+        return [
+            'first_name' => 'Tested',
+            'last_name' => 'Again',
+            'email' => 'a@b.com',
+            'test_work_id' => TestWork::factory(),
+        ];
+    }
+}

+ 26 - 0
tests/Fixtures/inmemory/TestWork.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class TestWork extends Model
+{
+    use HasFactory;
+
+    protected static function newFactory()
+    {
+        return TestWorkFactory::new();
+    }
+
+    public function users()
+    {
+        return $this->hasMany(TestUser::class);
+    }
+
+    public function departments()
+    {
+        return $this->hasMany(TestDepartment::class, 'test_work_id');
+    }
+}

+ 26 - 0
tests/Fixtures/inmemory/TestWorkApiResource.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class TestWorkApiResource extends JsonResource
+{
+    /**
+     * Transform the resource into an array.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     *
+     * @return array
+     */
+    public function toArray($request)
+    {
+        return [
+            'id' => $this->id,
+            'name' => $this->name,
+            'departments' => $this->whenLoaded('departments', function () {
+                return TestDepartmentApiResource::collection($this->departments);
+            }),
+        ];
+    }
+}

+ 20 - 0
tests/Fixtures/inmemory/TestWorkFactory.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Knuckles\Scribe\Tests\Fixtures\inmemory;
+
+use Illuminate\Database\Eloquent\Factories\Factory;
+
+class TestWorkFactory extends Factory
+{
+    protected $model = TestWork::class;
+
+    /**
+     * @inheritDoc
+     */
+    public function definition()
+    {
+        return [
+            'name' => 'My best work',
+        ];
+    }
+}

+ 106 - 0
tests/Strategies/Responses/UseApiResourceTagsTest.php

@@ -2,15 +2,22 @@
 
 namespace Knuckles\Scribe\Tests\Strategies\Responses;
 
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
 use Illuminate\Routing\Route;
+use Illuminate\Support\Facades\Schema;
 use Knuckles\Camel\Extraction\ExtractedEndpointData;
 use Knuckles\Scribe\Extracting\Strategies\Responses\UseApiResourceTags;
 use Knuckles\Scribe\ScribeServiceProvider;
 use Knuckles\Scribe\Tests\BaseLaravelTest;
+use Knuckles\Scribe\Tests\Fixtures\inmemory\TestWork as InMemoryTestWork;
+use Knuckles\Scribe\Tests\Fixtures\inmemory\TestPet as InMemoryTestPet;
+use Knuckles\Scribe\Tests\Fixtures\inmemory\TestUser as InmemoryTestUser;
 use Knuckles\Scribe\Tests\Fixtures\TestController;
 use Knuckles\Scribe\Tests\Fixtures\TestPet;
 use Knuckles\Scribe\Tests\Fixtures\TestUser;
 use Knuckles\Scribe\Tools\DocumentationConfig;
+use Knuckles\Scribe\Tools\Globals;
 use Knuckles\Scribe\Tools\Utils;
 use Mpociot\Reflection\DocBlock\Tag;
 use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
@@ -377,6 +384,60 @@ class UseApiResourceTagsTest extends BaseLaravelTest
         ], $results);
     }
 
+    /** @test */
+    public function loads_specified_many_to_many_and_nested_relations_for_generated_model_with_inmemory_db()
+    {
+        $this->setUpInmemory();
+
+        $config = new DocumentationConfig([]);
+
+        $route = new Route(['POST'], "/somethingRandom", ['uses' => [TestController::class, 'dummy']]);
+
+        $strategy = new UseApiResourceTags($config);
+        $tags = [
+            new Tag('apiResource', '\Knuckles\Scribe\Tests\Fixtures\inmemory\TestUserApiResource'),
+            new Tag('apiResourceModel', '\Knuckles\Scribe\Tests\Fixtures\inmemory\TestUser with=work.departments,children.pets'),
+        ];
+        $results = $strategy->getApiResourceResponse($strategy->getApiResourceTag($tags), $tags, ExtractedEndpointData::fromRoute($route));
+
+        $this->assertArraySubset([
+            [
+                'status' => 200,
+                'content' => json_encode([
+                    'data' => [
+                        'id' => 1,
+                        'name' => 'Tested Again',
+                        'email' => 'a@b.com',
+                        'children' => [
+                            [
+                                'id' => 2,
+                                'name' => 'Tested Again',
+                                'email' => 'a@b.com',
+                                'pets' => [
+                                    [
+                                        'id' => 1,
+                                        'name' => 'Mephistopheles',
+                                        'species' => 'dog'
+                                    ],
+                                ],
+                            ]
+                        ],
+                        'work' => [
+                            'id' => 1,
+                            'name' => 'My best work',
+                            'departments' => [
+                                [
+                                    'id' => 1,
+                                    'name' => 'My best department',
+                                ]
+                            ],
+                        ]
+                    ],
+                ]),
+            ],
+        ], $results);
+    }
+
     /** @test */
     public function loads_specified_many_to_many_relations_for_generated_model_with_pivot()
     {
@@ -641,4 +702,49 @@ class UseApiResourceTagsTest extends BaseLaravelTest
             ],
         ], $results);
     }
+
+    protected function setUpInmemory()
+    {
+        config(['scribe.database_connections_to_transact' => [[
+            'driver' => 'sqlite',
+            'database' => ':memory:',
+        ]]]);
+        Globals::$shouldBeVerbose = true;
+
+        (new class extends Migration {
+            function up() {
+                Schema::create('test_users', function (Blueprint $table) {
+                    $table->id();
+                    $table->string('first_name')->nullable();
+                    $table->string('last_name')->nullable();
+                    $table->string('email')->nullable();
+                    $table->foreignIdFor(InMemoryTestWork::class);
+                    $table->foreignIdFor(InMemoryTestUser::class, 'parent_id')->nullable();
+                    $table->timestamps();
+                });
+                Schema::create('test_pets', function (Blueprint $table) {
+                    $table->id();
+                    $table->string('name')->nullable();
+                    $table->string('species')->nullable();
+                    $table->timestamps();
+                });
+                Schema::create('test_pet_test_user', function (Blueprint $table) {
+                    $table->foreignIdFor(InMemoryTestUser::class);
+                    $table->foreignIdFor(InMemoryTestPet::class);
+                    $table->integer('duration')->nullable();
+                });
+                Schema::create('test_works', function (Blueprint $table) {
+                    $table->id();
+                    $table->string('name')->nullable();
+                    $table->timestamps();
+                });
+                Schema::create('test_departments', function (Blueprint $table) {
+                    $table->id();
+                    $table->string('name')->nullable();
+                    $table->foreignIdFor(InMemoryTestWork::class);
+                    $table->timestamps();
+                });
+            }
+        })->up();
+    }
 }