Yii2中处理API响应数据的推荐设计模式咨询
嘿,我是常年泡Stack Overflow的Yii2老玩家,碰到过好多你这种问题,来给你唠唠怎么优化~
你用的是PHP7的Yii2应用,没法用严格类型,调用外部API返回的响应应该是这种嵌套数组格式:
$response = [ 'session' => [ 'userData' => ['user_id' => 232, ...], 'sessionData' => ['status' => 'ONGOING', 'details' => 'Some details here...', ...], 'additionalData' => [...] ] ];
现在你处理这些响应的时候,得手动检查每个必填键(比如session、userData、user_id)存不存在,还要核对值的类型才能放心用,结果代码里到处都是重复的样板代码,属实有点闹心对吧?
给你推荐几个适配Yii2的靠谱方案:
活用Yii2自带的模型验证:你可以创建一个继承
yii\base\Model的响应模型类,把要校验的字段定义成模型属性,然后在rules()里写好校验规则(比如必填、整数、数组这些)。拿到API返回的数组后,直接赋值给模型实例,调用validate()就能一次性完成所有校验,错误信息还能统一管理,再也不用写一堆isset()和类型判断了。举个简单的例子:
class ApiSessionResponse extends \yii\base\Model { public $session; public function rules() { return [ [['session'], 'required'], ['session', 'validateSessionStructure'], ]; } // 自定义校验session的内部结构 public function validateSessionStructure($attribute, $params) { $session = $this->$attribute; if (!is_array($session) || !isset($session['userData'], $session['sessionData'])) { $this->addError($attribute, 'Session结构不符合要求'); return; } // 嵌套校验userData里的user_id $userData = $session['userData']; if (!isset($userData['user_id']) || !is_int($userData['user_id'])) { $this->addError($attribute, '用户ID格式错误'); } } }业务代码里这么用:
$response = $apiClient->getResponse(); // 获取API返回的数组 $responseModel = new ApiSessionResponse(); $responseModel->load($response, ''); // 因为是纯数组不是表单提交,第二个参数传空串 if ($responseModel->validate()) { // 校验通过,放心用$responseModel->session就行 } else { // 处理错误,比如打印$responseModel->getErrors() }试试数据传输对象(DTO)模式:虽然PHP7没有严格类型,但可以写DTO类来封装API响应的数据,在构造函数里完成所有校验和赋值。这样业务代码里直接用DTO的属性,完全不用再担心数组键不存在或者类型不对的问题。比如:
class SessionDataDto { public $status; public $details; public function __construct(array $sessionData) { if (!isset($sessionData['status'], $sessionData['details'])) { throw new \InvalidArgumentException('Session数据结构不完整'); } $this->status = $sessionData['status']; $this->details = $sessionData['details']; } } class UserDataDto { public $userId; public function __construct(array $userData) { if (!isset($userData['user_id']) || !is_int($userData['user_id'])) { throw new \InvalidArgumentException('用户ID无效'); } $this->userId = $userData['user_id']; } } class ApiResponseDto { public $userData; public $sessionData; public $additionalData; public function __construct(array $response) { if (!isset($response['session'])) { throw new \InvalidArgumentException('API响应缺少session字段'); } $session = $response['session']; $this->userData = new UserDataDto($session['userData']); $this->sessionData = new SessionDataDto($session['sessionData']); $this->additionalData = $session['additionalData'] ?? []; } }业务代码里调用:
try { $response = $apiClient->getResponse(); $dto = new ApiResponseDto($response); // 直接用$dto->userData->userId、$dto->sessionData->status这些属性就行 } catch (\InvalidArgumentException $e) { // 处理校验失败的情况,比如记录日志或者返回错误提示 }封装校验工具类(适合简单场景):如果不想创建太多模型或DTO类,可以写个全局的工具类,把常用的校验逻辑封装成静态方法,比如
checkRequiredKeys()、checkValueType(),需要的时候直接调用,能减少一部分重复代码。不过这种方式的可维护性不如前两种,适合业务逻辑比较简单的场景。
另外,Yii2的yii\helpers\ArrayHelper里有个getValue()方法也挺实用的,能安全获取嵌套数组的值,避免Undefined index错误,比如$userId = \yii\helpers\ArrayHelper::getValue($response, 'session.userData.user_id', null);,但这只能解决单个字段的获取问题,配合前面的模型或DTO模式用效果更好。
备注:内容来源于stack exchange,提问作者rosecabbagedragon

