Yii2使用异常记录

联表查询问题

最近在使用with进行关联查询时,数据库中有数据,但是查不出关联数据,代码如下

public function getRegister(){
        return $this->hasOne(Register::className(),['user_id'=>'user_id','assess_id'=>'assess_id']);
    }

该关联需要使用到两个条件,当使用一个条件时能查询到相关数据,两个条件返回的对象中,register的值为空。查看sql执行记录,发现关联的查询的语句能查询到记录,那么说明问题出现在给关联对象赋值的地方。

找到负责处理关联数据的文件vender/yiisoft/yii2/db/ActiveRelationTrait.php
在这里插入图片描述
红框中的代码,负责吧查询出来的关联数据赋值给model,我们打印==$buckets==,结果如下
在这里插入图片描述

我们看到,查询出来的关联数据列表,key值是用关联字段的值序列化后得到的。我们再打印==$key==,输出:“{a:2{i:0;s:37290;i:1;s:14}}”
$key的值和关联数据表查询的结果集key值不同,所以在这一步赋值失败了。为什么会出现这种情况呢,我们打印主表的结果集,如下
在这里插入图片描述

主表用于关联的两个字段类型为字符串类型,而关联表的两个两个字段为数值型。生成的$key是用主表结果集中的字段serialize后生成的,导致不能在关联表结果集中匹配到对用的key。
但是查看我的数据表后,主表的这两个字段设置的确实是数值型,说明是yii框架把查询的结果修改了。

找到负责验证数据的文件vender/yiisoft/yii2/db/Schema.php
在这里插入图片描述
问题出在565行,我的php版本是32位的,而且上面两个关联字段,我勾选了unsigned(无符号)选项,导致数值型的字段被转换成了字符串类型,把无符号项勾选掉后,查询结果就回复了正常,以上。

数据模型set方法问题

通过数据提供器获取数据列表后,某些字段我们可能想要单独赋值,比如当username为空时,我们想要给username赋值为“游客”。

$list = $dataProvider->getModels();
foreahc($list as $val){
	//usernam必须是主表中的字段
	if(!$val->username)	$val->username = '游客';
}
$dataProvider->setModels($list);

但是上面的赋值方式中,当username不是主表字段时,会报错。比如数据提供器查询中,使用了with关联users,users作为一个关联值,修改时会提示不能修改只读属性。
在这里插入图片描述

这种情况下,我们需要在数据提供器model中添加如下代码

//model中加入如下代码
public function __set($attributes,$val){
	//我们只对指定的字段进行自动赋值
	//如果不做这个判断,模型的的增删改操作会有数据异常的情况,
	if($attributes == 'users')	$this->$attributes = $val;
}
//赋值代码
$list = $dataProvider->getModels();
foreahc($list as $val){
	if(!$val->users)	$val->users= object(['username'=>'游客']);
}
$dataProvider->setModels($list);

数据模型中,会追加一个可读的users对象属性。调用时,会优先读取该users属性

leftjoin 查询数据与原生sql不同

$model->find()->alias('t1')
	->select('t1.*,t2.name')
	->leftJoin('table t2','t1.id=t2.pid')
	->where(['t1.id'=>10])->asArray()->all();

该查询中,t1和t2是一对多的关系。如果t2中,有十条记录的pid是10的话,将会查询出10条t1的重复记录,但是在该model查询中,只返回了一条记录,这不符合我们的预期,显然是yii2框架帮我们做了去主表重复值的处理。

大部分情况下,这种处理是好的。但当我们需要显示重复值值时,这样查询就无法做到,此时我们需要处理以下查询字段,如下

$model->find()->alias('t1')
	->select('t1.id as ID,t1.time,t1.status,t2.name')
	->leftJoin('table t2','t1.id=t2.pid')
	->where(['t1.id'=>10])->asArray()->all();

把主表想要查询的字段一一列举出来,给主键id设置别名,这样就能查出想要的重复值

别名和命名空间冲突问题

