我有两个实体,MediaObject
和Book
。 MediaObject
是用于管理文件的通用实体,并包含size
,mimeType
和filePath
之类的字段。 Book
具有类似title
,author
的字段,并且还包括指向其MediaObject
图像文件的关联cover
的链接。
我如何POST
一个Book
实体及其关联的带有API平台的MediaObject
cover
图像?我想将其作为一项原子操作来完成。我不想保存没有封面图像的书,也不想孤立的封面图像。因此,我不想POST
一个MediaObject
封面图像,然后使用POST
新建一个Book
时获得的ID。 (反之亦然)
https://api-platform.com/docs/core/file-upload/
class MediaObject
{
...
public $filePath;
...
}
class Book
{
...
public $coverImage; // i.e. mediaObjectId; associated MediaObject to an image file
...
}
文档具有选项"deserialize"= false
。这意味着此操作不会发生反序列化。因此,您必须将整个反序列化过程自己编写到处理程序控制器。您还必须为swagger文档编写字段。
例如:
<?php
declare(strict_types=1);
namespace App\Entity;
// more use...
/**
* @ApiResource(
* iri="http://schema.org/MediaObject",
* normalizationContext={
* "groups" = {"media:read"}
* },
* collectionOperations={
* "post" = {
* "controller" = MediaHandler::class,
* "deserialize" = false,
* "access_control" = "is_granted('ROLE_USER')",
* "validation_groups" = {"Default", "media:collection:post"},
* "openapi_context" = {
* "requestBody" = {
* "content" = {
* "multipart/form-data" = {
* "schema" = {
* "type" = "object",
* "properties" = {
* "file" = {
* "type" = "string",
* "format" = "binary"
* },
* "name" = {
* "type" = "string"
* }
* }
* }
* }
* }
* }
* }
* },
* "get"
* },
* itemOperations={
* "get"
* }
* )
* @Vich\Uploadable
* @ORM\Entity(repositoryClass="App\Repository\MediaRepository")
*/
class Media
{
/**
* @ApiProperty(iri="http://schema.org/contentUrl")
* @Groups({"media:read"})
*/
public $contentUrl;
/**
* @Assert\NotNull(groups={"media:collection:post"})
* @Vich\UploadableField(mapping="media", fileNameProperty="filePath")
* @Assert\File(
* maxSize="2M",
* mimeTypes={
* "application/pdf",
* "application/x-pdf",
* "image/jpeg",
* "image/jpg",
* "image/png"
* },
* groups={"media:collection:post"}
* )
*/
public $file;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=512)
*/
private $filePath;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
//...
}
控制器处理程序示例:
<?php
declare(strict_types=1);
namespace App\Controller\Api;
// use ...
class MediaHandler extends AbstractController
{
/**
* @var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function __invoke(Request $request): Media
{
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$mediaObject = new Media();
$mediaObject->file = $uploadedFile;
$mediaObject->setName($request->request->get('name'));
return $mediaObject;
}
}
如果“书”存在。并且要将Book
添加到MediaObject
,可以设置iri字符串并在controller-handler中对其进行解析:
//...
$iriBook = $request->request->get('book');
$book = null;
if ($iriBook) {
if (!\preg_match('`^/?api/books/(\d+)$`', $iriBook, $match)) {
throw new BadRequestHttpException('Invalid IRI "'.$iriBook.'"');
}
$book = $this->entityManager->getRepository(Book::class)
->find($match[1]);
if (!$book) {
throw new BadRequestHttpException('Item not found for "'.$iriBook.'"');
}
}
//..
$mediaObject->setBook($book);
DataPersist
)。接下来您需要去https://api-platform.com/docs/core/data-persisters/并创建DataPesist
处理程序
示例:
<?php
declare(strict_types=1);
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Media;
use App\ExtendTrait\ContextAwareDataTrait;
use Doctrine\ORM\EntityManagerInterface;
class MediaObjectDataPersister implements ContextAwareDataPersisterInterface
{
use ContextAwareDataTrait;
/**
* @var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* {@inheritdoc}
*/
public function supports($data, array $context = []): bool
{
return $this->isCollection('post', $context) && $data instanceof Media;
}
/**
* {@inheritdoc}
*
* @param $data Media
*
* @throws \Exception
*/
public function persist($data, array $context = []): void
{
$book = new Book();
$book->setName($data->getName());
// begin transaction and persist and flush $book and $data
}
/**
* {@inheritdoc}
*/
public function remove($data, array $context = []): void
{
// todo remove book
}
}
P.S。我不测试此代码。我写的想法;)P.S.S. $this->isCollection()
它从我的特征开始起作用,可能需要您使用它:
<?php
declare(strict_types=1);
namespace App\ExtendTrait;
/**
* Trait ContextAwareDataTrait.
*
* Helps confirm the operation name
*/
trait ContextAwareDataTrait
{
public function isItem(string $operationName, array $context, string $resourceClass = null): bool
{
if ($resourceClass && ($context['resource_class'] ?? false) !== $resourceClass) {
return false;
}
return ($context['item_operation_name'] ?? null) === $operationName;
}
public function isCollection(string $operationName, array $context, string $resourceClass = null): bool
{
if ($resourceClass && ($context['resource_class'] ?? false) !== $resourceClass) {
return false;
}
return ($context['collection_operation_name'] ?? null) === $operationName;
}
}