Skip to content

Commit bcf7145

Browse files
committed
Merge 4.1
2 parents 5b266e5 + 4ec2855 commit bcf7145

File tree

10 files changed

+308
-2
lines changed

10 files changed

+308
-2
lines changed

ApiPlatformProvider.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
use ApiPlatform\Laravel\Eloquent\PropertyAccess\PropertyAccessor as EloquentPropertyAccessor;
8989
use ApiPlatform\Laravel\Eloquent\PropertyInfo\EloquentExtractor;
9090
use ApiPlatform\Laravel\Eloquent\Serializer\EloquentNameConverter;
91+
use ApiPlatform\Laravel\Eloquent\Serializer\Mapping\Loader\RelationMetadataLoader;
9192
use ApiPlatform\Laravel\Eloquent\Serializer\SerializerContextBuilder as EloquentSerializerContextBuilder;
9293
use ApiPlatform\Laravel\GraphQl\Controller\EntrypointController as GraphQlEntrypointController;
9394
use ApiPlatform\Laravel\GraphQl\Controller\GraphiQlController;
@@ -218,12 +219,21 @@ public function register(): void
218219
$this->app->bind(LoaderInterface::class, AttributeLoader::class);
219220
$this->app->bind(ClassMetadataFactoryInterface::class, ClassMetadataFactory::class);
220221
$this->app->singleton(ClassMetadataFactory::class, function (Application $app) {
222+
/** @var ConfigRepository */
223+
$config = $app['config'];
224+
$nameConverter = $config->get('api-platform.name_converter', SnakeCaseToCamelCaseNameConverter::class);
225+
if ($nameConverter && class_exists($nameConverter)) {
226+
$nameConverter = new EloquentNameConverter($app->make($nameConverter));
227+
}
228+
221229
return new ClassMetadataFactory(
222230
new LoaderChain([
223231
new PropertyMetadataLoader(
224232
$app->make(PropertyNameCollectionFactoryInterface::class),
233+
$nameConverter
225234
),
226235
new AttributeLoader(),
236+
// new RelationMetadataLoader($app->make(ModelMetadata::class)),
227237
])
228238
);
229239
});
@@ -261,6 +271,10 @@ public function register(): void
261271
$this->app->singleton(PropertyMetadataFactoryInterface::class, function (Application $app) {
262272
/** @var ConfigRepository $config */
263273
$config = $app['config'];
274+
$nameConverter = $config->get('api-platform.name_converter', SnakeCaseToCamelCaseNameConverter::class);
275+
if ($nameConverter && class_exists($nameConverter)) {
276+
$nameConverter = new EloquentNameConverter($app->make($nameConverter));
277+
}
264278

265279
return new CachePropertyMetadataFactory(
266280
new SchemaPropertyMetadataFactory(
@@ -274,7 +288,8 @@ public function register(): void
274288
new EloquentPropertyMetadataFactory(
275289
$app->make(ModelMetadata::class),
276290
),
277-
)
291+
),
292+
$nameConverter
278293
),
279294
$app->make(ResourceClassResolverInterface::class)
280295
),
@@ -287,6 +302,10 @@ public function register(): void
287302
$this->app->singleton(PropertyNameCollectionFactoryInterface::class, function (Application $app) {
288303
/** @var ConfigRepository $config */
289304
$config = $app['config'];
305+
$nameConverter = $config->get('api-platform.name_converter', SnakeCaseToCamelCaseNameConverter::class);
306+
if ($nameConverter && class_exists($nameConverter)) {
307+
$nameConverter = new EloquentNameConverter($app->make($nameConverter));
308+
}
290309

291310
return new CachePropertyNameCollectionMetadataFactory(
292311
new ClassLevelAttributePropertyNameCollectionFactory(
@@ -296,7 +315,8 @@ public function register(): void
296315
new PropertyInfoPropertyNameCollectionFactory($app->make(PropertyInfoExtractorInterface::class)),
297316
$app->make(ResourceClassResolverInterface::class)
298317
)
299-
)
318+
),
319+
$nameConverter
300320
),
301321
true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
302322
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 ApiPlatform\Laravel\Eloquent\Serializer;
15+
16+
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
17+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
18+
19+
class SerializerClassMetadataFactory implements ClassMetadataFactoryInterface
20+
{
21+
public function __construct(private readonly ClassMetadataFactoryInterface $decorated)
22+
{
23+
}
24+
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
public function getMetadataFor($value): ClassMetadataInterface
29+
{
30+
return $this->decorated->getMetadataFor($value);
31+
}
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public function hasMetadataFor(mixed $value): bool
37+
{
38+
return $this->decorated->hasMetadataFor($value);
39+
}
40+
}

Tests/EloquentTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Workbench\Database\Factories\CommentMorphFactory;
2626
use Workbench\Database\Factories\GrandSonFactory;
2727
use Workbench\Database\Factories\PostWithMorphManyFactory;
28+
use Workbench\Database\Factories\TimeSlotFactory;
2829
use Workbench\Database\Factories\WithAccessorFactory;
2930

