Skip to content

Commit 1a44dcf

Browse files
committed
test(laravel): #7337
1 parent 726cf51 commit 1a44dcf

File tree

10 files changed

+291
-11
lines changed

10 files changed

+291
-11
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Tests;
15+
16+
use ApiPlatform\Laravel\Test\ApiTestAssertionsTrait;
17+
use Illuminate\Foundation\Testing\RefreshDatabase;
18+
use Illuminate\Support\Facades\DB;
19+
use Orchestra\Testbench\Concerns\WithWorkbench;
20+
use Orchestra\Testbench\TestCase;
21+
use Workbench\Database\Factories\ActiveBookFactory;
22+
23+
class OrderFilterTest extends TestCase
24+
{
25+
use ApiTestAssertionsTrait;
26+
use RefreshDatabase;
27+
use WithWorkbench;
28+
29+
public function testQueryParameterWithCamelCaseProperty(): void
30+
{
31+
ActiveBookFactory::new(['is_active' => true])->count(2)->create();
32+
ActiveBookFactory::new(['is_active' => false])->count(3)->create();
33+
34+
DB::enableQueryLog();
35+
$response = $this->get('/api/active_books?sort[isActive]=asc', ['Accept' => ['application/ld+json']]);
36+
$response->assertStatus(200);
37+
$this->assertEquals(\DB::getQueryLog()[1]['query'], 'select * from "active_books" order by "isActive" asc limit 30 offset 0');
38+
DB::flushQueryLog();
39+
$response = $this->get('/api/active_books?sort[isActive]=desc', ['Accept' => ['application/ld+json']]);
40+
$response->assertStatus(200);
41+
$this->assertEquals(DB::getQueryLog()[1]['query'], 'select * from "active_books" order by "isActive" desc limit 30 offset 0');
42+
}
43+
}

src/Laravel/Tests/EloquentTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
use Illuminate\Support\Str;
2020
use Orchestra\Testbench\Concerns\WithWorkbench;
2121
use Orchestra\Testbench\TestCase;
22+
use Workbench\App\Http\Requests\StoreSlotRequest;
2223
use Workbench\App\Models\PostWithMorphMany;
24+
use Workbench\Database\Factories\AreaFactory;
2325
use Workbench\Database\Factories\AuthorFactory;
2426
use Workbench\Database\Factories\BookFactory;
2527
use Workbench\Database\Factories\CommentMorphFactory;
@@ -614,4 +616,20 @@ public function testCreateDeliveryRequestWithPickupSlot(): void
614616
'note' => 'This is a test note.',
615617
]);
616618
}
619+
620+
public function testIriIsNotDenormalizedBeforeFormRequestValidation(): void
621+
{
622+
$area = AreaFactory::new()->create();
623+
624+
$this->postJson(
625+
'/api/slots',
626+
[
627+
'name' => 'Morning Slot',
628+
'area' => '/api/areas/'.$area->id,
629+
],
630+
['accept' => 'application/ld+json', 'content-type' => 'application/ld+json']
631+
)->assertStatus(201);
632+
633+
$this->assertSame(StoreSlotRequest::$receivedArea->name, $area->name);
634+
}
617635
}

src/Laravel/workbench/app/ApiResource/SlotsForDate.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<?php
22

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+
312
declare(strict_types=1);
413

514
namespace Workbench\App\ApiResource;
@@ -24,7 +33,8 @@ class SlotsForDate
2433
public string $name = 'Morning Slot';
2534
public string $note = 'This is a morning slot';
2635

27-
public static function provide() {
36+
public static function provide()
37+
{
2838
return [];
2939
}
3040
}

src/Laravel/workbench/app/Http/Requests/GetDropOffSlotsRequest.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<?php
22

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+
312
declare(strict_types=1);
413

514
namespace Workbench\App\Http\Requests;
@@ -23,8 +32,8 @@ protected function prepareForValidation(): void
2332
'pickupDate' => $this->query('pickupDate'),
2433
'pickupSlotId' => $this->query('pickupSlotId'),
2534
]);
26-
2735
}
36+
2837
/**
2938
* Get the validation rules that apply to the request.
3039
*
@@ -42,14 +51,9 @@ public function rules(): array
4251
protected function failedValidation(Validator $validator): void
4352
{
4453
$violations = collect($validator->errors())
45-
->map(fn($m, $f) => ['propertyPath' => $f, 'message' => $m[0]]) //** @phpstan-ignore-line */
54+
->map(fn ($m, $f) => ['propertyPath' => $f, 'message' => $m[0]]) // ** @phpstan-ignore-line */
4655
->values()->all();
4756

