Skip to content

Commit c0a8d88

Browse files
committed
Merge 4.2
2 parents 05a5d4d + 7732896 commit c0a8d88

19 files changed

Lines changed: 271 additions & 413 deletions

File tree

AGENTS.md

Lines changed: 36 additions & 401 deletions
Large diffs are not rendered by default.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
"jangregor/phpstan-prophecy": "^2.1.11",
148148
"justinrainbow/json-schema": "^6.5.2",
149149
"mcp/sdk": "^0.3.0",
150+
"laravel/framework": "^11.0 || ^12.0",
150151
"orchestra/testbench": "^10.9",
151152
"phpspec/prophecy-phpunit": "^2.2",
152153
"phpstan/extension-installer": "^1.1",

docs/AGENTS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Context: API Platform Documentation
2+
3+
## Documentation Standards
4+
5+
* **Tone:** Technical, concise, and helpful. Avoid overly marketing language.
6+
* **Format:** Markdown (GitHub flavored).
7+
* **Links:** Use relative links for internal navigation.
8+
9+
## **Directory Structure**
10+
11+
* guides/: Learning oriented guides (How-to).
12+
* adr/: Architectural Decision Records.
13+
* src/: API Reference generation source.
14+
15+
## **Contribution**
16+
17+
* Documentation changes for new features are **mandatory**.
18+
* Verify links before finalizing content.

src/Hydra/JsonSchema/SchemaFactory.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,6 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
8181
'required' => ['@type'],
8282
];
8383

84-
/**
85-
* @var array<string, true>
86-
*/
87-
private array $transformed = [];
88-
8984
/**
9085
* @param array<string, mixed> $defaultContext
9186
*/
@@ -284,7 +279,7 @@ public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
284279

285280
private function decorateItemDefinition(string $definitionName, \ArrayObject $definitions, string $prefix, string $type, ?array $serializerContext): void
286281
{
287-
if (!isset($definitions[$definitionName]) || ($this->transformed[$definitionName] ?? false)) {
282+
if (!isset($definitions[$definitionName])) {
288283
return;
289284
}
290285

@@ -294,6 +289,12 @@ private function decorateItemDefinition(string $definitionName, \ArrayObject $de
294289
$baseName = self::ITEM_WITHOUT_ID_BASE_SCHEMA_NAME;
295290
}
296291

292+
// Check if the definition is already decorated to prevent double wrapping (recursion or cache)
293+
$currentDef = $definitions[$definitionName];
294+
if (($currentDef['allOf'][0]['$ref'] ?? null) === $prefix.$baseName) {
295+
return;
296+
}
297+
297298
if (!isset($definitions[$baseName])) {
298299
$definitions[$baseName] = $hasNoId ? self::ITEM_BASE_SCHEMA_WITHOUT_ID : self::ITEM_BASE_SCHEMA_WITH_ID;
299300
}
@@ -309,7 +310,5 @@ private function decorateItemDefinition(string $definitionName, \ArrayObject $de
309310

310311
$definitions[$definitionName] = $allOf;
311312
unset($definitions[$definitionName]['allOf'][1]['description']);
312-
313-
$this->transformed[$definitionName] = true;
314313
}
315314
}

src/Laravel/AGENTS.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Context: API Platform (Laravel Bridge)
2+
3+
## Laravel-Specific Protocols
4+
5+
You are currently working within the src/Laravel component. This environment differs from the main Symfony-based core.
6+
7+
### 1. Setup & Environment
8+
9+
* Execution Scope: Run all commands from the src/Laravel directory.
10+
* Dependency Linking: You MUST link the root dependencies before running tests here.
11+
* Fixtures live in `workbench/app/ApiResource` or `workbench/app/Models`, `workbench` is a classic laravel project.
12+
13+
```
14+
composer link ../../
15+
vendor/bin/testbench workbench:build
16+
```
17+
18+
If you need a fresh fixtures start:
19+
20+
```
21+
vendor/bin/testbench workbench:drop-sqlite-db
22+
```
23+
24+
### 2. Testing Workflow
25+
26+
**Running Tests:**
27+
28+
```
29+
vendor/bin/testbench package:test Tests/NoOperationResourceTest.php
30+
```
31+
32+
**Static Analysis (Laravel):**
33+
34+
```
35+
./vendor/bin/phpstan analyse
36+
```
37+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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 Illuminate\Contracts\Config\Repository;
17+
use Illuminate\Foundation\Application;
18+
use Orchestra\Testbench\Concerns\WithWorkbench;
19+
use Orchestra\Testbench\TestCase;
20+
21+
class NoOperationResourceTest extends TestCase
22+
{
23+
use WithWorkbench;
24+
25+
/**
26+
* @param Application $app
27+
*/
28+
protected function defineEnvironment($app): void
29+
{
30+
tap($app['config'], static function (Repository $config): void {
31+
$config->set('api-platform.resources', [app_path('ApiResource')]);
32+
});
33+
}
34+
35+
public function testNoRoutesAreGeneratedForNoOperationResource(): void
36+
{
37+
$response = $this->get('/api/no_operations/notfound', headers: ['accept' => 'application/ld+json']);
38+
$response->assertNotFound();
39+
$response->assertJson(['detail' => 'This route does not aim to be called.']);
40+
}
41+
}

src/Laravel/routes/api.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151

5252
if (!class_exists($controller)) {
5353
$controller = $app->make($controller);
54+
55+
if (is_callable([$controller, '__invoke'])) {
56+
$controller = '\\'.$controller::class.'@__invoke'; // see Illuminate\Routing\RouteAction
57+
}
5458
}
5559

5660
$route = Route::addRoute($operation->getMethod(), $uriTemplate, ['uses' => $controller, 'prefix' => $operation->getRoutePrefix() ?? ''])
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\ApiResource;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
18+
#[ApiResource(operations: [])]
19+
class NoOperation
20+
{
21+
public int $id = 1;
22+
}

src/Symfony/Bundle/Resources/config/api.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
use ApiPlatform\Symfony\Routing\SkolemIriConverter;
4646
use Negotiation\Negotiator;
4747
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
48+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
4849

4950
return function (ContainerConfigurator $container) {
5051
$services = $container->services();
@@ -131,6 +132,19 @@
131132
])
132133
->tag('serializer.normalizer', ['priority' => -895]);
133134

135+
$services->set('api_platform.normalizer.object', ObjectNormalizer::class)
136+
->args([
137+
service('serializer.mapping.class_metadata_factory'),
138+
service('api_platform.name_converter')->ignoreOnInvalid(),
139+
service('serializer.property_accessor'),
140+
service('property_info')->ignoreOnInvalid(),
141+
service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(),
142+
null,
143+
[],
144+
service('property_info')->ignoreOnInvalid(),
145+
])
146+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -1000]);
147+
134148
$services->set('api_platform.serializer.mapping.class_metadata_factory', ClassMetadataFactory::class)
135149
->decorate('serializer.mapping.class_metadata_factory', null, -1)
136150
->args([service('api_platform.serializer.mapping.class_metadata_factory.inner')]);

src/Symfony/Bundle/Resources/config/graphql.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@
252252

253253
$services->set('api_platform.graphql.normalizer.object', ObjectNormalizer::class)
254254
->args([
255-
service('serializer.normalizer.object'),
255+
service('api_platform.normalizer.object'),
256256
service('api_platform.symfony.iri_converter'),
257257
service('api_platform.api.identifiers_extractor'),
258258
])

0 commit comments

Comments
 (0)