{"id":364618,"date":"2024-05-21T02:20:36","date_gmt":"2024-05-21T02:20:36","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=364618"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=364618","title":{"rendered":"<span>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Symfony \/ PHP<\/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<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u042f, \u0410\u043d\u0434\u0440\u0435\u0439, Symfony \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a &#8212; \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0430\u0439\u0442\u044b. \u041a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c \u0443 \u043d\u0430\u0441 \u0443\u0445\u043e\u0434\u0438\u0442 \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043d\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u043e\u043f\u044b\u0442\u043e\u043c, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Symfony<em> <\/em>\u0434\u043b\u044f \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u0438\u0445 \u0437\u0430\u0442\u0440\u0430\u0442, \u043a\u0430\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043c\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0438 \u043a\u0430\u043a \u043c\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u043c \u0441 <em>REST <\/em>\u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438.\u00a0\u041f\u043e\u0435\u0445\u0430\u043b\u0438 (\u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430).<\/p>\n<h2>Namespace \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u0448\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 <code>User<\/code>, \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u00ab\u0434\u043e\u043c\u0435\u043d\u044b\u00bb \u0438\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u043f\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0430\u043c. \u0422\u0430\u043a \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434\u0435 \u0438 \u043f\u0440\u043e\u0449\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441\u044b.\u00a0<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/237\/917\/3b3\/2379173b38b9d5eef85a45d69a0df334.png\" alt=\"Image\" title=\"\u041f\u0440\u0438\u043c\u0435\u0440 \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\" width=\"954\" height=\"942\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/237\/917\/3b3\/2379173b38b9d5eef85a45d69a0df334.png\"\/><\/p>\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440 \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u044b\u0448\u0435<em> <\/em><code>Api<\/code><em> \u2014 <\/em>\u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 <em>REST<\/em> \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 <em>\u0431\u0430\u0437\u043e\u0432\u0443\u044e<\/em> \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0438\u0437 <code>src<\/code>. \u041a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044b \u0432 \u044d\u0442\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043f\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 <code>src<\/code>, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c, \u0447\u0442\u043e \u0442\u0430\u043c \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432\u0441\u0451, \u0447\u0442\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0440\u0430\u0437\u0434\u0435\u043b\u044b <code>services<\/code> \u0438 <code>routes<\/code> \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u0445\u043e\u0436\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/59b\/7ff\/edd\/59b7ffedd173a9902e54ec59b4f1d04b.png\" alt=\"Image\" title=\"\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\" width=\"880\" height=\"606\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/59b\/7ff\/edd\/59b7ffedd173a9902e54ec59b4f1d04b.png\"\/><\/p>\n<div><figcaption>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f<\/figcaption><\/div>\n<\/figure>\n<pre><code class=\"yaml\">doctrine:     orm:         mappings:             User:                 is_bundle: false                 type: attribute                 dir: '%kernel.project_dir%\/src\/User\/Entity'                 prefix: 'User\\Entity'   framework:     messenger:         routing:             'User\\Messenger\\Message\\ClientSettingsMessage': async   services:     _defaults:         autowire: true         autoconfigure: true         public: false      # base section     User\\:         resource: '..\/..\/src\/User'         exclude: '..\/..\/src\/User\/{Exception,Entity,View,Messenger\/Message}'      # api section     Api\\User\\:         resource: '..\/..\/src\/Api\/User'         exclude: '..\/..\/src\/Api\/User\/{Exception,Entity,View}'<\/code><\/pre>\n<p>\u041f\u043e\u0434\u043e\u0431\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. <br \/>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 <code>Kernel<\/code> \u043a\u043b\u0430\u0441\u0441, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0432\u043e\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.\u00a0\u00a0<\/p>\n<pre><code class=\"php\">&lt;?php declare(strict_types=1);  class Kernel extends Symfony\\Component\\HttpKernel\\Kernel {    use MicroKernelTrait;     protected function configureContainer(ContainerConfigurator $container): void    {        $container->import('..\/config\/{packages}\/*.yaml');        $container->import('..\/config\/{packages}\/'.$this->environment.'\/*.yaml');         $container->import('..\/config\/{services}\/*.yaml');        $container->import('..\/config\/{services}\/'.$this->environment.'\/*.yaml');    }     protected function configureRoutes(RoutingConfigurator $routes): void    {        $routes->import('..\/config\/{routes}\/'.$this->environment.'\/*.yaml');        $routes->import('..\/config\/{routes}\/*.yaml');         if (\\is_file(\\dirname(__DIR__).'\/config\/routes.yaml')) {            $routes->import('..\/config\/routes.yaml');        } else {            $routes->import('..\/config\/{routes}.php');        }    } } <\/code><\/pre>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>src<\/code> \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u043a\u0430\u043a \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 <code>namespace<\/code><em> <\/em>\u0432\u043c\u0435\u0441\u0442\u043e <code>App,<\/code> \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u0422.\u0435. \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u043a\u043b\u0430\u0441\u0441\u0430\u043c \u0432\u043d\u0443\u0442\u0440\u0438 <code>src<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: <code>\\User\\Entity\\User<\/code>, <code>\\Api\\User\\Action\\MeAction<\/code> \u0438 \u0442.\u0434. <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0435\u043a\u0446\u0438\u044e <code>autoloader<\/code> \u0432\u00a0<code>composer.json<\/code> \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>bin\/console.php<\/code> \u0438 <code>public\/index.php<\/code>, \u043f\u043e\u043c\u0435\u043d\u044f\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 namespace.<\/p>\n<pre><code class=\"json\">\"autoload\": {    \"psr-4\": {        \"\": \"src\/\"    } }<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 composer \u0432 <em>prod<\/em> \u0441\u0440\u0435\u0434\u0435 \u044d\u0442\u043e \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c:<\/p>\n<pre><code class=\"bash\">composer install --no-dev --optimize-autoloader --classmap-authoritative --apcu-autoloader<\/code><\/pre>\n<h2>Actions (ADR) \u0432\u043c\u0435\u0441\u0442\u043e Controller<\/h2>\n<p>\u041c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441 <em>REST API<\/em> \u0438 \u043d\u0435 \u043b\u044e\u0431\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0444\u0430\u0439\u043b\u044b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b\u0438 \u0434\u043b\u044f \u0441\u0435\u0431\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u043e: \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0431\u043e\u0440 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439, \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442 \u043d\u0430\u0448\u0438\u0445 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u044d\u043a\u0448\u0435\u043d\u043e\u0432 <code>GetAction<\/code><em>, <\/em><code>MutationAction<\/code><em>, <\/em><code>GetWithFormAction<\/code>.<em>\u00a0<\/em><\/p>\n<p><code>Action<\/code> &#8212; \u044d\u0442\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u0440, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439, \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0438\u0441.\u00a0 <a href=\"https:\/\/symfony.com\/doc\/current\/controller\/service.html#invokable-controllers\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/symfony.com\/doc\/current\/controller\/service.html#invokable-controllers<\/u><\/a><\/p>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0432 <code>Action<\/code><em> <\/em>\u043c\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <code>getSubscribedServices<\/code>. \u0422\u0430\u043a\u043e\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e \u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f, \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0435\u043d \u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<p>\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0451\u043d \u043d\u0438\u0436\u0435. \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>AbstractController<\/code> \u043e\u0442 Symfony, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438-\u0445\u0435\u043b\u043f\u0435\u0440\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a.<\/p>\n<pre><code class=\"php\">&lt;?php declare(strict_types=1);  use Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController;  abstract class AbstractAction extends AbstractController {    public static function getSubscribedServices(): array    {        return [ \/\/ You can add your services here \/\/ 'request_stack' => RequestStack::class,        ];    } } <\/code><\/pre>\n<p>\u0414\u043b\u044f \u0432\u0441\u0435\u0445 <em>GET <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <code>GetAction<\/code>. \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u043d \u043f\u0443\u0441\u0442\u043e\u0439, \u043d\u043e \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0443\u0436\u0434 \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c\u0441\u044f:<\/p>\n<pre><code class=\"php\">&lt;?php declare(strict_types=1);  namespace Api\\Action;  use Action\\AbstractAction;  class GetAction extends AbstractAction {    public static function getSubscribedServices(): array    {        return [ \/\/ You can add your services here \/\/ 'request_stack' => RequestStack::class,        ];    } } <\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 &#8212; <code>MutationAction<\/code> &#8212; \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u0435\u043d <em>GET <\/em>\u043a\u043b\u0430\u0441\u0441\u0443, \u043d\u043e \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u043c\u0438. <code>MutationAction<\/code><em> <\/em>\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0442\u0435\u043b\u0435 <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0444\u043e\u0440\u043c. \u041f\u0440\u043e <code>ApiFormTrait<\/code> \u0431\u0443\u0434\u0435\u0442 \u043d\u0438\u0436\u0435 \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0441\u0442\u0435.<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class MutationAction extends \\AbstractAction {    use ApiFormTrait;     public static function getSubscribedServices(): array    {        return parent::getSubscribedServices() + [                'form.factory' => FormFactoryInterface::class,                'request_stack' => RequestStack::class,            ];    } } <\/code><\/pre>\n<p>\u0412 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u0431\u0430\u0437\u043e\u0432\u044b\u043c <em>GET\/POST<\/em> \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c, \u043c\u044b \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>ListAction<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e <code>GetAction<\/code>, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u043d\u0443\u044e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<hr\/>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u043d\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442, \u043a\u0430\u043a \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0438\u0436\u0435. <br \/>\u0414\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a (\u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e):<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  #[Route('\/me', methods: 'GET')] #[IsGranted('ROLE_USER')] class MeAction extends GetAction {    public function __invoke(): Response    {        return new PrivateUserView($this->getUser());    } } <\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  #[Route(\"\/user\/settings\/update\", methods: ['POST'])] #[IsGranted(\"ROLE_USER\")] class UpdateUserSettingsAction extends MutationAction {    use \\EntityManagerAwareTrait;    use \\MessageBusAwareTrait;    use \\LoggerAwareTrait;     public function __invoke(): ViewInterface    {        return $this->handleApiCall(UserSettingsForm::class, function (UserSettingsDto $dto) {            return new PrivateUserView($this->update($this->getUser(), $dto));        });    }     private function update(User $user, UserSettingsDto $data): void    {        $this->em->beginTransaction();        try {            $this->em->lock($user, LockMode::PESSIMISTIC_WRITE);            $this->em->refresh($user);             $user->updateSettings($data);            $this->em->persist($user);            $this->em->flush();             \/**             * SettingsMessage uses transactional routing,             * Therefore, the message could be processed only if the transaction has been successfully committed,             * ie, after all, real updates to the database.             *\/            $this->bus->dispatch(new SettingsMessage($user));             $this->em->commit();        } catch (\\Throwable $e) {            $this->logger->error('Error occurred while update user settings', ['error' => $e]);            $this->em->rollback();            throw $e;        }    } }  <\/code><\/pre>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0432\u043e\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0442\u0440\u0435\u0439\u0442\u044b <code>\\EntityManagerAwareTrait<\/code>, <code>\\MessageBusAwareTrait<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u0437\u0436\u0435. \u041e\u043d\u0438 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438.\u00a0<\/p>\n<h2>Mutation \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u0411\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <em>Symfony<\/em> \u0444\u043e\u0440\u043c\u044b.  \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0433\u0434\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <em>Data Transfer Object (DTO) <\/em>+ \u0441\u0430\u043c\u0430 \u0444\u043e\u0440\u043c\u0430 \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0442 <code>Symfony\\Component\\Form\\AbstractType<\/code>, \u0433\u0434\u0435 \u043c\u044b \u0437\u0430\u0434\u0430\u0451\u043c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u043c\u044b:<\/p>\n<ol>\n<li>\n<p>\u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438, \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0444\u043e\u0440\u043c \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b. \u0418\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <em>DTO.<\/em><\/p>\n<\/li>\n<li>\n<p>\u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438. \u041d\u0430\u043c \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0444\u043e\u0440\u043c\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0438 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u00a0<br \/>\u041a\u0430\u043a \u0438 \u0441 <code>Action<\/code>, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0444\u043e\u0440\u043c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f <code>MutationAction<\/code><em> <\/em>\u2014 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 <code>MutationForm<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0436\u0438\u0434\u0430\u0435\u0442 <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f <code>allow_extra_fields<\/code>. \u0414\u043b\u044f <code>GetAction<\/code><em> <\/em>\u0438 <code>ListAction<\/code><em> <\/em>\u043d\u0430\u043f\u0440\u043e\u0442\u0438\u0432 &#8212; \u043e\u0436\u0438\u0434\u0430\u0435\u0442 <em>GET <\/em>\u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f.<\/p>\n<p>\u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0441 <em>JSON <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u0438 \u0443\u0431\u0440\u0430\u043b\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 namespace \u0443 \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0445 \u0444\u043e\u0440\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0443, \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0432 \u044d\u0442\u043e \u0447\u0435\u0440\u0435\u0437 <code>getBlockPrefix<\/code> \u0432 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0445.<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class PostForm extends AbstractType {    public function configureOptions(OptionsResolver $resolver): void    {        $resolver->setDefaults([            'method' => 'POST',        ]);    }     public function getBlockPrefix(): string    {        return '';    } } <\/code><\/pre>\n<p><code>PostForm<\/code>\u043d\u0443\u0436\u0435\u043d, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>configureOptions<\/code> \u0431\u0435\u0437 \u0432\u044b\u0437\u043e\u0432\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0432 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0430\u0445 <code>MutationForm<\/code><\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  abstract class MutationForm extends AbstractType {    public function getBlockPrefix(): string    {        return '';    }     public function getParent(): string    {        return PostForm::class;    } } <\/code><\/pre>\n<p>\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class UserSettingsForm extends MutationForm {    public function configureOptions(OptionsResolver $resolver): void    {        $resolver->setDefaults([            'data_class' => UserSettingsDto::class,        ]);    }     public function buildForm(FormBuilderInterface $builder, array $options): void    {        $builder            ->add('firstName', TextType::class, [                'constraints' => [                    new Length(['max' => $max = 255]),                ],            ])            ->add('lastName', TextType::class, [                'constraints' => [                    new Length(['max' => $max]),                ],            ]);    } } <\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 <em>DTO <\/em>\u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u043d \u043d\u0438\u0436\u0435.\u00a0\u042d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 <em>DTO <\/em>\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u0412 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u043c\u044b \u0431\u044b \u043c\u043e\u0433\u043b\u0438 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438, \u043d\u043e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <em>DTO <\/em>\u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class UserSettingsDto {    public string|null $firstName;    public string|null $lastName; } <\/code><\/pre>\n<h2>ApiFormTrait<\/h2>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 <em>POST<\/em> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043c\u044b \u0432\u044b\u043d\u0435\u0441\u043b\u0438 \u043e\u0431\u0449\u0438\u0439 \u043a\u043e\u0434 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0444\u043e\u0440\u043c \u0432 <em>trait<\/em>. \u041e\u043d \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u0432 <code>MutationAction<\/code><em> <\/em>\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p>\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043c\u044b \u043e\u0442\u0434\u0430\u0451\u043c \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435 &#8212; \u0434\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432\u00a0\u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043c\u0435\u0442\u043e\u0434\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0443 \u043f\u043e\u043b\u0435\u0439 \u0444\u043e\u0440\u043c\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u044d\u0442\u043e \u0441\u0438\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0442\u0441\u0442\u0432\u0438\u0435. \u041e\u0448\u0438\u0431\u043a\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e \u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.<\/p>\n<p>\u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430\u0448\u0435\u0433\u043e <em>trait<\/em>, \u043e\u043d \u0440\u0435\u0448\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u0430: \u0431\u0435\u0440\u0451\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u043d\u0443\u0436\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f. <\/p>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u0438\u0434\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 <code>MutationForm<\/code><em> <\/em>\u0441 \u043f\u0440\u043e\u0441\u0442\u044b\u043c <em>JSON <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u043e\u0442 \u0432\u0441\u0435\u0445 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445, \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 <em>Symfony <\/em>\u0444\u043e\u0440\u043c, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  trait ApiFormTrait {    use FormTrait;     protected function handleApiCall(FormInterface|string $form, callable|null $callable = null): ViewInterface    {        if (!$form instanceof FormInterface &amp;&amp; \\is_string($form)) {            $form = $this->container->get('form.factory')->create($form);        }          $request = $this->container->get('request_stack')->getCurrentRequest();          return $this->onFormSubmitted(             $form->getConfig()->getType()->getInnerType() instanceof MutationForm                ? $form->submit($this->convertRequestToArray($request))                : $form->handleRequest($request),            $callable        );    }     private function convertRequestToArray(Request $request): array    {        $data = [];        if ('json' === $request->getContentTypeFormat() &amp;&amp; $request->getContent()) {            try{                $data = $request->toArray();            } catch (\\Throwable $e){                throw new BadRequestHttpException('Could not convert content into a valid JSON.', $e);            }        }         return $data;    } } <\/code><\/pre>\n<pre><code class=\"php\">&lt;?php \/** . *\/  trait FormTrait {    protected function createSuccessResponse(array $data = []): DataView|ResponseView    {        return $data ? new DataView($data) : new ResponseView();    }     protected function createExceptionResponse(): FailureView    {        return $this->createFailureResponse(Response::HTTP_INTERNAL_SERVER_ERROR);    }     protected function createSuccessHtmlResponse(string $view, array $parameters = []): Response|SuccessHtmlView    {        $request = $this->container->get('request_stack')->getCurrentRequest();        if ($request->isXmlHttpRequest()) {            return new SuccessHtmlView([                'html' => $this->renderView($view, $parameters),            ]);        }         return $this->render($view, $parameters);    }     protected function createFailureResponse(int $status = Response::HTTP_BAD_REQUEST): FailureView    {        return new FailureView($status);    }     protected function createValidationFailedResponse(FormInterface $form): ValidationFailedView    {        return new ValidationFailedView($this->serializeFormErrors($form));    }     protected function handleFormCall(FormInterface|string $form, callable|null $callable = null): Response|ViewInterface    {        if (!\\is_string($form) &amp;&amp; !$form instanceof FormInterface) {            throw new \\TypeError(\\sprintf('Passed $form must be of type \"%s\", \"%s\" given.', \\implode(',', ['string', FormInterface::class]), \\get_debug_type($form)));        }         if (\\is_string($form)) {            $form = $this->container->get('form.factory')->create($form);        }         $request = $this->container->get('request_stack')->getCurrentRequest();        $form->handleRequest($request);         if (!$form->isSubmitted()) {            throw $this->createSubmittedFormRequiredException(\\get_class($form));        }         return $this->createSubmittedFormResponse($form, $callable);    }     protected function createSubmittedFormResponse(FormInterface $form, callable|null $callable = null): Response|ViewInterface    {        return $this->onFormSubmitted($form, $callable);    }     \/**     * @param null|callable $callable must return @see \\Dev\\ViewBundle\\View\\ViewInterface, array or null     *\/    protected function onFormSubmitted(FormInterface $form, callable|null $callable = null): Response|ViewInterface    {        if (!$form->isValid()) {            return $this->createValidationFailedResponse($form);        }         if (null === $callable || null === $response = \\call_user_func($callable, $form->getData())) {            return $this->createSuccessResponse();        }         if (!\\is_array($response) &amp;&amp; !$response instanceof ViewInterface &amp;&amp; !$response instanceof Response) {            throw new \\TypeError(\\sprintf('Passed closure must return %s, returned %s', \\implode('|', [ViewInterface::class, Response::class, 'array']), \\get_debug_type($response)));        }         return \\is_array($response) ? $this->createSuccessResponse($response) : $response;    }     protected function serializeFormErrors(FormInterface $form): array    {        return $this->serialiseErrors($form->getErrors(true, false));    }     protected function createNotXmlHttpRequestException(): XmlHttpRequestRequiredException    {        return new XmlHttpRequestRequiredException();    }     protected function createSubmittedFormRequiredException(string $type): SubmittedFormRequiredException    {        return new SubmittedFormRequiredException($type);    }     private function serialiseErrors(FormErrorIterator $iterator, array $paths = []): array    {        if ('' !== $name = $iterator->getForm()->getName()) {            $paths[] = $name;        }        $id = \\implode('_', $paths);        $path = \\implode('.', $paths);         $violations = [];        foreach ($iterator as $formErrorIterator) {            if ($formErrorIterator instanceof FormErrorIterator) {                $violations = \\array_merge($violations, $this->serialiseErrors($formErrorIterator, $paths));                continue;            }             \/* @var FormError $formErrorIterator *\/            $violationEntry = new ViolationView(                $id,                $formErrorIterator->getMessage(),                $formErrorIterator->getMessageParameters(),                $path            );             $cause = $formErrorIterator->getCause();            if ($cause instanceof ConstraintViolation) {                if (null !== $code = $cause->getCode()) {                    $violationEntry->type = \\sprintf('urn:uuid:%s', $code);                }            }            $violations[] = $violationEntry;        }         return $violations;    } } <\/code><\/pre>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435<code>*View<\/code> \u043a\u043b\u0430\u0441\u0441\u044b, \u043e \u043d\u0438\u0445 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043d\u0438\u0436\u0435, \u043d\u043e \u0432 \u0446\u0435\u043b\u043e\u043c \u043a \u043d\u0438\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u043a \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439.<\/p>\n<h2>\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041a\u0430\u043a \u044f \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b \u0440\u0430\u043d\u0435\u0435, \u043c\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 \u0444\u043e\u0440\u043c. \u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a:\u00a0<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/     public function buildForm(FormBuilderInterface $builder, array $options): void    {        $builder            ->add('firstName', TextType::class, [                'constraints' => [                    new Length(['max' => $max = 255]),                ],            ])            ->add('lastName', TextType::class, [                'constraints' => [                    new Length(['max' => $max]),                ],            ]);    } } <\/code><\/pre>\n<p>\u041d\u0438\u0436\u0435 &#8212; \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0433\u0434\u0435 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0442\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0438\u043c\u044f \u0438 \u0444\u0430\u043c\u0438\u043b\u0438\u044f \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0443\u0441\u0442\u044b\u043c\u0438:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class UserSettingsForm extends FormType {    public function configureOptions(OptionsResolver $resolver): void    {        $resolver->setDefaults([            'data_class' => UserSettingsDto::class,            'constraints' => [                new Callback(static function (UserSettingsDto|null $dto, ExecutionContextInterface $context) {                    if (null === $dto) {                        return;                    }                     if (!$dto->lastName &amp;&amp; !$dto->firstName) {                        $constraint = new NotBlank();                        $context                            ->buildViolation('First and last names must not be simultaneously empty.')                            ->setCode($constraint::IS_BLANK_ERROR)                            ->setCause($constraint)                            ->atPath('firstName')                            ->addViolation();                    }                }),            ],        ]);    }     public function buildForm(FormBuilderInterface $builder, array $options): void    {        $builder            ->add('firstName', TextType::class, [                'constraints' => [                    new Length(['max' => $max = 255]),                ],            ])            ->add('lastName', TextType::class, [                'constraints' => [                    new Length(['max' => $max]),                ],            ]);         $builder->get('lastName')->addEventListener(FormEvents::POST_SUBMIT, static function (PostSubmitEvent $event): void {            if ($event->getData()) {                $form = $event->getForm()->getParent();                $form->add('middleName', TextType::class, [                    'constraints' => [                        new NotBlank(),                        new Length(['max' => 255]),                    ],                ]);            }        });    } } <\/code><\/pre>\n<p>\u0414\u043e\u0441\u0442\u0438\u0447\u044c \u0442\u0430\u043a\u043e\u0439 \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442\u044b, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u043e \u044d\u0442\u043e \u0437\u0430\u0439\u043c\u0451\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/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\/articles\/784660\/\"> https:\/\/habr.com\/ru\/articles\/784660\/<\/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<p>\u041f\u0440\u0438\u0432\u0435\u0442! \u042f, \u0410\u043d\u0434\u0440\u0435\u0439, Symfony \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a &#8212; \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0430\u0439\u0442\u044b. \u041a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c \u0443 \u043d\u0430\u0441 \u0443\u0445\u043e\u0434\u0438\u0442 \u043c\u043d\u043e\u0433\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043d\u0430 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c \u043e\u043f\u044b\u0442\u043e\u043c, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a Symfony<em> <\/em>\u0434\u043b\u044f \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u0438\u0445 \u0437\u0430\u0442\u0440\u0430\u0442, \u043a\u0430\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043c\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0438 \u043a\u0430\u043a \u043c\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u043c \u0441 <em>REST <\/em>\u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438.\u00a0\u041f\u043e\u0435\u0445\u0430\u043b\u0438 (\u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430).<\/p>\n<h2>Namespace \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u0448\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 <code>User<\/code>, \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u00ab\u0434\u043e\u043c\u0435\u043d\u044b\u00bb \u0438\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u043f\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0430\u043c. \u0422\u0430\u043a \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434\u0435 \u0438 \u043f\u0440\u043e\u0449\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441\u044b.\u00a0<\/p>\n<figure class=\"bordered full-width\">\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440 \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u044b\u0448\u0435<em> <\/em><code>Api<\/code><em> \u2014 <\/em>\u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 <em>REST<\/em> \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 <em>\u0431\u0430\u0437\u043e\u0432\u0443\u044e<\/em> \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0438\u0437 <code>src<\/code>. \u041a\u043e\u0440\u043d\u0435\u0432\u044b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044b \u0432 \u044d\u0442\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043f\u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 <code>src<\/code>, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c, \u0447\u0442\u043e \u0442\u0430\u043c \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432\u0441\u0451, \u0447\u0442\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0440\u0430\u0437\u0434\u0435\u043b\u044b <code>services<\/code> \u0438 <code>routes<\/code> \u043c\u044b \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u0445\u043e\u0436\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f<\/figcaption><\/div>\n<\/figure>\n<pre><code class=\"yaml\">doctrine:     orm:         mappings:             User:                 is_bundle: false                 type: attribute                 dir: '%kernel.project_dir%\/src\/User\/Entity'                 prefix: 'User\\Entity'   framework:     messenger:         routing:             'User\\Messenger\\Message\\ClientSettingsMessage': async   services:     _defaults:         autowire: true         autoconfigure: true         public: false      # base section     User\\:         resource: '..\/..\/src\/User'         exclude: '..\/..\/src\/User\/{Exception,Entity,View,Messenger\/Message}'      # api section     Api\\User\\:         resource: '..\/..\/src\/Api\/User'         exclude: '..\/..\/src\/Api\/User\/{Exception,Entity,View}'<\/code><\/pre>\n<p>\u041f\u043e\u0434\u043e\u0431\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. <br \/>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 <code>Kernel<\/code> \u043a\u043b\u0430\u0441\u0441, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0432\u043e\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.\u00a0\u00a0<\/p>\n<pre><code class=\"php\">&lt;?php declare(strict_types=1);  class Kernel extends Symfony\\Component\\HttpKernel\\Kernel {    use MicroKernelTrait;     protected function configureContainer(ContainerConfigurator $container): void    {        $container->import('..\/config\/{packages}\/*.yaml');        $container->import('..\/config\/{packages}\/'.$this->environment.'\/*.yaml');         $container->import('..\/config\/{services}\/*.yaml');        $container->import('..\/config\/{services}\/'.$this->environment.'\/*.yaml');    }     protected function configureRoutes(RoutingConfigurator $routes): void    {        $routes->import('..\/config\/{routes}\/'.$this->environment.'\/*.yaml');        $routes->import('..\/config\/{routes}\/*.yaml');         if (\\is_file(\\dirname(__DIR__).'\/config\/routes.yaml')) {            $routes->import('..\/config\/routes.yaml');        } else {            $routes->import('..\/config\/{routes}.php');        }    } } <\/code><\/pre>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>src<\/code> \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u043a\u0430\u043a \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 <code>namespace<\/code><em> <\/em>\u0432\u043c\u0435\u0441\u0442\u043e <code>App,<\/code> \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u0422.\u0435. \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u043a\u043b\u0430\u0441\u0441\u0430\u043c \u0432\u043d\u0443\u0442\u0440\u0438 <code>src<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c: <code>\\User\\Entity\\User<\/code>, <code>\\Api\\User\\Action\\MeAction<\/code> \u0438 \u0442.\u0434. <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441\u0435\u043a\u0446\u0438\u044e <code>autoloader<\/code> \u0432\u00a0<code>composer.json<\/code> \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>bin\/console.php<\/code> \u0438 <code>public\/index.php<\/code>, \u043f\u043e\u043c\u0435\u043d\u044f\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 namespace.<\/p>\n<pre><code class=\"json\">\"autoload\": {    \"psr-4\": {        \"\": \"src\/\"    } }<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 composer \u0432 <em>prod<\/em> \u0441\u0440\u0435\u0434\u0435 \u044d\u0442\u043e \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c:<\/p>\n<pre><code class=\"bash\">composer install --no-dev --optimize-autoloader --classmap-authoritative --apcu-autoloader<\/code><\/pre>\n<h2>Actions (ADR) \u0432\u043c\u0435\u0441\u0442\u043e Controller<\/h2>\n<p>\u041c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441 <em>REST API<\/em> \u0438 \u043d\u0435 \u043b\u044e\u0431\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0444\u0430\u0439\u043b\u044b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043b\u0438 \u0434\u043b\u044f \u0441\u0435\u0431\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u043e: \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0431\u043e\u0440 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439, \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u044d\u0442\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442 \u043d\u0430\u0448\u0438\u0445 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u044d\u043a\u0448\u0435\u043d\u043e\u0432 <code>GetAction<\/code><em>, <\/em><code>MutationAction<\/code><em>, <\/em><code>GetWithFormAction<\/code>.<em>\u00a0<\/em><\/p>\n<p><code>Action<\/code> &#8212; \u044d\u0442\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u0440, \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439, \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0438\u0441.\u00a0 <a href=\"https:\/\/symfony.com\/doc\/current\/controller\/service.html#invokable-controllers\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/symfony.com\/doc\/current\/controller\/service.html#invokable-controllers<\/u><\/a><\/p>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0432 <code>Action<\/code><em> <\/em>\u043c\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <code>getSubscribedServices<\/code>. \u0422\u0430\u043a\u043e\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e \u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f, \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0435\u043d \u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d.<\/p>\n<p>\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0451\u043d \u043d\u0438\u0436\u0435. \u041c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>AbstractController<\/code> \u043e\u0442 Symfony, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438-\u0445\u0435\u043b\u043f\u0435\u0440\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a.<\/p>\n<pre><code class=\"php\">&lt;?php declare(strict_types=1);  use Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController;  abstract class AbstractAction extends AbstractController {    public static function getSubscribedServices(): array    {        return [ \/\/ You can add your services here \/\/ 'request_stack' => RequestStack::class,        ];    } } <\/code><\/pre>\n<p>\u0414\u043b\u044f \u0432\u0441\u0435\u0445 <em>GET <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 <code>GetAction<\/code>. \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u043d \u043f\u0443\u0441\u0442\u043e\u0439, \u043d\u043e \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0443\u0436\u0434 \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c\u0441\u044f:<\/p>\n<pre><code class=\"php\">&lt;?php declare(strict_types=1);  namespace Api\\Action;  use Action\\AbstractAction;  class GetAction extends AbstractAction {    public static function getSubscribedServices(): array    {        return [ \/\/ You can add your services here \/\/ 'request_stack' => RequestStack::class,        ];    } } <\/code><\/pre>\n<p>\u041a\u043b\u0430\u0441\u0441 \u0434\u043b\u044f <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 &#8212; <code>MutationAction<\/code> &#8212; \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u0435\u043d <em>GET <\/em>\u043a\u043b\u0430\u0441\u0441\u0443, \u043d\u043e \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u043c\u0438. <code>MutationAction<\/code><em> <\/em>\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0442\u0435\u043b\u0435 <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0444\u043e\u0440\u043c. \u041f\u0440\u043e <code>ApiFormTrait<\/code> \u0431\u0443\u0434\u0435\u0442 \u043d\u0438\u0436\u0435 \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0441\u0442\u0435.<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class MutationAction extends \\AbstractAction {    use ApiFormTrait;     public static function getSubscribedServices(): array    {        return parent::getSubscribedServices() + [                'form.factory' => FormFactoryInterface::class,                'request_stack' => RequestStack::class,            ];    } } <\/code><\/pre>\n<p>\u0412 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u0431\u0430\u0437\u043e\u0432\u044b\u043c <em>GET\/POST<\/em> \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c, \u043c\u044b \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>ListAction<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e <code>GetAction<\/code>, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u043d\u0443\u044e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441.<\/p>\n<hr\/>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u043d\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442, \u043a\u0430\u043a \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0438\u0436\u0435. <br \/>\u0414\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a (\u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e):<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  #[Route('\/me', methods: 'GET')] #[IsGranted('ROLE_USER')] class MeAction extends GetAction {    public function __invoke(): Response    {        return new PrivateUserView($this->getUser());    } } <\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  #[Route(\"\/user\/settings\/update\", methods: ['POST'])] #[IsGranted(\"ROLE_USER\")] class UpdateUserSettingsAction extends MutationAction {    use \\EntityManagerAwareTrait;    use \\MessageBusAwareTrait;    use \\LoggerAwareTrait;     public function __invoke(): ViewInterface    {        return $this->handleApiCall(UserSettingsForm::class, function (UserSettingsDto $dto) {            return new PrivateUserView($this->update($this->getUser(), $dto));        });    }     private function update(User $user, UserSettingsDto $data): void    {        $this->em->beginTransaction();        try {            $this->em->lock($user, LockMode::PESSIMISTIC_WRITE);            $this->em->refresh($user);             $user->updateSettings($data);            $this->em->persist($user);            $this->em->flush();             \/**             * SettingsMessage uses transactional routing,             * Therefore, the message could be processed only if the transaction has been successfully committed,             * ie, after all, real updates to the database.             *\/            $this->bus->dispatch(new SettingsMessage($user));             $this->em->commit();        } catch (\\Throwable $e) {            $this->logger->error('Error occurred while update user settings', ['error' => $e]);            $this->em->rollback();            throw $e;        }    } }  <\/code><\/pre>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0432\u043e\u0438 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0442\u0440\u0435\u0439\u0442\u044b <code>\\EntityManagerAwareTrait<\/code>, <code>\\MessageBusAwareTrait<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u0437\u0436\u0435. \u041e\u043d\u0438 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438.\u00a0<\/p>\n<h2>Mutation \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u0411\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 <em>Symfony<\/em> \u0444\u043e\u0440\u043c\u044b.  \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0433\u0434\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <em>Data Transfer Object (DTO) <\/em>+ \u0441\u0430\u043c\u0430 \u0444\u043e\u0440\u043c\u0430 \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0442 <code>Symfony\\Component\\Form\\AbstractType<\/code>, \u0433\u0434\u0435 \u043c\u044b \u0437\u0430\u0434\u0430\u0451\u043c \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438, \u043c\u044b:<\/p>\n<ol>\n<li>\n<p>\u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438, \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0444\u043e\u0440\u043c \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430 \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b. \u0418\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0446\u0435\u043b\u0435\u0439 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <em>DTO.<\/em><\/p>\n<\/li>\n<li>\n<p>\u041d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438. \u041d\u0430\u043c \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0444\u043e\u0440\u043c\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0438 \u0441 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u00a0<br \/>\u041a\u0430\u043a \u0438 \u0441 <code>Action<\/code>, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0444\u043e\u0440\u043c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f <code>MutationAction<\/code><em> <\/em>\u2014 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 <code>MutationForm<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0436\u0438\u0434\u0430\u0435\u0442 <em>POST <\/em>\u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f <code>allow_extra_fields<\/code>. \u0414\u043b\u044f <code>GetAction<\/code><em> <\/em>\u0438 <code>ListAction<\/code><em> <\/em>\u043d\u0430\u043f\u0440\u043e\u0442\u0438\u0432 &#8212; \u043e\u0436\u0438\u0434\u0430\u0435\u0442 <em>GET <\/em>\u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u044b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f.<\/p>\n<p>\u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0441 <em>JSON <\/em>\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u0438 \u0443\u0431\u0440\u0430\u043b\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 namespace \u0443 \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u0445 \u0444\u043e\u0440\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0443, \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0432 \u044d\u0442\u043e \u0447\u0435\u0440\u0435\u0437 <code>getBlockPrefix<\/code> \u0432 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0445.<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class PostForm extends AbstractType {    public function configureOptions(OptionsResolver $resolver): void    {        $resolver->setDefaults([            'method' => 'POST',        ]);    }     public function getBlockPrefix(): string    {        return '';    } } <\/code><\/pre>\n<p><code>PostForm<\/code>\u043d\u0443\u0436\u0435\u043d, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <code>configureOptions<\/code> \u0431\u0435\u0437 \u0432\u044b\u0437\u043e\u0432\u0430 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0432 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0430\u0445 <code>MutationForm<\/code><\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  abstract class MutationForm extends AbstractType {    public function getBlockPrefix(): string    {        return '';    }     public function getParent(): string    {        return PostForm::class;    } } <\/code><\/pre>\n<p>\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043a \u043f\u0440\u0438\u043c\u0435\u0440\u0443 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u043d \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"php\">&lt;?php \/** . *\/  class UserSettingsForm extends MutationForm {    public function configureOptions(OptionsResolver $resolver): void    {        $resolver->setDefaults([            'data_class' => UserSettingsDto::class,        ]);    }     public function buildForm(FormBuilderInterface $builder, array $options): void   <\/code><\/pre>\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-364618","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/364618","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=364618"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/364618\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=364618"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=364618"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=364618"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}