You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yii2 REST API模型:如何让WebSocket传递数据遵循API定义格式?

问题

我在项目中使用Yii2 REST API,一切运行正常。我通过api\modules\v1\models\Comment模型的fields()方法定义了返回数据的格式:

<?php
namespace api\modules\v1\models;

use Carbon\Carbon;
use Yii;

class Comment extends \common\models\Comment
{
    public function fields()
    {
        return [
            'id',
            'user' => function(Comment $model) {
                return User::findOne($model->user_id);
            },
            'text',
            'image' => function(Comment $model) {
                return Yii::$app->params['link'].$model->image;
            },
            'created_at' => function(Comment $model) {
                Carbon::setLocale(Yii::$app->language);
                return Carbon::createFromTimeStamp(strtotime($model->created_at))->diffForHumans();
            },
            'children' => function(Comment $model) {
                $comments = Comment::find()
                    ->where(['comment_id' => $model->id]);
                if (!$comments->exists()) {
                    return false;
                }
                return $comments->all();
            },
            'like',
            'news_id',
            'comment_id'
        ];
    }
}

REST接口能按指定格式返回数据,但我需要通过WebSocket向客户端推送数据,比如新评论产生时推送给所有用户。控制器中的相关代码如下:

$post = Yii::$app->request->post();
$image = UploadedFile::getInstanceByName('image');
$model = new \api\modules\v1\models\Comment([
    'news_id' => $post['feed_id'],
    'comment_id' => $post['comment_id'] ?? null,
    'user_id' => Yii::$app->user->identity->id,
]);
$model->text = $model->findLinks($post['text']);
if ($image && !$image->error) {
    if (!file_exists(Yii::$app->params['comment.pathAbsolute'])) {
        if (!FileHelper::createDirectory(Yii::$app->params['comment.pathAbsolute'], 0777)) {
            throw new \Exception('Помилка створення папки');
        }
    }
    $serverName = Yii::$app->security->generateRandomString(16).'.'.$image->extension;
    if ($image->saveAs(Yii::$app->params['comment.pathAbsolute'].$serverName)) {
        $model->image = $serverName;
    } else {
        throw new \Exception($image->error);
    }
}
if (!$model->save()) {
    throw new \Exception($model->error());
}
Helper::ws(false, 'updateComment', ['feed_id' => $post['feed_id'], 'comment' => $model]);

当直接传递$model时,数据为数据库中存储的原始格式,请问是否可以调用某个方法,让传递的数据遵循API模型中定义的格式?

解决方案

直接调用Yii2 ActiveRecord提供的toArray()方法即可,该方法会自动应用你在fields()中定义的格式规则,把模型实例转换成符合API输出格式的数组。

修改WebSocket推送的代码行:

Helper::ws(false, 'updateComment', ['feed_id' => $post['feed_id'], 'comment' => $model->toArray()]);

另外,注意你在fields()的children字段中返回的是Comment模型数组,这些子评论模型也需要调用toArray()来格式化。可以修改children的闭包逻辑,把模型数组转成格式化后的数组:

'children' => function(Comment $model) {
    $comments = Comment::find()
        ->where(['comment_id' => $model->id])
        ->all();
    if (empty($comments)) {
        return false;
    }
    // 将子评论模型数组转成格式化后的数组
    return array_map(function($comment) {
        return $comment->toArray();
    }, $comments);
},

这样无论是主评论还是嵌套的子评论,都会按照你在fields()中定义的格式输出,和REST接口返回的数据结构完全一致。

内容的提问来源于stack exchange,提问作者Diefair

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 21:11:38