PHP的Yii框架使用中的一些错误解决方法与建议


Posted in PHP onAugust 21, 2015

 此文意在记录 Yii 开发过程中的小问题解决方案,不全面,不权威,不是教程。自己写过,觉得可以解决问题,以后也可能用上,就记记吧。

    1. Yii 中 Js 和 Css 文件的引入。
    我们就从最简单的问题开始吧,说起来也不是问题,只是语法罢了。假设我们的 js 文件都放在和 protected 同一层的 js 文件夹里,css 文件都放在和 protected 同一层的 css 文件夹里,好吧,规范就是这样的...那我们可以在对应的 view 界面按下面这样写,css 和 js 函数的参数是不同的哦...(之前因为这个调了一个小时..)
    注册 js 文件的第二个参数是 js 所放的位置,可选三个:CClientScript::POS_HEAD 放在 Head 部分  CClientScript::POS_BEGIN  放在 Body 开始处  CClientScript::POS_END  放在 Body 结束处,没有特别要求就不用填了...注册 Css 文件的第二个参数是 media,,有兴趣的同学点这里,目前还是默认就好...
    对于 Jquery 这样的 js ,用 registerCoreScript 不会造成莫名奇妙的错误...

//注册 js 文件 
Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl.'/js/project1.js',CClientScript::POS_HEAD); 
//注册 css 文件 
Yii::app()->clientScript->registerCssFile(Yii::app()->baseUrl.'/css/project1.css'); 
//注册 Jquery 文件 
Yii::app()->clientScript->registerCoreScript('jquery');

    2. Yii isNewRecord 修复
    Yii 的 Model 的 isNewRecord 属性是很好用的,可以根据这个属性进行分情况讨论。但是,假如我们开启了事务机制或是其他情况,造成数据插入后又被回滚了,这时数据库里没有该条记录,但是 isNewRecord 是 flase,即认为已经不是新纪录了。解决方法是用主键去访问数据库,判断究竟是不是新纪录,而我们在用到这个属性之前要先按下面处理一下。以下 Model 是 Post,主键是 id:

if(!$model->isNewRecord) 
{ 
  $db_exist = Post::model()->findByPk($model->id); 
  if($db_exist == NULL) 
    $model->isNewRecord = true; 
}

    3.Yii 生成 隐藏输入域
    虽然自己写一个输入域很容易(不就是 display:none 嘛),但是有时架不住需要按照 Yii 的表单代码格式呀,反正就一句话...

<?php echo $form->hiddenField($model,'name'); ?> 
<?php if($model->isNewRecord) echo $form->hiddenField($model,'path',array('size'=>60,'maxlength'=>128,'id'=>'path1')); ?>

    4. Yii 生成下拉菜单
     很多时候我们在 form 里需要一个下拉菜单,这时候 Chtml 的 listdata 就很好用的。假如我们数据库里的字段只有很少的可能,比如 0 和 1,可以按下面写:

echo $form->dropDownList($model,'is_marry',array('0'=>'否','1'=>'是'));

    这时候,你看到的就是 是 和 否 的下拉菜单,选择 '是' 提交的时候这个字段填的就是 1 ,'否' 就是 0 。当然,经常不只这么简单,我们可以在 Model 里面添加一个函数用于生成下拉菜单的数组,然后在 view 里去调用就行了。这个函数的数据可以自己写的,或者在数据库查找得来的。下面用了 listdata, 具体意思是以 model 中 id 为 键, name 为值。

/* 写在 model 里 */ 
public function getUserOptions() 
{ 
  $models = User::model()->findAll(); 
  $models = User::model()->findAllByAttributes(array('is_regeister'=>'1')); 
  return CHtml::listdata($models, 'id', 'name'); 
} 
 
/* 写在 view 的界面里 */ 
echo $form->dropDownList($model,'user_id',User::model()->getUserOptions());

5.Yii  开启事务机制
    在你同时保存几条记录到数据库时,你可能很有必要开启事务机制。Yii 开启事务机制很容易,只要三句话就够了。

