如何在写入数据之前使用 API-平台事件改变数据

How to mutate data with API-Platform events before writting it

我有一个用户和一个具有多对一关系的大学实体(一所大学可以有很多用户)。我在大学名称上添加了唯一性约束以避免重复。

因此,当我在 /users/$id 路由上使用包含存在的大学名称的有效负载执行 PUT HTTP 请求时,我得到以下信息:

Integrity constraint violation: 1062 Duplicate entry 'INSA' for key 'unique_university_name'

我想要的是在 doctrine 将条目写入数据库之前,如果它已经存在则对其进行变异。

为此,我尝试使用 API 平台的事件系统:https://api-platform.com/docs/core/events

通过 kernel.request 事件,我可以访问数据,但我没能改变它。我没有找到更改请求的 "content" 的方法。我也试过这个:Is there a way to modify request body in a Symfony kernel event_listener 最后我遇到了这个错误。

Serialization for the format html is not supported

我想用 kernel.view 事件来做,例如 PRE_VALIDATE、POST_VALIDATE 或 PRE_WRITE,但由于某种原因它们没有被触发。

我使用的方法正确吗?如果根据 "name"?

大学已经存在,我应该如何将大学添加到用户

如果我理解正确,您想修改嵌入在 user 实体中的 university 对象。

如果您正在按照文档进行操作,并且这样做,那么它将不起作用,因为主要对象是一个 User 实例。

$object = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$object instanceof University || Request::METHOD_POST !== $method) {
  return;
}

一种方法是:

$object = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$object instanceof User || Request::METHOD_POST !== $method) {
  return;
}

$university = $user->getUniversity();
// modify or replace your university here.

另一种方法是使用 doctrine event subscriber, keep in mind that this gets executed after entity validation

为了解决您的问题,我看到了 3 种可能性:

  • 添加一个自定义反规范化程序来验证名称并即时更改它(详见下文)
  • 添加 custom listener for doctrine 预持久化事件
  • 使用域事件参见 this lib 示例

以下是创建自定义反规范化器的方法:

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

final class UniversityDenormalizer implements DenormalizerInterface
{
    private $decoratedDenormalizer;
    private $repository;
    public function __construct(DenormalizerInterface $decorated, UniversityRepository $repository) {
        $this->repository = $repository;
        $this->decoratedNormalizer = $decorated;
    }

    public function denormalize($data, $class, $format = null, array $context = array())
    {
        if ($count = $this->repository->countUniversitiesStartingBy($data['name'])) {
            $data['name'] .= '_' . ($this->repository->countUniversities() + 1);
        }
        return $this->decoratedDenormalizer->denormalize($data, $class, $context);
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return is_string($data) && University::class === $type;
    }
}

您还需要将其注册为服务:

App\Serializer\UniversityDenormalizer:
    arguments:
        # Choose the denormalizer depending on your output.
        # For example, for JSON+LD it's "api_platform.jsonld.normalizer.item"
        - '@api_platform.json.normalizer.item'
        - '@App\Repository\UniversityRepository'
    tags:
        # Priority is important but any value more than 8 should be ok
        - { name: serializer.normalizer, priority: 17 }