Skip to content

Commit dc53b75

Browse files
committed
fix(laravel): Allow LinksHandler to handle polymorphic relationships
1 parent 64ff50f commit dc53b75

File tree

7 files changed

+171
-4
lines changed

7 files changed

+171
-4
lines changed

src/Laravel/Eloquent/State/LinksHandler.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Illuminate\Contracts\Foundation\Application;
2323
use Illuminate\Database\Eloquent\Builder;
2424
use Illuminate\Database\Eloquent\Model;
25+
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
2526

2627
/**
2728
* @implements LinksHandlerInterface<Model>
@@ -103,6 +104,13 @@ private function buildQuery(Builder $builder, Link $link, mixed $identifier): Bu
103104
if ($from = $link->getFromProperty()) {
104105
$relation = $this->application->make($link->getFromClass());
105106
$relationQuery = $relation->{$from}();
107+
108+
if ($relationQuery instanceof MorphOneOrMany) {
109+
return $builder
110+
->where($relationQuery->getForeignKeyName(), $identifier)
111+
->where($relationQuery->getMorphType(), $relationQuery->getMorphClass());
112+
}
113+
106114
if (!method_exists($relationQuery, 'getQualifiedForeignKeyName') && method_exists($relationQuery, 'getQualifiedForeignPivotKeyName')) {
107115
return $builder->getModel()
108116
->join(

src/Laravel/Tests/EloquentTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
use Illuminate\Support\Str;
2020
use Orchestra\Testbench\Concerns\WithWorkbench;
2121
use Orchestra\Testbench\TestCase;
22+
use Workbench\App\Models\PostWithMorphMany;
2223
use Workbench\Database\Factories\AuthorFactory;
2324
use Workbench\Database\Factories\BookFactory;
25+
use Workbench\Database\Factories\CommentMorphFactory;
2426
use Workbench\Database\Factories\GrandSonFactory;
27+
use Workbench\Database\Factories\PostWithMorphManyFactory;
2528
use Workbench\Database\Factories\WithAccessorFactory;
2629

2730
class EloquentTest extends TestCase
@@ -538,4 +541,41 @@ public function testPostWithEmptyMorphMany(): void
538541
'comments' => [['content' => 'hello']],
539542
]);
540543
}
544+
545+
public function testPostCommentsCollectionFromMorphMany(): void
546+
{
547+
PostWithMorphManyFactory::new()->create();
548+
549+
CommentMorphFactory::new()->count(5)->create([
550+
'commentable_id' => 1,
551+
'commentable_type' => PostWithMorphMany::class,
552+
]);
553+
554+
$response = $this->getJson('/api/post_with_morph_manies/1/comments', [
555+
'accept' => 'application/ld+json',
556+
]);
557+
$response->assertStatus(200);
558+
$response->assertJsonCount(5, 'member');
559+
}
560+
561+
public function testPostCommentItemFromMorphMany(): void
562+
{
563+
PostWithMorphManyFactory::new()->create();
564+
565+
CommentMorphFactory::new()->count(5)->create([
566+
'commentable_id' => 1,
567+
'commentable_type' => PostWithMorphMany::class,
568+
])->first();
569+
570+
$response = $this->getJson('/api/post_with_morph_manies/1/comments/1', [
571+
'accept' => 'application/ld+json',
572+
]);
573+
$response->assertStatus(200);
574+
$response->assertJson([
575+
'@context' => '/api/contexts/CommentMorph',
576+
'@id' => '/api/post_with_morph_manies/1/comments/1',
577+
'@type' => 'CommentMorph',
578+
'id' => 1,
579+
]);
580+
}
541581
}

src/Laravel/workbench/app/Models/CommentMorph.php

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,40 @@
1414
namespace Workbench\App\Models;
1515

1616
use ApiPlatform\Metadata\ApiProperty;
17-
use ApiPlatform\Metadata\NotExposed;
17+
use ApiPlatform\Metadata\ApiResource;
18+
use ApiPlatform\Metadata\Get;
19+
use ApiPlatform\Metadata\GetCollection;
20+
use ApiPlatform\Metadata\Link;
1821
use Illuminate\Database\Eloquent\Model;
1922
use Illuminate\Database\Eloquent\Relations\MorphTo;
2023
use Symfony\Component\Serializer\Attribute\Groups;
2124

22-
#[NotExposed]
25+
#[ApiResource(
26+
operations: [
27+
new GetCollection(
28+
uriTemplate: '/post_with_morph_manies/{id}/comments',
29+
uriVariables: [
30+
'id' => new Link(
31+
fromProperty: 'comments',
32+
fromClass: PostWithMorphMany::class,
33+
),
34+
]
35+
),
36+
new Get(
37+
uriTemplate: '/post_with_morph_manies/{postId}/comments/{id}',
38+
uriVariables: [
39+
'postId' => new Link(
40+
fromProperty: 'comments',
41+
fromClass: PostWithMorphMany::class,
42+
),
43+
'id' => new Link(
44+
fromClass: CommentMorph::class,
45+
),
46+
]
47+
),
48+
]
49+
)]
50+
#[ApiProperty(identifier: true, serialize: new Groups(['comments']), property: 'id')]
2351
#[ApiProperty(serialize: new Groups(['comments']), property: 'content')]
2452
class CommentMorph extends Model
2553
{

src/Laravel/workbench/database/factories/CommentFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use Workbench\App\Models\Comment;
1818

1919
/**
20-
* @template TModel of \Workbench\App\Models\Author
20+
* @template TModel of \Workbench\App\Models\Comment
2121
*
2222
* @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>
2323
*/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Workbench\Database\Factories;
15+
16+
use Illuminate\Database\Eloquent\Factories\Factory;
17+
use Workbench\App\Models\CommentMorph;
18+
19+
/**
20+
* @template TModel of \Workbench\App\Models\CommentMorph
21+
*
22+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>
23+
*/
24+
class CommentMorphFactory extends Factory
25+
{
26+
/**
27+
* The name of the factory's corresponding model.
28+
*
29+
* @var class-string<TModel>
30+
*/
31+
protected $model = CommentMorph::class;
32+
33+
/**
34+
* Define the model's default state.
35+
*
36+
* @return array<string, mixed>
37+
*/
38+
public function definition(): array
39+
{
40+
return [
41+
'commentable_id' => PostWithMorphManyFactory::new(),
42+
'commentable_type' => PostWithMorphManyFactory::class,
43+
'content' => fake()->text(),
44+
];
45+
}
46+
}

src/Laravel/workbench/database/factories/PostFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use Workbench\App\Models\Post;
1818

1919
/**
20-
* @template TModel of \Workbench\App\Models\Author
20+
* @template TModel of \Workbench\App\Models\Post
2121
*
2222
* @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>
2323
*/
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Workbench\Database\Factories;
15+
16+
use Illuminate\Database\Eloquent\Factories\Factory;
17+
use Workbench\App\Models\PostWithMorphMany;
18+
19+
/**
20+
* @template TModel of \Workbench\App\Models\PostWithMorphMany
21+
*
22+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>
23+
*/
24+
class PostWithMorphManyFactory extends Factory
25+
{
26+
/**
27+
* The name of the factory's corresponding model.
28+
*
29+
* @var class-string<TModel>
30+
*/
31+
protected $model = PostWithMorphMany::class;
32+
33+
/**
34+
* Define the model's default state.
35+
*
36+
* @return array<string, mixed>
37+
*/
38+
public function definition(): array
39+
{
40+
return [
41+
'title' => fake()->unique()->sentence(10),
42+
'content' => fake()->sentences(10, true),
43+
];
44+
}
45+
}

0 commit comments

Comments
 (0)