/*开启事务机制*/ 
$transaction = Yii::app()->db->beginTransaction(); 
try 
{ 
  /* 成功则 commit */ 
  $transaction->commit(); 
} 
catch(Exception $e) 
{ 
  $transaction->rollBack(); 
}

    比较完整的像这样:

if($_POST['ModelA']) 
{ 
  /*开启事务机制*/ 
  $transaction = Yii::app()->db->beginTransaction(); 
  try 
  { 
    /*此处省略一堆逻辑*/ 
    $modelA->save(); 
    $modelB->save(); 
 
    /* 成功则 commit */ 
    $transaction->commit(); 
    $this->redirect(array('view','id'=>$model->id)); 
  } 
  catch(Exception $e) 
  { 
    $transaction->rollBack(); 
  } 
}

    不过我一般会像下面这样,有什么好处请自行体会...

if($_POST['ModelA']) 
{ 
  /*开启事务机制*/ 
  $transaction = Yii::app()->db->beginTransaction(); 
  try 
  { 
    $validated = true; 
 
    /*此处省略一堆逻辑*/ 
    $valid = $modelA->save(); 
    $validated = $valid & $validated; 
 
    /*此处继续省略一堆逻辑*/ 
    $valid = $modelB->save(); 
    $validated = $valid & $validated; 
 
    /* 成功则 commit */ 
    if($validated) 
    { 
      $transaction->commit(); 
      $this->redirect(array('view','id'=>$model->id)); 
    } 
    else 
    { 
      /*不成功即回滚 */ 
      $transaction->rollBack(); 
    } 
  } 
  catch(Exception $e) 
  { 
    $transaction->rollBack(); 
  } 
}

6.关联表查询相同字段出错。
    有时候我们建了两个表,但是两个表有相同的字段,在用 CDbCriteria 进行 with 关联查询搜索时候,如果没有进行额外设置,那会出现查询错误,大概的意思就是 Mysql 语句模糊。这时候,我们在主表设置一个别名就好了,然后查询相关字段的时候注意把 名字加上就行。
    比如:两个 Model, Post 和 User,都有一个 id, 在 我们可以像下面这样写:

$criteria=new CDbCriteria; 
 
$criteria->alias = "post"; 
 
$criteria->with = array('user'); 
 
$criteria->compare('post.id',$Post->id,true); 
 
$model = Post::model()->find($criteria);

7.文件上传
    说起来这个不算是 Yii 的,基本都是原生的 HTML 和 PHP,懒得分,就直接放这里吧。
    下面是 HTML,action 改为你自己的 url, id 和 name 也由你自己定义。

<form action="your url" method='post' enctype="multipart/form-data" id='fileform'> 
  <p style='display:inline-block'>文件上传  </p><input id='file1' name='file1' type='file' ></input> 
  <br /> 
  <input type='submit' value='上传'> 
</form>

    这是服务器端接收并保存文件的代码,文件最后保存到了 attached 文件夹的 file 文件夹里:

if(isset($_FILES['file1'])) 
{ 
  $xlsfile = $_FILES['file1']; 
  $tmp_name = $xlsfile['tmp_name']; 
  /*获取文件名*/ 
  $file_name = basename($xlsfile_name); 
 
  if($xlsfile['error'] > 0) 
  { 
    echo "文件上传出错!请重试。<br />"; 
    exit; 
  } 
  else 
  { 
    if(file_exists("attached/tmp/".$file_name)) 
      echo "文件已存在!本次不予保存!"; 
    else 
    { 
      if(!is_dir("attached/tmp/")) 
      { 
        /*新建文件夹,默认权限 777, true 意味着可以递归从创建*/ 
        if(!mkdir("attached/tmp/",0777,true)) 
        { 
          echo "找不到 attached/tmp 文件夹,且创建失败!<br />"; 
          exit; 
        } 
      } 
 
      /*这个函数仅用于上传文件的移动*/ 
      move_uploaded_file($tmp_name,"attached/tmp/".$file_name); 
    } 
  } 
}

    下面是把已存在的文件从 old_file 路径移到 attached/file 里面的当前日期文件夹。这里的移动用 rename