3031
class EloquentTest extends TestCase
@@ -589,4 +590,28 @@ public function testPostCommentItemFromMorphMany(): void
589590
'id' => 1,
590591
]);
591592
}
593+
594+
public function testCreateDeliveryRequestWithPickupSlot(): void
595+
{
596+
$pickupTimeSlot = TimeSlotFactory::new()->create(['note' => 'Morning slot']);
597+
598+
$response = $this->postJson('/api/delivery_requests', [
599+
'pickupTimeSlot' => '/api/time_slots/'.$pickupTimeSlot->id, // @phpstan-ignore-line
600+
'note' => 'This is a test note.',
601+
], ['accept' => 'application/ld+json', 'content-type' => 'application/ld+json']);
602+
603+
$response->assertStatus(201);
604+
$response->assertJson([
605+
'@context' => '/api/contexts/DeliveryRequest',
606+
'@id' => '/api/delivery_requests/1',
607+
'@type' => 'DeliveryRequest',
608+
'pickupTimeSlot' => [
609+
'@id' => '/api/time_slots/'.$pickupTimeSlot->id, // @phpstan-ignore-line
610+
'@type' => 'TimeSlot',
611+
'name' => $pickupTimeSlot->name, // @phpstan-ignore-line
612+
'note' => $pickupTimeSlot->note, // @phpstan-ignore-line
613+
],
614+
'note' => 'This is a test note.',
615+
]);
616+
}
592617
}

workbench/app/Models/Author.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
1717
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
1818
use ApiPlatform\Laravel\Eloquent\Filter\PartialSearchFilter;
19+
use ApiPlatform\Metadata\ApiProperty;
1920
use ApiPlatform\Metadata\IsApiResource;
2021
use ApiPlatform\Metadata\QueryParameter;
2122
use Illuminate\Database\Eloquent\Factories\HasFactory;
2223
use Illuminate\Database\Eloquent\Model;
24+
use Symfony\Component\Serializer\Attribute\Groups;
2325

2426
#[QueryParameter(key: ':property', filter: PartialSearchFilter::class)]
2527
#[QueryParameter(key: 'createdAt', filter: DateFilter::class)]
2628
#[QueryParameter(key: 'order[:property]', filter: OrderFilter::class)]
29+
#[ApiProperty(property: 'name', serialize: [new Groups('read')])]
2730
class Author extends Model
2831
{
2932
use HasFactory;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\App\Models;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\ApiResource;
18+
use ApiPlatform\Metadata\Post;
19+
use Illuminate\Database\Eloquent\Factories\HasFactory;
20+
use Illuminate\Database\Eloquent\Model;
21+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
22+
use Symfony\Component\Serializer\Attribute\Groups;
23+
24+
#[ApiResource(
25+
operations: [
26+
new Post(
27+
normalizationContext: [
28+
'groups' => [
29+
'delivery_request:read',
30+
],
31+
],
32+
denormalizationContext: [
33+
'groups' => [
34+
'delivery_request:write',
35+
],
36+
]
37+
),
38+
]
39+
)]
40+
#[ApiProperty(property: 'pickupTimeSlot', serialize: new Groups(['delivery_request:read', 'delivery_request:write']))]
41+
#[ApiProperty(property: 'note', serialize: new Groups(['delivery_request:read', 'delivery_request:write']))]
42+
class DeliveryRequest extends Model
43+
{
44+
use HasFactory;
45+
46+
public function pickupTimeSlot(): BelongsTo
47+
{
48+
return $this->belongsTo(TimeSlot::class, 'pickup_time_slot_id');
49+
}
50+
}

workbench/app/Models/Slot.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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\App\Models;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use Illuminate\Database\Eloquent\Factories\HasFactory;
18+
use Illuminate\Database\Eloquent\Model;
19+
20+
#[ApiResource]
21+
class Slot extends Model
22+
{
23+
use HasFactory;
24+
25+
protected $fillable = [
26+
'name',
27+
'note',
28+
];
29+
}

workbench/app/Models/TimeSlot.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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\App\Models;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\ApiResource;
18+
use Illuminate\Database\Eloquent\Factories\HasFactory;
19+
use Illuminate\Database\Eloquent\Model;
20+
use Symfony\Component\Serializer\Attribute\Groups;
21+
22+
#[ApiResource]
23+
#[ApiProperty(property: 'name', serialize: new Groups(['delivery_request:read', 'delivery_request:write']))]
24+
#[ApiProperty(property: 'note', serialize: new Groups(['delivery_request:read', 'delivery_request:write']))]
25+
class TimeSlot extends Model
26+
{
27+
use HasFactory;
28+
29+
protected $fillable = [
30+
'name',
31+
'note',
32+
];
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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\DeliveryRequest;
18+
19+
class DeliveryRequestFactory extends Factory
20+
{
21+
protected $model = DeliveryRequest::class;
22+
23+
public function definition(): array
24+
{
25+
return [
26+
'note' => $this->faker->sentence(),
27+
];
28+
}
29+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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\TimeSlot;
18+
19+
class TimeSlotFactory extends Factory
20+
{
21+
protected $model = TimeSlot::class;
22+
23+
public function definition(): array
24+
{
25+
return [
26+
'name' => $this->faker->word(),
27+
'note' => $this->faker->sentence(),
28+
];
29+
}
30+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
use Illuminate\Database\Migrations\Migration;
15+
use Illuminate\Database\Schema\Blueprint;
16+
use Illuminate\Support\Facades\Schema;
17+
18+
return new class extends Migration {
19+
/**
20+
* Run the migrations.
21+
*/
22+
public function up(): void
23+
{
24+
Schema::create('time_slots', function (Blueprint $table): void {
25+
$table->id();
26+
$table->string('name');
27+
$table->string('note')->nullable();
28+
$table->timestamps();
29+
});
30+
31+
Schema::create('delivery_requests', function (Blueprint $table): void {
32+
$table->id();
33+
$table->foreignId('pickup_time_slot_id')->nullable()->constrained('time_slots');
34+
$table->string('note')->nullable();
35+
$table->timestamps();
36+
});
37+
}
38+
39+
/**
40+
* Reverse the migrations.
41+
*/
42+
public function down(): void
43+
{
44+
Schema::dropIfExists('delivery_requests');
45+
Schema::dropIfExists('time_slots');
46+
}
47+
};

0 commit comments

Comments
 (0)