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下删除字符串中HTML标签的函数
Aug 27 PHP
PHP 5.3新特性命名空间规则解析及高级功能
Mar 11 PHP
php Smarty模板生成html文档的方法
Apr 12 PHP
Thinkphp模板中使用自定义函数的方法
Sep 23 PHP
基于PHP静态类的原罪详解
May 06 PHP
基于php常用正则表达式的整理汇总
Jun 08 PHP
PHP实现的封装验证码类详解
Jun 18 PHP
PHP中is_file不能替代file_exists的理由
Mar 04 PHP
PHP中echo和print的区别
Aug 28 PHP
PHP实用函数分享之去除多余的0
Feb 06 PHP
PHP中的访问修饰符简单比较
Feb 02 PHP
php使用curl伪造浏览器访问操作示例
Sep 30 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 XML操作的各种方法解析(比较详细)
2010/06/17 PHP
phpmailer发送邮件之后,返回收件人是否阅读了邮件的方法
2014/07/19 PHP
PHP 使用memcached简单示例分享
2015/03/05 PHP
Laravel学习笔记之Artisan命令生成自定义模板的方法
2018/11/22 PHP
php实现根据身份证获取精准年龄
2020/02/26 PHP
php中加密解密DES类的简单使用方法示例
2020/03/26 PHP
XP折叠菜单&amp;仿QQ2006菜单
2006/12/16 Javascript
jquery ajax提交表单数据的两种方式
2009/11/24 Javascript
基于jQuery的history历史记录插件
2010/12/11 Javascript
JS 如何获取radio选中后的值及不选择取radio的值
2013/10/28 Javascript
浏览器中url存储的JavaScript实现
2015/07/07 Javascript
深入理解JS函数的参数(arguments)的使用
2016/05/28 Javascript
xtemplate node.js 的使用方法实例解析
2016/08/22 Javascript
JS判断form内所有表单是否为空的简单实例
2016/09/09 Javascript
canvas实现简易的圆环进度条效果
2017/02/28 Javascript
react-native中ListView组件点击跳转的方法示例
2017/09/30 Javascript
JS使用正则表达式获取小括号、中括号及花括号内容的方法示例
2018/06/01 Javascript
vue.js将时间戳转化为日期格式的实现代码
2018/06/05 Javascript
4个顶级JavaScript高级文本编辑器
2018/10/10 Javascript
[47:39]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 LGD vs OPTIC
2018/03/31 DOTA
[52:37]完美世界DOTA2联赛循环赛 Forest vs DM BO2第一场 10.29
2020/10/29 DOTA
Python3.2模拟实现webqq登录
2016/02/15 Python
python+requests+unittest API接口测试实例(详解)
2017/06/10 Python
学python安装的软件总结
2019/10/12 Python
Python笔记之facade模式
2019/11/20 Python
Pycharm在指定目录下生成文件和删除文件的实现
2020/12/28 Python
使用CSS3中的calc()属性来以算式表达尺寸数值
2016/06/06 HTML / CSS
canvas 阴影和图形变换的示例代码
2018/01/02 HTML / CSS
工业设计毕业生自荐信
2014/04/13 职场文书
目标责任书范文
2014/04/14 职场文书
离婚协议书范本及离婚须知
2014/10/15 职场文书
2016年教代会开幕词
2016/03/04 职场文书
创业计划书之书店
2019/09/10 职场文书
Python 语言实现六大查找算法
2021/06/30 Python
浅谈克隆 JavaScript
2021/11/02 Javascript
pytorch分类模型绘制混淆矩阵以及可视化详解
2022/04/07 Python