winwin/mapper-generator 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

winwin/mapper-generator

Composer 安装命令:

composer require winwin/mapper-generator

包简介

Generator for object property mapper

README 文档

README

mapstruct 项目启发,移植相关功能。

简介

项目中模型包括 DO(Data Object), DTO (Data Transfer Object), VO (View Object) 等,经常需要在模型之间进行转换,模型转换的过程称为 mapping ,包含 mapping 函数的类称为 mapper。编写 mapper 是非常枯燥,而且容易出错的过程。通过代码生成,可以简化 mapper 类的编写。

安装

composer require --dev winwin/mapper-generator

快速开始

假如我们项目中有如下类定义:

<?php
class CarType extends \kuiper\helper\Enum {
    public const SUV = 'suv';
    public const MINI = 'mini';
}

class Car {
    /**
     * @var string|null
     */
    private $make;
    /**
     * @var int|null
     */
    private $numberOfSeats;
    /**
     * @var CarType|null
     */
    private $type;
 
    //constructor, getters, setters etc.
}

class CarDto {
    /**
     * @var string|null
     */
    private $make;
    /**
     * @var int|null
     */
    private $seatCount;
    /**
     * @var string|null
     */
    private $type;
 
    //constructor, getters, setters etc.
}

我们可以定义这样一个 mapper 类转换 Car 对象为 CarDto 对象:

<?php

use winwin\mapper\annotations\Mapper;
use winwin\mapper\annotations\Mapping;

/**
 * @Mapper
 */
class CarMapper
{
    /**
     * @Mapping(target="seatCount", source="numberOfSeats")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {

    }
}

运行命令:

./vendor/bin/mapper-generator src/CarMapper.php

生成代码如下:

<?php

use winwin\mapper\annotations\Mapper;
use winwin\mapper\annotations\Mapping;

/**
 * @Mapper
 */
class CarMapper
{
    /**
     * @Mapping(target="seatCount", source="numberOfSeats")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {
        $carDto = new \CarDto();
        $carDto->setMake($car->getMake());
        $carDto->setSeatCount($car->getNumberOfSeats());
        $carDto->setType($car->getType() === null ? null : $car->getType()->name);
        return $carDto;
    }
}

生成器原理

只有使用 @\winwin\mapper\annotations\Mapper 注解标记的类才会进行代码生成。代码生成过程使用 PHP Parser 解析代码为 AST,替换需要 mapping 函数的方法体。可以确保只修改 mapping 函数部分,而其他函数仍保持不变。 这个类中符合以下特征的方法会生成 mapping 函数体:

  • 必须是 public 实例方法 (即不能是 static 方法)
  • 函数原型满足以下情况:
  1. 一个参数,一个返回值,且都有类型声明。此时参数为转换来源对象,返回值为转换生成对象。

例如:

<?php

public function toCarDto(Car $car): CarDto
{
}
  1. 多个参数,一个返回值,参数中有且仅有一个使用 @MappingSource 指定为转换来源对象

例如:

<?php

/**
 * @MappingSource("car")
 */
public function toCarDto(Car $car, $arg1, $arg2): CarDto
{
}
  1. 两个参数,都有类型声明,返回值为 void,有且仅有一个使用 @MapperTarget@MappingSource 指定其中一个参数角色

例如:

<?php

/**
 * @MappingTarget("dto")
 */
public function updateCarDto(Car $car, CarDto $dto): void
{
}
  1. 多个参数,返回值为 void 有且仅有一个使用 @MappingSource 指定转换来源对象, @MappingTarget 指定为转换生成对象

例如:

<?php

/**
 * @MappingSource("car")
 * @MappingTarget("dto")
 */
public function updateCarDto(Car $car, CarDto $dto, $arg1, $arg2): void
{
}

以上四种情况的函数可以提取出没有歧义的 source 对象和 target 对象,将从 source 对象和 target 对象中提取字段进行映射。source 对象字段规则为 public 属性或 getX(), isX(), hasX() 方法;target 对象字段规则为 public 属性或 setX($value) 方法。 mapping 生成的代码都是通过对象的 getter, setter 或者公开属性值赋值方式,而不是通过反射,性能上和手写是相同的。

Mapping 配置

默认字段按同名规则进行映射。如果字段名不一致,可以使用 @Mapping 注解指定映射规则,参考前面 CarMapper 示例。 如果两个方法需要使用相同映射规则,可以通过 @InheritConfiguration 继承,例如:

<?php
class CarMapper
{
    /**
     * @Mapping(target="seatCount", source="numberOfSeats")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {
    }

    /**
     * @InheritConfiguration("carToCarDto")
     */
    public function updateCarDto(\Car $car, \CarDto $carDto): void
    {
    }
}

字段名反向映射可以通过 @InheritInverseConfiguration 注解实现,例如:

<?php
class CarMapper
{
    /**
     * @Mapping(target="seatCount", source="numberOfSeats")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {
    }

    /**
     * @InheritInverseConfiguration("carToCarDto")
     */
    public function carDtoToCar(\CarDto $carDto): \Car
    {
    }
}

字段类型不同情况下将使用以下转换规则:

  1. php 原生标量类型(int, bool, float, double, string 等)通过类型强制转换
  2. \DateTime 和 string 类型可以相互转换,默认格式为 Y-m-d H:i:s ,如果需要使用其他格式,可以在 @Mapping 中使用 dateFormat 指定
  3. \kuiper\helper\Enum 和 int, string 可以相互转换

其他类型不匹配将产生错误。 对于类型无法自动完成转换的情况,可以通过 @Mapping 中 expression 或 qualifiedByName 实现。 expression 用于设置 php 表达式,例如:

<?php
class CarMapper
{
    /**
     * @Mapping(target="large", expression="$car->getNumberOfSeats()>20")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {
    }
}

qualifiedByName用于指定 mapper 类中的方法进行转换,例如:

<?php
class CarMapper
{
    /**
     * @Mapping(target="large", source="numberOfSeats", qualifiedByName="isLarge")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {
    }
  
    private function isLarge(int $numberOfSeats): bool
    {
        return $numberOfSeats > 20;
    }
}

condition 用于设置一个表达式,当满足表达式值的时候才进行转换,例如:

<?php
class CarMapper
{
    /**
     * @Mapping(target="seatCount", source="numberOfSeats", condition="> 0")
     */
    public function carToCarDto(\Car $car) : \CarDto
    {
    }
}

winwin/mapper-generator 适用场景与选型建议

winwin/mapper-generator 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.87k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2020 年 09 月 15 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 winwin/mapper-generator 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 winwin/mapper-generator 我们能提供哪些服务?
定制开发 / 二次开发

基于 winwin/mapper-generator 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 1.87k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 2
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-09-15