{"id":343174,"date":"2022-12-26T15:02:57","date_gmt":"2022-12-26T15:02:57","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=343174"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2022-12-26T15:02:57","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=343174","title":{"rendered":"<span>API platform \u043f\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u043c\u0443<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/912\/ee3\/d9e\/912ee3d9e1a5d036c55ac88b0abce3cf.jpg\" width=\"3508\" height=\"2480\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/912\/ee3\/d9e\/912ee3d9e1a5d036c55ac88b0abce3cf.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/api-platform.com\" rel=\"noopener noreferrer nofollow\">API platform<\/a> \u044d\u0442\u043e \u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 REST API, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0437\u0430 \u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 \u043c\u0438\u043d\u0443\u0442\u044b.\u0412\u043e\u0442 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0444\u0438\u0447:<\/p>\n<p>&#8212; \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f CRUD<\/p>\n<p>&#8212; \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GraphQL<\/p>\n<p>&#8212; \u041c\u0430\u0448\u0438\u043d\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f API \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 Hydra \u0438 Swagger\/Open API, \u0433\u0435\u043d\u0435\u043d\u0440\u0438\u0442\u0441\u044f \u0438\u0437 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 PHPDoc, Serializer, Validator \u0438 Doctrine ORM \/ MongoDB ODM<\/p>\n<p>&#8212; \u0425\u043e\u0440\u043e\u0448\u0430\u044f \u0443\u0434\u043e\u0431\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 Swagger (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0443) \u0438 \/ \u0438\u043b\u0438 ReDoc<\/p>\n<p>&#8212; \u041f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f<\/p>\n<p>&#8212; \u041a\u0443\u0447\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432<\/p>\n<p>&#8212; \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 Symfony Validator (\u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0433\u0440\u0443\u043f\u043f)<\/p>\n<p>&#8212; \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<p>&#8212; \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443 Symfony Serializer (\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0433\u0440\u0443\u043f\u043f, \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0439, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0433\u043b\u0443\u0431\u0438\u043d\u0430&#8230;)<\/p>\n<p>&#8212; \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 JWT \u0438 OAuth- \u0424\u0430\u0439\u043b\u044b \u0438 \\DateTime, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/p>\n<p>&#8212; \u0412\u0441\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043c\u043e\u0449\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438 \u0441\u0438\u043b\u044c\u043d\u043e\u043c\u0443 \u041e\u041e\u041f.<\/p>\n<hr\/>\n<p>\u0421\u0440\u0430\u0437\u0443 \u043e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e \u0432 Symfony \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c API \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440<\/p>\n<pre><code>php bin\/console make:controller --no-template SuperApiController<\/code><\/pre>\n<p>\u0438 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438 <a href=\"https:\/\/symfony.com\/bundles\/SensioFrameworkExtraBundle\/current\/annotations\/security.html\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u0441\u0442\u0443\u043f\u044b \u0447\u0435\u0440\u0435\u0437 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b<\/a><\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">namespace App\\Controller;  use Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController; use Symfony\\Component\\HttpFoundation\\JsonResponse; use Symfony\\Component\\Routing\\Annotation\\Route; use Symfony\\Component\\Security\\Http\\Attribute\\IsGranted;  class SuperApiController extends AbstractController {     #[Route('\/super\/api', name: 'app_super_api')]     #[IsGranted(\"ROLE_ADMIN\")]     public function index(): JsonResponse     {         return $this->json([             'message' => 'Welcome to your new controller!',             'path' => 'src\/Controller\/SuperApiController.php',         ]);     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434, \u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u0436\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0439, \u043f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043d\u0435 \u043f\u043e\u0439\u0442\u0438 \u0442\u0430\u043a\u043e\u0439 \u0434\u043e\u0440\u043e\u0433\u043e\u0439? <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u0442\u044b \u0443\u0437\u043d\u0430\u0435\u0448\u044c:<\/p>\n<ul>\n<li>\n<p>\u041a\u0430\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e API platform \u0441\u043e\u0437\u0434\u0430\u0442\u044c CRUD \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e swagger.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u044b \u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043c \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0447\u0435\u0440\u0435\u0437 API \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u0435.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c API platform<\/h3>\n<pre><code>composer require api<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<pre><code>php bin\/console make:user<\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c,\u00a0\u0434\u0430\u0432\u0430\u0439 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u00a0<strong>[ApiResource]<\/strong>\u00a0\u0443 \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438<\/p>\n<pre><code class=\"php\">namespace App\\Entity;  use ApiPlatform\\Metadata\\ApiResource;   #[ApiResource] \/\/&lt;-- \u0414\u043e\u0431\u0430\u0432\u0438\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e #[ORM\\Entity(repositoryClass: UserRepository::class)] class User implements UserInterface, PasswordAuthenticatedUserInterface { ... <\/code><\/pre>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e!\u00a0\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 REST Api.<\/p>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435\u00a0<a href=\"http:\/\/localhost\/api\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost\/api<\/a><\/p>\n<p>\u0418 \u0432\u0438\u0434\u0438\u043c,\u00a0\u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f CRUD.\u00a0\u041f\u043e \u0441\u0443\u0442\u0438,\u00a0\u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u043e\u0447\u043a\u0443 \u043a\u043e\u0434\u0430,\u00a0\u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u043e\u0434\u0438\u043d \u0430\u0442\u0440\u0438\u0431\u0443\u0442,\u00a0\u0438 \u0443\u0436\u0435 \u0438\u043c\u0435\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u043c\u043e\u0449\u043d\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/14a\/931\/da2\/14a931da25a7b21f9c327f3797d79f60.png\" width=\"2748\" height=\"1016\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/14a\/931\/da2\/14a931da25a7b21f9c327f3797d79f60.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e,\u00a0\u0434\u0432\u0438\u0436\u0435\u043c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<h3>\u0414\u0435\u043b\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0440\u0435\u0433\u0438\u0441\u0442\u0430\u0440\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/h3>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u0435 \u0442\u043e\u043a\u0435\u043d \u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<pre><code>php bin\/console make:entity User<\/code><\/pre>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u0435\u00a0<strong>token<\/strong><\/p>\n<p>\u0412 \u0441\u0430\u043c\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438,\u00a0\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u0435\u00a0<em>plainPassword<\/em>,\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">use Symfony\\Component\\Serializer\\Annotation\\SerializedName; \/\/....  class User implements UserInterface, PasswordAuthenticatedUserInterface { \/\/...  #[SerializedName('password')] private $plainPassword;     \/**      * @return mixed      *\/     public function getPlainPassword()     {         return $this->plainPassword;     }      \/**      * @param   mixed  $plainPassword      *\/     public function setPlainPassword($plainPassword): void     {         $this->plainPassword = $plainPassword;     }    \/**      * @see UserInterface      *\/     public function eraseCredentials()     {         \/\/ If you store any temporary, sensitive data on the user, clear it here          $this->plainPassword = null; \/\/ &lt;--- \u0420\u0430\u0441\u043a\u043e\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u044d\u0442\u0443 \u0441\u0442\u0440\u043e\u0447\u043a\u0443      } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439,\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0445\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u043e\u043b\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p><a href=\"https:\/\/github.com\/kxxb\/docs-ru\/blob\/3.0\/core\/state-processors-ru.md\" rel=\"noopener noreferrer nofollow\"><strong><em>\u0421\u043c\u043e\u0442\u0440\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u0440\u043e \u041f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u044b \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439<\/em><\/strong><\/a><\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f.<\/p>\n<p>\u041f\u043e\u0435\u0445\u0430\u043b\u0438<\/p>\n<pre><code>php bin\/console make:state-processor UserPasswordHasherProcessor<\/code><\/pre>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435\u00a0services.yaml<\/p>\n<pre><code class=\"yaml\">#services.yaml services:      App\\State\\UserPasswordHasherProcessor:     bind:       '@api_platform.doctrine.orm.state.persist_processor'<\/code><\/pre>\n<p>\u0421\u0430\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a.<\/p>\n<details class=\"spoiler\">\n<summary>UserPasswordHasherProcessor<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace App\\State;  use ApiPlatform\\Metadata\\Operation; use ApiPlatform\\Metadata\\Post; use ApiPlatform\\State\\ProcessorInterface; use App\\Entity\\User; use Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface;  class UserPasswordHasherProcessor implements ProcessorInterface {     public function __construct(private readonly ProcessorInterface $processor, private readonly UserPasswordHasherInterface $passwordHasher)     {     }       \/**      * @param   User      $data      * @param   Operation  $operation      * @param   array      $uriVariables      * @param   array      $context      *      * @return mixed      * @throws \\Exception      *\/     public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])     {         if (!$data->getPlainPassword()) {             return $this->processor->process($data, $operation, $uriVariables, $context);         }          $hashedPassword = $this->passwordHasher->hashPassword(             $data,             $data->getPlainPassword()         );         $data->setPassword($hashedPassword);         $data->eraseCredentials();         \/\/ \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0442\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u043c \u0442\u043e\u043a\u0435 \u0438 \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u0440\u043e\u043b\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e          if ($operation instanceof Post){             $data->setToken(bin2hex(random_bytes(60)));             $data->setRoles($data->getRoles());         }          return $this->processor->process($data, $operation, $uriVariables, $context);     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f,\u00a0\u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043d\u0430\u0448 \u0440\u0435\u0441\u0443\u0440\u0441 <em>API<\/em> \u0441 \u043b\u043e\u0433\u0438\u043a\u043e\u0439,\u00a0\u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0439 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0435.\u00a0\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <em>operations<\/em> \u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 <strong>User<\/strong> \u0438 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u0432\u0438\u0434\u0435 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 API.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435,\u00a0\u044f \u0443\u043a\u0430\u0437\u0430\u043b \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430\u043c\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u00a0<strong>UserPasswordHasherProcessor<\/strong>\u00a0\u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445 <strong>Post()<\/strong> \u0438 <strong>Patch()<\/strong>.\u00a0\u0410 \u0442\u0430\u043a \u0436\u0435,\u00a0\u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u00a0<em>uriTemplate<\/em>\u00a0\u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c\u00a0\/registration,\u00a0\u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442 uri \u0443 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0441\u00a0\/api\/users\u00a0\u043d\u0430\u00a0\/api\/registration.<\/p>\n<pre><code class=\"php\">use ApiPlatform\\Metadata\\Delete; use ApiPlatform\\Metadata\\GetCollection; use ApiPlatform\\Metadata\\Patch; use ApiPlatform\\Metadata\\Post; use ApiPlatform\\Metadata\\Put; use ApiPlatform\\Metadata\\Get;  ...  #[ApiResource( operations: [     new GetCollection(),     new Post(processor: UserPasswordHasherProcessor::class,              uriTemplate: '\/registration'),     new Get(),     new Patch(processor: UserPasswordHasherProcessor::class),     new Delete(),     ] )]  #[ORM\\Entity(repositoryClass: UserRepository::class)] #[ORM\\Table(name: '`user`')] class User implements UserInterface, PasswordAuthenticatedUserInterface { <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e,\u00a0\u0434\u0430\u0432\u0430\u0439 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0442\u043e,\u00a0\u043a\u0430\u043a \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 swagger.\u00a0\u0418 \u0432\u0438\u0434\u0438\u043c,\u00a0\u0442\u043e \u0447\u0442\u043e \u0440\u0430\u043d\u044c\u0448\u0435 \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b\u0438 \u043f\u043e\u043b\u044f \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 Example value,\u00a0\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0443\u0441\u0442\u043e.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d2d\/bef\/c8e\/d2dbefc8e4a1d3c0e13481316f02cd12.png\" width=\"1818\" height=\"1240\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d2d\/bef\/c8e\/d2dbefc8e4a1d3c0e13481316f02cd12.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412\u0441\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e!\u00a0<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u044f \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b,\u00a0\u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435\u00a0operations,\u00a0API Platform \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442,\u00a0\u043a\u0430\u043a\u0438\u0435 \u043f\u043e\u043b\u044f \u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043a\u0430\u043a \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b,\u00a0\u0438 \u043a\u0430\u043a\u0438\u0435 \u043f\u043e\u043b\u044f \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044b \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043c\u0435\u0442\u043e\u0434\u0430.\u00a0\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0413\u0440\u0443\u043f\u043f\u044b \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p><a href=\"https:\/\/github.com\/kxxb\/docs-ru\/blob\/3.0\/core\/serialization-ru.md\" rel=\"noopener noreferrer nofollow\"><strong><em>\u0421\u043c\u043e\u0442\u0440\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u0440\u043e \u0421\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e<\/em><\/strong><\/a><\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0435\u00a0ApiResource\u00a0\u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\u00a0User\u00a0\u043e\u043f\u0438\u0448\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\u00a0normalizationContext\u00a0\u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438,\u00a0\u0438\u00a0denormalizationContext\u00a0\u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c.<\/p>\n<pre><code class=\"php\">use Symfony\\Component\\Serializer\\Annotation\\Groups; ...  #[ApiResource(         normalizationContext: ['groups' => ['user:read']],         denormalizationContext: ['groups' => ['user:create', 'user:update']],         operations: [ <\/code><\/pre>\n<p>\u0410 \u0434\u0430\u043b\u044c\u0448\u0435,\u00a0\u043d\u0430\u0437\u043d\u0430\u0447\u0438\u043c \u044d\u0442\u0438 \u0433\u0440\u0443\u043f\u043f\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439 \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\u00a0User.\u00a0\u0422\u0430\u043a,\u00a0\u0447\u0442\u043e\u0431\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c,\u00a0\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u043b\u0438 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 <\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">class User implements UserInterface, PasswordAuthenticatedUserInterface {     #[ORM\\Id]     #[ORM\\GeneratedValue]     #[ORM\\Column]     private ?int $id = null;      #[Groups(['user:create', 'user:update'])]     #[ORM\\Column(length: 180, unique: true)]     private ?string $email = null;      #[ORM\\Column]     private array $roles = [];      \/**      * @var string The hashed password      *\/     #[ORM\\Column]     private ?string $password = null;      #[ORM\\Column(length: 255)]     #[Groups(['user:read'])]     private ?string $token = null;       #[Groups(['user:create', 'user:update'])]     #[SerializedName('password')]     private $plainPassword;<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c,\u00a0\u043a\u0430\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432 Swagger\u00a0<strong>Example value<\/strong>\u00a0\u0432 \u043c\u0435\u0442\u043e\u0434\u0435\u00a0\/api\/registration.\u00a0\u041e\u0431\u043d\u043e\u0432\u0438\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0443 \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0438 \u0432\u0438\u0434\u0438\u043c \u0447\u0442\u043e,\u00a0\u0432\u0441\u0435 \u0442\u0430\u043a \u043a\u0430\u043a \u0438 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043b\u0438.<\/p>\n<pre><code class=\"json\">{   \"email\": \"string\",   \"password\": \"string\" }<\/code><\/pre>\n<p>\u0418 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u043c\u044b \u0432\u0438\u0434\u0438\u043c,\u00a0\u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0440\u0430\u0449\u0435\u043d \u0442\u043e\u043a\u0435\u043d<\/p>\n<pre><code class=\"json\">{   \"@context\": \"string\",   \"@id\": \"string\",   \"@type\": \"string\",   \"token\": \"string\" }<\/code><\/pre>\n<p>\u0422\u044b \u043d\u0435 \u043f\u043e\u0432\u0435\u0440\u0438\u0448\u044c,\u00a0\u043d\u043e \u043c\u0435\u0442\u043e\u0434 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!\u00a0\u041c\u043e\u0436\u043d\u043e \u0443\u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0436\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 execute \u0438 \u0432\u0441\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!\u00a0\u041e\u043f\u0430,\u00a0\u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u044b\u0432\u0430\u043b\u0438\u043b\u0430\u0441\u044c!<\/p>\n<p>\u041d\u0443 \u043a\u043e\u043d\u0435\u0447\u043d\u043e!\u00a0\u0410 \u0442\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0411\u0414 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445?<\/p>\n<h3>\u0411\u0414 \u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0411\u0414.<\/p>\n<pre><code>#.env DATABASE_URL=\"mysql:\/\/root:root@mysql:3306\/app?serverVersion=8&amp;charset=utf8mb4\"<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e<\/p>\n<pre><code>php bin\/console make:migration<\/code><\/pre>\n<p>\u0418 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0435\u0451<\/p>\n<pre><code>php bin\/console doctrine:migrations:migrate<\/code><\/pre>\n<h3>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 API<\/h3>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 Swagger\u00a0<strong>Example value<\/strong>\u00a0\u0432 \u043c\u0435\u0442\u043e\u0434\u0435\u00a0\/api\/registration\u00a0\u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c execute<\/p>\n<p>\u042d\u0442\u043e \u0436\u0435 amazing \u043a\u0430\u043a\u043e\u0439-\u0442\u043e!<\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438,\u00a0\u043c\u0435\u0442\u043e\u0434 Patch \u0442\u043e\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.\u00a0\u0418 \u0432 \u043d\u0435\u043c \u0442\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0432\u0430 \u043f\u043e\u043b\u044f<\/p>\n<pre><code class=\"json\">{   \"email\": \"string\",   \"password\": \"string\" } <\/code><\/pre>\n<p>\u0418 \u0432 \u043e\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043d\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f token.\u00a0\u0422\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u043a\u0435\u043d \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u043d\u043e\u0432\u044c,\u00a0\u0442\u0430\u043a \u043a\u0430\u043a \u0432\u00a0UserPasswordHasherProcessor,\u00a0\u0435\u0441\u0442\u044c \u0442\u0430\u043a\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435:<\/p>\n<pre><code class=\"php\">    ...     if ($operation instanceof Post){ \/\/ &lt;- \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 Post          $data->setToken(bin2hex(random_bytes(60)));         $data->setRoles($data->getRoles());     }<\/code><\/pre>\n<h3>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0412 swagger,\u00a0\u0441\u0432\u0435\u0440\u0445\u0443 \u0435\u0441\u0442\u044c \u0442\u0430\u043a\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430\u00a0<strong>Authorize<\/strong>,\u00a0\u043d\u0430\u0436\u0430\u0432 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u0443\u044e,\u00a0\u043c\u044b \u0432\u0438\u0434\u0438\u043c&#8230;\u043f\u0443\u0441\u0442\u043e\u0435 \u043e\u043a\u043d\u043e.\u00a0\u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0435 API \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u043c\u043e\u0433 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u043a\u0435\u043d,\u00a0\u0430 \u0432 swagger \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 API Platform.\u00a0\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0430\u043a\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u00a0config\/packages\/api_platform.yaml<\/p>\n<pre><code class=\"yaml\">#config\/packages\/api_platform.yaml api_platform:   # The title of the API.   title: 'Edu API'    # The description of the API.   description: 'Edu API description'    # The version of the API.   version: '0.0.1'    # Set this to false if you want Webby to disappear.   show_webby: false    mapping:     paths: ['%kernel.project_dir%\/src\/Entity']   patch_formats:     json: ['application\/merge-patch+json']   swagger:     versions: [3]     api_keys:       Bearer:         name: AUTH-TOKEN         type: header <\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0436\u0430\u0432 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443\u00a0<strong>Authorize<\/strong>,\u00a0\u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0444\u043e\u0440\u043c\u0430,\u00a0\u0441 \u043f\u043e\u043b\u0435\u043c\u00a0AUTH-TOKEN\u00a0\u043a\u0443\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.\u00a0\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b\u00a0<strong>user<\/strong>.<\/p>\n<p>\u041d\u043e \u043f\u043e\u043a\u0430,\u00a0\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043d\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0443 \u043d\u0430\u0441 \u0432 API,\u00a0\u0438 \u0445\u043e\u0442\u044c swagger \u0440\u0430\u0434\u043e\u0441\u0442\u043d\u043e \u0441\u043e\u043e\u0431\u0449\u0438\u0442,\u00a0\u0447\u0442\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043b\u0441\u044f,\u00a0\u0438 \u0431\u0443\u0434\u0435\u0442 \u0447\u0435\u0441\u0442\u043d\u043e \u0441\u043b\u0430\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435,\u00a0API \u0435\u0433\u043e \u043d\u0435 \u0432\u043e\u0441\u043f\u0440\u0438\u043c\u0435\u0442.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440,\u00a0\u0438 \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c.<\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0438 \u0432\u044b\u0431\u0435\u0440\u0435\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 0\u00a0Empty authenticator\u00a0\u0438 \u0434\u0430\u0434\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u00a0<strong>ApiTokenAuthenticator<\/strong><\/p>\n<pre><code>php bin\/console make:auth   What style of authentication do you want? [Empty authenticator]:   [0] Empty authenticator   [1] Login form authenticator  > ApiTokenAuthenticator <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c,\u00a0\u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0432 \u043d\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b:<\/p>\n<details class=\"spoiler\">\n<summary>ApiKeyAuthenticator<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">namespace App\\Security;  use Symfony\\Component\\HttpFoundation\\Request; use Symfony\\Component\\HttpFoundation\\Response; use Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface; use Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException; use Symfony\\Component\\Security\\Core\\Exception\\CustomUserMessageAuthenticationException; use Symfony\\Component\\Security\\Http\\Authenticator\\AbstractAuthenticator; use Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Badge\\UserBadge; use Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Passport; use Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\SelfValidatingPassport;  class ApiKeyAuthenticator extends AbstractAuthenticator {      protected const HEADER_AUTH_TOKEN = 'AUTH-TOKEN';      \/**      * @inheritDoc      *\/     public function supports(Request $request): ?bool     {         return $request->headers->has(self::HEADER_AUTH_TOKEN);     }      \/**      * @inheritDoc      *\/     public function authenticate(Request $request): Passport     {         $apiToken = $request->headers->get(self::HEADER_AUTH_TOKEN);          if (null === $apiToken) {             throw new CustomUserMessageAuthenticationException('Auth token not found (header: \"{{ header }}\")', [                 '{{ header }}' => self::HEADER_AUTH_TOKEN,             ]);         }          return new SelfValidatingPassport(new UserBadge($apiToken));     }      \/**      * @inheritDoc      *\/     public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response     {         return null;     }      \/**      * @inheritDoc      *\/     public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response     {         throw $exception;     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043c UserProvider \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043f\u043e\u043b\u0435\u043c token \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c firewall<\/p>\n<pre><code class=\"php\">#config\/packages\/security.yaml  security:     #....     providers:                  app_user_provider:             entity:                 class: App\\Entity\\User                 property: token # &lt;-- \u0442\u0443\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u043b\u0435 token      firewalls:         dev:             pattern: ^\/(_(profiler|wdt)|css|images|js)\/             security: false         main:             lazy: true             stateless: true  # &lt;-- \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c stateless: true \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u0435\u0441\u0441\u0438\u0438               provider: app_user_provider             custom_authenticator: App\\Security\\ApiKeyAuthenticator  # &lt;-- \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c ApiKeyAuthenticator     #...  <\/code><\/pre>\n<p>\u0412\u043e\u0442 \u0442\u0435\u043f\u0435\u0440\u044c,\u00a0\u043d\u0430\u0448 API \u0433\u043e\u0442\u043e\u0432 \u043a \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.\u00a0\u0422\u043e\u043b\u044c\u043a\u043e \u0432 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442,\u00a0\u0432\u0441\u0435 \u0447\u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0431\u0435\u0441\u043f\u043e\u043b\u0435\u0437\u043d\u043e,\u00a0\u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0430\u0441 \u043d\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u044b.<\/p>\n<h3>\u0414\u043e\u0441\u0442\u0443\u043f\u044b \u0438 \u0440\u043e\u043b\u0438<\/h3>\n<h4>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0440\u043e\u043b\u0438 \u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0414\u0430\u0432\u0430\u0439 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c,\u00a0\u0430 \u043a\u0430\u043a \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u0435\u043a\u0443\u0449\u0438\u043c API.<\/p>\n<ol>\n<li>\n<p>\u041c\u0435\u0442\u043e\u0434\u00a0<strong>POST \/api\/registration<\/strong>\u00a0\u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432\u0441\u0435\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0438\u0441\u044c.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u00a0<strong>POST \/api\/login<\/strong>\u00a0\u0442\u043e\u0436\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432\u0441\u0435\u043c, \u0438 \u0432 \u043e\u0442\u0432\u0435\u0442 \u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u0435\u0436\u0438\u0439 \u0442\u043e\u043a\u0435\u043d \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>\u041c\u0435\u0442\u043e\u0434\u044b\u00a0<strong>GET \/api\/users \u0438 \/api\/users\/{id}<\/strong>\u00a0\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u041c\u0435\u0442\u043e\u0434\u044b\u00a0<strong>PATCH \/api\/users\/{id}<\/strong>\u00a0\u043c\u043e\u0436\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u0435, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c<\/p>\n<\/li>\n<li>\n<p>\u041c\u0435\u0442\u043e\u0434\u044b\u00a0<strong>DELETE \/api\/users\/{id}<\/strong>\u00a0\u043c\u043e\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u044f, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043b\u043e\u0433\u0438\u043d\u0435\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u043f\u0443\u043d\u043a\u0442,\u00a0\u0443\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d.<\/p>\n<p>\u0414\u043b\u044f \u043f\u0443\u043d\u043a\u0442\u0430 2,\u00a0\u0434\u0430\u0432\u0430\u0439 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 login \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<pre><code>php bin\/console make:state-processor UserLoginProcessor<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u00a0findOneByLogin\u00a0\u0432\u00a0<strong>UserRepository<\/strong>\u00a0\u0434\u043b\u044f \u043d\u0430\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e email<\/p>\n<pre><code class=\"php\">  public function findOneByLogin($value): ?User     {         return $this->createQueryBuilder('u')             ->andWhere('u.email = :val')             ->setParameter('val', $value)             ->getQuery()             ->getOneOrNullResult();     }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u0434\u043b\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>UserLoginProcessor<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">namespace App\\State;  use ApiPlatform\\Metadata\\Operation; use ApiPlatform\\State\\ProcessorInterface; use App\\Entity\\User; use App\\Repository\\UserRepository; use Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException; use Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException; use Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface;  class UserLoginProcessor implements ProcessorInterface {     public function __construct(private UserRepository $repository, private UserPasswordHasherInterface $userPasswordEncoder)     {      }      \/**      * @param   User      $data      * @param   Operation  $operation      * @param   array      $uriVariables      * @param   array      $context      *      * @return User      * @throws \\Exception      *\/     public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])     {         $user = $this->repository->findOneByLogin($data->getEmail());         if ($user instanceof  User) {              if (!$this->userPasswordEncoder->isPasswordValid($user,  $data->getPlainPassword())) {                 throw new AccessDeniedHttpException();             }              $user->setToken(bin2hex(random_bytes(60)));             $this->repository->save($user, true);             return  $user;         }          throw new NotFoundHttpException();     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0415\u0441\u043b\u0438 \u0441\u043b\u0443\u0436\u0431\u0430 \u0430\u0432\u0442\u043e\u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438 \u0430\u0432\u0442\u043e\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0430\u00a0(\u043e\u043d\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e),\u00a0\u0442\u043e \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e!<\/p>\n<pre><code class=\"yaml\"># api\/config\/services.yaml services:     # default configuration for services in this file     _defaults:         autowire: true      # Automatically injects dependencies in your services.         autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.<\/code><\/pre>\n<p>\u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435,\u00a0\u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439,\u00a0\u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u043b\u0443\u0436\u0431\u0443 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0435\u0433 api_platform.state_processor.<\/p>\n<pre><code class=\"yaml\"># api\/config\/services.yaml services:     # ...     App\\State\\UserLoginProcessor: ~     # Uncomment only if autoconfiguration is disabled     #tags: [ 'api_platform.state_processor' ]<\/code><\/pre>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u043d\u0430 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u044b,\u00a0\u043a\u0430\u043a \u043e\u043f\u0438\u0441\u0430\u043d\u043e \u0432 \u043f\u0443\u043d\u043a\u0442\u0430\u0445 3-5<\/p>\n<h4>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u044b<\/h4>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439 \u043f\u0443\u043d\u043a\u0442\u0430 2,\u00a0\u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\u00a0User\u00a0\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430\u00a0<strong>POST \/api\/login<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">use App\\State\\UserLoginProcessor;  ...  #[ApiResource(     normalizationContext: ['groups' => ['user:read']],     denormalizationContext: ['groups' => ['user:create']],     operations: [         new GetCollection(),         new Post(processor: UserPasswordHasherProcessor::class, uriTemplate: '\/registration'),         new Post(processor: UserLoginProcessor::class,             uriTemplate: '\/login'         ),         new Get(),         new Patch(),         new Delete(),     ] )]<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0437\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430 swagger,\u00a0\u0442\u043e \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434,\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043b\u044f \u043f\u0443\u043d\u043a\u0442\u0430 3,\u00a0\u043d\u0430\u0448\u0438\u0445 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439,\u00a0\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u00a0security\u00a0\u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439\u00a0<strong>Get() \u0438 GetCollection()<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">use App\\State\\UserLoginProcessor;  ...  #[ApiResource(     normalizationContext: ['groups' => ['user:read']],     denormalizationContext: ['groups' => ['user:create']],     operations: [         new GetCollection(security: \"is_granted('ROLE_USER')\"),         new Post(processor: UserPasswordHasherProcessor::class, uriTemplate: '\/registration'),         new Post(processor: UserLoginProcessor::class,             uriTemplate: '\/login'         ),         new Get(security: \"is_granted('ROLE_USER')\"),         new Patch(),         new Delete(),     ] )]<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c,\u00a0\u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 swagger,\u00a0\u0431\u0435\u0437 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430,\u00a0\u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0443\u00a0<strong>403 \u0414\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d<\/strong><\/p>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c,\u00a0\u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435\u00a0&#8212;\u00a0\u043f\u0443\u043d\u043a\u0442\u044b 4 \u0438 5.<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">use App\\State\\UserLoginProcessor;  ...  #[ApiResource(     normalizationContext: ['groups' => ['user:read']],     denormalizationContext: ['groups' => ['user:create']],     operations: [         new GetCollection(security: \"is_granted('ROLE_USER')\"),         new Post(processor: UserPasswordHasherProcessor::class, uriTemplate: '\/registration'),         new Post(processor: UserLoginProcessor::class,             uriTemplate: '\/login'         ),         new Get(security: \"is_granted('ROLE_USER')\"),         new Patch(security: \"object == user\",             securityMessage: '\u041f\u0430\u0440\u043e\u043b\u044c \u043c\u043e\u0436\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u0435'),         new Delete(security: \"object == user\",             securityMessage: '\u0423\u0434\u0430\u043b\u044f\u0442\u044c \u043c\u043e\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u044f'),     ] )]<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f:<\/p>\n<p><code>user<\/code>:\u00a0\u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043e\u0431\u044a\u0435\u043a\u0442,\u00a0\u0432\u043e\u0448\u0435\u0434\u0448\u0438\u0439 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443\u00a0<\/p>\n<p><code>object<\/code>:\u00a0\u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043a\u043b\u0430\u0441\u0441 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u0435\u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438,\u00a0\u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0440\u0435\u0441\u0443\u0440\u0441 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432\u00a0<\/p>\n<p><code>previous_object<\/code>: (\u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435\u00a0<code>securityPostDenormalize<\/code>)\u00a0\u043a\u043b\u043e\u043d \u043e\u0431\u044a\u0435\u043a\u0442\u0430,\u00a0\u0434\u043e \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439\u00a0&#8212;\u00a0\u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0440\u0430\u0432\u043d\u043e\u00a0<code>null<\/code>\u00a0\u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u00a0<\/p>\n<p><code>request<\/code>\u00a0(\u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432):\u00a0\u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441<\/p>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0435 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430\u00a0\u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0430\u043f\u043e\u043c \u0434\u0435\u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.\u00a0\u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442,\u00a0\u0447\u0442\u043e \u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 PUT \u0438\u043b\u0438 PATCH \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435,\u00a0\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c,\u00a0\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f,\u00a0\u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0435\u0441\u044f \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c\u043e\u0441\u0442\u0438\u00a0(persistence layer).<\/p>\n<p>\u0418 \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u043c\u043e\u043c\u0435\u043d\u0442\u00a0&#8212;\u00a0\u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438\u00a0<strong>Patch()<\/strong>\u00a0\u0441\u0435\u0439\u0447\u0430\u0441 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044f<\/p>\n<pre><code class=\"json\">{   \"email\": \"string\",   \"password\": \"string\" }<\/code><\/pre>\n<p>\u0410 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u043b\u044f \u0442\u0430\u043a,\u00a0\u0447\u0442\u043e\u0431\u044b \u043c\u0435\u043d\u044f\u0442\u044c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u0440\u043e\u043b\u044c.<\/p>\n<p>\u0418 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442,\u00a0\u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435 token,\u00a0\u0430 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 email.\u00a0\u0414\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0446\u0435\u043b\u0435\u0439,\u00a0\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u044b \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0445 \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043b\u044f \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u0412 \u0438\u0442\u043e\u0433\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c User \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">use App\\State\\UserLoginProcessor;  ...  #[ApiResource(     normalizationContext: ['groups' => ['user:read']],     denormalizationContext: ['groups' => ['user:create']],     operations: [         new GetCollection(security: \"is_granted('ROLE_USER')\"),         new Post(processor: UserPasswordHasherProcessor::class, uriTemplate: '\/registration'),         new Post(processor: UserLoginProcessor::class,             uriTemplate: '\/login'         ),         new Get(security: \"is_granted('ROLE_USER')\"),         new Patch(security: \"object == user\",             securityMessage: '\u041f\u0430\u0440\u043e\u043b\u044c \u043c\u043e\u0436\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u0435',             normalizationContext: ['groups' => ['user:passwordRead']],             denormalizationContext: ['groups' => ['user:passwordUpdate']],             ),         new Delete(security: \"object == user\",             securityMessage: '\u0423\u0434\u0430\u043b\u044f\u0442\u044c \u043c\u043e\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u044f'),     ] )] #[ORM\\Entity(repositoryClass: UserRepository::class)] #[ORM\\Table(name: '`user`')] class User implements UserInterface, PasswordAuthenticatedUserInterface {     #[ORM\\Id]     #[ORM\\GeneratedValue]     #[ORM\\Column]     private ?int $id = null;      #[Groups(['user:create', 'user:passwordRead'])]     #[ORM\\Column(length: 180, unique: true)]     private ?string $email = null;      #[ORM\\Column]     private array $roles = [];      \/**      * @var string The hashed password      *\/     #[ORM\\Column]     private ?string $password = null;      #[ORM\\Column(length: 255)]     #[Groups(['user:read'])]     private ?string $token = null;       #[Groups(['user:create', 'user:passwordUpdate'])]     #[SerializedName('password')]     private $plainPassword;<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a,\u00a0\u0443 \u043d\u0430\u0441 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u0433\u0438\u0431\u043a\u0438\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.\u00a0\u0418,\u00a0\u043e\u0431\u0440\u0430\u0442\u0438 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043a\u0430\u043a \u043c\u0430\u043b\u043e \u043a\u043e\u0434\u0430 \u0431\u044b\u043b\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e!\u00a0\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u0445,\u00a0\u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u0440\u043e \u0434\u0440\u0443\u0433\u0438\u0435 \u0444\u0438\u0447\u0438 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c \u0432\u0441\u0451 \u0431\u044b\u043b\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u043e,\u00a0\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e.<\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u043a\u043e\u0434 \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/kxxb\/api-edu\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>.  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439 <a href=\"https:\/\/github.com\/kxxb\/docs-ru\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a> <\/p>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u0432\u0430\u0448\u0438\u043c \u0434\u043e\u0431\u0440\u044b\u043c \u0441\u043e\u0432\u0435\u0442\u0430\u043c, \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f\u043c \u0438 \u0437\u0434\u043e\u0440\u043e\u0432\u043e\u0439 \u043a\u0440\u0438\u0442\u0438\u043a\u0438. <\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e\u0433\u043e \u0434\u043d\u044f!<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/707666\/\"> https:\/\/habr.com\/ru\/post\/707666\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/api-platform.com\" rel=\"noopener noreferrer nofollow\">API platform<\/a> \u044d\u0442\u043e \u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 REST API, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 \u0437\u0430 \u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 \u043c\u0438\u043d\u0443\u0442\u044b.\u0412\u043e\u0442 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0444\u0438\u0447:<\/p>\n<p>&#8212; \u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f CRUD<\/p>\n<p>&#8212; \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GraphQL<\/p>\n<p>&#8212; \u041c\u0430\u0448\u0438\u043d\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f API \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 Hydra \u0438 Swagger\/Open API, \u0433\u0435\u043d\u0435\u043d\u0440\u0438\u0442\u0441\u044f \u0438\u0437 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 PHPDoc, Serializer, Validator \u0438 Doctrine ORM \/ MongoDB ODM<\/p>\n<p>&#8212; \u0425\u043e\u0440\u043e\u0448\u0430\u044f \u0443\u0434\u043e\u0431\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 Swagger (\u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0443) \u0438 \/ \u0438\u043b\u0438 ReDoc<\/p>\n<p>&#8212; \u041f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f<\/p>\n<p>&#8212; \u041a\u0443\u0447\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432<\/p>\n<p>&#8212; \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 Symfony Validator (\u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0433\u0440\u0443\u043f\u043f)<\/p>\n<p>&#8212; \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<p>&#8212; \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443 Symfony Serializer (\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0433\u0440\u0443\u043f\u043f, \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0439, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0433\u043b\u0443\u0431\u0438\u043d\u0430&#8230;)<\/p>\n<p>&#8212; \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 JWT \u0438 OAuth- \u0424\u0430\u0439\u043b\u044b \u0438 \\DateTime, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/p>\n<p>&#8212; \u0412\u0441\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043c\u043e\u0449\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438 \u0441\u0438\u043b\u044c\u043d\u043e\u043c\u0443 \u041e\u041e\u041f.<\/p>\n<hr\/>\n<p>\u0421\u0440\u0430\u0437\u0443 \u043e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e \u0432 Symfony \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c API \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440<\/p>\n<pre><code>php bin\/console make:controller --no-template SuperApiController<\/code><\/pre>\n<p>\u0438 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438 <a href=\"https:\/\/symfony.com\/bundles\/SensioFrameworkExtraBundle\/current\/annotations\/security.html\" rel=\"noopener noreferrer nofollow\">\u0434\u043e\u0441\u0442\u0443\u043f\u044b \u0447\u0435\u0440\u0435\u0437 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b<\/a><\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">namespace App\\Controller;  use Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController; use Symfony\\Component\\HttpFoundation\\JsonResponse; use Symfony\\Component\\Routing\\Annotation\\Route; use Symfony\\Component\\Security\\Http\\Attribute\\IsGranted;  class SuperApiController extends AbstractController {     #[Route('\/super\/api', name: 'app_super_api')]     #[IsGranted(\"ROLE_ADMIN\")]     public function index(): JsonResponse     {         return $this->json([             'message' => 'Welcome to your new controller!',             'path' => 'src\/Controller\/SuperApiController.php',         ]);     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u043e \u0435\u0441\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434, \u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0443\u0436\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0439, \u043f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043d\u0435 \u043f\u043e\u0439\u0442\u0438 \u0442\u0430\u043a\u043e\u0439 \u0434\u043e\u0440\u043e\u0433\u043e\u0439? <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u0442\u044b \u0443\u0437\u043d\u0430\u0435\u0448\u044c:<\/p>\n<ul>\n<li>\n<p>\u041a\u0430\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e API platform \u0441\u043e\u0437\u0434\u0430\u0442\u044c CRUD \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e swagger.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u044b \u043a \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u043c \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043c \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c \u0447\u0435\u0440\u0435\u0437 API \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u0431\u0435.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c API platform<\/h3>\n<pre><code>composer require api<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<pre><code>php bin\/console make:user<\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c,\u00a0\u0434\u0430\u0432\u0430\u0439 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u00a0<strong>[ApiResource]<\/strong>\u00a0\u0443 \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438<\/p>\n<pre><code class=\"php\">namespace App\\Entity;  use ApiPlatform\\Metadata\\ApiResource;   #[ApiResource] \/\/&lt;-- \u0414\u043e\u0431\u0430\u0432\u0438\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u044d\u0442\u043e #[ORM\\Entity(repositoryClass: UserRepository::class)] class User implements UserInterface, PasswordAuthenticatedUserInterface { ... <\/code><\/pre>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e!\u00a0\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0435 REST Api.<\/p>\n<p>\u0417\u0430\u0445\u043e\u0434\u0438 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435\u00a0<a href=\"http:\/\/localhost\/api\" rel=\"noopener noreferrer nofollow\">http:\/\/localhost\/api<\/a><\/p>\n<p>\u0418 \u0432\u0438\u0434\u0438\u043c,\u00a0\u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0434\u043b\u044f CRUD.\u00a0\u041f\u043e \u0441\u0443\u0442\u0438,\u00a0\u043c\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u043e\u0447\u043a\u0443 \u043a\u043e\u0434\u0430,\u00a0\u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u043e\u0434\u0438\u043d \u0430\u0442\u0440\u0438\u0431\u0443\u0442,\u00a0\u0438 \u0443\u0436\u0435 \u0438\u043c\u0435\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u043c\u043e\u0449\u043d\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e,\u00a0\u0434\u0432\u0438\u0436\u0435\u043c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<h3>\u0414\u0435\u043b\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0440\u0435\u0433\u0438\u0441\u0442\u0430\u0440\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/h3>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u0435 \u0442\u043e\u043a\u0435\u043d \u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/p>\n<pre><code>php bin\/console make:entity User<\/code><\/pre>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u0435\u00a0<strong>token<\/strong><\/p>\n<p>\u0412 \u0441\u0430\u043c\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438,\u00a0\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u0435\u00a0<em>plainPassword<\/em>,\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u0443\u0449\u043d\u043e\u0441\u0442\u044c User<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">use Symfony\\Component\\Serializer\\Annotation\\SerializedName; \/\/....  class User implements UserInterface, PasswordAuthenticatedUserInterface { \/\/...  #[SerializedName('password')] private $plainPassword;     \/**      * @return mixed      *\/     public function getPlainPassword()     {         return $this->plainPassword;     }      \/**      * @param   mixed  $plainPassword      *\/     public function setPlainPassword($plainPassword): void     {         $this->plainPassword = $plainPassword;     }    \/**      * @see UserInterface      *\/     public function eraseCredentials()     {         \/\/ If you store any temporary, sensitive data on the user, clear it here          $this->plainPassword = null; \/\/ &lt;--- \u0420\u0430\u0441\u043a\u043e\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u044d\u0442\u0443 \u0441\u0442\u0440\u043e\u0447\u043a\u0443      } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439,\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443 \u0445\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u043e\u043b\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p><a href=\"https:\/\/github.com\/kxxb\/docs-ru\/blob\/3.0\/core\/state-processors-ru.md\" rel=\"noopener noreferrer nofollow\"><strong><em>\u0421\u043c\u043e\u0442\u0440\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u0440\u043e \u041f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u044b \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439<\/em><\/strong><\/a><\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f.<\/p>\n<p>\u041f\u043e\u0435\u0445\u0430\u043b\u0438<\/p>\n<pre><code>php bin\/console make:state-processor UserPasswordHasherProcessor<\/code><\/pre>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435\u00a0services.yaml<\/p>\n<pre><code class=\"yaml\">#services.yaml services:      App\\State\\UserPasswordHasherProcessor:     bind:       '@api_platform.doctrine.orm.state.persist_processor'<\/code><\/pre>\n<p>\u0421\u0430\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a.<\/p>\n<details class=\"spoiler\">\n<summary>UserPasswordHasherProcessor<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  namespace App\\State;  use ApiPlatform\\Metadata\\Operation; use ApiPlatform\\Metadata\\Post; use ApiPlatform\\State\\ProcessorInterface; use App\\Entity\\User; use Symfony\\Component\\PasswordHasher\\Hasher\\UserPasswordHasherInterface;  class UserPasswordHasherProcessor implements ProcessorInterface {     public function __construct(private readonly ProcessorInterface $processor, private readonly UserPasswordHasherInterface $passwordHasher)     {     }       \/**      * @param   User      $data      * @param   Operation  $operation      * @param   array      $uriVariables      * @param   array      $context      *      * @return mixed      * @throws \\Exception      *\/     public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])     {         if (!$data->getPlainPassword()) {             return $this->processor->process($data, $operation, $uriVariables, $context);         }          $hashedPassword = $this->passwordHasher->hashPassword(             $data,             $data->getPlainPassword()         );         $data->setPassword($hashedPassword);         $data->eraseCredentials();         \/\/ \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0442\u043e \u0433\u0435\u043d\u0435\u0440\u0438\u043c \u0442\u043e\u043a\u0435 \u0438 \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u0440\u043e\u043b\u044c \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e          if ($operation instanceof Post){             $data->setToken(bin2hex(random_bytes(60)));             $data->setRoles($data->getRoles());         }          return $this->processor->process($data, $operation, $uriVariables, $context);     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f,\u00a0\u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043d\u0430\u0448 \u0440\u0435\u0441\u0443\u0440\u0441 <em>API<\/em> \u0441 \u043b\u043e\u0433\u0438\u043a\u043e\u0439,\u00a0\u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0439 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0435.\u00a0\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <em>operations<\/em> \u043a \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 <strong>User<\/strong> \u0438 \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 \u0432\u0438\u0434\u0435 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 API.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435,\u00a0\u044f \u0443\u043a\u0430\u0437\u0430\u043b \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430\u043c\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u00a0<strong>UserPasswordHasherProcessor<\/strong>\u00a0\u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445 <strong>Post()<\/strong> \u0438 <strong>Patch()<\/strong>.\u00a0\u0410 \u0442\u0430\u043a \u0436\u0435,\u00a0\u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u00a0<em>uriTemplate<\/em>\u00a0\u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c\u00a0\/registration,\u00a0\u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442 uri \u0443 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0441\u00a0\/api\/users\u00a0\u043d\u0430\u00a0\/api\/registration.<\/p>\n<pre><code class=\"php\">use ApiPlatform\\Metadata\\Delete; use ApiPlatform\\Metadata\\GetCollection; use ApiPlatform\\Metadata\\Patch; use ApiPlatform\\Metadata\\Post; use ApiPlatform\\Metadata\\Put; use ApiPlatform\\Metadata\\Get;  ...  #[ApiResource( operations: [     new GetCollection(),     new Post(processor: UserPasswordHasherProcessor::class,              uriTemplate: '\/registration'),     new Get(),     new Patch(processor: UserPasswordHasherProcessor::class),     new Delete(),     ] )]  #[ORM\\Entity(repositoryClass: UserRepository::class)] #[ORM\\Table(name: '`user`')] class User implements UserInterface, PasswordAuthenticatedUserInterface { <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e,\u00a0\u0434\u0430\u0432\u0430\u0439 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0442\u043e,\u00a0\u043a\u0430\u043a \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 swagger.\u00a0\u0418 \u0432\u0438\u0434\u0438\u043c,\u00a0\u0442\u043e \u0447\u0442\u043e \u0440\u0430\u043d\u044c\u0448\u0435 \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b\u0438 \u043f\u043e\u043b\u044f \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 Example value,\u00a0\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u043f\u0443\u0441\u0442\u043e.\u00a0<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0412\u0441\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e!\u00a0<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u044f \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b,\u00a0\u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435\u00a0operations,\u00a0API Platform \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442,\u00a0\u043a\u0430\u043a\u0438\u0435 \u043f\u043e\u043b\u044f \u0435\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u043a\u0430\u043a \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b,\u00a0\u0438 \u043a\u0430\u043a\u0438\u0435 \u043f\u043e\u043b\u044f \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044b \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u043c\u0435\u0442\u043e\u0434\u0430.\u00a0\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h3>\u0413\u0440\u0443\u043f\u043f\u044b \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/h3>\n<p><a href=\"https:\/\/github.com\/kxxb\/docs-ru\/blob\/3.0\/core\/serialization-ru.md\" rel=\"noopener noreferrer nofollow\"><strong><em>\u0421\u043c\u043e\u0442\u0440\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u0440\u043e \u0421\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e<\/em><\/strong><\/a><\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0435\u00a0ApiResource\u00a0\u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\u00a0User\u00a0\u043e\u043f\u0438\u0448\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\u00a0normalizationContext\u00a0\u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438,\u00a0\u0438\u00a0denormalizationContext\u00a0\u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c.<\/p>\n<pre><code class=\"php\">use Symfony\\Component\\Serializer\\Annotation\\Groups; ...  #[ApiResource(         normalizationContext: ['groups' => ['user:read']],         denormalizationContext: ['groups' => ['user:create', 'user:update']],         operations: [ <\/code><\/pre>\n<p>\u0410 \u0434\u0430\u043b\u044c\u0448\u0435,\u00a0\u043d\u0430\u0437\u043d\u0430\u0447\u0438\u043c \u044d\u0442\u0438 \u0433\u0440\u0443\u043f\u043f\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439 \u0432 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438\u00a0User.\u00a0\u0422\u0430\u043a,\u00a0\u0447\u0442\u043e\u0431\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c,\u00a0\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u043b\u0438 \u0442\u043e\u043a\u0435\u043d.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 <\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">class User implements UserInterface, PasswordAuthenticatedUserInterface {     #[ORM\\Id]     #[ORM\\GeneratedValue]     #[ORM\\Column]     private ?int $id = null;      #[Groups(['user:create', 'user:update'])]     #[ORM\\Column(length: 180, unique: true)]     private ?string $email = null;      #[ORM\\Column]     private array $roles = [];      \/**      * @var string The hashed password      *\/     #[ORM\\Column]     private ?string $password = null;      #[ORM\\Column(length: 255)]     #[Groups(['user:read'])]     private ?string $token = null;       #[Groups(['user:create', 'user:update'])]     #[SerializedName('password')]     private $plainPassword;<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c,\u00a0\u043a\u0430\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432 Swagger\u00a0<strong>Example value<\/strong>\u00a0\u0432 \u043c\u0435\u0442\u043e\u0434\u0435\u00a0\/api\/registration.\u00a0\u041e\u0431\u043d\u043e\u0432\u0438\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0443 \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0438 \u0432\u0438\u0434\u0438\u043c \u0447\u0442\u043e,\u00a0\u0432\u0441\u0435 \u0442\u0430\u043a \u043a\u0430\u043a \u0438 \u0437\u0430\u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043b\u0438.<\/p>\n<pre><code class=\"json\">{   \"email\": \"string\",   \"password\": \"string\" }<\/code><\/pre>\n<p>\u0418 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u043c\u044b \u0432\u0438\u0434\u0438\u043c,\u00a0\u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0440\u0430\u0449\u0435\u043d \u0442\u043e\u043a\u0435\u043d<\/p>\n<pre><code class=\"json\">{   \"@context\": \"string\",   \"@id\": \"string\",   \"@type\": \"string\",   \"token\": \"string\" }<\/code><\/pre>\n<p>\u0422\u044b \u043d\u0435 \u043f\u043e\u0432\u0435\u0440\u0438\u0448\u044c,\u00a0\u043d\u043e \u043c\u0435\u0442\u043e\u0434 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0443\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!\u00a0\u041c\u043e\u0436\u043d\u043e \u0443\u0436\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0436\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 execute \u0438 \u0432\u0441\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442!\u00a0\u041e\u043f\u0430,\u00a0\u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u044b\u0432\u0430\u043b\u0438\u043b\u0430\u0441\u044c!<\/p>\n<p>\u041d\u0443 \u043a\u043e\u043d\u0435\u0447\u043d\u043e!\u00a0\u0410 \u0442\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0411\u0414 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445?<\/p>\n<h3>\u0411\u0414 \u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0411\u0414.<\/p>\n<pre><code>#.env DATABASE_URL=\"mysql:\/\/root:root@mysql:3306\/app?serverVersion=8&amp;charset=utf8mb4\"<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e<\/p>\n<pre><code>php bin\/console make:migration<\/code><\/pre>\n<p>\u0418 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0435\u0451<\/p>\n<pre><code>php bin\/console doctrine:migrations:migrate<\/code><\/pre>\n<h3>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 API<\/h3>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 Swagger\u00a0<strong>Example value<\/strong>\u00a0\u0432 \u043c\u0435\u0442\u043e\u0434\u0435\u00a0\/api\/registration\u00a0\u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c execute<\/p>\n<p>\u042d\u0442\u043e \u0436\u0435 amazing \u043a\u0430\u043a\u043e\u0439-\u0442\u043e!<\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438,\u00a0\u043c\u0435\u0442\u043e\u0434 Patch \u0442\u043e\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.\u00a0\u0418 \u0432 \u043d\u0435\u043c \u0442\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0434\u0432\u0430 \u043f\u043e\u043b\u044f<\/p>\n<pre><code class=\"json\">{   \"email\": \"string\",   \"password\": \"string\" } <\/code><\/pre>\n<p>\u0418 \u0432 \u043e\u0442\u0432\u0435\u0442 \u0432\u0435\u0440\u043d\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f token.\u00a0\u0422\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u043a\u0435\u043d \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u043d\u043e\u0432\u044c,\u00a0\u0442\u0430\u043a \u043a\u0430\u043a \u0432\u00a0UserPasswordHasherProcessor,\u00a0\u0435\u0441\u0442\u044c \u0442\u0430\u043a\u043e\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u0435:<\/p>\n<pre><code class=\"php\">    ...     if ($operation instanceof Post){ \/\/ &lt;- \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 Post          $data->setToken(bin2hex(random_bytes(60)));         $data->setRoles($data->getRoles());     }<\/code><\/pre>\n<h3>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0412 swagger,\u00a0\u0441\u0432\u0435\u0440\u0445\u0443 \u0435\u0441\u0442\u044c \u0442\u0430\u043a\u0430\u044f \u043a\u043d\u043e\u043f\u043a\u0430\u00a0<strong>Authorize<\/strong>,\u00a0\u043d\u0430<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-343174","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/343174","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=343174"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/343174\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=343174"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=343174"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=343174"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}