LoginSignup
0
1

More than 3 years have passed since last update.

API PlatformでカスタムDELETEアクションを作成するときの注意点

Posted at

はじめに

1API Platformを使っていてカスタムDELETEアクションを作成した所、思わぬ挙動になったので、その時の備忘録です。

やりたかったこと

UserBookFavoriteBookの3つEntityがあります。FavoriteBookはユーザーのお気に入りの本を表す中間テーブルです。
リレーションはこんな感じです。
ER (1).png

APIとして提供するのはUserBookのCRUD操作だけで、FavoriteBookのCRUD操作のエンドポイントは提供しません。代わりに、/api/books/{bookId}/favoriteにPOSTとDLETEメソッドを設け、ここでログインしているユーザーに紐づくFavoriteBookを作成します。

実装

重要な所だけ載せます。

Bookに設定されているアノテーションはこのようになってます。

src/Entity/Book.php

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use App\Controller\AddFavoriteAction;
use App\Controller\DeleteFavoriteAction;
use App\Repository\BookRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ApiResource(
 *     itemOperations={
 *          "get",
 *          "delete",
 *          "put",
 *          "add_favorite"={
 *              "method"="POST",
 *              "path"="/books/{id}/favorite",
 *              "controller"=AddFavoriteAction::class,
 *              "denormalization_context"={"groups"={"book:favorite"}},
 *          },
 *          "delete_favorite"={
 *              "method"="DELETE",
 *              "path"="/books/{id}/favorite",
 *              "controller"=DeleteFavoriteAction::class,
 *          }
 *     },
 *     collectionOperations={
 *          "get",
 *          "post",
 *     }
 * )
 * @ORM\Entity(repositoryClass=BookRepository::class)
 */
class Book
{

AddFavoriteActionDeleteFavoriteActionはそれぞれこのようになっています。

src/Controller/AddFavoriteAction.php

<?php

namespace App\Controller;

use App\Entity\Book;
use App\Entity\FavoriteBook;
use App\Entity\User;
use App\Repository\FavoriteBookRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

class AddFavoriteAction
{
    private $entityManager;

    private $tokenStorage;

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

    public function __invoke(Book $data)
    {
        $user = $this->tokenStorage->getToken()->getUser();
        if (!$user || !$user instanceof User) {
            throw new AuthenticationException();
        }
        /** @var FavoriteBookRepository $favoriteBookRepository */
        $favoriteBookRepository = $this->entityManager->getRepository(FavoriteBook::class);
        $favorite = $favoriteBookRepository->findOrCreateByUserAndBook($user, $data);
        $this->entityManager->persist($favorite);
        $this->entityManager->flush();

        return $data;
    }
}
src/Controller/DeleteFavoriteAction.php

<?php

namespace App\Controller;

use App\Entity\Book;
use App\Entity\FavoriteBook;
use App\Entity\User;
use App\Repository\FavoriteBookRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

class DeleteFavoriteAction
{
    private $entityManager;

    private $tokenStorage;

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

    public function __invoke(Book $data)
    {
        $user = $this->tokenStorage->getToken()->getUser();
        if (!$user || !$user instanceof User) {
            throw new AuthenticationException();
        }

        /** @var FavoriteBookRepository $favoriteBookRepository */
        $favoriteBookRepository = $this->entityManager->getRepository(FavoriteBook::class);
        $favorite = $favoriteBookRepository->findOneBy(['user' => $user, 'book' => $data]);
        if ($favorite) {
            $this->entityManager->remove($favorite);
            $this->entityManager->flush();
        }

        return $data;
    }
}

/api/docs/にアクセスすると、このような表示になっています。
スクリーンショット 2020-09-14 21.48.07.png

実際にBookを登録してから、/api/books/{id}.favoriteのPOSTメソッドを叩くと...OKお気に入り登録されている。
DELETEを叩くと...OKお気に入り解除されてr...あれ?Bookも消えてない...??

原因

カスタムアクションで指定したDeleteFavoriteAction__invoke()メソッドでobjectをreturnしてしまうと、どうやらそのobjectがremoveされてしまうようです。

なので、__invoke()メソッドのreturnを削除してしまえばOKでした。

余談

__invoke()メソッドの引数ですが、変数名ベースのインジェクション2を行っているらしく、$data以外の名前をつけるとエラーになりました。
ここらへんドキュメントに書いてあるのかなあ...。

参考文献

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1