API 平台 - onPreSerialize 与标准化组中的 MediaObject URI 解析器

9le*_*lex 3 symfony api-platform.com

我有一个 MediaObject 作为规范化上下文组“模块”中的子资源公开:

/**
 * @ApiResource(
 *     attributes={"access_control"="is_granted('ROLE_ADMIN')"},
 *     collectionOperations={
 *          "get",
 *          "post",
 *          "allData"={
 *              "method"="GET",
 *              "path"="/appdata",
 *              "normalization_context"={"groups"={"modules"}},
 *              "access_control"="is_granted('IS_AUTHENTICATED_ANONYMOUSLY')"
 *          }
 *     }
 * )
 * @ORM\Entity(repositoryClass="App\Repository\LearningModuleRepository")
 * @UniqueEntity("identifier")
 *
 */
class LearningModule
Run Code Online (Sandbox Code Playgroud)
/**
 * @ORM\Entity(
 *     repositoryClass="App\Repository\MediaObjectRepository"
 * )
 * @ApiResource(
 *     iri="http://schema.org/MediaObject",
 *     normalizationContext={
 *         "groups"={"media_object_read"},
 *     },
 *     attributes={"access_control"="is_granted('ROLE_ADMIN')"},
 *     collectionOperations={
 *         "post"={
 *             "controller"=CreateMediaObject::class,
 *             "deserialize"=false,
 *             "validation_groups"={"Default", "media_object_create"},
 *             "swagger_context"={
 *                 "consumes"={
 *                     "multipart/form-data",
 *                 },
 *                 "parameters"={
 *                     {
 *                         "in"="formData",
 *                         "name"="file",
 *                         "type"="file",
 *                         "description"="The file to upload",
 *                     },
 *                 },
 *             },
 *         },
 *         "get",
 *     },
 *     itemOperations={
 *         "get",
 *         "delete"
 *     },
 * )
 * @Vich\Uploadable
 */
class MediaObject


    /**
     * @var string|null
     *
     * @ApiProperty(iri="http://schema.org/contentUrl")
     * @Groups({"media_object_read", "modules"})
     */
    public $contentUrl;

    /**
     * @var string|null
     * @Groups({"modules"})
     * @ORM\Column(nullable=true)
     */
    public $filePath;
Run Code Online (Sandbox Code Playgroud)

当通过规范化组公开时,我只得到 filePath 而不是 contentUrl。我假设问题与官方文档中概述的 ContentUrlSubscriber 有关:

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::VIEW => ['onPreSerialize', EventPriorities::PRE_SERIALIZE],
        ];
    }

    public function onPreSerialize(GetResponseForControllerResultEvent $event): void
    {
        $controllerResult = $event->getControllerResult();
        $request = $event->getRequest();

        if ($controllerResult instanceof Response || !$request->attributes->getBoolean('_api_respond', true)) {
            return;
        }

        if (!($attributes = RequestAttributesExtractor::extractAttributes($request)) || !\is_a($attributes['resource_class'], MediaObject::class, true)) {
            return;
        }

        $mediaObjects = $controllerResult;

        if (!is_iterable($mediaObjects)) {
            $mediaObjects = [$mediaObjects];
        }

        foreach ($mediaObjects as $mediaObject) {
            if (!$mediaObject instanceof MediaObject) {
                continue;
            }

            $mediaObject->contentUrl = $this->storage->resolveUri($mediaObject, 'file');
        }
    }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,有人知道如何处理 preSerialization 吗?

谢谢你

9le*_*lex 6

我找到了解决这个问题的方法:

  • PRE_SERIALIZE 上的回调被中止,因为 LearningModule 对象(不是 MediaObject)触发了序列化($attributes['resource_class'] === LearningModule)。

  • 为了克服这个限制,一个装饰的归一化器应该如下使用:

<?php

namespace App\Serializer;

use App\Entity\MediaObject;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Vich\UploaderBundle\Storage\StorageInterface;

final class MediaObjectContentUrlNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
{
    private $decorated;
    private $storage;

    public function __construct(NormalizerInterface $decorated, StorageInterface $storage)
    {
        if (!$decorated instanceof DenormalizerInterface) {
            throw new \InvalidArgumentException(sprintf('The decorated normalizer must implement the %s.', DenormalizerInterface::class));
        }

        $this->decorated = $decorated;
        $this->storage = $storage;
    }

    public function supportsNormalization($data, $format = null)
    {
        return $this->decorated->supportsNormalization($data, $format);
    }

    public function normalize($object, $format = null, array $context = [])
    {
        $data = $this->decorated->normalize($object, $format, $context);
        if ($object instanceof MediaObject) {
            $data['contentUrl'] = $this->storage->resolveUri( $object, 'file');
        }

        return $data;
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return $this->decorated->supportsDenormalization($data, $type, $format);
    }

    public function denormalize($data, $class, $format = null, array $context = [])
    {
        return $this->decorated->denormalize($data, $class, $format, $context);
    }

    public function setSerializer(SerializerInterface $serializer)
    {
        if($this->decorated instanceof SerializerAwareInterface) {
            $this->decorated->setSerializer($serializer);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

实现细节可以在这里找到:https : //api-platform.com/docs/core/serialization/#decorating-a-serializer-and-adding-extra-data

  • 需要明确的是,一旦规范化器就位,您就可以删除订阅者 (2认同)