如何通過反射(Reflection)+註解(Attribute)來實現陣列轉化為物件

kabunx發表於2021-09-16

場景

在日常開發中,我們可能會遇到和第三方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(from: "TimeOfUse", to: "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 協議》,轉載必須註明作者和本文連結

相關文章