Symfony 5.4中NelmioApiDocBundle数组对象类型指定问题求助
问题背景
使用Symfony 5.4、PHP 7.4和MongoDB,通过NelmioApiDocBundle生成API文档时,Order实体的address字段是@MongoDB\Field(type="collection")类型,对应包含两个地址对象的数组。当前配置下运行时收到错误:
Property "App\Entity\Order::address" is an array, but its items type isn't specified. You can specify that by using the type string[] for instance or @OA\Property(type="array", @OA\Items(type="string"))
Order实体代码:
<?php namespace App\Entity; use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB; /** * @MongoDB\Document */ class Order { /** * @MongoDB\Id */ protected $id; /** * @MongoDB\Field(type="string") */ protected $orderNumber; /** * @MongoDB\Field(type="string") */ protected $firstname; /** * @MongoDB\Field(type="string") */ protected $lastname; /** * @MongoDB\Field(type="collection") */ protected $address; /** * @MongoDB\Field(type="string") */ protected $orderFile; public function getId(): string { return $this->id; } public function getFirstname(): string { return $this->firstname; } public function setFirstname(string $firstname): void { $this->firstname = $firstname; } public function getLastname(): string { return $this->lastname; } public function setLastname(string $lastname): void { $this->lastname = $lastname; } public function getAddress(): array { return $this->address; } public function setAddress(array $address): void { $this->address = $address; } }
控制器API注解:
<?php namespace App\Controller\Api; use App\Entity\Order; use Doctrine\ODM\MongoDB\DocumentManager; use Nelmio\ApiDocBundle\Annotation\Model; use OpenApi\Annotations as OA; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; /** * @Route("/api/orders") */ class OrderApiController extends AbstractController { /** * Creates a new order. * @Route("", methods={"POST"}) * @OA\Response(response=200, description="Creates a new order", @Model(type=Order::class)) * @OA\RequestBody( * required=true, * @OA\MediaType( * mediaType="application/json", * @OA\Schema( * type="object", * required={"address"}, * @OA\Property(property="address", type="array", @OA\Items(type="object")) * ) * ) * ) * @OA\Tag(name="Order") */ public function create(DocumentManager $dm, Request $request): JsonResponse { // ... some code here } }
解答
1. 为什么必须指定数组项类型?
NelmioApiDocBundle基于OpenAPI规范生成文档,而OpenAPI要求数组类型必须明确声明项的具体类型。只标注type="array"无法让文档使用者知道数组里存的是字符串、数字还是复杂对象,会导致API文档信息缺失,客户端无法准确构造请求或解析响应。所以必须补充数组项的类型定义。
2. 如何正确指定数组项类型?
有两种常用解决方案,根据实际场景选择:
方案一:创建地址类并引用(推荐)
单独定义一个Address类(可以是值对象或DTO,无需映射到MongoDB),通过@Model注解引用它,让文档结构更清晰可复用。
第一步:创建App\Entity\Address.php
<?php namespace App\Entity; use OpenApi\Annotations as OA; /** * @OA\Schema( * type="object", * required={"addresstype", "country"}, * @OA\Property(property="addresstype", type="string", description="地址类型:delivery/invoice"), * @OA\Property(property="company", type="string"), * @OA\Property(property="firstname", type="string"), * @OA\Property(property="lastname", type="string"), * @OA\Property(property="address1", type="string"), * @OA\Property(property="address2", type="string"), * @OA\Property(property="zip", type="string"), * @OA\Property(property="city", type="string"), * @OA\Property(property="country", type="string", example="DE") * ) */ class Address { // 可按需添加属性和getter/setter,无需MongoDB映射 public string $addresstype; public string $company; public string $firstname; public string $lastname; public string $address1; public string $address2; public string $zip; public string $city; public string $country; }
第二步:修改Order实体的address字段注解
/** * @MongoDB\Field(type="collection") * @OA\Property(type="array", @OA\Items(ref=@Model(type=Address::class))) */ protected $address;
第三步:优化控制器的RequestBody注解(可选)
直接引用Order实体的Schema,避免重复定义:
@OA\RequestBody( required=true, @OA\MediaType( mediaType="application/json", @OA\Schema(ref=@Model(type=Order::class)) ) )
方案二:直接在实体注解中定义项结构
如果不想单独创建Address类,可以直接在Order实体的address字段注解里定义数组项的完整结构:
/** * @MongoDB\Field(type="collection") * @OA\Property( * type="array", * @OA\Items( * type="object", * required={"addresstype", "country"}, * @OA\Property(property="addresstype", type="string", example="delivery"), * @OA\Property(property="company", type="string"), * @OA\Property(property="firstname", type="string"), * @OA\Property(property="lastname", type="string"), * @OA\Property(property="address1", type="string"), * @OA\Property(property="address2", type="string"), * @OA\Property(property="zip", type="string"), * @OA\Property(property="city", type="string", example="Musterstadt"), * @OA\Property(property="country", type="string", example="DE") * ) * ) */ protected $address;
完成以上修改后,重新生成API文档即可解决报错,同时文档会清晰展示address数组的具体结构。
内容的提问来源于stack exchange,提问作者Magnus