48-
throw new \ApiPlatform\Laravel\ApiResource\ValidationError(
49-
$violations[0]['message'] ?? 'Validation failed.',
50-
hash('xxh3', implode(',', array_column($violations, 'propertyPath'))),
51-
new \Illuminate\Validation\ValidationException($validator),
52-
$violations
53-
);
57+
throw new \ApiPlatform\Laravel\ApiResource\ValidationError($violations[0]['message'] ?? 'Validation failed.', hash('xxh3', implode(',', array_column($violations, 'propertyPath'))), new \Illuminate\Validation\ValidationException($validator), $violations);
5458
}
5559
}
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+
namespace Workbench\App\Http\Requests;
15+
16+
use ApiPlatform\Metadata\IriConverterInterface;
17+
use Illuminate\Foundation\Http\FormRequest;
18+
use Illuminate\Support\Facades\App;
19+
use Workbench\App\Models\Area;
20+
21+
class StoreSlotRequest extends FormRequest
22+
{
23+
public static ?Area $receivedArea = null;
24+
25+
public function authorize(): bool
26+
{
27+
return true;
28+
}
29+
30+
public function rules(): array
31+
{
32+
$iriConverter = App::get(IriConverterInterface::class);
33+
34+
return [
35+
'name' => ['required', 'string', 'max:255'],
36+
'area' => [
37+
'required',
38+
'string',
39+
function (string $attribute, mixed $value, \Closure $fail) use ($iriConverter): void {
40+
if (!(self::$receivedArea = $iriConverter->getResourceFromIri($value))) {
41+
$fail("The {$attribute} is invalid.");
42+
}
43+
},
44+
],
45+
];
46+
}
47+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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\NotExposed;
17+
use Illuminate\Database\Eloquent\Factories\HasFactory;
18+
use Illuminate\Database\Eloquent\Model;
19+
use Illuminate\Database\Eloquent\Relations\HasMany;
20+
21+
#[NotExposed]
22+
class Area extends Model
23+
{
24+
use HasFactory;
25+
26+
protected $fillable = [
27+
'name',
28+
];
29+
30+
public function slots(): HasMany
31+
{
32+
return $this->hasMany(Slot::class);
33+
}
34+
}

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

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

1616
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Delete;
18+
use ApiPlatform\Metadata\Get;
19+
use ApiPlatform\Metadata\GetCollection;
20+
use ApiPlatform\Metadata\Patch;
21+
use ApiPlatform\Metadata\Post;
22+
use ApiPlatform\Metadata\Put;
1723
use Illuminate\Database\Eloquent\Factories\HasFactory;
1824
use Illuminate\Database\Eloquent\Model;
25+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
26+
use Workbench\App\Http\Requests\StoreSlotRequest;
1927

20-
#[ApiResource]
28+
#[ApiResource(
29+
operations: [
30+
new GetCollection(),
31+
new Get(),
32+
new Post(
33+
rules: StoreSlotRequest::class,
34+
),
35+
new Put(),
36+
new Patch(),
37+
new Delete(),
38+
],
39+
)]
2140
class Slot extends Model
2241
{
2342
use HasFactory;
43+
protected $table = 'slots';
2444

2545
protected $fillable = [
2646
'name',
27-
'note',
47+
'area_id',
2848
];
49+
50+
public function area(): BelongsTo
51+
{
52+
return $this->belongsTo(Area::class);
53+
}
2954
}
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\Area;
18+
19+
class AreaFactory extends Factory
20+
{
21+
protected $model = Area::class;
22+
23+
public function definition(): array
24+
{
25+
return [
26+
'name' => $this->faker->city,
27+
];
28+
}
29+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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\Area;
18+
use Workbench\App\Models\Slot;
19+
20+
class SlotFactory extends Factory
21+
{
22+
protected $model = Slot::class;
23+
24+
public function definition(): array
25+
{
26+
return [
27+
'name' => $this->faker->name,
28+
'area_id' => Area::factory(),
29+
];
30+
}
31+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
public function up(): void
20+
{
21+
Schema::create('slots', function (Blueprint $table): void {
22+
$table->id();
23+
$table->string('name');
24+
$table->foreignId('area_id')->constrained('areas')->onDelete('cascade');
25+
$table->timestamps();
26+
});
27+
Schema::create('areas', function (Blueprint $table): void {
28+
$table->id();
29+
$table->string('name');
30+
$table->timestamps();
31+
});
32+
}
33+
34+
public function down(): void
35+
{
36+
Schema::dropIfExists('slots');
37+
Schema::dropIfExists('areas');
38+
}
39+
};

0 commit comments

Comments
 (0)