Skip to content

Commit 61c6dee

Browse files
committed
Merge 4.3
2 parents effc99e + 0f14353 commit 61c6dee

File tree

28 files changed

+960
-40
lines changed

28 files changed

+960
-40
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,7 @@ jobs:
14691469
laravel new test-api-platform-install --pest --no-interaction
14701470
- name: Install api-platform/laravel
14711471
run: |
1472+
composer require laravel/framework:"^13.0" --no-update
14721473
composer global link ../src/Laravel --permanent
14731474
composer config minimum-stability dev
14741475
composer config prefer-stable true

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## v4.3.1
4+
5+
### Bug fixes
6+
7+
* [13cc3950c](https://github.com/api-platform/core/commit/13cc3950cbf89cefb0005d61d39451ed8f907717) fix(doctrine): reset nested_properties_info for non-nested properties in FreeTextQueryFilter (#7850)
8+
* [1fa8c05ca](https://github.com/api-platform/core/commit/1fa8c05cab2bc58984d988ad367f0896cb5e64f4) fix(validator): fallback to message when detail is uninitialized (#7844)
9+
* [aff1cf2cc](https://github.com/api-platform/core/commit/aff1cf2cc6ce451322c58b1ee03e4f5d6bcd3649) fix(symfony): do not exclude resources from DI (#7847)
10+
* [e45b5791d](https://github.com/api-platform/core/commit/e45b5791db3c5c7f0874816675e9891bacb20ca6) fix(validator): skip ValidateProcessor when ObjectMapper is not used (#7848)
11+
312
## v4.3.0
413

514
### Features
@@ -88,6 +97,13 @@
8897
* **LDP-compliant response headers** (#6917): API responses now include `Allow` and `Accept-Post` headers per the Linked Data Platform specification. These are informational headers that help clients discover API capabilities and should not break existing integrations.
8998
* **Scalar API Reference UI** (#7817): Scalar is now available as an alternative documentation UI alongside Swagger UI. It is enabled by default when TwigBundle is available. Access it via `?ui=scalar`. To disable it, set `enable_scalar: false` in your API Platform configuration.
9099

100+
## v4.2.22
101+
102+
### Bug fixes
103+
104+
* [3e96fc679](https://github.com/api-platform/core/commit/3e96fc6798d6460802bc07f5566c2a3158b53eb8) fix(serializer): evaluate ApiProperty security on input DTOs (#7852)
105+
* [c7ababf2d](https://github.com/api-platform/core/commit/c7ababf2dfee43c8f21e8d4d02ad4f1072d3cbba) fix(hydra): use compact IRI for owl:onProperty and Collection @id in DocumentationNormalizer (#7849)
106+
91107
## v4.2.21
92108

93109
### Bug fixes

composer.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,18 +137,18 @@
137137
"friends-of-behat/symfony-extension": "^2.1",
138138
"friendsofphp/php-cs-fixer": "^3.93",
139139
"guzzlehttp/guzzle": "^6.0 || ^7.0",
140-
"illuminate/config": "^11.0 || ^12.0",
141-
"illuminate/contracts": "^11.0 || ^12.0",
142-
"illuminate/database": "^11.0 || ^12.0",
143-
"illuminate/http": "^11.0 || ^12.0",
144-
"illuminate/pagination": "^11.0 || ^12.0",
145-
"illuminate/routing": "^11.0 || ^12.0",
146-
"illuminate/support": "^11.0 || ^12.0",
140+
"illuminate/config": "^11.0 || ^12.0 || ^13.0",
141+
"illuminate/contracts": "^11.0 || ^12.0 || ^13.0",
142+
"illuminate/database": "^11.0 || ^12.0 || ^13.0",
143+
"illuminate/http": "^11.0 || ^12.0 || ^13.0",
144+
"illuminate/pagination": "^11.0 || ^12.0 || ^13.0",
145+
"illuminate/routing": "^11.0 || ^12.0 || ^13.0",
146+
"illuminate/support": "^11.0 || ^12.0 || ^13.0",
147147
"jangregor/phpstan-prophecy": "^2.1.11",
148148
"justinrainbow/json-schema": "^6.5.2",
149149
"mcp/sdk": "^0.4.0",
150-
"laravel/framework": "^11.0 || ^12.0",
151-
"orchestra/testbench": "^10.9",
150+
"laravel/framework": "^11.0 || ^12.0 || ^13.0",
151+
"orchestra/testbench": "^10.9 || ^11.0",
152152
"phpspec/prophecy-phpunit": "^2.2",
153153
"phpstan/extension-installer": "^1.1",
154154
"phpstan/phpdoc-parser": "^1.29 || ^2.0",

src/Doctrine/Odm/Filter/FreeTextQueryFilter.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ public function apply(Builder $aggregationBuilder, string $resourceClass, ?Opera
4949
$subParameter = $parameter->withProperty($property);
5050

5151
$nestedPropertiesInfo = $parameter->getExtraProperties()['nested_properties_info'] ?? [];
52-
if (isset($nestedPropertiesInfo[$property])) {
53-
$subParameter = $subParameter->withExtraProperties([
54-
...$subParameter->getExtraProperties(),
55-
'nested_properties_info' => [$property => $nestedPropertiesInfo[$property]],
56-
]);
57-
}
52+
$subParameter = $subParameter->withExtraProperties([
53+
...$subParameter->getExtraProperties(),
54+
'nested_properties_info' => isset($nestedPropertiesInfo[$property])
55+
? [$property => $nestedPropertiesInfo[$property]]
56+
: [],
57+
]);
5858

5959
$newContext = ['parameter' => $subParameter, 'match' => $context['match'] ?? $aggregationBuilder->match()->expr()] + $context;
6060
$this->filter->apply(

src/Doctrine/Orm/Filter/FreeTextQueryFilter.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $q
5555
$subParameter = $parameter->withProperty($property);
5656

5757
$nestedPropertiesInfo = $parameter->getExtraProperties()['nested_properties_info'] ?? [];
58-
if (isset($nestedPropertiesInfo[$property])) {
59-
$subParameter = $subParameter->withExtraProperties([
60-
...$subParameter->getExtraProperties(),
61-
'nested_properties_info' => [$property => $nestedPropertiesInfo[$property]],
62-
]);
63-
}
58+
$subParameter = $subParameter->withExtraProperties([
59+
...$subParameter->getExtraProperties(),
60+
'nested_properties_info' => isset($nestedPropertiesInfo[$property])
61+
? [$property => $nestedPropertiesInfo[$property]]
62+
: [],
63+
]);
6464

6565
$this->filter->apply(
6666
$qb,

src/Laravel/ApiPlatformDeferredProvider.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ public function register(): void
267267
),
268268
$app->make('filters'),
269269
$app->make(CamelCaseToSnakeCaseNameConverter::class),
270-
$this->app->make(LoggerInterface::class)
270+
$this->app->make(LoggerInterface::class),
271+
$app->make(ResourceClassResolverInterface::class),
271272
),
272273
$app->make('filters')
273274
)

src/Laravel/Eloquent/Metadata/ModelMetadata.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ private function getCastType(string $column, Model $model): ?string
268268
/**
269269
* Gets the model casts, including any date casts.
270270
*
271+
* In Laravel 11+, casts can be defined via the protected casts() method
272+
* in addition to the $casts property. Since models may be instantiated
273+
* without calling the constructor (newInstanceWithoutConstructor),
274+
* initializeHasAttributes() is never called and the casts() method
275+
* results are not merged into $casts. We call casts() via reflection
276+
* to ensure both sources are included.
277+
*
271278
* @return array<string, mixed>
272279
*/
273280
private function getCastsWithDates(Model $model): array
@@ -280,7 +287,15 @@ private function getCastsWithDates(Model $model): array
280287
}
281288
}
282289

283-
return array_merge($dateCasts, $model->getCasts());
290+
$casts = $model->getCasts();
291+
292+
try {
293+
$castsMethod = new \ReflectionMethod($model, 'casts');
294+
$casts = array_merge($casts, $castsMethod->invoke($model));
295+
} catch (\ReflectionException) {
296+
}
297+
298+
return array_merge($dateCasts, $casts);
284299
}
285300

286301
/**

src/Laravel/Tests/Eloquent/Metadata/ModelMetadataTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Orchestra\Testbench\Concerns\WithWorkbench;
2121
use Orchestra\Testbench\TestCase;
2222
use Workbench\App\Models\Book;
23+
use Workbench\App\Models\BookWithMethodCasts;
2324
use Workbench\App\Models\Device;
2425

2526
/**
@@ -82,6 +83,25 @@ public function secret(): HasMany // @phpstan-ignore-line
8283
$this->assertCount(1, $metadata->getRelations($model));
8384
}
8485

86+
/**
87+
* Casts defined via the casts() method (Laravel 11+) should be detected
88+
* just like those defined via the $casts property.
89+
*
90+
* @see https://github.com/api-platform/core/issues/7662
91+
*/
92+
public function testCastsMethodIsDetected(): void
93+
{
94+
// Use newInstanceWithoutConstructor to replicate how API Platform creates models
95+
$refl = new \ReflectionClass(BookWithMethodCasts::class);
96+
$model = $refl->newInstanceWithoutConstructor();
97+
98+
$metadata = new ModelMetadata();
99+
$attributes = $metadata->getAttributes($model);
100+
101+
$this->assertSame('boolean', $attributes['is_available']['cast']);
102+
$this->assertSame('datetime', $attributes['publication_date']['cast']);
103+
}
104+
85105
/**
86106
* When a model has a custom primary key (e.g. device_id) and a HasMany
87107
* relation whose foreign key on the related table has the same name,

src/Laravel/composer.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@
3838
"api-platform/openapi": "^4.3",
3939
"api-platform/serializer": "^4.3",
4040
"api-platform/state": "^4.3",
41-
"illuminate/config": "^11.0 || ^12.0",
42-
"illuminate/container": "^11.0 || ^12.0",
43-
"illuminate/contracts": "^11.0 || ^12.0",
44-
"illuminate/database": "^11.0 || ^12.0",
45-
"illuminate/http": "^11.0 || ^12.0",
46-
"illuminate/pagination": "^11.0 || ^12.0",
47-
"illuminate/routing": "^11.0 || ^12.0",
48-
"illuminate/support": "^11.0 || ^12.0",
49-
"laravel/framework": "^11.0 || ^12.0",
41+
"illuminate/config": "^11.0 || ^12.0 || ^13.0",
42+
"illuminate/container": "^11.0 || ^12.0 || ^13.0",
43+
"illuminate/contracts": "^11.0 || ^12.0 || ^13.0",
44+
"illuminate/database": "^11.0 || ^12.0 || ^13.0",
45+
"illuminate/http": "^11.0 || ^12.0 || ^13.0",
46+
"illuminate/pagination": "^11.0 || ^12.0 || ^13.0",
47+
"illuminate/routing": "^11.0 || ^12.0 || ^13.0",
48+
"illuminate/support": "^11.0 || ^12.0 || ^13.0",
49+
"laravel/framework": "^11.0 || ^12.0 || ^13.0",
5050
"symfony/deprecation-contracts": "^3.6",
5151
"symfony/type-info": "^7.4 || ^8.0",
5252
"symfony/web-link": "^6.4 || ^7.4 || ^8.0",
@@ -60,7 +60,7 @@
6060
"larastan/larastan": "^2.0 || ^3.0",
6161
"laravel/sanctum": "^4.0",
6262
"mcp/sdk": "^0.4.0",
63-
"orchestra/testbench": "^10.1",
63+
"orchestra/testbench": "^10.1 || ^11.0",
6464
"phpdocumentor/type-resolver": "^1.7",
6565
"phpstan/phpdoc-parser": "^1.29 || ^2.0",
6666
"phpunit/phpunit": "^12.2",
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 Workbench\App\Models;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\GetCollection;
19+
use Illuminate\Database\Eloquent\Concerns\HasUlids;
20+
use Illuminate\Database\Eloquent\Model;
21+
22+
/**
23+
* Model that uses the casts() method instead of the $casts property.
24+
*
25+
* @see https://github.com/api-platform/core/issues/7662
26+
*/
27+
#[ApiResource(
28+
operations: [
29+
new Get(),
30+
new GetCollection(),
31+
],
32+
)]
33+
class BookWithMethodCasts extends Model
34+
{
35+
use HasUlids;
36+
37+
protected $table = 'books';
38+
39+
protected $visible = ['name', 'isbn', 'publication_date', 'is_available'];
40+
protected $fillable = ['name', 'isbn', 'publication_date', 'is_available'];
41+
42+
protected function casts(): array
43+
{
44+
return [
45+
'is_available' => 'boolean',
46+
'publication_date' => 'datetime',
47+
];
48+
}
49+
}

0 commit comments

Comments
 (0)