diff --git a/CHANGELOG.md b/CHANGELOG.md index 08838116214..564ed77d2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,55 @@ # Changelog +## v4.0.1 + +### Bug fixes + +* [eb80a1a56](https://github.com/api-platform/core/commit/eb80a1a5651b81cab13b018662a0d21e05facbfe) fix(state): precise format on content-location (#6627) + +### Features + +* [4a2271670](https://github.com/api-platform/core/commit/4a2271670a88c318ab38bd7eb2a1c0b93a5c0ea0) feat: api-platform/json-hal component (#6621) + +## v4.0.0 + +### Bug fixes + +* [7c5689626](https://github.com/api-platform/core/commit/7c568962634691892fe1057b3c982765a1c20ba2) fix(laravel): call authorize on delete but not validation (#6618) +* [d74b2b5fa](https://github.com/api-platform/core/commit/d74b2b5fa939a9f5ca11d3538e358070047a6c3d) fix: swagger ui with route identifier (#6616) +* [de6e3f546](https://github.com/api-platform/core/commit/de6e3f546a26c5ad5444d8e2448d81faec36bd73) fix(laravel): validate enum schema within filter (#6615) +* [0a461d749](https://github.com/api-platform/core/commit/0a461d749b7b4ac706f3b7b6138a13cb6e4a9d2d) fix(symfony): allow schema restriction for collection like property from choice constraint (#6520) +* [0c66a494d](https://github.com/api-platform/core/commit/0c66a494d3bda5817e59da2e43f0232e2e8fea15) fix(laravel): cache metadata, add trace on debug mode (#6555) +* [1b6e7c6cc](https://github.com/api-platform/core/commit/1b6e7c6ccf3d2c61816a7dceb35f1d5980ea0565) fix(laravel): disable GraphQL by default and fix provider +* [290944103](https://github.com/api-platform/core/commit/290944103039a4a6d64904d1a89264b800c809d5) fix(laravel): SwaggerUI title (#6527) +* [2df0860b5](https://github.com/api-platform/core/commit/2df0860b577bb1ae0882096436f3eaeb91281901) fix(laravel): Eloquent date and datetime type detection (#6529) +* [2fc74f2e6](https://github.com/api-platform/core/commit/2fc74f2e651f229e982343c1cb0c6a2c5d5eee64) fix: remove PUT from default operations (#6570) +* [3b42c9ff2](https://github.com/api-platform/core/commit/3b42c9ff235de5feac555d0283c513a6e4643953) fix: deserialization path for not denormalizable relations collected errors (#6537) +* [3c554a605](https://github.com/api-platform/core/commit/3c554a605ec9d5f36dfd852c4f93f0ce582064c9) fix(laravel): docs _format and open swagger ui (#6595) +* [3c5aea80f](https://github.com/api-platform/core/commit/3c5aea80fdbed20216764f6d721fe4f37cf2889d) fix(symfony): load isApiResource metadata (#6562) +* [4ee209eff](https://github.com/api-platform/core/commit/4ee209effb0fd11789db9f016d6e90aa3cb942a9) fix(laravel): visible and hidden fields support (#6538) +* [6e15eb95f](https://github.com/api-platform/core/commit/6e15eb95fffb2deec3d381f5d6fd87e189772270) fix(laravel): register HydraPartialCollectionViewNormalizer (#6588) +* [86365be2a](https://github.com/api-platform/core/commit/86365be2a5b8e8d0050e09d4e401bb758aa8b7a8) fix(laravel): Eloquent PropertyAccessor (#6536) +* [a1dd0b54d](https://github.com/api-platform/core/commit/a1dd0b54d137d70f2163fa03690c1f4c74a549c0) fix(laravel): entrypoint with doc formats (#6552) +* [a4a53ab48](https://github.com/api-platform/core/commit/a4a53ab4838f4d17d3677952157b44ec165e3e3a) fix(laravel): identitifer is not writable unless marked as writable (#6531) +* [a6f355358](https://github.com/api-platform/core/commit/a6f355358a88e7cf7759db0dee41e185157ddc68) fix(laravel): do not normalize exception originalTrace (#6533) +* [bd6a57c4c](https://github.com/api-platform/core/commit/bd6a57c4c0b38d6f880a5bd79031a87033f707e6) fix(laravel): snake case props (#6532) +* [c31566602](https://github.com/api-platform/core/commit/c315666022185839742b8c5ef81601d85d8c3f4b) fix(laravel): api_doc route regex +* [ebc61d59d](https://github.com/api-platform/core/commit/ebc61d59d60eda3a020593cf4cb46c5d30548e46) fix(laravel): entrypoint serialization (#6541) + +### Features + +* [00787f32d](https://github.com/api-platform/core/commit/00787f32da54418de7d869cff218e22d8ae2ae1d) feat(laravel): automatically register policies (#6623) +* [06a647a80](https://github.com/api-platform/core/commit/06a647a80d4c6b7bfb3474d0685bcb445b56a5a8) feat(laravel): add CSV support (#6617) +* [a49bde1ea](https://github.com/api-platform/core/commit/a49bde1ea79ae4226b70c20f9bf967ac77e9ab89) feat(laravel): filter validations rules +* [03357fb90](https://github.com/api-platform/core/commit/03357fb90ac0003f0cec2002df01711d0fb99a1e) feat(laravel): supports more Eloquent types (#6544) +* [05e75be83](https://github.com/api-platform/core/commit/05e75be834c629e0487caaaedfe9fdf0bd5a7226) feat(doctrine): add new filter for filtering an entity using PHP backed enum, resolves #6506 (#6547) (#6560) +* [538648840](https://github.com/api-platform/core/commit/538648840dc7439b12033ed6f413f3167705da4d) feat(laravel): enable graphQl support (#6550) +* [5b9767be2](https://github.com/api-platform/core/commit/5b9767be215afdf61fccf8490d0a3a7018078ce5) feat(laravel): policy, auth and gate (#6523) +* [5e1233c57](https://github.com/api-platform/core/commit/5e1233c57a497d00a7c4bd3e3ad0cac25aeac014) feat(laravel): search filter (#6534) +* [9c461626f](https://github.com/api-platform/core/commit/9c461626f7d02f8d15487134b636f11966c19a5e) feat(laravel): provide a trait in addition to the annotation (#6543) +* [c9f18d4fb](https://github.com/api-platform/core/commit/c9f18d4fb833ea0b89ef18021cad491cf0600ef1) feat(laravel): eloquent filters (search, date, equals, or) (#6593) +* [e09e73efc](https://github.com/api-platform/core/commit/e09e73efc5b4a39ab33d00c5d5422d8d9f7b5e89) feat: remove hydra prefix (#6418) + ## v4.0.0-alpha.5 ### Bug fixes @@ -45,6 +95,48 @@ * [0d5f35683](https://github.com/api-platform/core/commit/0d5f356839eb6aa9f536044abe4affa736553e76) feat(laravel): laravel component (#5882) +## v3.4.0 + +### Deprecations: + +Namespaces like `ApiPlatform/Api` or `ApiPlatform/Util` are deprecated and will be removed in 4.0. +You should now install `api-platform/symfony` instead of `api-platform/core`. + +Read the [upgrade guide](https://api-platform.com/docs/core/upgrade-guide/) for detailed steps. + +### Bug fixes + +* [56153b755](https://github.com/api-platform/core/commit/56153b755151ee11c8c17fdc3fd919d544f078ac) fix(hydra): error hydra prefix (#6599) +* [7c89f66a5](https://github.com/api-platform/core/commit/7c89f66a534cc1100c3dab0a129381d307d9d8b4) fix: replace ApiPlatform\\Exception use by ApiPlatform\Metadata\\Exception (#6597) +* [a3a4a990d](https://github.com/api-platform/core/commit/a3a4a990d527136f093b022782a82e1d5b04c0b5) fix(metadata): loop on operations can be null +* [ef0ee6427](https://github.com/api-platform/core/commit/ef0ee6427f8056bcb2617c228a7cf9ffd9d29ccd) fix(doctrine): use parameter.property for filter value (#6572) +* [17c6b586c](https://github.com/api-platform/core/commit/17c6b586c5ab49437ac11dd092efdd5f0baf569b) fix(state): log on missing provider (#6519) +* [601ccfb42](https://github.com/api-platform/core/commit/601ccfb4243803f40a7fa7179e0661da59c88b86) fix(doctrine): move event listeners to doctrine/common (#6573) +* [6499e0aa5](https://github.com/api-platform/core/commit/6499e0aa5dd61fdff7706e7940cdf8c1fc3e18ef) fix: deprecate url generator interface namespace (#6575) +* [3c5aea80f](https://github.com/api-platform/core/commit/3c5aea80fdbed20216764f6d721fe4f37cf2889d) fix(symfony): load isApiResource metadata (#6562) +* [61af0cc90](https://github.com/api-platform/core/commit/61af0cc90c1e095edb12e32ef433a742ef46637e) fix(doctrine): allow doctrine/dbal:^4 +* [e063b80af](https://github.com/api-platform/core/commit/e063b80afe012ca4a6c8999de55b59193e8ae0ae) fix: parameter context for filters (#6535) +* [e22392193](https://github.com/api-platform/core/commit/e22392193bb1fc71ece5abf393fa54b0745fc287) fix(state): security parameter with listeners (#6457) + +Various fixes for components isolation. + +### Features + +* [130fb5a8c](https://github.com/api-platform/core/commit/130fb5a8c833430e5e09624b06f296e0bcb7ceed) feat: better path sorting for openapi UIs (#6583) +* [26d700e06](https://github.com/api-platform/core/commit/26d700e06035eaf4d04ddd52f3101dae690734d8) feat(symfony): add error page (#6389) +* [48267c9b6](https://github.com/api-platform/core/commit/48267c9b6a942b1fb54f0efcfa5b2d2ac47c93bf) feat(openapi): add error resources schemes (#6332) +* [e91c783a2](https://github.com/api-platform/core/commit/e91c783a2abf51bf2bdcf1230826108632c44a0d) feat(state): "deserializer_type" context (#6429) +* [17c6b586c](https://github.com/api-platform/core/commit/17c6b586c5ab49437ac11dd092efdd5f0baf569b) feat(state): log on missing provider (#6519) +* [05e75be83](https://github.com/api-platform/core/commit/05e75be834c629e0487caaaedfe9fdf0bd5a7226) feat(doctrine): add new filter for filtering an entity using PHP backed enum, resolves #6506 (#6547) (#6560) +* [0b985ae76](https://github.com/api-platform/core/commit/0b985ae760bc4689d3f5bbacebb21b35b334d0be) feat(state): add security to parameters (#6435) +* [63ccfd58c](https://github.com/api-platform/core/commit/63ccfd58c95b5aa4aa0353eb122d96ef35187222) feat: BackedEnum resources (#6309) +* [65296eaf1](https://github.com/api-platform/core/commit/65296eaf1eb18dc725e9316e9ab49b191aae43a3) feat(openapi): allow optional request body content (#6374) +* [7399fcf7e](https://github.com/api-platform/core/commit/7399fcf7eaf28cd649d137da1fdd54f69093e275) feat(symfony): skip error handler (#6463) +* [74986cb55](https://github.com/api-platform/core/commit/74986cb552182dc645bd1fc967faa0954dd59e0a) feat: inflector as service (#6447) +* [b47edb2a4](https://github.com/api-platform/core/commit/b47edb2a499c34e79c167f963e3a626a3e9d040a) feat(serializer): context IRI in HAL or JsonApi format (#6215) +* [db1241c66](https://github.com/api-platform/core/commit/db1241c66a08d226f57d8d61e0ec519071c6afdb) feat(openapi): make open_api_override_responses act on default 404 response generation (#6551) +* [e09e73efc](https://github.com/api-platform/core/commit/e09e73efc5b4a39ab33d00c5d5422d8d9f7b5e89) feat: remove hydra prefix (#6418) + ## v3.4.0-alpha.5 ### Deprecations: diff --git a/composer.json b/composer.json index cc4ead3ddaf..50fe150e926 100644 --- a/composer.json +++ b/composer.json @@ -25,18 +25,18 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "doctrine/inflector": "^1.0 || ^2.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/container": "^1.0 || ^2.0", "symfony/deprecation-contracts": "^3.1", - "symfony/http-foundation": "^6.4 || ^7.1", - "symfony/http-kernel": "^6.4 || ^7.1", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/http-foundation": "^6.4 || ^7.0", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/translation-contracts": "^3.3", - "symfony/web-link": "^6.4 || ^7.1", + "symfony/web-link": "^6.4 || ^7.0", "willdurand/negotiation": "^3.1" }, "require-dev": { @@ -49,6 +49,7 @@ "api-platform/openapi": "^3.4 || ^4.0", "api-platform/ramsey-uuid": "^3.4 || ^4.0", "api-platform/jsonld": "^3.4 || ^4.0", + "api-platform/json-hal": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", "api-platform/json-api": "^3.3 || ^4.0", "api-platform/http-cache": "^3.4 || ^4.0", @@ -96,36 +97,36 @@ "soyuka/contexts": "^3.3.10", "soyuka/pmu": "^0.0.12", "soyuka/stubs-mongodb": "^1.0", - "symfony/asset": "^6.4 || ^7.1", - "symfony/browser-kit": "^6.4 || ^7.1", - "symfony/cache": "^6.4 || ^7.1", - "symfony/config": "^6.4 || ^7.1", - "symfony/console": "^6.4 || ^7.1", - "symfony/css-selector": "^6.4 || ^7.1", - "symfony/dependency-injection": "^6.4 || ^7.1", - "symfony/doctrine-bridge": "^6.4.2 || ^7.1.2", - "symfony/dom-crawler": "^6.4 || ^7.1", - "symfony/error-handler": "^6.4 || ^7.1", - "symfony/event-dispatcher": "^6.4 || ^7.1", - "symfony/expression-language": "^6.4 || ^7.1", - "symfony/finder": "^6.4 || ^7.1", - "symfony/form": "^6.4 || ^7.1", - "symfony/framework-bundle": "^6.4 || ^7.1", - "symfony/http-client": "^6.4 || ^7.1", - "symfony/intl": "^6.4 || ^7.1", + "symfony/asset": "^6.4 || ^7.0", + "symfony/browser-kit": "^6.4 || ^7.0", + "symfony/cache": "^6.4 || ^7.0", + "symfony/config": "^6.4 || ^7.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/css-selector": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/doctrine-bridge": "^6.4.2 || ^7.0.2", + "symfony/dom-crawler": "^6.4 || ^7.0", + "symfony/error-handler": "^6.4 || ^7.0", + "symfony/event-dispatcher": "^6.4 || ^7.0", + "symfony/expression-language": "^6.4 || ^7.0", + "symfony/finder": "^6.4 || ^7.0", + "symfony/form": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", + "symfony/http-client": "^6.4 || ^7.0", + "symfony/intl": "^6.4 || ^7.0", "symfony/maker-bundle": "^1.24", "symfony/mercure-bundle": "*", - "symfony/messenger": "^6.4 || ^7.1", - "symfony/routing": "^6.4 || ^7.1", - "symfony/security-bundle": "^6.4 || ^7.1", - "symfony/security-core": "^6.4 || ^7.1", - "symfony/stopwatch": "^6.4 || ^7.1", - "symfony/string": "^6.4 || ^7.1", - "symfony/twig-bundle": "^6.4 || ^7.1", - "symfony/uid": "^6.4 || ^7.1", - "symfony/validator": "^6.4 || ^7.1", - "symfony/web-profiler-bundle": "^6.4 || ^7.1", - "symfony/yaml": "^6.4 || ^7.1", + "symfony/messenger": "^6.4 || ^7.0", + "symfony/routing": "^6.4 || ^7.0", + "symfony/security-bundle": "^6.4 || ^7.0", + "symfony/security-core": "^6.4 || ^7.0", + "symfony/stopwatch": "^6.4 || ^7.0", + "symfony/string": "^6.4 || ^7.0", + "symfony/twig-bundle": "^6.4 || ^7.0", + "symfony/uid": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/web-profiler-bundle": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0", "twig/twig": "^1.42.3 || ^2.12 || ^3.0", "webonyx/graphql-php": "^15.0" }, @@ -135,7 +136,7 @@ "doctrine/orm": "<2.14.0", "doctrine/mongodb-odm": "<2.4", "doctrine/persistence": "<1.3", - "symfony/framework-bundle": "6.4.6 || 7.1.6", + "symfony/framework-bundle": "6.4.6 || 7.0.6", "symfony/var-exporter": "<6.1.1", "phpunit/phpunit": "<9.5", "phpspec/prophecy": "<1.15" @@ -190,10 +191,14 @@ "dev-main": "4.0.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" }, "pmu": { "projects": ["./src/*/composer.json", "src/Doctrine/*/composer.json"] + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } } } diff --git a/docs/composer.json b/docs/composer.json index 941dde8f54e..58f45c2fc20 100644 --- a/docs/composer.json +++ b/docs/composer.json @@ -44,5 +44,11 @@ }, "require-dev": { "phpunit/phpunit": "^10" + }, + "extra": { + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" + } } } diff --git a/features/hydra/collection.feature b/features/hydra/collection.feature index 2ba5de7f1b3..0f29bf4c711 100644 --- a/features/hydra/collection.feature +++ b/features/hydra/collection.feature @@ -77,6 +77,7 @@ Feature: Collections support When I send a "GET" request to "/dummies?page=7" Then the response status code should be 200 And the response should be in JSON + And the header "Content-Location" should be equal to "/dummies.jsonld?page=7" And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON should be valid according to this schema: """ diff --git a/features/main/crud.feature b/features/main/crud.feature index 1fc310d63bb..a167ef70e45 100644 --- a/features/main/crud.feature +++ b/features/main/crud.feature @@ -22,7 +22,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/dummies/1" + And the header "Content-Location" should be equal to "/dummies/1.jsonld" And the header "Location" should be equal to "/dummies/1" And the JSON should be equal to: """ @@ -106,6 +106,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + And the header "Content-Location" should be equal to "/dummies.jsonld" And the JSON should be equal to: """ { @@ -513,7 +514,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/dummies/1" + And the header "Content-Location" should be equal to "/dummies/1.jsonld" And the JSON should be equal to: """ { @@ -571,7 +572,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/processor_entities/1" + And the header "Content-Location" should be equal to "/processor_entities/1.jsonld" And the header "Location" should be equal to "/processor_entities/1" And the JSON should be equal to: """ @@ -596,7 +597,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/provider_entities/1" + And the header "Content-Location" should be equal to "/provider_entities/1.jsonld" And the header "Location" should be equal to "/provider_entities/1" And the JSON should be equal to: """ diff --git a/features/main/crud_abstract.feature b/features/main/crud_abstract.feature index 87d6ca2578f..12d99f397eb 100644 --- a/features/main/crud_abstract.feature +++ b/features/main/crud_abstract.feature @@ -16,7 +16,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the header "Location" should be equal to "/concrete_dummies/1" And the JSON should be equal to: """ @@ -92,7 +92,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the JSON should be equal to: """ { @@ -118,7 +118,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the JSON should be equal to: """ { @@ -150,7 +150,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the header "Location" should be equal to "/concrete_dummies/1" And the JSON should be equal to: """ diff --git a/features/main/crud_uri_variables.feature b/features/main/crud_uri_variables.feature index fb86d203961..68aef4a3173 100644 --- a/features/main/crud_uri_variables.feature +++ b/features/main/crud_uri_variables.feature @@ -13,7 +13,7 @@ Feature: Uri Variables Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/companies/1" + And the header "Content-Location" should be equal to "/companies/1.jsonld" And the header "Location" should be equal to "/companies/1" And the JSON should be equal to: """ @@ -51,7 +51,7 @@ Feature: Uri Variables Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/companies/1/employees/1" + And the header "Content-Location" should be equal to "/companies/1/employees/1.jsonld" And the header "Location" should be equal to "/companies/1/employees/1" And the JSON should be equal to: """ diff --git a/features/main/custom_normalized.feature b/features/main/custom_normalized.feature index 67a668fa0f0..7d1a49f3fed 100644 --- a/features/main/custom_normalized.feature +++ b/features/main/custom_normalized.feature @@ -16,7 +16,7 @@ Feature: Using custom normalized entity Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_normalized_dummies/1" + And the header "Content-Location" should be equal to "/custom_normalized_dummies/1.jsonld" And the header "Location" should be equal to "/custom_normalized_dummies/1" And the JSON should be equal to: """ @@ -43,7 +43,7 @@ Feature: Using custom normalized entity Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/json; charset=utf-8" - And the header "Content-Location" should be equal to "/related_normalized_dummies/1" + And the header "Content-Location" should be equal to "/related_normalized_dummies/1.json" And the header "Location" should be equal to "/related_normalized_dummies/1" And the JSON should be equal to: """ @@ -92,7 +92,7 @@ Feature: Using custom normalized entity Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/json; charset=utf-8" - And the header "Content-Location" should be equal to "/related_normalized_dummies/1" + And the header "Content-Location" should be equal to "/related_normalized_dummies/1.json" And the JSON should be equal to: """ { @@ -158,7 +158,7 @@ Feature: Using custom normalized entity Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_normalized_dummies/1" + And the header "Content-Location" should be equal to "/custom_normalized_dummies/1.jsonld" And the JSON should be equal to: """ { @@ -182,7 +182,7 @@ Feature: Using custom normalized entity Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_normalized_dummies/1" + And the header "Content-Location" should be equal to "/custom_normalized_dummies/1.jsonld" And the JSON should be equal to: """ { diff --git a/features/main/custom_writable_identifier.feature b/features/main/custom_writable_identifier.feature index d2878f0afc5..097d253b9ad 100644 --- a/features/main/custom_writable_identifier.feature +++ b/features/main/custom_writable_identifier.feature @@ -16,7 +16,7 @@ Feature: Using custom writable identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/my_slug" + And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/my_slug.jsonld" And the header "Location" should be equal to "/custom_writable_identifier_dummies/my_slug" And the JSON should be equal to: """ @@ -81,7 +81,7 @@ Feature: Using custom writable identifier on resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/slug_modified" + And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/slug_modified.jsonld" And the JSON should be equal to: """ { diff --git a/features/main/operation_resource.feature b/features/main/operation_resource.feature index 070cb5be9fd..b4bd729fcaf 100644 --- a/features/main/operation_resource.feature +++ b/features/main/operation_resource.feature @@ -53,7 +53,7 @@ Feature: Resource operations Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/operation_resources/1" + And the header "Content-Location" should be equal to "/operation_resources/1.jsonld" And the JSON should be equal to: """ { diff --git a/features/main/uuid.feature b/features/main/uuid.feature index d0634b0bc94..a7506a15bec 100644 --- a/features/main/uuid.feature +++ b/features/main/uuid.feature @@ -16,7 +16,7 @@ Feature: Using uuid identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78" + And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78.jsonld" And the header "Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78" Scenario: Get a resource @@ -69,7 +69,7 @@ Feature: Using uuid identifier on resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78" + And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78.jsonld" And the JSON should be equal to: """ { @@ -90,7 +90,7 @@ Feature: Using uuid identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_generated_identifiers/foo" + And the header "Content-Location" should be equal to "/custom_generated_identifiers/foo.jsonld" And the header "Location" should be equal to "/custom_generated_identifiers/foo" And the JSON should be equal to: """ diff --git a/src/Doctrine/Common/State/PersistProcessor.php b/src/Doctrine/Common/State/PersistProcessor.php index 20e93ac4399..abc21bf9e15 100644 --- a/src/Doctrine/Common/State/PersistProcessor.php +++ b/src/Doctrine/Common/State/PersistProcessor.php @@ -48,7 +48,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = // PUT: reset the existing object managed by Doctrine and merge data sent by the user in it // This custom logic is needed because EntityManager::merge() has been deprecated and UPSERT isn't supported: // https://github.com/doctrine/orm/issues/8461#issuecomment-1250233555 - if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? false)) { + if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? true)) { \assert(method_exists($manager, 'getReference')); $newData = $data; $identifiers = array_reverse($uriVariables); diff --git a/src/Doctrine/Common/composer.json b/src/Doctrine/Common/composer.json index 1e9e415cd37..8ecfb1da2a5 100644 --- a/src/Doctrine/Common/composer.json +++ b/src/Doctrine/Common/composer.json @@ -3,10 +3,11 @@ "description": "Common files used by api-platform/doctrine-orm and api-platform/doctrine-odm", "type": "library", "keywords": [ - "DOCTRINE", - "ORM", - "ODM", - "COMMON" + "doctrine", + "orm", + "odm", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -22,7 +23,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "doctrine/collections": "^2.1", @@ -63,7 +64,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Doctrine/Odm/composer.json b/src/Doctrine/Odm/composer.json index 069c5340d8a..59b1988f834 100644 --- a/src/Doctrine/Odm/composer.json +++ b/src/Doctrine/Odm/composer.json @@ -5,7 +5,10 @@ "keywords": [ "Doctrine", "ODM", - "MongoDB" + "MongoDB", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -21,25 +24,25 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "doctrine/mongodb-odm": "^2.2", - "doctrine/mongodb-odm-bundle": "^5.0", - "symfony/property-info": "^6.4 || ^7.1" + "doctrine/mongodb-odm": "^2.6", + "doctrine/mongodb-odm-bundle": "^4.0 || ^5.0", + "symfony/property-info": "^6.4 || ^7.0" }, "require-dev": { "doctrine/doctrine-bundle": "^2.11", "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", - "symfony/cache": "^6.4 || ^7.1", - "symfony/framework-bundle": "^6.4 || ^7.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", "symfony/property-access": "^6.4 || ^7.0", "symfony/serializer": "^6.4 || ^7.0", - "symfony/uid": "^6.4 || ^7.1", - "symfony/validator": "^6.4 || ^7.1", - "symfony/yaml": "^6.4 || ^7.1" + "symfony/uid": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "autoload": { "psr-4": { @@ -61,7 +64,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Doctrine/Orm/composer.json b/src/Doctrine/Orm/composer.json index f9b962226ee..9bc1aa9ecf5 100644 --- a/src/Doctrine/Orm/composer.json +++ b/src/Doctrine/Orm/composer.json @@ -4,7 +4,10 @@ "type": "library", "keywords": [ "Doctrine", - "ORM" + "ORM", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -20,26 +23,26 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "doctrine/doctrine-bundle": "^2.11", "doctrine/orm": "^2.17 || ^3.0", - "symfony/property-info": "^6.4 || ^7.1" + "symfony/property-info": "^6.4 || ^7.0" }, "require-dev": { + "doctrine/doctrine-bundle": "^2.11", "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", "ramsey/uuid": "^4.0", "ramsey/uuid-doctrine": "^2.0", - "symfony/cache": "^6.4 || ^7.1", - "symfony/framework-bundle": "^6.4 || ^7.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", "symfony/property-access": "^6.4 || ^7.0", "symfony/serializer": "^6.4 || ^7.0", - "symfony/uid": "^6.4 || ^7.1", - "symfony/validator": "^6.4 || ^7.1", - "symfony/yaml": "^6.4 || ^7.1" + "symfony/uid": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "autoload": { "psr-4": { @@ -61,7 +64,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Documentation/composer.json b/src/Documentation/composer.json index 9f07e064525..71c570c8c48 100644 --- a/src/Documentation/composer.json +++ b/src/Documentation/composer.json @@ -20,6 +20,7 @@ } ], "require": { + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0" }, "extra": { @@ -28,7 +29,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "require-dev": { diff --git a/src/Elasticsearch/composer.json b/src/Elasticsearch/composer.json index ea99dbb472d..7d4961afef3 100644 --- a/src/Elasticsearch/composer.json +++ b/src/Elasticsearch/composer.json @@ -3,8 +3,10 @@ "description": "API Platform Elasticsearch bridge", "type": "library", "keywords": [ - "Filter", - "Elasticsearch", + "REST", + "API", + "filter", + "elasticsearch", "search" ], "homepage": "https://api-platform.com", @@ -21,16 +23,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "elasticsearch/elasticsearch": "^8.9", + "elasticsearch/elasticsearch": "^8.4", "symfony/cache": "^6.4 || ^7.0", "symfony/console": "^6.4 || ^7.0", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/uid": "^6.4 || ^7.0" }, "require-dev": { @@ -62,7 +64,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/GraphQl/composer.json b/src/GraphQl/composer.json index 2abccd95161..f676ddae850 100644 --- a/src/GraphQl/composer.json +++ b/src/GraphQl/composer.json @@ -20,21 +20,21 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", - "webonyx/graphql-php": "^14.0 || ^15.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", + "webonyx/graphql-php": "^15.0", "willdurand/negotiation": "^3.1" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", "api-platform/validator": "^3.4 || ^4.0", - "twig/twig": "^3.7", + "twig/twig": "^1.42.3 || ^2.12 || ^3.0", "symfony/mercure-bundle": "*", - "symfony/routing": "^6.4 || ^7.1", + "symfony/routing": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2" }, "autoload": { @@ -67,7 +67,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Hal/composer.json b/src/Hal/composer.json index df27693cd26..2da21682e20 100644 --- a/src/Hal/composer.json +++ b/src/Hal/composer.json @@ -50,7 +50,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/HttpCache/composer.json b/src/HttpCache/composer.json index 80e65df7201..6b42bcabde8 100644 --- a/src/HttpCache/composer.json +++ b/src/HttpCache/composer.json @@ -3,8 +3,10 @@ "description": "API Platform HttpCache component", "type": "library", "keywords": [ - "Cache", - "Http" + "REST", + "API", + "cache", + "HTTP" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -20,16 +22,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "symfony/http-foundation": "^6.4 || ^7.1" + "symfony/http-foundation": "^6.4 || ^7.0" }, "require-dev": { "guzzlehttp/guzzle": "^6.0 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.1", + "symfony/dependency-injection": "^6.4 || ^7.0", "phpspec/prophecy-phpunit": "^2.2", - "symfony/http-client": "^6.4 || ^7.1", + "symfony/http-client": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2" }, "autoload": { @@ -56,7 +58,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Hydra/composer.json b/src/Hydra/composer.json index 9e1518af2d7..eece6d0e331 100644 --- a/src/Hydra/composer.json +++ b/src/Hydra/composer.json @@ -8,10 +8,7 @@ "API", "JSON-LD", "Hydra", - "JSONAPI", - "OpenAPI", - "HAL", - "Swagger" + "JSONAPI" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -27,14 +24,14 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/state": "^3.4 || ^4.0", "api-platform/documentation": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/jsonld": "^3.4 || ^4.0", "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", - "symfony/web-link": "^6.4 || ^7.1" + "symfony/web-link": "^6.4 || ^7.0" }, "require-dev": { "api-platform/doctrine-odm": "^3.4 || ^4.0", @@ -68,7 +65,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonApi/composer.json b/src/JsonApi/composer.json index 4d690461c3a..60e5fd25bef 100644 --- a/src/JsonApi/composer.json +++ b/src/JsonApi/composer.json @@ -4,14 +4,8 @@ "type": "library", "keywords": [ "REST", - "GraphQL", "API", - "JSON-LD", - "Hydra", - "JSONAPI", - "OpenAPI", - "HAL", - "Swagger" + "JSONAPI" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -27,14 +21,14 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/documentation": "^3.4 || ^4.0", "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "symfony/error-handler": "^6.4 || ^7.1", - "symfony/http-foundation": "^6.4 || ^7.1" + "symfony/error-handler": "^6.4 || ^7.0", + "symfony/http-foundation": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy": "^1.19", @@ -65,7 +59,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php index 6be47cd3a27..8d6e2aefb27 100644 --- a/src/JsonLd/Serializer/ItemNormalizer.php +++ b/src/JsonLd/Serializer/ItemNormalizer.php @@ -175,7 +175,7 @@ public function denormalize(mixed $data, string $class, ?string $format = null, } catch (ItemNotFoundException $e) { $operation = $context['operation'] ?? null; - if (!('PUT' === $operation?->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? false))) { + if (!('PUT' === $operation?->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? true))) { throw $e; } } diff --git a/src/JsonLd/composer.json b/src/JsonLd/composer.json index 57ff30a042f..d6566f62a41 100644 --- a/src/JsonLd/composer.json +++ b/src/JsonLd/composer.json @@ -7,11 +7,7 @@ "GraphQL", "API", "JSON-LD", - "Hydra", - "JSONAPI", - "OpenAPI", - "HAL", - "Swagger" + "Hydra" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -27,7 +23,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/state": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0" @@ -56,7 +52,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonSchema/composer.json b/src/JsonSchema/composer.json index 3c7704a8530..0695729b5eb 100644 --- a/src/JsonSchema/composer.json +++ b/src/JsonSchema/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "symfony/console": "^6.4 || ^7.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/uid": "^6.4 || ^7.0" }, "require-dev": { @@ -59,7 +59,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Laravel/composer.json b/src/Laravel/composer.json index ce2378f9c0a..eeab8e2bd2c 100644 --- a/src/Laravel/composer.json +++ b/src/Laravel/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/documentation": "^4.0", "api-platform/hydra": "^4.0", "api-platform/json-hal": "^4.0", @@ -85,6 +85,13 @@ "branch-alias": { "dev-main": "4.0.x-dev", "dev-3.4": "3.4.x-dev" + }, + "symfony": { + "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "autoload-dev": { diff --git a/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php index 9f7411b1c3b..4c47bf90d3e 100644 --- a/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php @@ -38,7 +38,7 @@ public function create(string $resourceClass): ResourceMetadataCollection foreach ($resourceMetadataCollection as $i => $resourceMetadata) { $newOperations = []; - foreach ($resourceMetadata->getOperations() as $operationName => $operation) { + foreach ($resourceMetadata->getOperations() ?? [] as $operationName => $operation) { $newOperations[$operationName] = $operation; if (null !== $operation->getProvider()) { @@ -49,7 +49,7 @@ public function create(string $resourceClass): ResourceMetadataCollection } $newGraphQlOperations = []; - foreach ($resourceMetadata->getGraphQlOperations() as $operationName => $operation) { + foreach ($resourceMetadata->getGraphQlOperations() ?? [] as $operationName => $operation) { $newGraphQlOperations[$operationName] = $operation; if (null !== $operation->getProvider()) { diff --git a/src/Metadata/composer.json b/src/Metadata/composer.json index fb408810539..af7647e2020 100644 --- a/src/Metadata/composer.json +++ b/src/Metadata/composer.json @@ -27,11 +27,11 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "doctrine/inflector": "^1.0 || ^2.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/property-info": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.0", "symfony/string": "^6.4 || ^7.0", "symfony/type-info": "^7.1" }, @@ -39,14 +39,14 @@ "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/openapi": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpdoc-parser": "^1.16", + "phpspec/prophecy-phpunit": "^2.2", + "phpstan/phpdoc-parser": "^1.13", "phpunit/phpunit": "^11.2", - "symfony/config": "^6.4 || ^7.1", - "symfony/routing": "^6.4 || ^7.1", + "symfony/config": "^6.4 || ^7.0", + "symfony/routing": "^6.4 || ^7.0", "symfony/var-dumper": "^6.4 || ^7.0", "symfony/web-link": "^6.4 || ^7.0", - "symfony/yaml": "^6.4 || ^7.1" + "symfony/yaml": "^6.4 || ^7.0" }, "suggest": { "phpstan/phpdoc-parser": "For PHP documentation support.", @@ -77,7 +77,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/OpenApi/composer.json b/src/OpenApi/composer.json index 3ca9cf67951..3576b855da7 100644 --- a/src/OpenApi/composer.json +++ b/src/OpenApi/composer.json @@ -7,11 +7,11 @@ "GraphQL", "API", "JSON-LD", - "Hydra", + "hydra", "JSONAPI", "OpenAPI", "HAL", - "Swagger" + "swagger" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -27,16 +27,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "symfony/console": "^6.4 || ^7.0", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1" + "symfony/property-access": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0" }, "require-dev": { - "phpspec/prophecy-phpunit": "^2.0", + "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/doctrine-orm": "^3.4 || ^4.0", @@ -66,7 +66,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/RamseyUuid/composer.json b/src/RamseyUuid/composer.json index ba67fa76560..5d31ceb4fdf 100644 --- a/src/RamseyUuid/composer.json +++ b/src/RamseyUuid/composer.json @@ -4,7 +4,9 @@ "type": "library", "keywords": [ "UUID", - "API" + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -20,9 +22,9 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", - "symfony/serializer": "^6.4 || ^7.1" + "symfony/serializer": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", @@ -51,7 +53,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Serializer/SerializerFilterContextBuilder.php b/src/Serializer/SerializerFilterContextBuilder.php index 7c64b9947b9..9073bbe1136 100644 --- a/src/Serializer/SerializerFilterContextBuilder.php +++ b/src/Serializer/SerializerFilterContextBuilder.php @@ -17,6 +17,7 @@ use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Metadata\Util\AttributesExtractor; use ApiPlatform\Serializer\Filter\FilterInterface; +use ApiPlatform\State\SerializerContextBuilderInterface as StateSerializerContextBuilderInterface; use Psr\Container\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -27,7 +28,7 @@ */ final class SerializerFilterContextBuilder implements SerializerContextBuilderInterface { - public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ContainerInterface $filterLocator, private readonly SerializerContextBuilderInterface $decorated) + public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ContainerInterface $filterLocator, private readonly SerializerContextBuilderInterface|StateSerializerContextBuilderInterface $decorated) { } diff --git a/src/Serializer/composer.json b/src/Serializer/composer.json index f3fe2df7af9..361fb2eea34 100644 --- a/src/Serializer/composer.json +++ b/src/Serializer/composer.json @@ -3,8 +3,10 @@ "description": "API Platform core Serializer", "type": "library", "keywords": [ - "Serializer", - "API" + "serializer", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -20,26 +22,26 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "doctrine/collections": "^2.1", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/validator": "^6.4 || ^7.0" }, "require-dev": { - "api-platform/openapi": "^3.2 || ^4.0", - "api-platform/json-schema": "^3.2 || ^4.0", + "api-platform/openapi": "^3.4 || ^4.0", + "api-platform/json-schema": "^3.4 || ^4.0", "phpspec/prophecy-phpunit": "^2.2", "symfony/mercure-bundle": "*", "symfony/var-dumper": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2", - "symfony/yaml": "^6.4 || ^7.1", - "api-platform/doctrine-odm": "^3.2 || ^4.0", - "api-platform/doctrine-orm": "^3.2 || ^4.0", - "api-platform/doctrine-common": "^3.2 || ^4.0" + "symfony/yaml": "^6.4 || ^7.0", + "api-platform/doctrine-odm": "^3.4 || ^4.0", + "api-platform/doctrine-orm": "^3.4 || ^4.0", + "api-platform/doctrine-common": "^3.4 || ^4.0" }, "suggest": { "api-platform/doctrine-orm": "To support doctrine ORM state options." @@ -68,7 +70,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/State/Pagination/TraversablePaginator.php b/src/State/Pagination/TraversablePaginator.php index 8a4624eed5f..abad26afe72 100644 --- a/src/State/Pagination/TraversablePaginator.php +++ b/src/State/Pagination/TraversablePaginator.php @@ -68,6 +68,10 @@ public function count(): int return (int) ceil($this->totalItems); } + if ($this->totalItems === $this->itemsPerPage) { + return (int) ceil($this->totalItems); + } + return $this->totalItems % $this->itemsPerPage; } diff --git a/src/State/Processor/RespondProcessor.php b/src/State/Processor/RespondProcessor.php index 15e9c030327..64b07308d3f 100644 --- a/src/State/Processor/RespondProcessor.php +++ b/src/State/Processor/RespondProcessor.php @@ -14,6 +14,9 @@ namespace ApiPlatform\State\Processor; use ApiPlatform\Metadata\Exception\HttpExceptionInterface; +use ApiPlatform\Metadata\Exception\InvalidArgumentException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\RuntimeException; use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\IriConverterInterface; use ApiPlatform\Metadata\Operation; @@ -92,18 +95,23 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class']; $hasData = !$hasOutput ? false : ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData))); - if ($hasData && $this->iriConverter) { + if ($hasData) { + $isAlternateResourceMetadata = $operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false; + $canonicalUriTemplate = $operation->getExtraProperties()['canonical_uri_template'] ?? null; + if ( !isset($headers['Location']) && 300 <= $status && $status < 400 - && (($operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false) || ($operation->getExtraProperties()['canonical_uri_template'] ?? null)) + && ($isAlternateResourceMetadata || $canonicalUriTemplate) ) { $canonicalOperation = $operation; - if ($this->operationMetadataFactory && null !== ($operation->getExtraProperties()['canonical_uri_template'] ?? null)) { - $canonicalOperation = $this->operationMetadataFactory->create($operation->getExtraProperties()['canonical_uri_template'], $context); + if ($this->operationMetadataFactory && null !== $canonicalUriTemplate) { + $canonicalOperation = $this->operationMetadataFactory->create($canonicalUriTemplate, $context); } - $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation); + if ($this->iriConverter) { + $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation); + } } elseif ('PUT' === $method && !$request->attributes->get('previous_data') && null === $status && ($operation instanceof Put && ($operation->getAllowCreate() ?? false))) { $status = 201; } @@ -111,12 +119,28 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $status ??= self::METHOD_TO_CODE[$method] ?? 200; - if ($hasData && $this->iriConverter && !isset($headers['Content-Location'])) { - $iri = $this->iriConverter->getIriFromResource($originalData); - $headers['Content-Location'] = $iri; + $requestParts = parse_url($request->getRequestUri()); + if ($this->iriConverter && !isset($headers['Content-Location'])) { + try { + $iri = null; + if ($hasData) { + $iri = $this->iriConverter->getIriFromResource($originalData); + } elseif ($operation->getClass()) { + $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation); + } - if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) { - $headers['Location'] = $iri; + if ($iri) { + $location = \sprintf('%s.%s', $iri, $request->getRequestFormat()); + if (isset($requestParts['query'])) { + $location .= '?'.$requestParts['query']; + } + + $headers['Content-Location'] = $location; + if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) { + $headers['Location'] = $iri; + } + } + } catch (InvalidArgumentException|ItemNotFoundException|RuntimeException) { } } diff --git a/src/State/Provider/DeserializeProvider.php b/src/State/Provider/DeserializeProvider.php index 1e8a68310bb..db735e19961 100644 --- a/src/State/Provider/DeserializeProvider.php +++ b/src/State/Provider/DeserializeProvider.php @@ -83,7 +83,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c && ( 'POST' === $method || 'PATCH' === $method - || ('PUT' === $method && !($operation->getExtraProperties()['standard_put'] ?? false)) + || ('PUT' === $method && !($operation->getExtraProperties()['standard_put'] ?? true)) ) ) { $serializerContext[AbstractNormalizer::OBJECT_TO_POPULATE] = $data; diff --git a/src/State/composer.json b/src/State/composer.json index b6968b9b394..72664396851 100644 --- a/src/State/composer.json +++ b/src/State/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "psr/container": "^1.0 || ^2.0", "symfony/http-kernel": "^6.4 || ^7.0" @@ -64,7 +64,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "suggest": { diff --git a/src/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/DependencyInjection/Configuration.php index e073b7c1844..f963fa8c9db 100644 --- a/src/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/DependencyInjection/Configuration.php @@ -164,6 +164,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addExceptionToStatusSection($rootNode); $this->addFormatSection($rootNode, 'formats', [ + 'jsonld' => ['mime_types' => ['application/ld+json']] ]); $this->addFormatSection($rootNode, 'patch_formats', [ 'json' => ['mime_types' => ['application/merge-patch+json']], diff --git a/src/Symfony/composer.json b/src/Symfony/composer.json index 66063a205d2..a251115188b 100644 --- a/src/Symfony/composer.json +++ b/src/Symfony/composer.json @@ -1,7 +1,7 @@ { "name": "api-platform/symfony", "description": "Symfony API Platform integration", - "type": "library", + "type": "symfony-bundle", "keywords": [ "Symfony", "REST", @@ -28,7 +28,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/documentation": "^3.4 || ^4.0", "api-platform/http-cache": "^3.4 || ^4.0", "api-platform/json-schema": "^3.4 || ^4.0", @@ -39,26 +39,26 @@ "api-platform/state": "^3.4 || ^4.0", "api-platform/validator": "^3.4 || ^4.0", "api-platform/openapi": "^3.4 || ^4.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/security-core": "^6.4 || ^7.0", - "willdurand/negotiation": "^3.0" + "willdurand/negotiation": "^3.1" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", - "symfony/routing": "^6.4 || ^7.1", + "symfony/routing": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2", - "symfony/validator": "^6.4 || ^7.1", + "symfony/validator": "^6.4 || ^7.0", "symfony/mercure-bundle": "*", - "webonyx/graphql-php": "^14.0 || ^15.0", + "webonyx/graphql-php": "^15.0", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/elasticsearch": "^3.4 || ^4.0", "api-platform/graphql": "^3.4 || ^4.0", "api-platform/doctrine-orm": "^3.4 || ^4.0", "api-platform/doctrine-odm": "^3.4 || ^4.0", "api-platform/parameter-validator": "^3.1", - "symfony/expression-language": "^6.4 || ^7.1" + "symfony/expression-language": "^6.4 || ^7.0" }, "suggest": { "api-platform/doctrine-orm": "To support Doctrine ORM.", @@ -105,7 +105,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Validator/composer.json b/src/Validator/composer.json index 3b76f5b0a96..a09c071ff18 100644 --- a/src/Validator/composer.json +++ b/src/Validator/composer.json @@ -3,8 +3,10 @@ "description": "API Platform validator component", "type": "library", "keywords": [ - "Validator", - "API" + "validator", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -20,15 +22,15 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", - "symfony/web-link": "^6.4 || ^7.1" + "symfony/web-link": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", "symfony/serializer": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2", - "symfony/validator": "^6.4 || ^7.1", + "symfony/validator": "^6.4 || ^7.0", "symfony/http-kernel": "^6.4 || ^7.0" }, "autoload": { @@ -52,7 +54,11 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/tests/Fixtures/app/AppKernel.php b/tests/Fixtures/app/AppKernel.php index c69826188b5..7d55824f4de 100644 --- a/tests/Fixtures/app/AppKernel.php +++ b/tests/Fixtures/app/AppKernel.php @@ -265,9 +265,6 @@ class_exists(NativePasswordHasher::class) ? 'password_hashers' : 'encoders' => [ 'public' => true, ], 'normalization_context' => ['skip_null_values' => false], - 'extra_properties' => [ - 'standard_put' => true, - ], 'operations' => [ Get::class, GetCollection::class, diff --git a/tests/State/Pagination/TraversablePaginatorTest.php b/tests/State/Pagination/TraversablePaginatorTest.php index 40518ec8a1e..66c420ce5cf 100644 --- a/tests/State/Pagination/TraversablePaginatorTest.php +++ b/tests/State/Pagination/TraversablePaginatorTest.php @@ -26,7 +26,7 @@ public function testInitialize( float $totalItems, float $lastPage, int $currentItems, - bool $hasNextPage, + bool $hasNextPage = false, ): void { $traversable = new \ArrayIterator($results); @@ -46,10 +46,12 @@ public static function initializeProvider(): array { return [ 'First of three pages of 3 items each' => [[0, 1, 2, 3, 4, 5, 6], 1, 3, 7, 3, 3, true], - 'Second of two pages of 3 items for the first page and 2 for the second' => [[0, 1, 2, 3, 4], 2, 3, 5, 2, 2, false], - 'Empty results' => [[], 1, 2, 0, 1, 0, false], - '0 items per page' => [[0, 1, 2, 3], 1, 0, 4, 1, 4, false], - 'Total items less than items per page' => [[0, 1, 2], 1, 4, 3, 1, 3, false], + 'Second of two pages of 3 items for the first page and 2 for the second' => [[0, 1, 2, 3, 4], 2, 3, 5, 2, 2], + 'Empty results' => [[], 1, 2, 0, 1, 0], + '0 items per page' => [[0, 1, 2, 3], 1, 0, 4, 1, 4], + 'Total items less than items per page' => [[0, 1, 2], 1, 4, 3, 1, 3], + 'Only one result' => [[0], 1, 1, 1, 1, 1], + 'Same result number than total page' => [[0, 2, 3], 1, 3, 3, 1, 3], ]; } } diff --git a/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index c2f322e5f12..84a1ed5066c 100644 --- a/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -75,7 +75,9 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm 'description' => 'description', 'version' => '1.0.0', 'show_webby' => true, - 'formats' => [], + 'formats' => [ + 'jsonld' => ['mime_types' => ['application/ld+json']], + ], 'docs_formats' => [ 'jsonopenapi' => ['mime_types' => ['application/vnd.openapi+json']], 'yamlopenapi' => ['mime_types' => ['application/vnd.openapi+yaml']],