项目,在yii2下建立了多个应用。跨应用调用时,提示找不到文件,目录结果如下
在这里插入图片描述
在100下,当调用appapi中的方法,提示找不到文件。最后发现是因为命名空间冲突的问题。
appapi应用下的文件命名空间都使用了app 开头。

yii2自动加载遵循了PSR-4规则,应用下的命名空间需要按照文件所在目录指定,才能根据自动加规则找到文件。而appapi应用下文件名没有使用appapi目录作为命名空间,却能正确加载,比如:\app\controller\v1\info.php
是因为Yii2默认当前应用的命名空间是app,而Yii2有一组预定义别名,如下

@yii 		表示Yii框架所在的目录,也是 BaseYii.php 文件所在的位置
@app 		表示正在运行的应用的根目录
@vendor 	表示Composer 第三方库所在目录,一般是 @app/vendor 或 @app/../vendor
@bower 		表示 Bower 第三方库所在目录,一般是 @vendor/bower
@npm 		表示 NPM 第三方库所在目录,一般是 @vendor/npm
@runtime 	表示正在运行的应用的运行时用于存放运行时文件的目录,一般是 @app/runtime
@webroot 	表示正在运行的应用的入口文件 index.php 所在的目录,一般是 @app/web
@web 		URL别名,表示当前应用的根URL,主要用于前端
@common 	表示通用文件夹
@frontend 	表示前台应用所在的文件夹
@backend 	表示后台应用所在的文件夹
@console 	表示命令行应用所在的文件夹

Yii2会先按PSR-4寻找app目录,找不到,使用预定义别名@app找

当我们在应用100中调用 \app\controller\v1\info.php ,首先根据PSR-4找不到app目录,然后根据别名@app,在当前应用目录下,也找不到info文件,这就导致报错了。

有三种方法解决该问题

方法一: 临时修改预定义别名
在调用appapi下文件时,修改别名@app的值为appapi目录

//设置别名@app为appapi下的目录
Yii::setAlias('@app', '\www\appapi');
//通过命名空间调用appapi下的文件和方法
\app\controller\v1\info::a();

方法二: 修改自动加载映射类$classMap
当代码中调用到尚未加载的类时,Yii的audoloader会扫描这个属性以获得需要加载的类文件名

Yii::$classMap['info'] = '\appapi\controller\v1\info.php';
info::a();

方法三: 修改配置文件

前面两种方法,当用的的地方多的时候,需要重复设置,降低了代码的可维护性。
我们可以通过修改配置文件的方式,在100应用中正常调用appapi中的方法,如下

首先在应用100的配置文件中,添加代码

$config = [
    'id' => '100',
    ...
    'aliases' => [
        '@app' => '\appapi'
    ],
    ...
];

然后就可以通过命名空间,正常调用appapi下的文件了

Indirect modification of overloaded property错误

代码如下

//$model是一个ActiveRecord对象,applyCheckLog是使用with关联到的子表数据
//代码目的是:获取关联子数据的最后一条,并赋值给model的属性
if($model->applyCheckLog){
   $last_task = end($model->applyCheckLog);
   $model->setAttribute('last_task_user', $last_task->platformUser->name);
}

上述代码在本地环境可以正常运行,放在线上环境后报上述错误。
查询后知道该错误出现的原因是:尝试修改了一个通过魔术方法(如 __get)间接获取的对象属性,而这个属性没有明确地定义在类中

这样就明白问题问题的原因了。关联数据applyCheckLog确实是通过get魔术方法获取到的,而end()方法修改了applyCheckLog数组的指针位置,相当于是修改了属性,所以报错了。
但不明清除为什么本地正常,本地使用的php版本是7.4.3,线上是7.4.33.

代码修改后,如下即可

if($model->applyCheckLog){
     $last_key = count($model->applyCheckLog);
     $last_task = $model->applyCheckLog[$last_key-1];
     $model->setAttribute('last_task_user', $last_task->platformUser->name);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值