salamov / dto-hydrator
API平台中Dto的Transformers动态注水
Requires
- php: >=7.2
- doctrine/common: ^2.7
- doctrine/orm: ^2.4
- symfony/dependency-injection: 4.2.*
- symfony/framework-bundle: 4.2.*
- symfony/http-foundation: ^4.0
- symfony/property-access: ^4.0
- symfony/property-info: ^4.0
- symfony/serializer: ^4.0
This package is auto-updated.
Last update: 2024-09-30 01:22:56 UTC
README
描述
为API Platform中Transformers使用的DTO提供动态注水。
安装
composer require salamov/dto-hydrator
用法
在使用API Platform时,使用自定义DTO(在@ApiResource
中定义自定义输入/输出)需要您处理自定义转换器,尤其是当您的DTO表示实体时。
-
当使用输入转换器时(例如:请求体反规范化为DTO之后),您应该脱盐DTO(即将其转换为实体以进行持久化)。
-
当使用输出转换器时(例如:实体检索并等待规范化之后),您应该注水DTO(即转换实体为DTO以进行显示)。
-
Dto内部的嵌入式对象必须是
@ApiResource
,以便生成swagger文档 -
如果嵌入式对象在反规范化请求体中定义了
id
,则将引用该数据并更新,否则将作为新的Entity
进行持久化。
示例
use Symfony\Component\Serializer\Annotation\Groups;
class OrderDto {
/**
* @var integer $id
* @Groups({"order:read","order:put"})
*/
protected $id;
/**
* @var Customer $customer
* @Groups({"order:read","order:put"})
*/
protected $customer;
/**
* @var Product[] $products
* @Groups({"order:read","order:put"})
*/
protected $products;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id)
{
$this->id = $id;
}
/**
* @return Customer
*/
public function getCustomer(): Customer
{
return $this->customer;
}
/**
* @param Customer $customer
*/
public function setCustomer(Customer $customer)
{
$this->customer = $customer;
}
/**
* @return Product[]
*/
public function getProducts(): array
{
return $this->products;
}
/**
* @param Product[] $products
*/
public function setProducts(array $products)
{
$this->products = $products;
}
}
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table
* @ORM\Entity
* @ApiResource(
* itemOperations={
* "get":{
* "method":"get",
* "input":false,
* "output":OrderDto::class,
* "status":200,
* "normalization_context":{"groups":{"order:read"}}
* },
* "put":{
* "method":"put",
* "input":OrderDto::class,
* "output":OrderDto::class,
* "status":200,
* "denormalization_context":{"groups":{"order:put"}},
* "normalization_context":{"groups":{"order:read"}}
* }
* },
* collectionOperations={}
* )
*/
class Order {
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @ApiProperty(identifier=true)
*/
protected $id;
/**
* @var Customer $customer
* @ORM\ManyToOne(targetEntity="Customer",cascade={"persist"})
* @ORM\JoinColumn(name="customerId", referencedColumnName="id")
*/
protected $customer;
/**
* @var Product[] $products
* @ORM\OneToMany(targetEntity="Product",cascade={"persist"})
*/
protected $products;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id)
{
$this->id = $id;
}
/**
* @return Customer
*/
public function getCustomer(): Customer
{
return $this->customer;
}
/**
* @param Customer $customer
*/
public function setCustomer(Customer $customer)
{
$this->customer = $customer;
}
/**
* @return Product[]
*/
public function getProducts(): array
{
return $this->products;
}
/**
* @param Product[] $products
*/
public function setProducts(array $products)
{
$this->products = $products;
}
}
在上面的例子中,我们为Order
实体定义了一个OrderDto
,其中包含两个嵌入式对象Customer
和Products
,一个通过ManyToOne
关系与Order
相关联,另一个通过OneToMany
关系与Order
相关联。
GET请求
GET https://domain.com/api/orders/{id}
上面的请求将首先通过您的自定义数据提供程序(如果您有的话)检索要转换和规范化的Order
实体,如果您为您的@ApiResource
定义了输出,那么您应该定义一个转换器,您的转换器将类似于以下内容
use DtoHydrator\Factory\DtoDtoFactory;
use DtoHydrator\Dto\Hydrator;
class OrderDtoOutputTransformer {
/** @var DtoFactory $factory */
protected $factory;
/**
* OrderDtoOutputTransformer constructor.
*
* @param DtoFactory $factory
*/
public function __construct(DtoFactory $factory) { $this->factory = $factory; }
/**
* Checks whether the transformation is supported for a given data and context.
*
* @param object|array $data object on normalize / array on denormalize
* @param string $to
* @param array $context
* @return bool
*/
public function supportsTransformation($data, string $to, array $context = []): bool
{
return OrderDto::class === $to && $data instanceof Order;
}
/**
* Transforms the given object to something else, usually another object.
* This must return the original object if no transformation has been done.
*
* @param Order $order
*
* @param string $to
* @param array $context
* @return OrderDto
*/
public function transform($order, string $to, array $context = []): OrderDto
{
$orderDto = new OrderDto();
$this->factory->make(Hydrator::class)->hydrate($order,$orderDto,$context);
return $orderDto;
}
}
PUT请求
PUT https://domain.com/api/orders/{id}
body={
id:1,
customer:{
id:1,
fname:"Omar",
lnaem:"Salamov"
},
products:[
{
id:10,
name:"Long Sleeves",
brand: "Gap"
},
{
id:11,
name:"Short Sleves",
brand: "H&M"
}
]
}
上面的请求将被反规范化为在@ApiResource
中定义的Input
类,然后到您的InputTransformer
,以便转换为Entity
并持久化,所以您的InputTransformer
将类似于以下内容
use DtoHydrator\Factory\DtoDtoFactory;
use DtoHydrator\Dto\Dehydrator;
use Doctrine\ORM\EntityManagerInterface;
class OrderDtoInputTransformer {
/** @var DtoFactory $factory */
protected $factory;
/** @var EntityManagerInterface $entityManager */
protected $entityManager;
/**
* OrderDtoInputTransformer constructor.
*
* @param DtoFactory $factory
* @param EntityManagerInterface $entityManager
*/
public function __construct(DtoFactory $factory, EntityManagerInterface $entityManager)
{
$this->factory = $factory;
$this->entityManager = $entityManager;
}
/**
* Checks whether the transformation is supported for a given data and context.
*
* @param object|array $data object on normalize / array on denormalize
* @param string $to
* @param array $context
* @return bool
*/
public function supportsTransformation($data, string $to, array $context = []): bool
{
return Order::class === $to && in_array('order:put',$context['groups']);
}
/**
* Transforms the given object to something else, usually another object.
* This must return the original object if no transformation has been done.
*
* @param OrderDto $order
* @param string $to
* @param array $context
* @return Order
* @throws Throwable
*/
public function transform($orderDto, string $to, array $context = []): Order
{
/** @var Order $order */
$order = $this->entityManager->getReference(Order::class, $orderDto->getId());
// in case you are creating a new order
// you will need to instantiate a new instance from order instead of referencing
$this->factory->make(Dehydrator::class)->hydrate($orderDto,$order,$context);
return $order;
}
}
注意:如果您的body/embedded body包含一个id
,它将直接引用相应实体的该id
并更新数据,如果没有,则创建。
安全性
如果您发现任何安全相关的问题,请通过omarfawzi96@gmail.com发送电子邮件,而不是使用问题跟踪器。
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。