/*创建文件夹*/ 
$date = date('Y-m-d',time()); 
$date = str_replace('-',"",$date); 
$dir = "attached/file/".$date.'/'; 
if(!is_dir($dir)) 
{ 
  if(!mkdir($dir,0777,true)) 
  { 
    exit('无法创建文件夹!'); 
  } 
} 
 
/*移动文件*/ 
$file_name = basename($old_file); 
$finish = rename($old_file,$dir.$file_name); 
if(!$finish) 
{ 
  exit('无法移动文件!'); 
}

8.YIi 场景与安全字段
    查看当前 Model 场景:

var_dump($model->scenario);

     查看场景的安全字段。安全字段的意思是说这些数据由用户提交的时候不会被 Yii 过滤掉。有次发现网页提交上来的东西有些有有些没,调了很久才知道在那个场景下部分被过滤了。

$arr = $model->getSafeAttributeNames($model->scenario); 
var_dump($arr);

    强制赋值避免 rule 规则过滤字段。用 setAttributes 可以强制取消 Yii 的安全过滤,只要第二个参数赋值为 false 就好。但是这也只能对这个 Model 生成时就拥有的字段生效,如果要对包括自己定义的所有字段不过滤,还是要定义场景然后在 rule 里指定安全字段比较好。

if(isset($_GET['Po'])) 
  $model->setAttributes($_GET['Post'],false);

检查日期格式合法性
    有时我们需要检验用户填写的日期是否合法,可以用下面的函数。

function checkDatetime($dateStr, $format = "Y-m-d H:i:s") 
{ 
  $time = strtotime($dateStr); 
  $checkDate = date($format, $time); 
 
  return $checkDate == $dateStr; 
}

Yii 渲染多个 model
    相信新手都有疑惑,_form 里面的表单都是渲染一个 model 然后提交给 controller 保存数据的,如果想要渲染多个 model 怎么办呢?
    下面,我们假设有两个 model 类,分别叫做 Person 和 Addr,我们想要做的是在一个 Person 的 _form 里再渲染几个 Addr 的 model ,意思是一个人可以有几个地址。基本思路其实还是很简单,就是你在 controller 里定义要渲染的 model 然后传给 view 界面,最后依然在 controller 里接收 Post 过来的数据。主要是写法问题而已,我相信下面大家都能看懂,有疑问的童鞋再留言好了。

//在 controller 里面 
 
$model=new Person; 
/* $addrs 存储 Addr model 的数组,放几个你就看着办吧*/ 
$addrs = array(); 
 
if(isset($_POST['Person'])) 
{ 
  $model->attributes = $_POST['Person']; 
  /*此处省略一堆逻辑*/ 
  foreach($_POST['Addr'] as $one_addr) 
  { 
    $addr = new Addr(); 
    $addr->attributes = $one_addr; 
    /*此处省略另一堆逻辑*/ 
  } 
} 
 
$this->render('create',array( 
  'model'=>$model, 
  'addrs' => $addrs, 
)); 
 
 
//在 view 里面 
 
/*可以循环输出你的多个 model */ 
$num = count($addrs); 
for($i = 0;$i < $num;++$i) 
{ 
  echo $form->labelEx($addrs[$i],"[{$i}]postcode"); 
  echo $form->textField($addrs[$i],"[{$i}]postcode",array('size'=>10,'maxlength'=>10)); 
  ...; 
} 
 
