如何通过反射(Reflection)+注解(Attribute)来实现数组转化为对象

场景

在日常开发中,我们可能会遇到和第三方API交互的情况,一般API都返回json格式的数据,我们会将json转化为array,剩下的就是操作array来完成自己的业务逻辑。但操作array有时候是一件令人头痛的事情,在编程的世界里,操作object可比操作array更让人能够理解,而且IDE的提示也更加友好。

实际场景

我们需要解析用户的简历信息,但OCR不是我们的主要业务,所以我们买了第三方的OCR解析,这个简历返回的数据是比较复杂的,而且我们要对一些字段特殊处理。比如性别sex,返回的是string类型,但我们的数据库是int类型,不仅仅是数据类型不一样,也可能字段名称不一样,这个时候我们就需要做数据转化。可是这个返回数据太复杂,不仅仅是性别,还有学历中的学校信息等等。

Hydrate

为了解决上述的问题,可以通过反射Reflection+注解Attribute来实现一个强大的水合器Hydrate。我们先来看代码使用,然后再分析是如何实现的。

代码

安装

composer require kabunx/hydrate -vvv

定义Entity

添加对数组entity的支持

<?php
declare(strict_types=1);
namespace App\Entities;

use Carbon\Carbon;
use Kabunx\Hydrate\ArrayEntity;
use Kabunx\Hydrate\Entity;
use Kabunx\Hydrate\Column;

/**
 * 简历主体信息
  */
class Resume extends Entity
{
    protected string $sourceKeyFormat = 'studly';

    public int $type = 0;

    public string $resumeGrade = '';

    // 在这里字段发生了变化
    #[Column("Married")]
    public bool $isMarried = false;

    // 此处省略其他字段(30多个)

    // 英语等级证书,优先选择大学最高证书
    #[Column("GradeOfEnglish")]
    public ?EnglishGrade $englishGrade;
    /**
     * 计算机(IT)技能
     * @var array|It[]
     */
    #[Column("ITSkills")]
    #[ArrayEntity(It::class)]
    public array $its = []

    public function setIsMarried(?string $value): bool
    {
        return $value ? ($value == '是' ? true : false) : false;
    }

    // IT技能
    // 如果定义了ArrayEntity,此方法可以直接忽略
    public function setIts(?array $its): array
    {
        if (is_null($its)) {
            return [];
        }
        $entities = [];
        foreach ($its as $it) {
            $entities[] = It::instance($it);
        }
        return $entities;
    }
}

/**
 * 英语成绩
 */
class EnglishGrade extends Entity
{
  protected string $sourceKeyFormat = 'studly';
  public string $nameOfCertificate = '';

  public string $score = '';

  public ?Carbon $receivingDate;
}

/**
 * IT技能情况
 */
class It extends Entity
{
  protected string $sourceKeyFormat = 'studly';
  // 技能类别
  public string $skillType = '';

  // 使用时间
  #[Column(source: "TimeOfUse", target: "used_at")]
  public string $usedTime = '';

  // 掌握程度
  public string $competencyLevel = '';
}

使用

<?php
use App\Entities\Resume;
// 此处模拟API的返回数据
$data = [
    "Type" => 1,
    "ResumeGrade" => '',
    "Married" => '是',
    "GradeOfEnglish" => [
        "NameOfCertificate" => "",
        "Score" => "优秀",
        "ReceivingDate" => "2021-01-01"
    ],
    "ITSkills" => [
        [
            "skillType" => "",
            "TimeOfUse" => "",
            "CompetencyLevel" => ""
        ]
    ]
];

// 如何转化为entity
$entity = Resume::instance($data);
// 这里可以操作entity了

// 如何转化为我们要的数据呐
$enttiy->toArray();
// 将会根据你的注解来转化,默认是snake格式

entity对象

如何通过反射(Reflection)+注解(Attribute)来实现数组转化为对象

转为数组

如何通过反射(Reflection)+注解(Attribute)来实现数组转化为对象

是不是很方便,在的实际工作中,我经常用。原来使用的注释doctrine/annotations被我替换为了原生的注解
entity中我们可以使用setProperty来修改数据,这个和laravel中的model修改器是一样的思路。

源码解析

具体的代码已经被我放到了GitHub上,地址hydrate
大家可以先看,我后续会补充说明。

已经添加了单元测试,对一些函数做了重命名,在实际工作中暂时没有发现什么问题。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!