{"id":40065888,"url":"https://github.com/winwin-inc/mapper-generator","last_synced_at":"2026-01-19T07:35:29.689Z","repository":{"id":57081152,"uuid":"295598922","full_name":"winwin-inc/mapper-generator","owner":"winwin-inc","description":"Object mapping code generator","archived":false,"fork":false,"pushed_at":"2023-05-08T02:35:20.000Z","size":118,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-03T07:49:10.986Z","etag":null,"topics":["generator","mapper","tool"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/winwin-inc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-15T03:00:32.000Z","updated_at":"2021-12-02T10:20:23.000Z","dependencies_parsed_at":"2022-08-24T14:58:14.339Z","dependency_job_id":null,"html_url":"https://github.com/winwin-inc/mapper-generator","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/winwin-inc/mapper-generator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winwin-inc%2Fmapper-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winwin-inc%2Fmapper-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winwin-inc%2Fmapper-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winwin-inc%2Fmapper-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/winwin-inc","download_url":"https://codeload.github.com/winwin-inc/mapper-generator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winwin-inc%2Fmapper-generator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28562995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T03:31:16.861Z","status":"ssl_error","status_checked_at":"2026-01-19T03:31:15.069Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["generator","mapper","tool"],"created_at":"2026-01-19T07:35:29.530Z","updated_at":"2026-01-19T07:35:29.673Z","avatar_url":"https://github.com/winwin-inc.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Generate mapper class code\n\n受 [mapstruct](https://mapstruct.org/) 项目启发，移植相关功能。\n\n## 简介\n项目中模型包括 DO（Data Object), DTO (Data Transfer Object), VO (View Object) 等，经常需要在模型之间进行转换，模型转换的过程称为 mapping ，包含 mapping 函数的类称为 mapper。编写 mapper 是非常枯燥，而且容易出错的过程。通过代码生成，可以简化 mapper 类的编写。\n## 安装\n```bash\ncomposer require --dev winwin/mapper-generator\n```\n## 快速开始\n假如我们项目中有如下类定义：\n```php\n\u003c?php\nclass CarType extends \\kuiper\\helper\\Enum {\n    public const SUV = 'suv';\n    public const MINI = 'mini';\n}\n\nclass Car {\n    /**\n     * @var string|null\n     */\n    private $make;\n    /**\n     * @var int|null\n     */\n    private $numberOfSeats;\n    /**\n     * @var CarType|null\n     */\n    private $type;\n \n    //constructor, getters, setters etc.\n}\n\nclass CarDto {\n    /**\n     * @var string|null\n     */\n    private $make;\n    /**\n     * @var int|null\n     */\n    private $seatCount;\n    /**\n     * @var string|null\n     */\n    private $type;\n \n    //constructor, getters, setters etc.\n}\n```\n我们可以定义这样一个 mapper 类转换 `Car` 对象为 `CarDto` 对象：\n```php\n\u003c?php\n\nuse winwin\\mapper\\annotations\\Mapper;\nuse winwin\\mapper\\annotations\\Mapping;\n\n/**\n * @Mapper\n */\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"seatCount\", source=\"numberOfSeats\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n\n    }\n}\n```\n运行命令：\n```bash\n./vendor/bin/mapper-generator src/CarMapper.php\n```\n生成代码如下：\n```php\n\u003c?php\n\nuse winwin\\mapper\\annotations\\Mapper;\nuse winwin\\mapper\\annotations\\Mapping;\n\n/**\n * @Mapper\n */\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"seatCount\", source=\"numberOfSeats\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n        $carDto = new \\CarDto();\n        $carDto-\u003esetMake($car-\u003egetMake());\n        $carDto-\u003esetSeatCount($car-\u003egetNumberOfSeats());\n        $carDto-\u003esetType($car-\u003egetType() === null ? null : $car-\u003egetType()-\u003ename);\n        return $carDto;\n    }\n}\n```\n## 生成器原理\n只有使用 `@\\winwin\\mapper\\annotations\\Mapper` 注解标记的类才会进行代码生成。代码生成过程使用 [PHP Parser](https://github.com/nikic/PHP-Parser) 解析代码为 AST，替换需要 mapping 函数的方法体。可以确保只修改 mapping 函数部分，而其他函数仍保持不变。\n这个类中符合以下特征的方法会生成 mapping 函数体：\n\n- 必须是 public 实例方法 (即不能是 static 方法)\n- 函数原型满足以下情况：\n1. 一个参数，一个返回值，且都有类型声明。此时参数为转换来源对象，返回值为转换生成对象。\n\n例如：\n```php\n\u003c?php\n\npublic function toCarDto(Car $car): CarDto\n{\n}\n```\n\n2. 多个参数，一个返回值，参数中有且仅有一个使用 `@MappingSource` 指定为转换来源对象\n\n例如：\n```php\n\u003c?php\n\n/**\n * @MappingSource(\"car\")\n */\npublic function toCarDto(Car $car, $arg1, $arg2): CarDto\n{\n}\n```\n\n3. 两个参数，都有类型声明，返回值为 void，有且仅有一个使用 `@MapperTarget` 或 `@MappingSource` 指定其中一个参数角色\n\n例如：\n```php\n\u003c?php\n\n/**\n * @MappingTarget(\"dto\")\n */\npublic function updateCarDto(Car $car, CarDto $dto): void\n{\n}\n```\n\n4. 多个参数，返回值为 void 有且仅有一个使用 `@MappingSource` 指定转换来源对象, `@MappingTarget` 指定为转换生成对象\n\n例如：\n```php\n\u003c?php\n\n/**\n * @MappingSource(\"car\")\n * @MappingTarget(\"dto\")\n */\npublic function updateCarDto(Car $car, CarDto $dto, $arg1, $arg2): void\n{\n}\n```\n以上四种情况的函数可以提取出没有歧义的 source 对象和 target 对象，将从 source 对象和 target 对象中提取字段进行映射。source 对象字段规则为 public 属性或 getX(), isX(), hasX() 方法；target 对象字段规则为 public 属性或 `setX($value)` 方法。\nmapping 生成的代码都是通过对象的 getter, setter 或者公开属性值赋值方式，而不是通过反射，性能上和手写是相同的。\n## Mapping 配置\n默认字段按同名规则进行映射。如果字段名不一致，可以使用 `@Mapping` 注解指定映射规则，参考前面 `CarMapper` 示例。\n如果两个方法需要使用相同映射规则，可以通过 `@InheritConfiguration` 继承，例如：\n```php\n\u003c?php\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"seatCount\", source=\"numberOfSeats\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n    }\n\n    /**\n     * @InheritConfiguration(\"carToCarDto\")\n     */\n    public function updateCarDto(\\Car $car, \\CarDto $carDto): void\n    {\n    }\n}\n```\n字段名反向映射可以通过 `@InheritInverseConfiguration` 注解实现，例如：\n```php\n\u003c?php\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"seatCount\", source=\"numberOfSeats\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n    }\n\n    /**\n     * @InheritInverseConfiguration(\"carToCarDto\")\n     */\n    public function carDtoToCar(\\CarDto $carDto): \\Car\n    {\n    }\n}\n```\n\n\n字段类型不同情况下将使用以下转换规则：\n\n1. php 原生标量类型（int, bool, float, double, string 等）通过类型强制转换\n1. \\DateTime 和 string 类型可以相互转换，默认格式为 `Y-m-d H:i:s` ，如果需要使用其他格式，可以在 `@Mapping` 中使用 `dateFormat` 指定\n1. \\kuiper\\helper\\Enum 和 int, string 可以相互转换\n\n其他类型不匹配将产生错误。\n对于类型无法自动完成转换的情况，可以通过 `@Mapping` 中 `expression` 或 `qualifiedByName` 实现。\n`expression` 用于设置 php 表达式，例如：\n```php\n\u003c?php\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"large\", expression=\"$car-\u003egetNumberOfSeats()\u003e20\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n    }\n}\n```\n`qualifiedByName`用于指定 mapper 类中的方法进行转换，例如：\n```php\n\u003c?php\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"large\", source=\"numberOfSeats\", qualifiedByName=\"isLarge\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n    }\n  \n    private function isLarge(int $numberOfSeats): bool\n    {\n        return $numberOfSeats \u003e 20;\n    }\n}\n```\n`condition` 用于设置一个表达式，当满足表达式值的时候才进行转换，例如：\n```php\n\u003c?php\nclass CarMapper\n{\n    /**\n     * @Mapping(target=\"seatCount\", source=\"numberOfSeats\", condition=\"\u003e 0\")\n     */\n    public function carToCarDto(\\Car $car) : \\CarDto\n    {\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinwin-inc%2Fmapper-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwinwin-inc%2Fmapper-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinwin-inc%2Fmapper-generator/lists"}