2

I hope I'm right to ask this. I've looked at (almost) all similar concern but I ain't satisfied yet.

I'm working on a User entity and for days (weeks actually) now i'm trying to POST a user with a custom body. Here's some part of my entity User :

/**
 * @ApiResource(
 *      normalizationContext={"groups"={"read"}},
 *      denormalizationContext={"groups"={"write"}},
 *      itemOperations={
 *          "get",
 *          "put",
 *          "delete",
 *          "get_active_user"={
 *              "method"="GET",
 *              "path"="/users/active/me",
 *              "controller"=UserReadAction::class,
 *              "defaults"={"_api_receive"=false},
 *              "swagger_context"={
 *                  "parameters"={
 *
 *                  }
 *              }
 *          },
 *      },
 *      collectionOperations={
 *          "change_password"={
 *              "method"="POST",
 *              "path"="/users/active/changepassword",
 *              "controller"=UserChangePasswordAction::class,
 *              "normalization_context"={"groups"={"afup"}},
 *              "defaults"={"_api_receive"=false},
 *              "swagger_context"={
 *                  "summary" = "Change user password",
 *                  "parameters"={
 *                      {
 *                          "name" = "User",
 *                          "in" = "body",
 *                          "schema" = {
 *                              "type" = "object",
 *                              "properties" = {
 *                                  "password" = {"type"="string"},
 *                                  "nom" = {"type"="string"},
 *                              }
 *                           },
 *                          "required" = "true",
 *                      }
 *                  },
 *              }
 *          }
 *      }
 * )
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @ORM\Table(name="users")
 */
class User implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"read", "write", "afup"})
     */
    private $id;

Here is the controller:

namespace App\Controller\SDK;


use App\Entity\User;
use App\Service\SDK\UserService;
use Symfony\Component\Security\Core\Security;

class UserChangePasswordAction
{
    public function __invoke(User $data)
    {
        var_dump($data);die;
    }
}

And the services.yaml (some part) file

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.
        public: false       # Allows optimizing the container by removing unused services; this also means
                            # fetching services directly from the container via $container->get() won't work.
                            # The best practice is to be explicit about your dependencies anyway.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

    # controllers are imported separately to make sure services can be injected
    # as action arguments even if you don't extend any base controller class
    App\Controller\:
        resource: '../src/Controller/*'
        tags: ['controller.service_arguments']

When I try this (see var_dump in controller), i get an error saying:

Cannot autowire argument $data of "App\Controller\SDK\UserChangePasswordAction()": it references class "App\Entity\User" no such service exists

I read the official doc and it seems that the _invoke method should automatically retrieve the entity. But it does not work for me.

Notice: I also defined a custom item operation "get_active_user" and it works fine.

Please I would like to understand :

  • what I did wrong,
  • how it actually works,

Thank you.

EDIT: In the collectionOperation definition, i removed the following setting which means that we manually want to handle data (User) retrieval :

  • "defaults"={"_api_receive"=false},

Now, the controller returns an empty User entity, not an error. I still can't get the submitted data.

Duhamel
  • 147
  • 1
  • 3
  • 16

2 Answers2

1

The edit of my question fix the concern. Actually, I just needed to remove this annotation from the POST opration definition :')

"defaults"={"_api_receive"=false},

Now, when I submit the data, I get them as on the following image : enter image description here

This annotation is important when you write custom GET operation.

Duhamel
  • 147
  • 1
  • 3
  • 16
0

It is not working because that is a CollectionOperation. In this case, you can get the user through TokenStorageInterface

namespace App\Controller\SDK;


use App\Entity\User;
use App\Service\SDK\UserService;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class UserChangePasswordAction
{
    private $tokenStorage;

    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    public function __invoke(Request $request) //Get request if you want o keep args empty
    {

        var_dump($this->tokenStorage->getToken()->getUser());die;
    }
}
Dhia Djobbi
  • 1,176
  • 2
  • 15
  • 35
AythaNzt
  • 1,057
  • 6
  • 14
  • Thank you. I tried that and it doesn't find the getUser() method from $tokenStorage var (and it seems that interface doesn't have this method). Moreover, when I var_dump the Request param, I don't have the body i sent. Is this normal ? thank you again. – Duhamel Feb 19 '19 at 22:28
  • Oh sorry... it is getToken()->getUser(). Anyway... why dont you use that Custom Operation inside the Item Operations? – AythaNzt Feb 19 '19 at 22:32
  • I tried and it just shows the logged use but not the data sent. In the other hand, POST is a collectionOperation (according to the doc : https://api-platform.com/docs/core/operations/) "Collection operations act on a collection of resources. By default two routes are implemented: POST and GET" Anyway, i put it in thee item operation and it acts the same. – Duhamel Feb 19 '19 at 22:43
  • Yeah sorry, I mean... Why dont use PUT if you are going to modify a property (password) of entity which already exists? – AythaNzt Feb 19 '19 at 22:48
  • I noticed it doesn't change anything except that I need to give the id. The most important for me is to be able to get data sent via the body, it can be a POST or PUT, you might need some day to use some fields. So POST seems to be the easiest way to try it. – Duhamel Feb 19 '19 at 22:55
  • Hi again, Im reviewing this again and... Which "use" of TokenStoragInterface did you import? You have to use: use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; Because this other has not getUser() method as you said: use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface; – AythaNzt Feb 20 '19 at 10:09
  • Right, I tried both of them and the first one return an empty User but the second one require a $tokenId for the getToken() method. – Duhamel Feb 20 '19 at 14:14
  • Hello @aythanzt , I finally fix it. you can see the answer below. Special thanks to you :) – Duhamel Feb 20 '19 at 14:57