/*也可以通过数字指定输出某个 model */ 
echo $form->labelEx($addrs[0],"[0]postcode"); 
echo $form->textField($addrs[0],"[0]postcode",array('size'=>10,'maxlength'=>10));
PHP 相关文章推荐
一个PHP操作Access类(PHP+ODBC+Access)
Jan 02 PHP
PHP HTML代码串 截取实现代码
Jun 29 PHP
PHP 实用代码收集
Jan 22 PHP
服务器变量 $_SERVER 的深入解析
Jul 02 PHP
php将url地址转化为完整的a标签链接代码(php为url地址添加a标签)
Jan 17 PHP
PHP中list()函数用法实例简析
Jan 08 PHP
PHP的Yii框架中使用数据库的配置和SQL操作实例教程
Mar 17 PHP
thinkPHP实现签到功能的方法
Mar 15 PHP
thinkphp框架page类与bootstrap分页(美化)
Jun 25 PHP
php检测mysql表是否存在的方法小结
Jul 20 PHP
PHP文件系统管理(实例讲解)
Sep 19 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
Feb 21 PHP
win7系统配置php+Apache+mysql环境的方法
Aug 21 #PHP
php生成图片验证码-附五种验证码
Aug 19 #PHP
以实例全面讲解PHP中多进程编程的相关函数的使用
Aug 18 #PHP
深入探究PHP的多进程编程方法
Aug 18 #PHP
使用PHP和JavaScript判断请求是否来自微信内浏览器
Aug 18 #PHP
PHP的命令行命令使用指南
Aug 18 #PHP
使用PHP生成图片的缩略图的方法
Aug 18 #PHP
You might like
php include类文件超时问题处理
2015/02/06 PHP
PHP中Cookie的使用详解(简单易懂)
2017/04/28 PHP
PHP implode()函数用法讲解
2019/03/08 PHP
(仅IE下有效)关于checkbox 三态
2007/05/12 Javascript
JavaScript基本语法讲解
2015/06/03 Javascript
AngularJS模仿Form表单提交的实现代码
2016/12/08 Javascript
vue日期组件 支持vue1.0和2.0
2017/01/09 Javascript
微信小程序 引用其他js文件实现代码
2017/02/22 Javascript
angular4 如何在全局设置路由跳转动画的方法
2017/08/30 Javascript
Nodejs+express+ejs简单使用实例代码
2017/09/18 NodeJs
Hexo已经看腻了,来手把手教你使用VuePress搭建个人博客
2018/04/26 Javascript
JS+H5 Canvas实现时钟效果
2018/07/20 Javascript
解决Vue使用swiper动态加载数据,动态轮播数据显示白屏的问题
2018/09/27 Javascript
微信小程序的线程架构【推荐】
2019/05/14 Javascript
JS数组方法reduce的用法实例分析
2020/03/03 Javascript
深入分析jQuery.one() 函数
2020/06/03 jQuery
Vue实现随机验证码功能
2020/12/29 Vue.js
[05:39]2014DOTA2国际邀请赛 DK晋级胜者组专访战队国士无双
2014/07/14 DOTA
Python3中多线程编程的队列运作示例
2015/04/16 Python
Python中if __name__ == '__main__'作用解析
2015/06/29 Python
python 输入一个数n,求n个数求乘或求和的实例
2018/11/13 Python
详解python中的数据类型和控制流
2019/08/08 Python
Python实现代码统计工具
2019/09/19 Python
Python实现线性插值和三次样条插值的示例代码
2019/11/13 Python
keras获得model中某一层的某一个Tensor的输出维度教程
2020/01/24 Python
css3实现顶部社会化分享按钮示例
2014/05/06 HTML / CSS
SportsDirect.com马来西亚:英国第一体育零售商
2018/11/21 全球购物
加拿大专业美发产品购物网站:Chatters
2021/02/28 全球购物
表扬信格式
2014/01/12 职场文书
一年级家长会邀请函
2014/01/25 职场文书
创业培训计划书
2014/05/03 职场文书
会计毕业生自荐书
2014/06/12 职场文书
2014年加油站工作总结
2014/12/04 职场文书
2016年五一促销广告语
2016/01/28 职场文书
vue3如何优雅的实现移动端登录注册模块
2021/03/29 Vue.js
Python制作春联的示例代码
2022/01/22 Python