From 9747eefe470e054ca88401e2ebc8dae31d5f817c Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Sat, 19 Aug 2023 03:32:43 +0300 Subject: [PATCH] Replacing jms/serializer with symfony/serializer and doing some DTO refactoring. --- composer.json | 2 +- composer.lock | 343 +++++------------- config/bundles.php | 2 - config/packages/jms_serializer.yaml | 30 -- .../config/serializer/DTO.Api.Auth.yml | 11 - .../config/serializer/DTO.Api.Comment.yml | 23 -- .../config/serializer/DTO.Api.MetaPost.yml | 10 - .../config/serializer/DTO.Api.Post.yml | 25 -- .../config/serializer/DTO.Api.PostsPage.yml | 10 - .../config/serializer/DTO.Api.User.yml | 49 --- .../Resources/config/services.yml | 1 - .../Service/Factory/UserFactoryTest.php | 7 +- src/Command/ImportUsersCommand.php | 2 +- src/Controller/Api/CrawlerController.php | 2 +- src/DTO/Api/Auth.php | 43 +-- src/DTO/Api/Comment.php | 101 +----- src/DTO/Api/MetaPost.php | 39 +- src/DTO/Api/Post.php | 124 ++----- src/DTO/Api/PostsPage.php | 32 +- src/DTO/Api/User.php | 201 +++------- src/DTO/Api/ValidableInterface.php | 2 + src/DataFixtures/LoadUserData.php | 2 +- src/Entity/Blog/Post.php | 6 +- src/Entity/User.php | 2 +- src/Factory/AbstractFactory.php | 2 +- src/Factory/Blog/CommentFactory.php | 21 -- src/Factory/Blog/PostFactory.php | 32 +- src/Factory/UserFactory.php | 15 +- src/Service/Api/AbstractApi.php | 25 +- src/Service/Api/PostApi.php | 22 -- src/Service/Api/UserApi.php | 14 +- symfony.lock | 12 - 32 files changed, 274 insertions(+), 938 deletions(-) delete mode 100644 config/packages/jms_serializer.yaml delete mode 100644 old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Auth.yml delete mode 100644 old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Comment.yml delete mode 100644 old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.MetaPost.yml delete mode 100644 old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Post.yml delete mode 100644 old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.PostsPage.yml delete mode 100644 old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.User.yml delete mode 100644 old/src/PointToolsBundle/Resources/config/services.yml delete mode 100644 src/Factory/Blog/CommentFactory.php delete mode 100644 src/Service/Api/PostApi.php diff --git a/composer.json b/composer.json index 8488fc7..5027560 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,6 @@ "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.14", "ghunti/highcharts-php": "^5.0", - "jms/serializer-bundle": "^5.2", "knplabs/knp-paginator-bundle": "^6.2", "league/commonmark": "^2.4", "phpdocumentor/reflection-docblock": "^5.3", @@ -34,6 +33,7 @@ "symfony/property-info": "6.3.*", "symfony/runtime": "6.3.*", "symfony/security-bundle": "6.3.*", + "symfony/serializer": "6.3.*", "symfony/string": "6.3.*", "symfony/translation": "6.3.*", "symfony/twig-bundle": "6.3.*", diff --git a/composer.lock b/composer.lock index d9ff62b..fec38bf 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": "274812d219a3d19c925c0e67b5f86926", + "content-hash": "0cb307d1ad554ab97a56f5805e8c6adb", "packages": [ { "name": "dflydev/dot-access-data", @@ -1689,253 +1689,6 @@ }, "time": "2023-04-26T21:37:31+00:00" }, - { - "name": "jms/metadata", - "version": "2.8.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/metadata.git", - "reference": "7ca240dcac0c655eb15933ee55736ccd2ea0d7a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/7ca240dcac0c655eb15933ee55736ccd2ea0d7a6", - "reference": "7ca240dcac0c655eb15933ee55736ccd2ea0d7a6", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0" - }, - "require-dev": { - "doctrine/cache": "^1.0", - "doctrine/coding-standard": "^8.0", - "mikey179/vfsstream": "^1.6.7", - "phpunit/phpunit": "^8.5|^9.0", - "psr/container": "^1.0|^2.0", - "symfony/cache": "^3.1|^4.0|^5.0", - "symfony/dependency-injection": "^3.1|^4.0|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Metadata\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "Class/method/property metadata management in PHP", - "keywords": [ - "annotations", - "metadata", - "xml", - "yaml" - ], - "support": { - "issues": "https://github.com/schmittjoh/metadata/issues", - "source": "https://github.com/schmittjoh/metadata/tree/2.8.0" - }, - "time": "2023-02-15T13:44:18+00:00" - }, - { - "name": "jms/serializer", - "version": "3.27.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/serializer.git", - "reference": "e8c812460d7b47b15bc0ccd78901276bd44ad452" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/e8c812460d7b47b15bc0ccd78901276bd44ad452", - "reference": "e8c812460d7b47b15bc0ccd78901276bd44ad452", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.13 || ^2.0", - "doctrine/instantiator": "^1.0.3 || ^2.0", - "doctrine/lexer": "^2.0 || ^3.0", - "jms/metadata": "^2.6", - "php": "^7.2||^8.0", - "phpstan/phpdoc-parser": "^0.4 || ^0.5 || ^1.0" - }, - "require-dev": { - "doctrine/coding-standard": "^8.1", - "doctrine/orm": "~2.1", - "doctrine/persistence": "^1.3.3|^2.0|^3.0", - "doctrine/phpcr-odm": "^1.3|^2.0", - "ext-pdo_sqlite": "*", - "jackalope/jackalope-doctrine-dbal": "^1.1.5", - "ocramius/proxy-manager": "^1.0|^2.0", - "phpbench/phpbench": "^1.0", - "phpstan/phpstan": "^1.0.2", - "phpunit/phpunit": "^8.5.21||^9.0||^10.0", - "psr/container": "^1.0|^2.0", - "symfony/dependency-injection": "^3.0|^4.0|^5.0|^6.0", - "symfony/expression-language": "^3.2|^4.0|^5.0|^6.0", - "symfony/filesystem": "^3.0|^4.0|^5.0|^6.0", - "symfony/form": "^3.0|^4.0|^5.0|^6.0", - "symfony/translation": "^3.0|^4.0|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "symfony/validator": "^3.1.9|^4.0|^5.0|^6.0", - "symfony/yaml": "^3.3|^4.0|^5.0|^6.0", - "twig/twig": "~1.34|~2.4|^3.0" - }, - "suggest": { - "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", - "symfony/cache": "Required if you like to use cache functionality.", - "symfony/uid": "Required if you'd like to serialize UID objects.", - "symfony/yaml": "Required if you'd like to use the YAML metadata format." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "JMS\\Serializer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "Library for (de-)serializing data of any complexity; supports XML, and JSON.", - "homepage": "http://jmsyst.com/libs/serializer", - "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" - ], - "support": { - "issues": "https://github.com/schmittjoh/serializer/issues", - "source": "https://github.com/schmittjoh/serializer/tree/3.27.0" - }, - "funding": [ - { - "url": "https://github.com/goetas", - "type": "github" - } - ], - "time": "2023-07-29T22:33:44+00:00" - }, - { - "name": "jms/serializer-bundle", - "version": "5.3.1", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/JMSSerializerBundle.git", - "reference": "3279738a958454793ca1e318a7dab6cfcff60124" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/JMSSerializerBundle/zipball/3279738a958454793ca1e318a7dab6cfcff60124", - "reference": "3279738a958454793ca1e318a7dab6cfcff60124", - "shasum": "" - }, - "require": { - "jms/metadata": "^2.5", - "jms/serializer": "^3.20", - "php": "^7.2 || ^8.0", - "symfony/dependency-injection": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/framework-bundle": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "doctrine/coding-standard": "^8.1", - "doctrine/orm": "^2.4", - "phpunit/phpunit": "^8.0 || ^9.0", - "symfony/expression-language": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/finder": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/form": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/stopwatch": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/templating": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/twig-bundle": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/uid": "^5.1 || ^6.0", - "symfony/validator": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "suggest": { - "symfony/expression-language": "Required for opcache preloading ^3.4 || ^4.0 || ^5.0 || ^6.0", - "symfony/finder": "Required for cache warmup, supported versions ^3.4 || ^4.0 || ^5.0 || ^6.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "JMS\\SerializerBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "Allows you to easily serialize, and deserialize data of any complexity", - "homepage": "http://jmsyst.com/bundles/JMSSerializerBundle", - "keywords": [ - "deserialization", - "json", - "serialization", - "xml" - ], - "support": { - "issues": "https://github.com/schmittjoh/JMSSerializerBundle/issues", - "source": "https://github.com/schmittjoh/JMSSerializerBundle/tree/5.3.1" - }, - "funding": [ - { - "url": "https://github.com/goetas", - "type": "github" - } - ], - "time": "2023-06-13T14:47:57+00:00" - }, { "name": "knplabs/knp-components", "version": "v4.2.0", @@ -6439,6 +6192,100 @@ ], "time": "2023-07-13T14:29:38+00:00" }, + { + "name": "symfony/serializer", + "version": "v6.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "33deb86d212893042d7758d452aa39d19ca0efe3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/33deb86d212893042d7758d452aa39d19ca0efe3", + "reference": "33deb86d212893042d7758d452aa39d19ca0efe3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<5.4", + "symfony/property-access": "<5.4", + "symfony/property-info": "<5.4.24|>=6,<6.2.11", + "symfony/uid": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "symfony/cache": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4.24|^6.2.11", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0", + "symfony/var-exporter": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.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/v6.3.3" + }, + "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-07-31T07:08:24+00:00" + }, { "name": "symfony/service-contracts", "version": "v3.3.0", diff --git a/config/bundles.php b/config/bundles.php index 6bc19b2..4d17931 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -1,5 +1,4 @@ ['all' => true], @@ -13,7 +12,6 @@ return [ Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], - JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true], Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], ]; diff --git a/config/packages/jms_serializer.yaml b/config/packages/jms_serializer.yaml deleted file mode 100644 index 9976674..0000000 --- a/config/packages/jms_serializer.yaml +++ /dev/null @@ -1,30 +0,0 @@ -jms_serializer: - visitors: - xml_serialization: - format_output: '%kernel.debug%' -# metadata: -# auto_detection: false -# directories: -# any-name: -# namespace_prefix: "My\\FooBundle" -# path: "@MyFooBundle/Resources/config/serializer" -# another-name: -# namespace_prefix: "My\\BarBundle" -# path: "@MyBarBundle/Resources/config/serializer" - -when@prod: - jms_serializer: - visitors: - json_serialization: - options: - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION - -when@dev: - jms_serializer: - visitors: - json_serialization: - options: - - JSON_PRETTY_PRINT - - JSON_UNESCAPED_SLASHES - - JSON_PRESERVE_ZERO_FRACTION diff --git a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Auth.yml b/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Auth.yml deleted file mode 100644 index 2dd12d4..0000000 --- a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Auth.yml +++ /dev/null @@ -1,11 +0,0 @@ -Skobkin\Bundle\PointToolsBundle\DTO\Api\Auth: - exclusion_policy: none - access_type: public_method - properties: - token: - type: string - csRfToken: - type: string - serialized_name: 'csrf_token' - error: - type: string diff --git a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Comment.yml b/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Comment.yml deleted file mode 100644 index 4e24a05..0000000 --- a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Comment.yml +++ /dev/null @@ -1,23 +0,0 @@ -Skobkin\Bundle\PointToolsBundle\DTO\Api\Comment: - exclusion_policy: none - access_type: public_method - properties: - postId: - serialized_name: 'post_id' - type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post' - max_depth: 2 - number: - serialized_name: 'id' - type: integer - toCommentId: - serialized_name: 'to_comment_id' - type: integer - created: - type: string - text: - type: string - author: - type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\User' - isRec: - serialized_name: 'is_rec' - type: boolean diff --git a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.MetaPost.yml b/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.MetaPost.yml deleted file mode 100644 index fe6290f..0000000 --- a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.MetaPost.yml +++ /dev/null @@ -1,10 +0,0 @@ -Skobkin\Bundle\PointToolsBundle\DTO\Api\MetaPost: - exclusion_policy: none - access_type: public_method - properties: - post: - type: 'Skobkin\Bundle\PointToolsBundle\DTO\Api\Post' - max_depth: 2 - comments: - type: 'array' - max_depth: 2 diff --git a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Post.yml b/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Post.yml deleted file mode 100644 index 31d3de4..0000000 --- a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.Post.yml +++ /dev/null @@ -1,25 +0,0 @@ -Skobkin\Bundle\PointToolsBundle\DTO\Api\Post: - exclusion_policy: none - access_type: public_method - properties: - id: - type: string - #groups: [] - tags: - type: 'array' - files: - type: 'array' - author: - type: Skobkin\Bundle\PointToolsBundle\DTO\Api\User - max_depth: 1 - text: - type: string - created: - type: string - type: - type: string - private: - type: boolean - - #callback_methods: - # post_deserialize: [foo, bar] diff --git a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.PostsPage.yml b/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.PostsPage.yml deleted file mode 100644 index 40ab540..0000000 --- a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.PostsPage.yml +++ /dev/null @@ -1,10 +0,0 @@ -Skobkin\Bundle\PointToolsBundle\DTO\Api\PostsPage: - exclusion_policy: none - access_type: public_method - properties: - posts: - type: 'array' - max_depth: 3 - hasNext: - serialized_name: 'has_next' - type: boolean diff --git a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.User.yml b/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.User.yml deleted file mode 100644 index 8df91bd..0000000 --- a/old/src/PointToolsBundle/Resources/config/serializer/DTO.Api.User.yml +++ /dev/null @@ -1,49 +0,0 @@ -Skobkin\Bundle\PointToolsBundle\DTO\Api\User: - exclusion_policy: none - access_type: public_method - properties: - id: - type: integer - groups: [user_short, user_full] - login: - type: string - groups: [user_short, user_full] - name: - type: string - groups: [user_short, user_full] - about: - type: string - groups: [user_full] - xmpp: - type: string - groups: [user_full] - created: - type: string - groups: [user_full] - gender: - type: boolean - groups: [user_full] - denyAnonymous: - serialized_name: 'deny_anonymous' - type: boolean - groups: [user_full] - private: - type: boolean - groups: [user_full] - birthDate: - serialized_name: 'birthdate' - type: string - groups: [user_full] - homepage: - type: string - groups: [user_full] - email: - type: string - groups: [user_full] - location: - type: string - groups: [user_full] - - # TODO automatically convert string date to DateTime - #callback_methods: - # post_deserialize: [foo, bar] diff --git a/old/src/PointToolsBundle/Resources/config/services.yml b/old/src/PointToolsBundle/Resources/config/services.yml deleted file mode 100644 index 0baad47..0000000 --- a/old/src/PointToolsBundle/Resources/config/services.yml +++ /dev/null @@ -1 +0,0 @@ -services: diff --git a/old/tests/Skobkin/PointToolsBundle/Service/Factory/UserFactoryTest.php b/old/tests/Skobkin/PointToolsBundle/Service/Factory/UserFactoryTest.php index c31312c..126f5bd 100644 --- a/old/tests/Skobkin/PointToolsBundle/Service/Factory/UserFactoryTest.php +++ b/old/tests/Skobkin/PointToolsBundle/Service/Factory/UserFactoryTest.php @@ -1,4 +1,5 @@ getOption('dry-run')) { $this->em->persist($user); diff --git a/src/Controller/Api/CrawlerController.php b/src/Controller/Api/CrawlerController.php index 85d3422..acec7a5 100644 --- a/src/Controller/Api/CrawlerController.php +++ b/src/Controller/Api/CrawlerController.php @@ -6,8 +6,8 @@ namespace App\Controller\Api; use App\DTO\Api\PostsPage; use App\Factory\Blog\PostFactory; use Doctrine\ORM\EntityManagerInterface; -use JMS\Serializer\SerializerInterface; use Symfony\Component\HttpFoundation\{Request, Response}; +use Symfony\Component\Serializer\SerializerInterface; class CrawlerController extends AbstractApiController { diff --git a/src/DTO/Api/Auth.php b/src/DTO/Api/Auth.php index 9eca83e..c8bcaa9 100644 --- a/src/DTO/Api/Auth.php +++ b/src/DTO/Api/Auth.php @@ -3,41 +3,18 @@ declare(strict_types=1); namespace App\DTO\Api; -/** TODO: Refactor to public readonly */ +use Symfony\Component\Serializer\Annotation\SerializedName; + class Auth implements ValidableInterface { - private ?string $token; - private ?string $csRfToken; - private ?string $error; - - public function getToken(): ?string - { - return $this->token; - } - - public function setToken(?string $token): void - { - $this->token = $token; - } - - public function getCsRfToken(): ?string - { - return $this->csRfToken; - } - - public function setCsRfToken(?string $csRfToken): void - { - $this->csRfToken = $csRfToken; - } - - public function getError(): ?string - { - return $this->error; - } - - public function setError(?string $error): void - { - $this->error = $error; + public function __construct( + #[SerializedName('token')] + public readonly ?string $token, + #[SerializedName('error')] + public readonly ?string $error, + #[SerializedName('csrf_token')] + public readonly ?string $csRfToken, + ) { } public function isValid(): bool diff --git a/src/DTO/Api/Comment.php b/src/DTO/Api/Comment.php index 550aeb2..dca530f 100644 --- a/src/DTO/Api/Comment.php +++ b/src/DTO/Api/Comment.php @@ -3,91 +3,26 @@ declare(strict_types=1); namespace App\DTO\Api; -/** TODO: Refactor to public readonly */ +use Symfony\Component\Serializer\Annotation\SerializedName; + class Comment implements ValidableInterface { - private ?string $postId; - private ?int $number; - private ?int $toCommentId; - private ?string $created; - private ?string $text; - private ?User $author; - private ?bool $isRec; - - - public function getPostId(): ?string - { - return $this->postId; - } - - public function setPostId(?string $postId): void - { - $this->postId = $postId; - } - - public function getNumber(): ?int - { - return $this->number; - } - - public function setNumber(?int $number): void - { - $this->number = $number; - } - - public function getToCommentId(): ?int - { - return $this->toCommentId; - } - - public function setToCommentId(?int $toCommentId): void - { - $this->toCommentId = $toCommentId; - } - - public function getCreated(): string - { - return $this->created; - } - - public function setCreated(?string $created): void - { - $this->created = $created; - } - - public function getText(): ?string - { - return $this->text; - } - - public function setText(?string $text): void - { - $this->text = $text; - } - - public function getAuthor(): ?User - { - return $this->author; - } - - public function setAuthor(?User $author): void - { - $this->author = $author; - } - - public function isIsRec(): ?bool - { - return $this->isRec; - } - - public function getIsRec(): ?bool - { - return $this->isRec; - } - - public function setIsRec(?bool $isRec): void - { - $this->isRec = $isRec; + public function __construct( + #[SerializedName('post_id')] + public readonly ?string $postId, + #[SerializedName('id')] + public readonly ?int $number, + #[SerializedName('to_comment_id')] + public readonly ?int $toCommentId, + #[SerializedName('created')] + public readonly ?\DateTimeImmutable $created, + #[SerializedName('text')] + public readonly ?string $text, + #[SerializedName('author')] + public readonly ?User $author, + #[SerializedName('is_rec')] + public readonly ?bool $isRec, + ) { } public function isValid(): bool diff --git a/src/DTO/Api/MetaPost.php b/src/DTO/Api/MetaPost.php index 9e31a1b..0c62a8c 100644 --- a/src/DTO/Api/MetaPost.php +++ b/src/DTO/Api/MetaPost.php @@ -3,34 +3,21 @@ declare(strict_types=1); namespace App\DTO\Api; -/** TODO: Refactor to public readonly */ +use Symfony\Component\Serializer\Annotation\{MaxDepth, SerializedName}; + class MetaPost implements ValidableInterface { - private ?Post $post; - /** @var Comment[]|null */ - private ?array $comments; - - - public function getPost(): ?Post - { - return $this->post; - } - - public function setPost(?Post $post): void - { - $this->post = $post; - } - - /** @return Comment[]|null */ - public function getComments(): ?array - { - return $this->comments; - } - - /** @param Comment[]|null $comments */ - public function setComments(?array $comments): void - { - $this->comments = $comments; + /** + * @param Comment[] $comments + */ + public function __construct( + #[SerializedName('post')] + #[MaxDepth(2)] + public readonly ?Post $post, + #[SerializedName('comments')] + #[MaxDepth(2)] + public readonly ?array $comments, + ) { } public function isValid(): bool diff --git a/src/DTO/Api/Post.php b/src/DTO/Api/Post.php index 7cdea5a..78269c0 100644 --- a/src/DTO/Api/Post.php +++ b/src/DTO/Api/Post.php @@ -3,108 +3,32 @@ declare(strict_types=1); namespace App\DTO\Api; -/** TODO: Refactor to public readonly */ +use App\Enum\Blog\PostTypeEnum; +use Symfony\Component\Serializer\Annotation\{Context, MaxDepth, SerializedName}; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; + class Post implements ValidableInterface { - private ?string $id; - /** @var string[]|null */ - private ?array $tags; - /** @var string[]|null */ - private ?array $files; - private ?User $author; - private ?string $text; - private ?string $created; - private ?string $type; - private ?bool $private; - - public function getId(): ?string - { - return $this->id; - } - - public function setId(?string $id): void - { - $this->id = $id; - } - - /** @return string[]|null */ - public function getTags(): ?array - { - return $this->tags; - } - - public function setTags(?array $tags): void - { - $this->tags = $tags; - } - - /** @return string[]|null */ - public function getFiles(): ?array - { - return $this->files; - } - - /** - * @param string[]|null $files - */ - public function setFiles(?array $files): void - { - $this->files = $files; - } - - public function getAuthor(): ?User - { - return $this->author; - } - - public function setAuthor(?User $author): void - { - $this->author = $author; - } - - public function getText(): ?string - { - return $this->text; - } - - public function setText(?string $text): void - { - $this->text = $text; - } - - public function getCreated(): ?string - { - return $this->created; - } - - public function setCreated(?string $created): void - { - $this->created = $created; - } - - public function getType(): ?string - { - return $this->type; - } - - public function setType(?string $type): void - { - $this->type = $type; - } - - public function getPrivate(): ?bool - { - return $this->private; - } - - public function isPrivate(): ?bool - { - return $this->private; - } - - public function setPrivate(?bool $private): void - { - $this->private = $private; + public function __construct( + #[SerializedName('id')] + public readonly ?string $id, + #[SerializedName('tags')] + public readonly ?array $tags, + #[SerializedName('files')] + public readonly ?array $files, + #[SerializedName('author')] + #[MaxDepth(1)] + public readonly ?User $author, + #[SerializedName('text')] + public readonly ?string $text, + #[SerializedName('created')] + #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d_H:i:s'])] + public readonly ?\DateTimeImmutable $created, + #[SerializedName('type')] + public readonly ?PostTypeEnum $type, + #[SerializedName('private')] + public readonly ?bool $private, + ) { } public function isValid(): bool diff --git a/src/DTO/Api/PostsPage.php b/src/DTO/Api/PostsPage.php index d0e4fa1..bcd4d46 100644 --- a/src/DTO/Api/PostsPage.php +++ b/src/DTO/Api/PostsPage.php @@ -3,33 +3,15 @@ declare(strict_types=1); namespace App\DTO\Api; -/** TODO: Refactor to public readonly */ class PostsPage implements ValidableInterface { - /** @var MetaPost[]|null */ - private ?array $posts; - private ?bool $hasNext; - - /** @return MetaPost[]|null */ - public function getPosts(): ?array - { - return $this->posts; - } - - /** @param MetaPost[]|null $posts */ - public function setPosts(?array $posts): void - { - $this->posts = $posts; - } - - public function getHasNext(): ?bool - { - return $this->hasNext; - } - - public function setHasNext(?bool $hasNext): void - { - $this->hasNext = $hasNext; + /** + * @param MetaPost[]|null $posts + */ + public function __construct( + public readonly ?array $posts, + public readonly ?bool $hasNext, + ) { } public function isValid(): bool diff --git a/src/DTO/Api/User.php b/src/DTO/Api/User.php index 345ccda..44bf52b 100644 --- a/src/DTO/Api/User.php +++ b/src/DTO/Api/User.php @@ -3,166 +3,57 @@ declare(strict_types=1); namespace App\DTO\Api; -/** TODO: Refactor to public readonly */ +use Symfony\Component\Serializer\Annotation\{Context, Groups, SerializedName}; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; + class User implements ValidableInterface { - private ?string $id; - private ?string $login; - private ?string $name; - private ?string $about; - private ?string $xmpp; - private ?string $created; - private ?bool $gender; - private ?bool $denyAnonymous; - private ?bool $private; - private ?string $birthDate; - private ?string $homepage; - private ?string $email; - private ?string $location; - private ?string $error; - - public function getId(): ?string - { - return $this->id; - } - - public function setId(?string $id): void - { - $this->id = $id; - } - - public function getLogin(): ?string - { - return $this->login; - } - - public function setLogin(?string $login): void - { - $this->login = $login; - } - - public function getName(): ?string - { - return $this->name; - } - - public function setName(?string $name): void - { - $this->name = $name; - } - - public function getAbout(): ?string - { - return $this->about; - } - - public function setAbout(?string $about): void - { - $this->about = $about; - } - - public function getXmpp(): ?string - { - return $this->xmpp; - } - - public function setXmpp(?string $xmpp): void - { - $this->xmpp = $xmpp; - } - - public function getCreated(): ?string - { - return $this->created; - } - - public function setCreated(?string $created): void - { - $this->created = $created; - } - - public function getGender(): ?bool - { - return $this->gender; - } - - public function setGender(?bool $gender): void - { - $this->gender = $gender; - } - - public function getDenyAnonymous(): ?bool - { - return $this->denyAnonymous; - } - - public function setDenyAnonymous(?bool $denyAnonymous): void - { - $this->denyAnonymous = $denyAnonymous; - } - - public function getPrivate(): ?bool - { - return $this->private; - } - - public function setPrivate(?bool $private): void - { - $this->private = $private; - } - - public function getBirthDate(): ?string - { - return $this->birthDate; - } - - public function setBirthDate(?string $birthDate): void - { - $this->birthDate = $birthDate; - } - - public function getHomepage(): ?string - { - return $this->homepage; - } - - public function setHomepage(?string $homepage): void - { - $this->homepage = $homepage; - } - - public function getEmail(): ?string - { - return $this->email; - } - - public function setEmail(?string $email): void - { - $this->email = $email; - } - - public function getLocation(): ?string - { - return $this->location; - } - - public function setLocation(?string $location): void - { - $this->location = $location; - } - - public function getError(): ?string - { - return $this->error; - } - - public function setError(?string $error): void - { - $this->error = $error; + public function __construct( + #[SerializedName('id')] + #[Groups(['user_short', 'user_full'])] + public readonly ?int $id, + #[SerializedName('login')] + #[Groups(['user_short', 'user_full'])] + public readonly ?string $login, + #[SerializedName('name')] + #[Groups(['user_short', 'user_full'])] + public readonly ?string $name, + #[SerializedName('about')] + #[Groups(['user_full'])] + public readonly ?string $about, + #[SerializedName('xmpp')] + #[Groups(['user_full'])] + public readonly ?string $xmpp, + #[SerializedName('created')] + #[Groups(['user_full'])] + #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d_H:i:s'])] + public readonly ?\DateTimeImmutable $created, + #[SerializedName('gender')] + #[Groups(['user_full'])] + public readonly ?bool $gender, + #[SerializedName('deny_anonymous')] + #[Groups(['user_full'])] + public readonly ?bool $denyAnonymous, + #[SerializedName('private')] + #[Groups(['user_full'])] + public readonly ?bool $private, + #[SerializedName('birthdate')] + #[Groups(['user_full'])] + public readonly ?string $birthDate, + #[SerializedName('homepage')] + #[Groups(['user_full'])] + public readonly ?string $homepage, + #[SerializedName('email')] + #[Groups(['user_full'])] + public readonly ?string $email, + #[SerializedName('location')] + #[Groups(['user_full'])] + public readonly ?string $location, + ) { } public function isValid(): bool { - return null === $this->error && null !== $this->id && null !== $this->login; + return null !== $this->id && null !== $this->login; } } diff --git a/src/DTO/Api/ValidableInterface.php b/src/DTO/Api/ValidableInterface.php index b7050cd..0af28cc 100644 --- a/src/DTO/Api/ValidableInterface.php +++ b/src/DTO/Api/ValidableInterface.php @@ -3,7 +3,9 @@ declare(strict_types=1); namespace App\DTO\Api; +/** @deprecated Use Symfony Validator instead */ interface ValidableInterface { + /** @deprecated Use Symfony Validator instead */ public function isValid(): bool; } diff --git a/src/DataFixtures/LoadUserData.php b/src/DataFixtures/LoadUserData.php index 4db9bfd..f6b2670 100644 --- a/src/DataFixtures/LoadUserData.php +++ b/src/DataFixtures/LoadUserData.php @@ -29,7 +29,7 @@ class LoadUserData extends Fixture implements OrderedFixtureInterface public function load(ObjectManager $om) { foreach ($this->users as $userData) { - $user = new User($userData['id'], new \DateTime(), $userData['login'], $userData['name']); + $user = new User($userData['id'], $userData['login'], new \DateTime(), $userData['name']); $user->updatePrivacy(!$userData['private'], $userData['whitelist-only']); $om->persist($user); diff --git a/src/Entity/Blog/Post.php b/src/Entity/Blog/Post.php index 6d54704..7a128f8 100644 --- a/src/Entity/Blog/Post.php +++ b/src/Entity/Blog/Post.php @@ -22,7 +22,7 @@ class Post private string $text; #[ORM\Column(name: 'created_at', type: 'datetime')] - private \DateTime $createdAt; + private \DateTimeImmutable $createdAt; #[ORM\Column(name: 'updated_at', type: 'datetime', nullable: true)] private ?\DateTime $updatedAt; @@ -56,7 +56,7 @@ class Post private Collection $comments; - public function __construct(string $id, User $author, \DateTime $createdAt, PostTypeEnum $type) + public function __construct(string $id, User $author, \DateTimeImmutable $createdAt, PostTypeEnum $type) { $this->id = $id; $this->author = $author; @@ -91,7 +91,7 @@ class Post return $this->text; } - public function getCreatedAt(): \DateTime + public function getCreatedAt(): \DateTimeImmutable { return $this->createdAt; } diff --git a/src/Entity/User.php b/src/Entity/User.php index 019923c..5b9ab77 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -54,8 +54,8 @@ class User public function __construct( int $id, + string $login, \DateTime $createdAt = null, - string $login = null, string $name = null ) { $this->id = $id; diff --git a/src/Factory/AbstractFactory.php b/src/Factory/AbstractFactory.php index a334d37..3137d29 100644 --- a/src/Factory/AbstractFactory.php +++ b/src/Factory/AbstractFactory.php @@ -8,7 +8,7 @@ use Psr\Log\LoggerInterface; abstract class AbstractFactory { public function __construct( - protected LoggerInterface $logger, + protected readonly LoggerInterface $logger, ) { } } diff --git a/src/Factory/Blog/CommentFactory.php b/src/Factory/Blog/CommentFactory.php deleted file mode 100644 index 0db3a32..0000000 --- a/src/Factory/Blog/CommentFactory.php +++ /dev/null @@ -1,21 +0,0 @@ -getPosts() as $postData) { + /** @var MetaPost $postData */ + foreach ($page->posts ?? [] as $postData) { try { - if (null === $this->postRepository->find($postData->getPost()->getId())) { + if (null === $this->postRepository->find($postData->post->id)) { $hasNew = true; } @@ -50,7 +49,7 @@ class PostFactory extends AbstractFactory $posts[] = $post; } catch (\Exception $e) { $this->logger->error('Error while processing post DTO', [ - 'post_id' => $postData->getPost()->getId(), + 'post_id' => $postData->post->id, 'exception' => get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), @@ -82,10 +81,10 @@ class PostFactory extends AbstractFactory throw new InvalidDataException('Invalid post data'); } - $postData = $metaPost->getPost(); + $postData = $metaPost->post; try { - $author = $this->userFactory->findOrCreateFromDTO($metaPost->getPost()->getAuthor()); + $author = $this->userFactory->findOrCreateFromDTO($postData->author); } catch (\Exception $e) { $this->logger->error('Error while creating user from DTO'); throw $e; @@ -94,14 +93,14 @@ class PostFactory extends AbstractFactory $post = $this->findOrCreateFromDto($postData, $author); try { - $this->updatePostTags($post, $postData->getTags() ?: []); + $this->updatePostTags($post, $postData->tags ?? []); } catch (\Exception $e) { $this->logger->error('Error while updating post tags'); throw $e; } try { - $this->updatePostFiles($post, $postData->getFiles() ?: []); + $this->updatePostFiles($post, $postData->files ?? []); } catch (\Exception $e) { $this->logger->error('Error while updating post files'); throw $e; @@ -112,20 +111,19 @@ class PostFactory extends AbstractFactory private function findOrCreateFromDto(PostDTO $postData, User $author): Post { - if (null === ($post = $this->postRepository->find($postData->getId()))) { - // Creating new post + if (null === ($post = $this->postRepository->find($postData->id))) { $post = new Post( - $postData->getId(), + $postData->id, $author, - new \DateTime($postData->getCreated()), - PostTypeEnum::tryFrom($postData->getType()) ?? PostTypeEnum::Post, + $postData->created, + $postData->type ?? PostTypeEnum::Post, ); $this->postRepository->save($post); } $post - ->setText($postData->getText()) - ->setPrivate($postData->getPrivate()) + ->setText($postData->text) + ->setPrivate($postData->private) ; return $post; diff --git a/src/Factory/UserFactory.php b/src/Factory/UserFactory.php index fac27c1..bb3e3ff 100644 --- a/src/Factory/UserFactory.php +++ b/src/Factory/UserFactory.php @@ -29,18 +29,19 @@ class UserFactory extends AbstractFactory } /** @var User $user */ - if (null === ($user = $this->userRepository->find($userData->getId()))) { + if (null === ($user = $this->userRepository->find($userData->id))) { $user = new User( - (int) $userData->getId(), - \DateTime::createFromFormat('Y-m-d_H:i:s', $userData->getCreated()) ?: new \DateTime() + $userData->id, + $userData->login, + $userData->created, ); - $this->userRepository->add($user); + $this->userRepository->save($user); } - $user->updateLoginAndName($userData->getLogin(), $userData->getName()); + $user->updateLoginAndName($userData->login, $userData->name); - if (null !== $userData->getDenyAnonymous() && null !== $userData->getPrivate()) { - $user->updatePrivacy(!$userData->getDenyAnonymous(), $userData->getPrivate()); + if (null !== $userData->denyAnonymous && null !== $userData->private) { + $user->updatePrivacy(!$userData->denyAnonymous, $userData->private); } return $user; diff --git a/src/Service/Api/AbstractApi.php b/src/Service/Api/AbstractApi.php index e45d165..a93b3ef 100644 --- a/src/Service/Api/AbstractApi.php +++ b/src/Service/Api/AbstractApi.php @@ -9,14 +9,14 @@ use App\Exception\Api\{ApiException, NotFoundException, UnauthorizedException, ServerProblemException}; -use JMS\Serializer\SerializerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; +use Symfony\Component\Serializer\Context\Normalizer\ObjectNormalizerContextBuilder; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; -use Symfony\Contracts\HttpClient\HttpClientInterface; -use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\HttpClient\{HttpClientInterface, ResponseInterface}; -class AbstractApi +abstract class AbstractApi { protected HttpClientInterface $client; // TODO: check if these are still needed @@ -32,24 +32,24 @@ class AbstractApi } /** Make GET request and return DTO objects */ - public function getGetJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null): array|object|null + public function getGetJsonData(string $path, array $parameters = [], string $type, array $groups = []): array|object|null { return $this->serializer->deserialize( $this->getGetResponseBody($path, $parameters), $type, 'json', - $context + $this->createContext($groups), ); } /** Make POST request and return DTO objects */ - public function getPostJsonData(string $path, array $parameters = [], string $type, DeserializationContext $context = null): array|object|null + public function getPostJsonData(string $path, array $parameters = [], string $type, array $groups = []): array|object|null { return $this->serializer->deserialize( $this->getPostResponseBody($path, $parameters), $type, 'json', - $context + $this->createContext($groups) ); } @@ -65,6 +65,13 @@ class AbstractApi return $this->sendPostRequest($path, $parameters)->getContent(); } + private function createContext(array $groups): array + { + return (new ObjectNormalizerContextBuilder()) + ->withGroups($groups) + ->toArray(); + } + private function sendGetRequest(string $path, array $parameters = []): ResponseInterface { $this->logger->debug('Sending GET request', ['path' => $path, 'parameters' => $parameters]); @@ -103,7 +110,7 @@ class AbstractApi // Temporary fix until @arts fixes this bug if ('{"error": "UserNotFound"}' === $response->getContent()) { throw new NotFoundException('Not found', SymfonyResponse::HTTP_NOT_FOUND); - } elseif ('{"message": "Forbidden", "code": 403, "error": "Forbidden"}' === (string) $response->getBody()) { + } elseif ('{"message": "Forbidden", "code": 403, "error": "Forbidden"}' === (string) $response->getContent()) { throw new ForbiddenException('Forbidden', SymfonyResponse::HTTP_FORBIDDEN); } diff --git a/src/Service/Api/PostApi.php b/src/Service/Api/PostApi.php deleted file mode 100644 index a41ee73..0000000 --- a/src/Service/Api/PostApi.php +++ /dev/null @@ -1,22 +0,0 @@ -getGetJsonData( self::PREFIX.urlencode($login).'/subscribers', [], - 'array<'.UserDTO::class.'>', - DeserializationContext::create()->setGroups(['user_short']), + UserDTO::class.'[]', + ['user_short'], ); } catch (NotFoundException $e) { throw new UserNotFoundException('User not found', 0, $e, null, $login); @@ -108,8 +108,8 @@ class UserApi extends AbstractApi $usersList = $this->getGetJsonData( self::PREFIX.'id/'.$id.'/subscribers', [], - 'array<'.UserDTO::class.'>', - DeserializationContext::create()->setGroups(['user_short']), + UserDTO::class.'[]', + ['user_short'], ); } catch (NotFoundException $e) { throw new UserNotFoundException('User not found', 0, $e, $id); @@ -128,7 +128,7 @@ class UserApi extends AbstractApi self::PREFIX.'login/'.urlencode($login), [], UserDTO::class, - DeserializationContext::create()->setGroups(['user_full']), + ['user_full'], ); } catch (NotFoundException $e) { throw new UserNotFoundException('User not found', 0, $e, null, $login); @@ -147,7 +147,7 @@ class UserApi extends AbstractApi self::PREFIX.'id/'.$id, [], UserDTO::class, - DeserializationContext::create()->setGroups(['user_full']), + ['user_full'], ); } catch (NotFoundException $e) { throw new UserNotFoundException('User not found', 0, $e, $id); diff --git a/symfony.lock b/symfony.lock index 3170f0c..1bf43fe 100644 --- a/symfony.lock +++ b/symfony.lock @@ -47,18 +47,6 @@ "migrations/.gitignore" ] }, - "jms/serializer-bundle": { - "version": "5.2", - "recipe": { - "repo": "github.com/symfony/recipes-contrib", - "branch": "main", - "version": "4.0", - "ref": "cc04e10cf7171525b50c18b36004edf64cb478be" - }, - "files": [ - "config/packages/jms_serializer.yaml" - ] - }, "knplabs/knp-paginator-bundle": { "version": "v6.2.0" },