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
相关产品推荐
相关产品推荐

