diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml
new file mode 100644
index 0000000..25628ce
--- /dev/null
+++ b/.idea/php-test-framework.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
index 77e060b..d1a8cde 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -89,6 +89,14 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/phpspec.xml b/.idea/phpspec.xml
index c7cfbc2..41f23c4 100644
--- a/.idea/phpspec.xml
+++ b/.idea/phpspec.xml
@@ -11,6 +11,9 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/symfony_crud.iml b/.idea/symfony_crud.iml
index fc70b11..f68ff09 100644
--- a/.idea/symfony_crud.iml
+++ b/.idea/symfony_crud.iml
@@ -26,6 +26,10 @@
+
+
+
+
@@ -74,8 +78,11 @@
+
+
+
@@ -83,6 +90,7 @@
+
diff --git a/composer.json b/composer.json
index 5f7a19d..98a0bea 100644
--- a/composer.json
+++ b/composer.json
@@ -10,11 +10,17 @@
"doctrine/doctrine-bundle": "^2.11",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^2.17",
+ "phpdocumentor/reflection-docblock": "^5.3",
+ "phpstan/phpdoc-parser": "^1.25",
"symfony/console": "7.0.*",
"symfony/dotenv": "7.0.*",
"symfony/flex": "^2",
"symfony/framework-bundle": "7.0.*",
+ "symfony/property-access": "7.0.*",
+ "symfony/property-info": "7.0.*",
"symfony/runtime": "7.0.*",
+ "symfony/security-bundle": "7.0.*",
+ "symfony/serializer": "7.0.*",
"symfony/yaml": "7.0.*"
},
"config": {
diff --git a/composer.lock b/composer.lock
index e9327e3..e0aa62f 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": "493ee96abe1c43083ef50968f0871c8c",
+ "content-hash": "f12b00a5f6428ebabd23986ec16b1a7f",
"packages": [
{
"name": "doctrine/cache",
@@ -1329,6 +1329,221 @@
},
"time": "2022-05-23T21:33:49+00:00"
},
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "5.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^7.2 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.3",
+ "webmozart/assert": "^1.9.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.2",
+ "psalm/phar": "^4.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ },
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "account@ijaap.nl"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+ },
+ "time": "2021-10-19T17:43:47+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "fad452781b3d774e3337b0c0b245dd8e5a4455fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fad452781b3d774e3337b0c0b245dd8e5a4455fc",
+ "reference": "fad452781b3d774e3337b0c0b245dd8e5a4455fc",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.4 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0",
+ "phpstan/phpdoc-parser": "^1.13"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^0.13.9",
+ "vimeo/psalm": "^4.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.0"
+ },
+ "time": "2024-01-11T11:49:22+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "1.25.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240",
+ "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^4.15",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^1.5",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.0",
+ "phpunit/phpunit": "^9.5",
+ "symfony/process": "^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "support": {
+ "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0"
+ },
+ "time": "2024-01-04T17:06:16+00:00"
+ },
{
"name": "psr/cache",
"version": "3.0.0",
@@ -1378,6 +1593,54 @@
},
"time": "2021-02-03T23:26:27+00:00"
},
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
+ },
{
"name": "psr/container",
"version": "2.0.2",
@@ -1703,6 +1966,80 @@
],
"time": "2023-09-25T12:52:38+00:00"
},
+ {
+ "name": "symfony/clock",
+ "version": "v7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/clock.git",
+ "reference": "67c5ae749ebabe7d8c84c3cab2544331eee7d2cf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/67c5ae749ebabe7d8c84c3cab2544331eee7d2cf",
+ "reference": "67c5ae749ebabe7d8c84c3cab2544331eee7d2cf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/now.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Clock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Decouples applications from the system clock",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clock",
+ "psr20",
+ "time"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/clock/tree/v7.0.2"
+ },
+ "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-12-27T08:42:13+00:00"
+ },
{
"name": "symfony/config",
"version": "v7.0.0",
@@ -2956,6 +3293,78 @@
],
"time": "2023-12-30T15:41:17+00:00"
},
+ {
+ "name": "symfony/password-hasher",
+ "version": "v7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/password-hasher.git",
+ "reference": "d2da68c2f7a240bd6edf7e96fdc7aca5e7beea66"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/password-hasher/zipball/d2da68c2f7a240bd6edf7e96fdc7aca5e7beea66",
+ "reference": "d2da68c2f7a240bd6edf7e96fdc7aca5e7beea66",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "conflict": {
+ "symfony/security-core": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0",
+ "symfony/security-core": "^6.4|^7.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/v7.0.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-11-07T10:26:03+00:00"
+ },
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.28.0",
@@ -3284,6 +3693,165 @@
],
"time": "2023-08-16T06:22:46+00:00"
},
+ {
+ "name": "symfony/property-access",
+ "version": "v7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/property-access.git",
+ "reference": "740e8cb8c54a4f16c82179e8558c29d9fc49901d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/property-access/zipball/740e8cb8c54a4f16c82179e8558c29d9fc49901d",
+ "reference": "740e8cb8c54a4f16c82179e8558c29d9fc49901d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/property-info": "^6.4|^7.0"
+ },
+ "require-dev": {
+ "symfony/cache": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\PropertyAccess\\": ""
+ },
+ "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 functions to read and write from/to an object or array using a simple string notation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "access",
+ "array",
+ "extraction",
+ "index",
+ "injection",
+ "object",
+ "property",
+ "property-path",
+ "reflection"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/property-access/tree/v7.0.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-09-27T14:05:33+00:00"
+ },
+ {
+ "name": "symfony/property-info",
+ "version": "v7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/property-info.git",
+ "reference": "ce627df05f5629ce4feec536ee827ad0a12689b6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/property-info/zipball/ce627df05f5629ce4feec536ee827ad0a12689b6",
+ "reference": "ce627df05f5629ce4feec536ee827ad0a12689b6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/string": "^6.4|^7.0"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "<5.2",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/serializer": "<6.4"
+ },
+ "require-dev": {
+ "phpdocumentor/reflection-docblock": "^5.2",
+ "phpstan/phpdoc-parser": "^1.0",
+ "symfony/cache": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/serializer": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\PropertyInfo\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kévin Dunglas",
+ "email": "dunglas@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Extracts information about PHP class' properties using metadata of popular sources",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "doctrine",
+ "phpdoc",
+ "property",
+ "symfony",
+ "type",
+ "validator"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/property-info/tree/v7.0.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-11-25T08:38:27+00:00"
+ },
{
"name": "symfony/routing",
"version": "v7.0.2",
@@ -3444,6 +4012,450 @@
],
"time": "2023-10-20T16:35:23+00:00"
},
+ {
+ "name": "symfony/security-bundle",
+ "version": "v7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-bundle.git",
+ "reference": "5c781fc5cc853286613d7fec1ecbe00cfbec967e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-bundle/zipball/5c781fc5cc853286613d7fec1ecbe00cfbec967e",
+ "reference": "5c781fc5cc853286613d7fec1ecbe00cfbec967e",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": ">=2.1",
+ "ext-xml": "*",
+ "php": ">=8.2",
+ "symfony/clock": "^6.4|^7.0",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/password-hasher": "^6.4|^7.0",
+ "symfony/security-core": "^6.4|^7.0",
+ "symfony/security-csrf": "^6.4|^7.0",
+ "symfony/security-http": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<6.4",
+ "symfony/console": "<6.4",
+ "symfony/framework-bundle": "<6.4",
+ "symfony/http-client": "<6.4",
+ "symfony/ldap": "<6.4",
+ "symfony/serializer": "<6.4",
+ "symfony/twig-bundle": "<6.4",
+ "symfony/validator": "<6.4"
+ },
+ "require-dev": {
+ "symfony/asset": "^6.4|^7.0",
+ "symfony/browser-kit": "^6.4|^7.0",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/css-selector": "^6.4|^7.0",
+ "symfony/dom-crawler": "^6.4|^7.0",
+ "symfony/expression-language": "^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/ldap": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/rate-limiter": "^6.4|^7.0",
+ "symfony/serializer": "^6.4|^7.0",
+ "symfony/translation": "^6.4|^7.0",
+ "symfony/twig-bridge": "^6.4|^7.0",
+ "symfony/twig-bundle": "^6.4|^7.0",
+ "symfony/validator": "^6.4|^7.0",
+ "symfony/yaml": "^6.4|^7.0",
+ "twig/twig": "^3.0.4",
+ "web-token/jwt-checker": "^3.1",
+ "web-token/jwt-signature-algorithm-ecdsa": "^3.1",
+ "web-token/jwt-signature-algorithm-eddsa": "^3.1",
+ "web-token/jwt-signature-algorithm-hmac": "^3.1",
+ "web-token/jwt-signature-algorithm-none": "^3.1",
+ "web-token/jwt-signature-algorithm-rsa": "^3.1"
+ },
+ "type": "symfony-bundle",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bundle\\SecurityBundle\\": ""
+ },
+ "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 the Security component into the Symfony full-stack framework",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-bundle/tree/v7.0.2"
+ },
+ "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-12-24T09:15:37+00:00"
+ },
+ {
+ "name": "symfony/security-core",
+ "version": "v7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-core.git",
+ "reference": "2ba040de8e6d93e07edc7307dc75b42e06137405"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-core/zipball/2ba040de8e6d93e07edc7307dc75b42e06137405",
+ "reference": "2ba040de8e6d93e07edc7307dc75b42e06137405",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3",
+ "symfony/password-hasher": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/http-foundation": "<6.4",
+ "symfony/ldap": "<6.4",
+ "symfony/validator": "<6.4"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "psr/container": "^1.1|^2.0",
+ "psr/log": "^1|^2|^3",
+ "symfony/cache": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/ldap": "^6.4|^7.0",
+ "symfony/string": "^6.4|^7.0",
+ "symfony/translation": "^6.4|^7.0",
+ "symfony/validator": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Security\\Core\\": ""
+ },
+ "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": "Symfony Security Component - Core Library",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-core/tree/v7.0.1"
+ },
+ "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-11-30T11:04:23+00:00"
+ },
+ {
+ "name": "symfony/security-csrf",
+ "version": "v7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-csrf.git",
+ "reference": "e261f2cc8d170ec2f310d037893b213850867b6b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-csrf/zipball/e261f2cc8d170ec2f310d037893b213850867b6b",
+ "reference": "e261f2cc8d170ec2f310d037893b213850867b6b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/security-core": "^6.4|^7.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<6.4"
+ },
+ "require-dev": {
+ "symfony/http-foundation": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Security\\Csrf\\": ""
+ },
+ "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": "Symfony Security Component - CSRF Library",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-csrf/tree/v7.0.1"
+ },
+ "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-11-30T11:04:23+00:00"
+ },
+ {
+ "name": "symfony/security-http",
+ "version": "v7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-http.git",
+ "reference": "acc9931d75cd16de08b1663223cb8ab36f61cc0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-http/zipball/acc9931d75cd16de08b1663223cb8ab36f61cc0c",
+ "reference": "acc9931d75cd16de08b1663223cb8ab36f61cc0c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/property-access": "^6.4|^7.0",
+ "symfony/security-core": "^6.4|^7.0",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/clock": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/http-client-contracts": "<3.0",
+ "symfony/security-bundle": "<6.4",
+ "symfony/security-csrf": "<6.4"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/cache": "^6.4|^7.0",
+ "symfony/clock": "^6.4|^7.0",
+ "symfony/expression-language": "^6.4|^7.0",
+ "symfony/http-client-contracts": "^3.0",
+ "symfony/rate-limiter": "^6.4|^7.0",
+ "symfony/routing": "^6.4|^7.0",
+ "symfony/security-csrf": "^6.4|^7.0",
+ "symfony/translation": "^6.4|^7.0",
+ "web-token/jwt-checker": "^3.1",
+ "web-token/jwt-signature-algorithm-ecdsa": "^3.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Security\\Http\\": ""
+ },
+ "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": "Symfony Security Component - HTTP Integration",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-http/tree/v7.0.1"
+ },
+ "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-11-30T11:04:23+00:00"
+ },
+ {
+ "name": "symfony/serializer",
+ "version": "v7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/serializer.git",
+ "reference": "dd7d7612f9ae288889caba4bdff79424ce4ffdf0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/dd7d7612f9ae288889caba4bdff79424ce4ffdf0",
+ "reference": "dd7d7612f9ae288889caba4bdff79424ce4ffdf0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/property-access": "<6.4",
+ "symfony/property-info": "<6.4",
+ "symfony/uid": "<6.4",
+ "symfony/validator": "<6.4",
+ "symfony/yaml": "<6.4"
+ },
+ "require-dev": {
+ "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0",
+ "seld/jsonlint": "^1.10",
+ "symfony/cache": "^6.4|^7.0",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/console": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/filesystem": "^6.4|^7.0",
+ "symfony/form": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/mime": "^6.4|^7.0",
+ "symfony/property-access": "^6.4|^7.0",
+ "symfony/property-info": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3",
+ "symfony/uid": "^6.4|^7.0",
+ "symfony/validator": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0",
+ "symfony/var-exporter": "^6.4|^7.0",
+ "symfony/yaml": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Serializer\\": ""
+ },
+ "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": "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/v7.0.2"
+ },
+ "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-12-29T15:37:40+00:00"
+ },
{
"name": "symfony/service-contracts",
"version": "v3.4.1",
@@ -3901,6 +4913,64 @@
}
],
"time": "2023-11-07T10:26:03+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+ },
+ "time": "2022-06-03T18:03:27+00:00"
}
],
"packages-dev": [
diff --git a/config/bundles.php b/config/bundles.php
index de8898b..6f862e7 100644
--- a/config/bundles.php
+++ b/config/bundles.php
@@ -5,4 +5,5 @@ return [
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
+ Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
];
diff --git a/config/packages/security.yaml b/config/packages/security.yaml
new file mode 100644
index 0000000..a08d6d1
--- /dev/null
+++ b/config/packages/security.yaml
@@ -0,0 +1,40 @@
+security:
+ # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
+ password_hashers:
+ Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
+ # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
+ providers:
+ users_in_memory: { memory: null }
+ firewalls:
+ dev:
+ pattern: ^/(_(profiler|wdt)|css|images|js)/
+ security: false
+ main:
+ lazy: true
+ provider: users_in_memory
+ custom_authenticator: App\Security\PrinterAuthenticator
+
+ # activate different ways to authenticate
+ # https://symfony.com/doc/current/security.html#the-firewall
+
+ # https://symfony.com/doc/current/security/impersonating_user.html
+ # switch_user: true
+
+ # Easy way to control access for large sections of your site
+ # Note: Only the *first* access control that matches will be used
+ access_control:
+ # - { path: ^/admin, roles: ROLE_ADMIN }
+ # - { path: ^/profile, roles: ROLE_USER }
+
+when@test:
+ security:
+ password_hashers:
+ # By default, password hashers are resource intensive and take time. This is
+ # important to generate secure password hashes. In tests however, secure hashes
+ # are not important, waste resources and increase test times. The following
+ # reduces the work factor to the lowest possible values.
+ Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
+ algorithm: auto
+ cost: 4 # Lowest possible value for bcrypt
+ time_cost: 3 # Lowest possible value for argon
+ memory_cost: 10 # Lowest possible value for argon
diff --git a/config/routes/security.yaml b/config/routes/security.yaml
new file mode 100644
index 0000000..f853be1
--- /dev/null
+++ b/config/routes/security.yaml
@@ -0,0 +1,3 @@
+_security_logout:
+ resource: security.route_loader.logout
+ type: service
diff --git a/src/Controller/PrinterCrudController.php b/src/Controller/PrinterCrudController.php
index 7dc275c..aa73abe 100644
--- a/src/Controller/PrinterCrudController.php
+++ b/src/Controller/PrinterCrudController.php
@@ -3,32 +3,86 @@
namespace App\Controller;
use App\Entity\Printer;
+use App\Enum\ErrorMessages;
use App\Repository\PrinterRepository;
+use App\Service\PrinterService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class PrinterCrudController extends AbstractController
{
- public function __construct(private readonly PrinterRepository $printerRepository)
+ public function __construct(
+ private readonly PrinterRepository $printerRepository,
+ private readonly PrinterService $printerService,
+ )
{
}
- #[Route('/printer', name: 'printer')]
- public function printer(): JsonResponse
+ #[Route('/printer', name: 'printer', methods: ['GET'])]
+ public function getAllPrinters(): JsonResponse
{
return $this->json($this->printerRepository->findAll());
}
- #[Route('/printer/{id}', name: 'app_printer', methods: ["GET"])]
- public function index(?Printer $printer): JsonResponse
+ #[Route('/printer/{id}', name: 'get_printer', methods: ['GET'])]
+ public function getPrinter(?Printer $printer): JsonResponse
{
if (!$printer) {
return $this->json([
- 'message' => 'printer does not exist',
+ 'message' => ErrorMessages::DOESNT_EXIST->value,
]);
}
return $this->json($printer);
}
+
+ #[Route('/printer/{id}', name: 'delete_printer', methods: ['DELETE'])]
+ public function deletePrinter(?Printer $printer): JsonResponse
+ {
+ if (!$printer) {
+ return $this->json([
+ 'message' => ErrorMessages::DOESNT_EXIST->value,
+ ]);
+ }
+
+ $this->printerService->deletePrinter($printer);
+
+ return $this->json([
+ 'message' => 'printer was deleted',
+ ]);
+ }
+
+ #[Route('/printer', name: 'create_printer', methods: ['POST'])]
+ public function createPrinter(Request $request): JsonResponse
+ {
+ $jsonContent = $request->getContent();
+ if (!$this->printerService->validateJson($jsonContent)) {
+ return $this->json([
+ 'message' => ErrorMessages::DATA_INCOMPLETE->value,
+ ]);
+ }
+
+ $printer = $this->printerService->createPrinter($jsonContent);
+
+ return $this->json($printer);
+ }
+
+ #[Route('/printer/{id}', name: 'edit_printer', methods: ['PUT'])]
+ public function editPrinter(?Printer $printer, Request $request): JsonResponse
+ {
+ if (!$printer) {
+ return $this->json([
+ 'message' => ErrorMessages::DOESNT_EXIST->value,
+ ]);
+ }
+ if (!$this->printerService->validateJson($request->getContent())) {
+ return $this->json([
+ 'message' => ErrorMessages::DATA_INCOMPLETE,
+ ]);
+ }
+
+
+ }
}
diff --git a/src/Enum/ErrorMessages.php b/src/Enum/ErrorMessages.php
new file mode 100644
index 0000000..8470652
--- /dev/null
+++ b/src/Enum/ErrorMessages.php
@@ -0,0 +1,9 @@
+entityManager->remove($printer);
$this->entityManager->flush();
}
+
+ public function createPrinter(string $jsonString): Printer
+ {
+ $printer = $this->serializer->deserialize($jsonString, Printer::class, 'json');
+ $this->entityManager->persist($printer);
+ $this->entityManager->flush();
+
+ return $printer;
+ }
+
+ public function validateJson(string $jsonString): bool
+ {
+ $array = json_decode($jsonString, true);
+ return isset($array['name'], $array['price'], $array['max_speed'], $array['build_volume']);
+ }
}
\ No newline at end of file
diff --git a/symfony.lock b/symfony.lock
index 5672608..ebee639 100644
--- a/symfony.lock
+++ b/symfony.lock
@@ -104,5 +104,18 @@
"./config/packages/routing.yaml",
"./config/routes.yaml"
]
+ },
+ "symfony/security-bundle": {
+ "version": "7.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.4",
+ "ref": "2ae08430db28c8eb4476605894296c82a642028f"
+ },
+ "files": [
+ "config/packages/security.yaml",
+ "config/routes/security.yaml"
+ ]
}
}
diff --git a/tests/Service/PrinterServiceTest.php b/tests/Service/PrinterServiceTest.php
index 28c796c..8bfe064 100644
--- a/tests/Service/PrinterServiceTest.php
+++ b/tests/Service/PrinterServiceTest.php
@@ -2,17 +2,82 @@
namespace App\Tests\Service;
+use App\Entity\Printer;
+use App\Service\PrinterService;
+use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\Attributes\Test;
-use PrinterService;
+use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
-use Symfony\Bundle\MakerBundle\Docker\DockerDatabaseServices;
+use Symfony\Component\Serializer\SerializerInterface;
+use function PHPUnit\Framework\once;
class PrinterServiceTest extends TestCase
{
- #[Test]
- public function EditPrinterShouldEditPrinter(){
- $printerService = new PrinterService();
+ private readonly EntityManagerInterface&MockObject $entityManager;
+ private readonly SerializerInterface&MockObject $serializer;
+ private readonly PrinterService $printerService;
- $printerService->editPrinter(Printer $printer, )
+ protected function setUp(): void
+ {
+ $this->entityManager = self::createMock(EntityManagerInterface::class);
+ $this->serializer = self::createMock(SerializerInterface::class);
+ $this->printerService = new PrinterService($this->entityManager, $this->serializer);
+ }
+
+ #[Test]
+ public function removeShouldInvokeRemoveOnPrinterAndFlush()
+ {
+ $printer = new Printer();
+
+ $this->entityManager->expects(self::once())->method('remove')->with($printer);
+ $this->entityManager->expects(self::once())->method('flush');
+
+ $this->printerService->deletePrinter($printer);
+ }
+
+ #[Test]
+ public function validateJsonShouldReturnFalseOnInputIncompleteJson()
+ {
+ $invalidJson = '{
+ "name": "Bambu A1"
+ }';
+
+ self::assertFalse($this->printerService->validateJson($invalidJson));
+ }
+
+ #[Test]
+ public function validateJsonShouldReturnTrueOnInputCompleteJson()
+ {
+ $validJson = '{
+ "name": "Bambu A1 Mini",
+ "price": 10.50,
+ "build_volume": "180x180x180",
+ "max_speed": 1000
+ }';
+
+ self::assertTrue($this->printerService->validateJson($validJson));
+ }
+
+ #[Test]
+ public function createPrinterShouldInvokePersistAndFlushOnCreatedPrinterObject(){
+ $json = '{
+ "name": "Bambu A1 Mini",
+ "price": 10.50,
+ "build_volume": "180x180x180",
+ "max_speed": 1000
+ }';
+
+ $printer = new Printer();
+ $printer
+ ->setName('Bambu A1 Mini')
+ ->setPrice(10.50)
+ ->setBuildVolume('180x180x180')
+ ->setMaxSpeed(1000);
+
+ $this->serializer->expects(self::once())->method('deserialize')->with($json)->willReturn($printer);
+ $this->entityManager->expects(self::once())->method('persist')->with($printer);
+ $this->entityManager->expects(self::once())->method('flush');
+
+ $this->printerService->createPrinter($json);
}
}