exception 'DOMException' with message 'Invalid Character Error' Php + Mongodb

问题描述:

项目属于MVC设计模式,技术和框架采用了php5.6 + Yii2.0 + MongoDB。

在我从Controller中调用Model 的 findAll([]) 方法获取数据打印到屏幕上时,报错:

exception 'DOMException' with message 'Invalid Character Error' in /home/user/YiiProject/src/vendor/yiisoft/yii2/web/XmlResponseFormatter.php:94
Stack trace:
#0 /home/user/YiiProject/src/vendor/yiisoft/yii2/web/XmlResponseFormatter.php(94): DOMElement->__construct('$id')
#1 /home/user/YiiProject/src/vendor/yiisoft/yii2/web/XmlResponseFormatter.php(83): yii\web\XmlResponseFormatter->buildXml(Object(DOMElement), Array)
#2 /home/user/YiiProject/src/vendor/yiisoft/yii2/web/XmlResponseFormatter.php(63): yii\web\XmlResponseFormatter->buildXml(Object(DOMElement), Object(MongoId))
#3 /home/user/YiiProject/src/vendor/yiisoft/yii2/web/Response.php(925): yii\web\XmlResponseFormatter->format(Object(yii\web\Response))
#4 /home/user/YiiProject/src/vendor/yiisoft/yii2/web/Response.php(312): yii\web\Response->prepare()
#5 /home/user/YiiProject/src/vendor/yiisoft/yii2/base/Application.php(381): yii\web\Response->send()
#6 /home/user/YiiProject/src/backend/web/index.php(18): yii\base\Application->run()
#7 {main}
--------------------------------------------------------
解决思路:

Controller的路由正确,可以访问到正确的action;Model 类collectionName, attrabutes, fields一应俱全,并且Mongo数据库中有格式正确的2条数据,打log 显示 findAll([]) 的长度为2,findAll([]) 的结果集是两条空数据 [{}, {}] 。好纳闷啊!!!

想到昨天向大神请教了一个关于fields()的问题,数据是空的,会不会是被fields()给过滤掉了?排查field()发现并没有删除数据而是直接返回数据库中的数据。因为Model是继承了PlainModel.php, 然后又找了一下Model的父类PlainModel.php, 发现PlainModel.php在处理'_id'(主键,mongoId类型)时,并不是直接将数据库里的'_id'返回,而是转成了string型返回。恍然大悟!我自己写的Model里的fields 返回的也是MongoId类型的,导致DOM在解析XML时认为是非法字符,引发异常。

MongoDB里的数据格式如下:

{
    "_id" : ObjectId("577325cfb58c1c90788b456b"),
    "memberId" : ObjectId("5761f44fb58c1c4a438b4567"),
    "cardId" : ObjectId("576bc973b58c1cec3a8b4567"),
    "createdAt" : ISODate("2016-06-29T01:35:11.793Z")
}
解决方法:

在 fields() 方法里将 MongoId 类型转为 string 类型

 1     public function fields()
 2     {
 3         return array_merge(
 4             parent::fields(),
 5             [
 6                 'memberId' => function($model) {
 7                     return $model->memberId . '';
 8                 },
 9                 'cardId' => function($model) {
10                     return $model->cardId . '';
11                 }
12             ]
13         );
14     }