salamov/dto-hydrator

API平台中Dto的Transformers动态注水

安装: 1

依赖: 0

建议者: 0

安全性: 0

星标: 0

关注者: 2

分支: 0

开放问题: 1

类型:symfony-bundle

1.0.2 2019-11-17 14:42 UTC

This package is auto-updated.

Last update: 2024-09-30 01:22:56 UTC


README

Software License Latest Stable Version Total Downloads

描述

为API Platform中Transformers使用的DTO提供动态注水。

安装

composer require salamov/dto-hydrator

用法

在使用API Platform时,使用自定义DTO(在@ApiResource中定义自定义输入/输出)需要您处理自定义转换器,尤其是当您的DTO表示实体时。

  1. 当使用输入转换器时(例如:请求体反规范化为DTO之后),您应该脱盐DTO(即将其转换为实体以进行持久化)。

  2. 当使用输出转换器时(例如:实体检索并等待规范化之后),您应该注水DTO(即转换实体为DTO以进行显示)。

  3. Dto内部的嵌入式对象必须是@ApiResource,以便生成swagger文档

  4. 如果嵌入式对象在反规范化请求体中定义了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,其中包含两个嵌入式对象CustomerProducts,一个通过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)。有关更多信息,请参阅许可证文件