• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 14380251321

10 Apr 2025 12:19PM UTC coverage: 8.487% (+0.3%) from 8.187%
14380251321

Pull #7075

github

web-flow
Merge 802b308ba into dfca9bf60
Pull Request #7075: fix(laravel): eloquent BelongsTo linking

0 of 70 new or added lines in 7 files covered. (0.0%)

1 existing line in 1 file now uncovered.

13399 of 157884 relevant lines covered (8.49%)

22.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Laravel/Tests/EloquentTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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 ApiPlatform\Laravel\workbench\app\Enums\BookStatus;
18
use Illuminate\Foundation\Testing\RefreshDatabase;
19
use Orchestra\Testbench\Concerns\WithWorkbench;
20
use Orchestra\Testbench\TestCase;
21
use Workbench\Database\Factories\AuthorFactory;
22
use Workbench\Database\Factories\BookFactory;
23
use Workbench\Database\Factories\GrandSonFactory;
24
use Workbench\Database\Factories\WithAccessorFactory;
25

26
class EloquentTest extends TestCase
27
{
28
    use ApiTestAssertionsTrait;
29
    use RefreshDatabase;
30
    use WithWorkbench;
31

32
    public function testBackedEnumsNormalization(): void
33
    {
34
        BookFactory::new([
×
35
            'status' => BookStatus::DRAFT,
×
36
        ])->has(AuthorFactory::new())->count(10)->create();
×
37

38
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
39
        $book = $response->json()['member'][0];
×
40

41
        $this->assertArrayHasKey('status', $book);
×
42
        $this->assertSame('DRAFT', $book['status']);
×
43
    }
44

45
    public function testSearchFilter(): void
46
    {
47
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
48

49
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
50
        $book = $response->json()['member'][0];
×
51

52
        $response = $this->get('/api/books?isbn='.$book['isbn'], ['Accept' => ['application/ld+json']]);
×
53
        $this->assertSame($response->json()['member'][0], $book);
×
54
    }
55

56
    public function testValidateSearchFilter(): void
57
    {
58
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
59

60
        $response = $this->get('/api/books?isbn=a', ['Accept' => ['application/ld+json']]);
×
61
        $this->assertSame($response->json()['detail'], 'The isbn field must be at least 2 characters.');
×
62
    }
63

64
    public function testSearchFilterRelation(): void
65
    {
66
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
67

68
        $response = $this->get('/api/books?author=1', ['Accept' => ['application/ld+json']]);
×
69
        $this->assertSame($response->json()['member'][0]['author'], '/api/authors/1');
×
70
    }
71

72
    public function testPropertyFilter(): void
73
    {
74
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
75

76
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
77
        $book = $response->json()['member'][0];
×
78

79
        $response = $this->get(\sprintf('%s.jsonld?properties[]=author', $book['@id']));
×
80
        $book = $response->json();
×
81

82
        $this->assertArrayHasKey('@id', $book);
×
83
        $this->assertArrayHasKey('author', $book);
×
84
        $this->assertArrayNotHasKey('name', $book);
×
85
    }
86

87
    public function testPartialSearchFilter(): void
88
    {
89
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
90

91
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
92
        $book = $response->json()['member'][0];
×
93

94
        if (!isset($book['name'])) {
×
95
            throw new \UnexpectedValueException();
×
96
        }
97

98
        $end = strpos($book['name'], ' ') ?: 3;
×
99
        $name = substr($book['name'], 0, $end);
×
100

101
        $response = $this->get('/api/books?name='.$name, ['Accept' => ['application/ld+json']]);
×
102
        $this->assertSame($response->json()['member'][0], $book);
×
103
    }
104

105
    public function testDateFilterEqual(): void
106
    {
107
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
108

109
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
110
        $book = $response->json()['member'][0];
×
111
        $updated = $this->patchJson(
×
112
            $book['@id'],
×
113
            ['publicationDate' => '2024-02-18 00:00:00'],
×
114
            [
×
115
                'Accept' => ['application/ld+json'],
×
116
                'Content-Type' => ['application/merge-patch+json'],
×
117
            ]
×
118
        );
×
119

120
        $response = $this->get('/api/books?publicationDate[eq]='.$updated['publicationDate'], ['Accept' => ['application/ld+json']]);
×
121
        $this->assertSame($response->json()['member'][0]['@id'], $book['@id']);
×
122
    }
123

124
    public function testDateFilterIncludeNull(): void
125
    {
126
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
127

128
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
129
        $book = $response->json()['member'][0];
×
130
        $updated = $this->patchJson(
×
131
            $book['@id'],
×
132
            ['publicationDate' => null],
×
133
            [
×
134
                'Accept' => ['application/ld+json'],
×
135
                'Content-Type' => ['application/merge-patch+json'],
×
136
            ]
×
137
        );
×
138

139
        $response = $this->get('/api/books?publicationWithNulls[gt]=9999-12-31', ['Accept' => ['application/ld+json']]);
×
140
        $this->assertGreaterThan(0, $response->json()['totalItems']);
×
141
    }
142

143
    public function testDateFilterExcludeNull(): void
144
    {
145
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
146

147
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
148
        $book = $response->json()['member'][0];
×
149
        $updated = $this->patchJson(
×
150
            $book['@id'],
×
151
            ['publicationDate' => null],
×
152
            [
×
153
                'Accept' => ['application/ld+json'],
×
154
                'Content-Type' => ['application/merge-patch+json'],
×
155
            ]
×
156
        );
×
157

158
        $response = $this->get('/api/books?publicationDate[gt]=9999-12-31', ['Accept' => ['application/ld+json']]);
×
159
        $this->assertSame(0, $response->json()['totalItems']);
×
160
    }
161

162
    public function testDateFilterGreaterThan(): void
163
    {
164
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
165

166
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
167
        $bookBefore = $response->json()['member'][0];
×
168
        $updated = $this->patchJson(
×
169
            $bookBefore['@id'],
×
170
            ['publicationDate' => '9998-02-18 00:00:00'],
×
171
            [
×
172
                'Accept' => ['application/ld+json'],
×
173
                'Content-Type' => ['application/merge-patch+json'],
×
174
            ]
×
175
        );
×
176

177
        $bookAfter = $response->json()['member'][1];
×
178
        $this->patchJson(
×
179
            $bookAfter['@id'],
×
180
            ['publicationDate' => '9999-02-18 00:00:00'],
×
181
            [
×
182
                'Accept' => ['application/ld+json'],
×
183
                'Content-Type' => ['application/merge-patch+json'],
×
184
            ]
×
185
        );
×
186

187
        $response = $this->get('/api/books?publicationDate[gt]='.$updated['publicationDate'], ['Accept' => ['application/ld+json']]);
×
188
        $this->assertSame($response->json()['member'][0]['@id'], $bookAfter['@id']);
×
189
        $this->assertSame($response->json()['totalItems'], 1);
×
190
    }
191

192
    public function testDateFilterLowerThanEqual(): void
193
    {
194
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
195
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
196
        $bookBefore = $response->json()['member'][0];
×
197
        $this->patchJson(
×
198
            $bookBefore['@id'],
×
199
            ['publicationDate' => '0001-02-18 00:00:00'],
×
200
            [
×
201
                'Accept' => ['application/ld+json'],
×
202
                'Content-Type' => ['application/merge-patch+json'],
×
203
            ]
×
204
        );
×
205

206
        $bookAfter = $response->json()['member'][1];
×
207
        $this->patchJson(
×
208
            $bookAfter['@id'],
×
209
            ['publicationDate' => '0002-02-18 00:00:00'],
×
210
            [
×
211
                'Accept' => ['application/ld+json'],
×
212
                'Content-Type' => ['application/merge-patch+json'],
×
213
            ]
×
214
        );
×
215

216
        $response = $this->get('/api/books?publicationDate[lte]=0002-02-18', ['Accept' => ['application/ld+json']]);
×
217
        $this->assertSame($response->json()['member'][0]['@id'], $bookBefore['@id']);
×
218
        $this->assertSame($response->json()['member'][1]['@id'], $bookAfter['@id']);
×
219
        $this->assertSame($response->json()['totalItems'], 2);
×
220
    }
221

222
    public function testDateFilterBetween(): void
223
    {
224
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
225
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
226
        $book = $response->json()['member'][0];
×
227
        $updated = $this->patchJson(
×
228
            $book['@id'],
×
229
            ['publicationDate' => '0001-02-18 00:00:00'],
×
230
            [
×
231
                'Accept' => ['application/ld+json'],
×
232
                'Content-Type' => ['application/merge-patch+json'],
×
233
            ]
×
234
        );
×
235

236
        $book2 = $response->json()['member'][1];
×
237
        $this->patchJson(
×
238
            $book2['@id'],
×
239
            ['publicationDate' => '0002-02-18 00:00:00'],
×
240
            [
×
241
                'Accept' => ['application/ld+json'],
×
242
                'Content-Type' => ['application/merge-patch+json'],
×
243
            ]
×
244
        );
×
245

246
        $book3 = $response->json()['member'][2];
×
247
        $updated3 = $this->patchJson(
×
248
            $book3['@id'],
×
249
            ['publicationDate' => '0003-02-18 00:00:00'],
×
250
            [
×
251
                'Accept' => ['application/ld+json'],
×
252
                'Content-Type' => ['application/merge-patch+json'],
×
253
            ]
×
254
        );
×
255

256
        $response = $this->get('/api/books?publicationDate[gte]='.substr($updated['publicationDate'], 0, 10).'&publicationDate[lt]='.substr($updated3['publicationDate'], 0, 10), ['Accept' => ['application/ld+json']]);
×
257
        $this->assertSame($response->json()['member'][0]['@id'], $book['@id']);
×
258
        $this->assertSame($response->json()['member'][1]['@id'], $book2['@id']);
×
259
        $this->assertSame($response->json()['totalItems'], 2);
×
260
    }
261

262
    public function testSearchFilterWithPropertyPlaceholder(): void
263
    {
264
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
265
        $response = $this->get('/api/authors', ['Accept' => ['application/ld+json']])->json();
×
266
        $author = $response['member'][0];
×
267

268
        $test = $this->get('/api/authors?name='.explode(' ', $author['name'])[0], ['Accept' => ['application/ld+json']])->json();
×
269
        $this->assertSame($test['member'][0]['id'], $author['id']);
×
270

271
        $test = $this->get('/api/authors?id='.$author['id'], ['Accept' => ['application/ld+json']])->json();
×
272
        $this->assertSame($test['member'][0]['id'], $author['id']);
×
273
    }
274

275
    public function testOrderFilterWithPropertyPlaceholder(): void
276
    {
277
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
278
        $res = $this->get('/api/authors?order[id]=desc', ['Accept' => ['application/ld+json']])->json();
×
279
        $this->assertSame($res['member'][0]['id'], 10);
×
280
    }
281

282
    public function testOrFilter(): void
283
    {
284
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
285
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']])->json()['member'];
×
286
        $book = $response[0];
×
287
        $book2 = $response[1];
×
288

289
        $res = $this->get(\sprintf('/api/books?name2[]=%s&name2[]=%s', $book['name'], $book2['name']), ['Accept' => ['application/ld+json']])->json();
×
290
        $this->assertSame($res['totalItems'], 2);
×
291
    }
292

293
    public function testRangeLowerThanFilter(): void
294
    {
295
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
296
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
297
        $bookBefore = $response->json()['member'][0];
×
298
        $this->patchJson(
×
299
            $bookBefore['@id'],
×
300
            ['isbn' => '12'],
×
301
            [
×
302
                'Accept' => ['application/ld+json'],
×
303
                'Content-Type' => ['application/merge-patch+json'],
×
304
            ]
×
305
        );
×
306

307
        $bookAfter = $response->json()['member'][1];
×
308
        $updated = $this->patchJson(
×
309
            $bookAfter['@id'],
×
310
            ['isbn' => '15'],
×
311
            [
×
312
                'Accept' => ['application/ld+json'],
×
313
                'Content-Type' => ['application/merge-patch+json'],
×
314
            ]
×
315
        );
×
316

317
        $response = $this->get('api/books?isbn_range[lt]='.$updated['isbn'], ['Accept' => ['application/ld+json']]);
×
318
        $this->assertSame($response->json()['member'][0]['@id'], $bookBefore['@id']);
×
319
        $this->assertSame($response->json()['totalItems'], 1);
×
320
    }
321

322
    public function testRangeLowerThanEqualFilter(): void
323
    {
324
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
325
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
326
        $bookBefore = $response->json()['member'][0];
×
327
        $this->patchJson(
×
328
            $bookBefore['@id'],
×
329
            ['isbn' => '12'],
×
330
            [
×
331
                'Accept' => ['application/ld+json'],
×
332
                'Content-Type' => ['application/merge-patch+json'],
×
333
            ]
×
334
        );
×
335

336
        $bookAfter = $response->json()['member'][1];
×
337
        $updated = $this->patchJson(
×
338
            $bookAfter['@id'],
×
339
            ['isbn' => '15'],
×
340
            [
×
341
                'Accept' => ['application/ld+json'],
×
342
                'Content-Type' => ['application/merge-patch+json'],
×
343
            ]
×
344
        );
×
345

346
        $response = $this->get('api/books?isbn_range[lte]='.$updated['isbn'], ['Accept' => ['application/ld+json']]);
×
347
        $this->assertSame($response->json()['member'][0]['@id'], $bookBefore['@id']);
×
348
        $this->assertSame($response->json()['member'][1]['@id'], $bookAfter['@id']);
×
349
        $this->assertSame($response->json()['totalItems'], 2);
×
350
    }
351

352
    public function testRangeGreaterThanFilter(): void
353
    {
354
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
355
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
356
        $bookBefore = $response->json()['member'][0];
×
357
        $updated = $this->patchJson(
×
358
            $bookBefore['@id'],
×
359
            ['isbn' => '999999999999998'],
×
360
            [
×
361
                'Accept' => ['application/ld+json'],
×
362
                'Content-Type' => ['application/merge-patch+json'],
×
363
            ]
×
364
        );
×
365

366
        $bookAfter = $response->json()['member'][1];
×
367
        $this->patchJson(
×
368
            $bookAfter['@id'],
×
369
            ['isbn' => '999999999999999'],
×
370
            [
×
371
                'Accept' => ['application/ld+json'],
×
372
                'Content-Type' => ['application/merge-patch+json'],
×
373
            ]
×
374
        );
×
375

376
        $response = $this->get('api/books?isbn_range[gt]='.$updated['isbn'], ['Accept' => ['application/ld+json']]);
×
377
        $this->assertSame($response->json()['member'][0]['@id'], $bookAfter['@id']);
×
378
        $this->assertSame($response->json()['totalItems'], 1);
×
379
    }
380

381
    public function testRangeGreaterThanEqualFilter(): void
382
    {
383
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
384
        $response = $this->get('/api/books', ['Accept' => ['application/ld+json']]);
×
385
        $bookBefore = $response->json()['member'][0];
×
386
        $updated = $this->patchJson(
×
387
            $bookBefore['@id'],
×
388
            ['isbn' => '999999999999998'],
×
389
            [
×
390
                'Accept' => ['application/ld+json'],
×
391
                'Content-Type' => ['application/merge-patch+json'],
×
392
            ]
×
393
        );
×
394

395
        $bookAfter = $response->json()['member'][1];
×
396
        $this->patchJson(
×
397
            $bookAfter['@id'],
×
398
            ['isbn' => '999999999999999'],
×
399
            [
×
400
                'Accept' => ['application/ld+json'],
×
401
                'Content-Type' => ['application/merge-patch+json'],
×
402
            ]
×
403
        );
×
404
        $response = $this->get('api/books?isbn_range[gte]='.$updated['isbn'], ['Accept' => ['application/ld+json']]);
×
405
        $json = $response->json();
×
406
        $this->assertSame($json['member'][0]['@id'], $bookBefore['@id']);
×
407
        $this->assertSame($json['member'][1]['@id'], $bookAfter['@id']);
×
408
        $this->assertSame($json['totalItems'], 2);
×
409
    }
410

411
    public function testWrongOrderFilter(): void
412
    {
413
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
414
        $res = $this->get('/api/authors?order[name]=something', ['Accept' => ['application/ld+json']]);
×
415
        $this->assertEquals($res->getStatusCode(), 422);
×
416
    }
417

418
    public function testWithAccessor(): void
419
    {
420
        WithAccessorFactory::new()->create();
×
421
        $res = $this->get('/api/with_accessors/1', ['Accept' => ['application/ld+json']]);
×
422
        $this->assertArraySubset(['name' => 'test'], $res->json());
×
423
    }
424

425
    public function testBooleanFilter(): void
426
    {
427
        BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
×
428
        $res = $this->get('/api/books?published=notabool', ['Accept' => ['application/ld+json']]);
×
429
        $this->assertEquals($res->getStatusCode(), 422);
×
430

431
        $res = $this->get('/api/books?published=0', ['Accept' => ['application/ld+json']]);
×
432
        $this->assertEquals($res->getStatusCode(), 200);
×
433
        $this->assertEquals($res->json()['totalItems'], 0);
×
434
    }
435

436
    public function testBelongsTo(): void
437
    {
NEW
438
        GrandSonFactory::new()->count(1)->create();
×
439

NEW
440
        $res = $this->get('/api/grand_sons/1/grand_father', ['Accept' => ['application/ld+json']]);
×
NEW
441
        $json = $res->json();
×
NEW
442
        $this->assertEquals($json['@id'], '/api/grand_sons/1/grand_father');
×
NEW
443
        $this->assertEquals($json['sons'][0], '/api/grand_sons/1');
×
444
    }
445
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc