diff --git a/CODING-CHALLENGE-12.md b/CODING-CHALLENGE-12.md new file mode 100644 index 0000000..8761ab7 --- /dev/null +++ b/CODING-CHALLENGE-12.md @@ -0,0 +1,7 @@ +# RESTful Webservices in Symfony + +## Coding Challenge 12 - Versioning + +### Tasks + +Let's see how we can deprecate a property or sunset an API endpoint. diff --git a/composer.json b/composer.json index 8900a78..3e24eef 100644 --- a/composer.json +++ b/composer.json @@ -11,10 +11,12 @@ "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.16", "lexik/jwt-authentication-bundle": "^2.19", + "nelmio/api-doc-bundle": "^4.12", "phpdocumentor/reflection-docblock": "^5.3", "phpstan/phpdoc-parser": "^1.24", "ramsey/uuid": "*", "ramsey/uuid-doctrine": "^2.0", + "symfony/asset": "6.3.*", "symfony/console": "6.3.*", "symfony/dotenv": "6.3.*", "symfony/expression-language": "6.3.*", @@ -25,8 +27,11 @@ "symfony/runtime": "6.3.*", "symfony/security-bundle": "6.3.*", "symfony/serializer": "6.3.*", + "symfony/twig-bundle": "6.3.*", "symfony/validator": "6.3.*", "symfony/yaml": "6.3.*", + "twig/extra-bundle": "^2.12|^3.0", + "twig/twig": "^2.12|^3.0", "webmozart/assert": "^1.11", "willdurand/negotiation": "^3.1" }, diff --git a/composer.lock b/composer.lock index f332e71..210c9ce 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ac54d56370ac828b2202fa578f49ea22", + "content-hash": "9c14bd1feb8cfe201a8f861708399599", "packages": [ { "name": "brick/math", @@ -61,6 +61,82 @@ ], "time": "2023-01-15T23:15:59+00:00" }, + { + "name": "doctrine/annotations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" + }, + "time": "2023-02-02T22:02:53+00:00" + }, { "name": "doctrine/cache", "version": "2.2.0", @@ -1700,6 +1776,118 @@ }, "time": "2016-12-05T07:27:31+00:00" }, + { + "name": "nelmio/api-doc-bundle", + "version": "v4.12.0", + "source": { + "type": "git", + "url": "https://github.com/nelmio/NelmioApiDocBundle.git", + "reference": "b9fc542143a4c38dc1a302b798a1caeb7d33484f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/b9fc542143a4c38dc1a302b798a1caeb7d33484f", + "reference": "b9fc542143a4c38dc1a302b798a1caeb7d33484f", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^2.0", + "ext-json": "*", + "php": ">=7.2", + "phpdocumentor/reflection-docblock": "^3.1|^4.0|^5.0", + "psr/cache": "^1.0|^2.0|^3.0", + "psr/container": "^1.0|^2.0", + "psr/log": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/framework-bundle": "^5.4.24|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/options-resolver": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "zircote/swagger-php": "^4.2.15" + }, + "conflict": { + "symfony/framework-bundle": "4.2.7" + }, + "require-dev": { + "api-platform/core": "^2.7.0|^3@dev", + "composer/package-versions-deprecated": "1.11.99.1", + "friendsofsymfony/rest-bundle": "^2.8|^3.0", + "jms/serializer": "^1.14|^3.0", + "jms/serializer-bundle": "^2.3|^3.0|^4.0|^5.0@beta", + "sensio/framework-extra-bundle": "^5.4|^6.0", + "symfony/asset": "^5.4|^6.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/cache": "^5.4|^6.0", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/dom-crawler": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4.23", + "symfony/property-access": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/templating": "^5.4|^6.0", + "symfony/twig-bundle": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0", + "willdurand/hateoas-bundle": "^1.0|^2.0" + }, + "suggest": { + "api-platform/core": "For using an API oriented framework.", + "friendsofsymfony/rest-bundle": "For using the parameters annotations.", + "jms/serializer-bundle": "For describing your models.", + "symfony/asset": "For using the Swagger UI.", + "symfony/cache": "For using a PSR-6 compatible cache implementation with the API doc generator.", + "symfony/form": "For describing your form type models.", + "symfony/monolog-bundle": "For using a PSR-3 compatible logger implementation with the API PHP describer.", + "symfony/serializer": "For describing your models.", + "symfony/twig-bundle": "For using the Swagger UI.", + "symfony/validator": "For describing the validation constraints in your models.", + "willdurand/hateoas-bundle": "For extracting HATEOAS metadata." + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nelmio\\ApiDocBundle\\": "" + }, + "exclude-from-classmap": [ + "Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nelmio", + "homepage": "http://nelm.io" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/nelmio/NelmioApiDocBundle/contributors" + } + ], + "description": "Generates documentation for your REST API from annotations", + "keywords": [ + "api", + "doc", + "documentation", + "rest" + ], + "support": { + "issues": "https://github.com/nelmio/NelmioApiDocBundle/issues", + "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.12.0" + }, + "time": "2023-06-16T10:39:21+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -2433,18 +2621,87 @@ ], "time": "2022-12-20T23:38:28+00:00" }, + { + "name": "symfony/asset", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset.git", + "reference": "b77a4cc8e266b7e0db688de740f9ee7253aa411c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset/zipball/b77a4cc8e266b7e0db688de740f9ee7253aa411c", + "reference": "b77a4cc8e266b7e0db688de740f9ee7253aa411c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "conflict": { + "symfony/http-foundation": "<5.4" + }, + "require-dev": { + "symfony/http-client": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Asset\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/asset/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T14:41:17+00:00" + }, { "name": "symfony/cache", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "e60d00b4f633efa4c1ef54e77c12762d9073e7b3" + "reference": "6c1a3ea078c4d88ee892530945df63a87981b2da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/e60d00b4f633efa4c1ef54e77c12762d9073e7b3", - "reference": "e60d00b4f633efa4c1ef54e77c12762d9073e7b3", + "url": "https://api.github.com/repos/symfony/cache/zipball/6c1a3ea078c4d88ee892530945df63a87981b2da", + "reference": "6c1a3ea078c4d88ee892530945df63a87981b2da", "shasum": "" }, "require": { @@ -2511,7 +2768,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.3.4" + "source": "https://github.com/symfony/cache/tree/v6.3.5" }, "funding": [ { @@ -2527,7 +2784,7 @@ "type": "tidelift" } ], - "time": "2023-08-05T09:10:27+00:00" + "time": "2023-09-26T15:48:55+00:00" }, { "name": "symfony/cache-contracts", @@ -2845,16 +3102,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "68a5a9570806a087982f383f6109c5e925892a49" + "reference": "2ed62b3bf98346e1f45529a7b6be2196739bb993" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/68a5a9570806a087982f383f6109c5e925892a49", - "reference": "68a5a9570806a087982f383f6109c5e925892a49", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2ed62b3bf98346e1f45529a7b6be2196739bb993", + "reference": "2ed62b3bf98346e1f45529a7b6be2196739bb993", "shasum": "" }, "require": { @@ -2906,7 +3163,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.3.4" + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.5" }, "funding": [ { @@ -2922,7 +3179,7 @@ "type": "tidelift" } ], - "time": "2023-08-16T17:55:17+00:00" + "time": "2023-09-25T16:46:40+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2993,16 +3250,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "589eeeb93669739ec1d8bd4593e4972d94e0981d" + "reference": "9977eb1adf999ceded213e88c1ac6dff7a1a0306" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/589eeeb93669739ec1d8bd4593e4972d94e0981d", - "reference": "589eeeb93669739ec1d8bd4593e4972d94e0981d", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/9977eb1adf999ceded213e88c1ac6dff7a1a0306", + "reference": "9977eb1adf999ceded213e88c1ac6dff7a1a0306", "shasum": "" }, "require": { @@ -3083,7 +3340,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v6.3.4" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.3.5" }, "funding": [ { @@ -3099,7 +3356,7 @@ "type": "tidelift" } ], - "time": "2023-08-08T10:40:25+00:00" + "time": "2023-09-29T16:16:03+00:00" }, { "name": "symfony/dotenv", @@ -3177,16 +3434,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.2", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a" + "reference": "1f69476b64fb47105c06beef757766c376b548c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/85fd65ed295c4078367c784e8a5a6cee30348b7a", - "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/1f69476b64fb47105c06beef757766c376b548c4", + "reference": "1f69476b64fb47105c06beef757766c376b548c4", "shasum": "" }, "require": { @@ -3231,7 +3488,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.3.2" + "source": "https://github.com/symfony/error-handler/tree/v6.3.5" }, "funding": [ { @@ -3247,7 +3504,7 @@ "type": "tidelift" } ], - "time": "2023-07-16T17:05:46+00:00" + "time": "2023-09-12T06:57:20+00:00" }, { "name": "symfony/event-dispatcher", @@ -3534,16 +3791,16 @@ }, { "name": "symfony/finder", - "version": "v6.3.3", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e" + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9915db259f67d21eefee768c1abcf1cc61b1fc9e", - "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", "shasum": "" }, "require": { @@ -3578,7 +3835,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.3" + "source": "https://github.com/symfony/finder/tree/v6.3.5" }, "funding": [ { @@ -3594,7 +3851,7 @@ "type": "tidelift" } ], - "time": "2023-07-31T08:31:44+00:00" + "time": "2023-09-26T12:56:25+00:00" }, { "name": "symfony/flex", @@ -3663,16 +3920,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "f822f54ff05cd88878910b4559f66c12176d952c" + "reference": "567cafcfc08e3076b47290a7558b0ca17a98b0ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/f822f54ff05cd88878910b4559f66c12176d952c", - "reference": "f822f54ff05cd88878910b4559f66c12176d952c", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/567cafcfc08e3076b47290a7558b0ca17a98b0ce", + "reference": "567cafcfc08e3076b47290a7558b0ca17a98b0ce", "shasum": "" }, "require": { @@ -3787,7 +4044,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.3.4" + "source": "https://github.com/symfony/framework-bundle/tree/v6.3.5" }, "funding": [ { @@ -3803,20 +4060,20 @@ "type": "tidelift" } ], - "time": "2023-08-16T18:04:38+00:00" + "time": "2023-09-29T10:45:15+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cac1556fdfdf6719668181974104e6fcfa60e844" + "reference": "b50f5e281d722cb0f4c296f908bacc3e2b721957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cac1556fdfdf6719668181974104e6fcfa60e844", - "reference": "cac1556fdfdf6719668181974104e6fcfa60e844", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b50f5e281d722cb0f4c296f908bacc3e2b721957", + "reference": "b50f5e281d722cb0f4c296f908bacc3e2b721957", "shasum": "" }, "require": { @@ -3864,7 +4121,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.4" + "source": "https://github.com/symfony/http-foundation/tree/v6.3.5" }, "funding": [ { @@ -3880,20 +4137,20 @@ "type": "tidelift" } ], - "time": "2023-08-22T08:20:46+00:00" + "time": "2023-09-04T21:33:54+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb" + "reference": "9f991a964368bee8d883e8d57ced4fe9fff04dfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", - "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f991a964368bee8d883e8d57ced4fe9fff04dfc", + "reference": "9f991a964368bee8d883e8d57ced4fe9fff04dfc", "shasum": "" }, "require": { @@ -3977,7 +4234,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.3.4" + "source": "https://github.com/symfony/http-kernel/tree/v6.3.5" }, "funding": [ { @@ -3993,36 +4250,30 @@ "type": "tidelift" } ], - "time": "2023-08-26T13:54:49+00:00" + "time": "2023-09-30T06:37:04+00:00" }, { - "name": "symfony/password-hasher", - "version": "v6.3.5", + "name": "symfony/options-resolver", + "version": "v6.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/password-hasher.git", - "reference": "278d3a49715073879f75e372ad80b8cfeca949d3" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/278d3a49715073879f75e372ad80b8cfeca949d3", - "reference": "278d3a49715073879f75e372ad80b8cfeca949d3", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "conflict": { - "symfony/security-core": "<5.4" - }, - "require-dev": { - "symfony/console": "^5.4|^6.0", - "symfony/security-core": "^5.4|^6.0" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\PasswordHasher\\": "" + "Symfony\\Component\\OptionsResolver\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -4034,22 +4285,23 @@ ], "authors": [ { - "name": "Robin Chalas", - "email": "robin.chalas@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides password hashing utilities", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", "keywords": [ - "hashing", - "password" + "config", + "configuration", + "options" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v6.3.5" + "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" }, "funding": [ { @@ -4065,27 +4317,99 @@ "type": "tidelift" } ], - "time": "2023-09-25T17:05:16+00:00" + "time": "2023-05-12T14:21:09+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "name": "symfony/password-hasher", + "version": "v6.3.5", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "url": "https://github.com/symfony/password-hasher.git", + "reference": "278d3a49715073879f75e372ad80b8cfeca949d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/278d3a49715073879f75e372ad80b8cfeca949d3", + "reference": "278d3a49715073879f75e372ad80b8cfeca949d3", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, - "suggest": { - "ext-intl": "For best performance" + "conflict": { + "symfony/security-core": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/security-core": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PasswordHasher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Chalas", + "email": "robin.chalas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides password hashing utilities", + "homepage": "https://symfony.com", + "keywords": [ + "hashing", + "password" + ], + "support": { + "source": "https://github.com/symfony/password-hasher/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-25T17:05:16+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "875e90aeea2777b6f135677f618529449334a612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { @@ -4625,16 +4949,16 @@ }, { "name": "symfony/routing", - "version": "v6.3.3", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a" + "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/e7243039ab663822ff134fbc46099b5fdfa16f6a", - "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a", + "url": "https://api.github.com/repos/symfony/routing/zipball/82616e59acd3e3d9c916bba798326cb7796d7d31", + "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31", "shasum": "" }, "require": { @@ -4688,7 +5012,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.3" + "source": "https://github.com/symfony/routing/tree/v6.3.5" }, "funding": [ { @@ -4704,7 +5028,7 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-09-20T16:05:51+00:00" }, { "name": "symfony/runtime", @@ -5137,16 +5461,16 @@ }, { "name": "symfony/serializer", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "96d28a58d5a128bf77c54534b380eb7c92c8f846" + "reference": "855fc058c8bdbb69f53834f2fdb3876c9bc0ab7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/96d28a58d5a128bf77c54534b380eb7c92c8f846", - "reference": "96d28a58d5a128bf77c54534b380eb7c92c8f846", + "url": "https://api.github.com/repos/symfony/serializer/zipball/855fc058c8bdbb69f53834f2fdb3876c9bc0ab7c", + "reference": "855fc058c8bdbb69f53834f2fdb3876c9bc0ab7c", "shasum": "" }, "require": { @@ -5211,7 +5535,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.3.4" + "source": "https://github.com/symfony/serializer/tree/v6.3.5" }, "funding": [ { @@ -5227,7 +5551,7 @@ "type": "tidelift" } ], - "time": "2023-08-24T14:35:28+00:00" + "time": "2023-09-29T16:18:53+00:00" }, { "name": "symfony/service-contracts", @@ -5375,16 +5699,16 @@ }, { "name": "symfony/string", - "version": "v6.3.2", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "53d1a83225002635bca3482fcbf963001313fb68" + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", - "reference": "53d1a83225002635bca3482fcbf963001313fb68", + "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", "shasum": "" }, "require": { @@ -5441,7 +5765,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.2" + "source": "https://github.com/symfony/string/tree/v6.3.5" }, "funding": [ { @@ -5457,7 +5781,7 @@ "type": "tidelift" } ], - "time": "2023-07-05T08:41:27+00:00" + "time": "2023-09-18T10:38:32+00:00" }, { "name": "symfony/translation-contracts", @@ -5537,18 +5861,211 @@ ], "time": "2023-05-30T17:17:10+00:00" }, + { + "name": "symfony/twig-bridge", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "18f2cbe1d46ad43c4d3bd45e5e6279172068e064" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/18f2cbe1d46ad43c4d3bd45e5e6279172068e064", + "reference": "18f2cbe1d46ad43c4d3bd45e5e6279172068e064", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<5.4", + "symfony/form": "<6.3", + "symfony/http-foundation": "<5.4", + "symfony/http-kernel": "<6.2", + "symfony/mime": "<6.2", + "symfony/translation": "<5.4", + "symfony/workflow": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^5.4|^6.0", + "symfony/asset-mapper": "^6.3", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/form": "^6.3", + "symfony/html-sanitizer": "^6.1", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^6.2", + "symfony/intl": "^5.4|^6.0", + "symfony/mime": "^6.2", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^5.4|^6.0", + "symfony/security-csrf": "^5.4|^6.0", + "symfony/security-http": "^5.4|^6.0", + "symfony/serializer": "^6.2", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^6.1", + "symfony/web-link": "^5.4|^6.0", + "symfony/workflow": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-12T06:57:20+00:00" + }, + { + "name": "symfony/twig-bundle", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "d0cd4d1675c0582d27c2e8bb0dc27c0303d8e3ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/d0cd4d1675c0582d27c2e8bb0dc27c0303d8e3ea", + "reference": "d0cd4d1675c0582d27c2e8bb0dc27c0303d8e3ea", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "php": ">=8.1", + "symfony/config": "^6.1", + "symfony/dependency-injection": "^6.1", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^6.2", + "symfony/twig-bridge": "^6.3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "symfony/framework-bundle": "<5.4", + "symfony/translation": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.10.4|^2", + "symfony/asset": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/framework-bundle": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/web-link": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of Twig into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bundle/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-06T09:53:41+00:00" + }, { "name": "symfony/validator", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "0c8435154920b9bbe93bece675234c244cadf73b" + "reference": "48e815ba3b5eb72e632588dbf7ea2dc4e608ee47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/0c8435154920b9bbe93bece675234c244cadf73b", - "reference": "0c8435154920b9bbe93bece675234c244cadf73b", + "url": "https://api.github.com/repos/symfony/validator/zipball/48e815ba3b5eb72e632588dbf7ea2dc4e608ee47", + "reference": "48e815ba3b5eb72e632588dbf7ea2dc4e608ee47", "shasum": "" }, "require": { @@ -5615,7 +6132,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.3.4" + "source": "https://github.com/symfony/validator/tree/v6.3.5" }, "funding": [ { @@ -5631,20 +6148,20 @@ "type": "tidelift" } ], - "time": "2023-08-17T15:49:05+00:00" + "time": "2023-09-29T07:41:15+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45" + "reference": "3d9999376be5fea8de47752837a3e1d1c5f69ef5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2027be14f8ae8eae999ceadebcda5b4909b81d45", - "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3d9999376be5fea8de47752837a3e1d1c5f69ef5", + "reference": "3d9999376be5fea8de47752837a3e1d1c5f69ef5", "shasum": "" }, "require": { @@ -5699,7 +6216,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.4" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.5" }, "funding": [ { @@ -5715,7 +6232,7 @@ "type": "tidelift" } ], - "time": "2023-08-24T14:51:05+00:00" + "time": "2023-09-12T10:11:35+00:00" }, { "name": "symfony/var-exporter", @@ -5863,6 +6380,151 @@ ], "time": "2023-07-31T07:08:24+00:00" }, + { + "name": "twig/extra-bundle", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/twig-extra-bundle.git", + "reference": "f10baafe6eb0ecd615d52d5cbfb713a39f68e8f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/f10baafe6eb0ecd615d52d5cbfb713a39f68e8f3", + "reference": "f10baafe6eb0ecd615d52d5cbfb713a39f68e8f3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/framework-bundle": "^5.4|^6.0", + "symfony/twig-bundle": "^5.4|^6.0", + "twig/twig": "^2.7|^3.0" + }, + "require-dev": { + "league/commonmark": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4|^6.3", + "twig/cache-extra": "^3.0", + "twig/cssinliner-extra": "^2.12|^3.0", + "twig/html-extra": "^2.12|^3.0", + "twig/inky-extra": "^2.12|^3.0", + "twig/intl-extra": "^2.12|^3.0", + "twig/markdown-extra": "^2.12|^3.0", + "twig/string-extra": "^2.12|^3.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Twig\\Extra\\TwigExtraBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Symfony bundle for extra Twig extensions", + "homepage": "https://twig.symfony.com", + "keywords": [ + "bundle", + "extra", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-07-29T15:34:56+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-08-28T11:09:02+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", @@ -5976,6 +6638,84 @@ "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" }, "time": "2022-01-30T20:08:53+00:00" + }, + { + "name": "zircote/swagger-php", + "version": "4.7.14", + "source": { + "type": "git", + "url": "https://github.com/zircote/swagger-php.git", + "reference": "e53c0c7a6e250c435cc13ab50792247a8eb07da3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/e53c0c7a6e250c435cc13ab50792247a8eb07da3", + "reference": "e53c0c7a6e250c435cc13ab50792247a8eb07da3", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.7 || ^2.0", + "ext-json": "*", + "php": ">=7.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "symfony/deprecation-contracts": "^2 || ^3", + "symfony/finder": ">=2.2", + "symfony/yaml": ">=3.3" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11", + "friendsofphp/php-cs-fixer": "^2.17 || ^3.0", + "phpstan/phpstan": "^1.6", + "phpunit/phpunit": ">=8", + "vimeo/psalm": "^4.23" + }, + "bin": [ + "bin/openapi" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "OpenApi\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Robert Allen", + "email": "zircote@gmail.com" + }, + { + "name": "Bob Fanger", + "email": "bfanger@gmail.com", + "homepage": "https://bfanger.nl" + }, + { + "name": "Martin Rademacher", + "email": "mano@radebatz.net", + "homepage": "https://radebatz.net" + } + ], + "description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations", + "homepage": "https://github.com/zircote/swagger-php/", + "keywords": [ + "api", + "json", + "rest", + "service discovery" + ], + "support": { + "issues": "https://github.com/zircote/swagger-php/issues", + "source": "https://github.com/zircote/swagger-php/tree/4.7.14" + }, + "time": "2023-09-12T00:18:13+00:00" } ], "packages-dev": [ @@ -6349,16 +7089,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.30.0", + "version": "v3.34.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f" + "reference": "7c7a4ad2ed8fe50df3e25528218b13d383608f23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/95c64693b2f149966a2bc05a7a4981b0343ea52f", - "reference": "95c64693b2f149966a2bc05a7a4981b0343ea52f", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7c7a4ad2ed8fe50df3e25528218b13d383608f23", + "reference": "7c7a4ad2ed8fe50df3e25528218b13d383608f23", "shasum": "" }, "require": { @@ -6379,6 +7119,9 @@ "symfony/process": "^5.4 || ^6.0", "symfony/stopwatch": "^5.4 || ^6.0" }, + "conflict": { + "stevebauman/unfinalize": "*" + }, "require-dev": { "facile-it/paraunit": "^1.3 || ^2.0", "justinrainbow/json-schema": "^5.2", @@ -6432,7 +7175,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.30.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.34.0" }, "funding": [ { @@ -6440,7 +7183,7 @@ "type": "github" } ], - "time": "2023-09-26T22:10:43+00:00" + "time": "2023-09-29T15:34:26+00:00" }, { "name": "hautelook/alice-bundle", @@ -8516,73 +9259,6 @@ ], "time": "2023-08-01T07:43:40+00:00" }, - { - "name": "symfony/options-resolver", - "version": "v6.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", - "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-12T14:21:09+00:00" - }, { "name": "symfony/phpunit-bridge", "version": "v6.3.2", diff --git a/config/bundles.php b/config/bundles.php index 4312488..e7639de 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -9,4 +9,7 @@ Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], + Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], ]; diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml new file mode 100644 index 0000000..fa4f54c --- /dev/null +++ b/config/packages/nelmio_api_doc.yaml @@ -0,0 +1,12 @@ +nelmio_api_doc: + documentation: + info: + title: RESTful Webservices in Symfony + description: "SymfonyLive Berlin 2023" + version: 1.0.0 + areas: + path_patterns: + - ^/api(?!/doc$) # Accepts routes under /api except /api/doc + - ^/workshops + - ^/attendees + diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml new file mode 100644 index 0000000..f9f4cc5 --- /dev/null +++ b/config/packages/twig.yaml @@ -0,0 +1,6 @@ +twig: + default_path: '%kernel.project_dir%/templates' + +when@test: + twig: + strict_variables: true diff --git a/config/routes/nelmio_api_doc.yaml b/config/routes/nelmio_api_doc.yaml new file mode 100644 index 0000000..f350dd6 --- /dev/null +++ b/config/routes/nelmio_api_doc.yaml @@ -0,0 +1,12 @@ +# Expose your documentation as JSON swagger compliant +app.swagger: + path: /api/doc.json + methods: GET + defaults: { _controller: nelmio_api_doc.controller.swagger } + +## Requires the Asset component and the Twig bundle +## $ composer require twig asset +app.swagger_ui: + path: /api/doc + methods: GET + defaults: { _controller: nelmio_api_doc.controller.swagger_ui } diff --git a/src/Controller/Attendee/CreateController.php b/src/Controller/Attendee/CreateController.php index 14e2dfd..fe1ce27 100644 --- a/src/Controller/Attendee/CreateController.php +++ b/src/Controller/Attendee/CreateController.php @@ -6,6 +6,8 @@ use App\Domain\AttendeeCreator; use App\Domain\Model\CreateAttendeeModel; +use Nelmio\ApiDocBundle\Annotation\Model; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -15,6 +17,20 @@ #[IsGranted('ROLE_USER')] #[Route('/attendees', name: 'create_attendee', methods: ['POST'])] +#[OA\Post( + description: 'Creates an attendee.', + summary: 'Creates an attendee.', + requestBody: new OA\RequestBody( + content: new Model(type: CreateAttendeeModel::class) + ), + tags: ['Attendee'], + responses: [ + new OA\Response( + response: 201, + description: 'Returns the created attendee.' + ), + ], +)] final class CreateController { public function __construct( diff --git a/src/Controller/Attendee/DeleteController.php b/src/Controller/Attendee/DeleteController.php index 4f52198..405ccf8 100644 --- a/src/Controller/Attendee/DeleteController.php +++ b/src/Controller/Attendee/DeleteController.php @@ -6,6 +6,7 @@ use App\Domain\AttendeeRemover; use App\Entity\Attendee; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -13,6 +14,7 @@ #[IsGranted('ROLE_ADMIN')] #[Route('/attendees/{identifier}', name: 'delete_attendee', methods: ['DELETE'])] +#[OA\Delete(tags: ['Attendee'])] class DeleteController { public function __construct( diff --git a/src/Controller/Attendee/ListController.php b/src/Controller/Attendee/ListController.php index 558a1cb..35d2d53 100644 --- a/src/Controller/Attendee/ListController.php +++ b/src/Controller/Attendee/ListController.php @@ -5,12 +5,43 @@ namespace App\Controller\Attendee; use App\Pagination\AttendeeCollectionFactory; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; #[Route('/attendees', name: 'list_attendee', methods: ['GET'])] +#[OA\Get( + description: 'Returns a paginated collection of attendees.', + summary: 'Returns a paginated collection of attendees.', + tags: ['Attendee'], + parameters: [ + new OA\Parameter( + name: 'page', + description: 'The field to specify the current page.', + in: 'query', + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'size', + description: 'The field to specify the current page size.', + in: 'query', + schema: new OA\Schema(type: 'integer') + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Returns a list of attendees.', + content: [ + new OA\MediaType(mediaType: 'application/json'), + new OA\MediaType(mediaType: 'application/hal+json'), + new OA\MediaType(mediaType: 'text/xml'), + ] + ), + ] +)] final class ListController { public function __construct( diff --git a/src/Controller/Attendee/ReadController.php b/src/Controller/Attendee/ReadController.php index 7c00636..d262518 100644 --- a/src/Controller/Attendee/ReadController.php +++ b/src/Controller/Attendee/ReadController.php @@ -5,6 +5,7 @@ namespace App\Controller\Attendee; use App\Entity\Attendee; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -13,6 +14,7 @@ #[IsGranted('ROLE_USER')] #[Route('/attendees/{identifier}', name: 'read_attendee', methods: ['GET'])] +#[OA\Get(tags: ['Attendee'])] final class ReadController { public function __construct( diff --git a/src/Controller/Attendee/UpdateController.php b/src/Controller/Attendee/UpdateController.php index c64717a..856ef57 100644 --- a/src/Controller/Attendee/UpdateController.php +++ b/src/Controller/Attendee/UpdateController.php @@ -7,6 +7,7 @@ use App\Domain\AttendeeUpdater; use App\Domain\Model\UpdateAttendeeModel; use App\Entity\Attendee; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -14,6 +15,7 @@ #[IsGranted('ROLE_USER')] #[Route('/attendees/{identifier}', name: 'update_attendee', methods: ['PUT'])] +#[OA\Put(tags: ['Attendee'])] final class UpdateController { public function __construct( diff --git a/src/Controller/Workshop/AddAttendeeToWorkshopController.php b/src/Controller/Workshop/AddAttendeeToWorkshopController.php index 39a91ab..a5080b7 100644 --- a/src/Controller/Workshop/AddAttendeeToWorkshopController.php +++ b/src/Controller/Workshop/AddAttendeeToWorkshopController.php @@ -7,6 +7,7 @@ use App\Entity\Attendee; use App\Entity\Workshop; use App\Repository\WorkshopRepository; +use OpenApi\Attributes as OA; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -15,6 +16,7 @@ #[IsGranted('ROLE_USER')] #[Route('/workshops/{identifier}/attendees/add/{attendee_identifier}', name: 'add_attendee_to_workshop', methods: ['POST'])] +#[OA\Post(tags: ['Workshop'])] class AddAttendeeToWorkshopController { public function __construct( diff --git a/src/Controller/Workshop/CreateController.php b/src/Controller/Workshop/CreateController.php index 8d43359..cb56c1e 100644 --- a/src/Controller/Workshop/CreateController.php +++ b/src/Controller/Workshop/CreateController.php @@ -6,6 +6,8 @@ use App\Domain\Model\CreateWorkshopModel; use App\Domain\WorkshopCreator; +use Nelmio\ApiDocBundle\Annotation\Model; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -15,6 +17,20 @@ #[IsGranted('ROLE_USER')] #[Route('/workshops', name: 'create_workshop', methods: ['POST'])] +#[OA\Post( + description: 'Creates an workshop.', + summary: 'Creates an workshop.', + requestBody: new OA\RequestBody( + content: new Model(type: CreateWorkshopModel::class) + ), + tags: ['Workshop'], + responses: [ + new OA\Response( + response: 201, + description: 'Returns the created workshop.' + ), + ], +)] final class CreateController { public function __construct( diff --git a/src/Controller/Workshop/DeleteController.php b/src/Controller/Workshop/DeleteController.php index 2a92cbe..9deaf09 100644 --- a/src/Controller/Workshop/DeleteController.php +++ b/src/Controller/Workshop/DeleteController.php @@ -6,6 +6,7 @@ use App\Domain\WorkshopRemover; use App\Entity\Workshop; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -13,6 +14,7 @@ #[IsGranted('ROLE_ADMIN')] #[Route('/workshops/{identifier}', name: 'delete_workshop', methods: ['DELETE'])] +#[OA\Delete(tags: ['Workshop'])] class DeleteController { public function __construct( diff --git a/src/Controller/Workshop/ListController.php b/src/Controller/Workshop/ListController.php index 99235ff..2223883 100644 --- a/src/Controller/Workshop/ListController.php +++ b/src/Controller/Workshop/ListController.php @@ -5,12 +5,43 @@ namespace App\Controller\Workshop; use App\Pagination\WorkshopCollectionFactory; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; #[Route('/workshops', name: 'list_workshop', methods: ['GET'])] +#[OA\Get( + description: 'Returns a paginated collection of workshops.', + summary: 'Returns a paginated collection of workshops.', + tags: ['Workshop'], + parameters: [ + new OA\Parameter( + name: 'page', + description: 'The field to specify the current page.', + in: 'query', + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'size', + description: 'The field to specify the current page size.', + in: 'query', + schema: new OA\Schema(type: 'integer') + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Returns a list of workshops.', + content: [ + new OA\MediaType(mediaType: 'application/json'), + new OA\MediaType(mediaType: 'application/hal+json'), + new OA\MediaType(mediaType: 'text/xml'), + ] + ), + ] +)] final class ListController { public function __construct( diff --git a/src/Controller/Workshop/ReadController.php b/src/Controller/Workshop/ReadController.php index 8bbc32e..82417cf 100644 --- a/src/Controller/Workshop/ReadController.php +++ b/src/Controller/Workshop/ReadController.php @@ -5,6 +5,7 @@ namespace App\Controller\Workshop; use App\Entity\Workshop; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -13,6 +14,7 @@ #[IsGranted('ROLE_USER')] #[Route('/workshops/{identifier}', name: 'read_workshop', methods: ['GET'])] +#[OA\Get(tags: ['Workshop'])] final class ReadController { public function __construct( diff --git a/src/Controller/Workshop/RemoveAttendeeFromWorkshopController.php b/src/Controller/Workshop/RemoveAttendeeFromWorkshopController.php index 3130c5f..0bf1ab5 100644 --- a/src/Controller/Workshop/RemoveAttendeeFromWorkshopController.php +++ b/src/Controller/Workshop/RemoveAttendeeFromWorkshopController.php @@ -7,6 +7,7 @@ use App\Entity\Attendee; use App\Entity\Workshop; use App\Repository\WorkshopRepository; +use OpenApi\Attributes as OA; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -15,6 +16,7 @@ #[IsGranted('ROLE_USER')] #[Route('/workshops/{identifier}/attendees/remove/{attendee_identifier}', name: 'remove_attendee_from_workshop', methods: ['POST'])] +#[OA\Post(tags: ['Workshop'])] class RemoveAttendeeFromWorkshopController { public function __construct( diff --git a/src/Controller/Workshop/UpdateController.php b/src/Controller/Workshop/UpdateController.php index 9488430..2ac7efe 100644 --- a/src/Controller/Workshop/UpdateController.php +++ b/src/Controller/Workshop/UpdateController.php @@ -7,6 +7,7 @@ use App\Domain\Model\UpdateWorkshopModel; use App\Domain\WorkshopUpdater; use App\Entity\Workshop; +use OpenApi\Attributes as OA; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -14,6 +15,7 @@ #[IsGranted('ROLE_USER')] #[Route('/workshops/{identifier}', name: 'update_workshop', methods: ['PUT'])] +#[OA\Put(tags: ['Workshop'])] final class UpdateController { public function __construct( diff --git a/src/EventListener/ResponseContentTypeListener.php b/src/EventListener/ResponseContentTypeListener.php index 8c6f19d..7d2756b 100644 --- a/src/EventListener/ResponseContentTypeListener.php +++ b/src/EventListener/ResponseContentTypeListener.php @@ -16,6 +16,10 @@ public function onKernelResponse(ResponseEvent $event): void $request = $event->getRequest(); $response = $event->getResponse(); + if ('app.swagger_ui' === $request->get('_route')) { + return; + } + $response->headers->set( 'Content-Type', sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())) diff --git a/symfony.lock b/symfony.lock index 51b7327..457ff40 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,4 +1,13 @@ { + "doctrine/annotations": { + "version": "2.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.10", + "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05" + } + }, "doctrine/doctrine-bundle": { "version": "2.10", "recipe": { @@ -75,6 +84,19 @@ "config/packages/nelmio_alice.yaml" ] }, + "nelmio/api-doc-bundle": { + "version": "4.12", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "3.0", + "ref": "c8e0c38e1a280ab9e37587a8fa32b251d5bc1c94" + }, + "files": [ + "config/packages/nelmio_api_doc.yaml", + "config/routes/nelmio_api_doc.yaml" + ] + }, "phpunit/phpunit": { "version": "10.3", "recipe": { @@ -184,6 +206,19 @@ "config/packages/security.yaml" ] }, + "symfony/twig-bundle": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.3", + "ref": "b7772eb20e92f3fb4d4fe756e7505b4ba2ca1a2c" + }, + "files": [ + "config/packages/twig.yaml", + "templates/base.html.twig" + ] + }, "symfony/validator": { "version": "6.3", "recipe": { @@ -204,5 +239,8 @@ "version": "1.0", "ref": "fe5a50faf580eb58f08ada2abe8afbd2d4941e05" } + }, + "twig/extra-bundle": { + "version": "v3.7.1" } } diff --git a/templates/base.html.twig b/templates/base.html.twig new file mode 100644 index 0000000..67598ac --- /dev/null +++ b/templates/base.html.twig @@ -0,0 +1,16 @@ + + +
+ +