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 相关文章推荐
将数组写入txt文件 var_export
Apr 21 PHP
PHP XML数据解析代码
May 26 PHP
Laravel框架中扩展函数、扩展自定义类的方法
Sep 04 PHP
php ajax异步读取rss文档数据
Mar 29 PHP
PHP中功能强大却很少使用的函数实例小结
Nov 10 PHP
记录一次排查PHP脚本执行卡住的问题
Dec 27 PHP
详解PHP安装mysql.so扩展的方法
Dec 31 PHP
浅析php如何实现爬取数据原理
Sep 27 PHP
thinkphp5.0整合phpsocketio完整攻略(绕坑)
Oct 12 PHP
PHP与Perl之间知识点区别整理
Mar 19 PHP
php伪静态验证码不显示的解决方案
Sep 26 PHP
PHP超全局变量实现原理及代码解析
Sep 01 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利用新浪接口查询ip获取地理位置示例
2014/01/20 PHP
php使用websocket示例详解
2014/03/12 PHP
PHP按行读取文件时删除换行符的3种方法
2014/05/04 PHP
PHP三元运算的2种写法代码实例
2014/05/12 PHP
PHP依赖注入原理与用法分析
2018/08/21 PHP
Centos7 Yum安装PHP7.2流程教程详解
2019/07/02 PHP
js实现上传图片之上传前预览图片
2013/03/25 Javascript
jQuery操作元素css样式的三种方法
2014/06/04 Javascript
js常用数组操作方法简明总结
2014/06/20 Javascript
Bootstrap CDN和本地化环境搭建
2016/10/26 Javascript
vue 自定义 select内置组件
2018/04/10 Javascript
JS获取一个字符串中指定字符串第n次出现的位置
2021/02/10 Javascript
[37:22]DOTA2上海特级锦标赛D组资格赛#2 Liquid VS VP第一局
2016/02/28 DOTA
[44:40]Serenity vs Pain 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
[43:47]完美世界DOTA2联赛PWL S3 LBZS vs Phoenix 第一场 12.09
2020/12/11 DOTA
高性能web服务器框架Tornado简单实现restful接口及开发实例
2014/07/16 Python
Python调用命令行进度条的方法
2015/05/05 Python
python中的随机函数小结
2018/01/27 Python
Python实现PS滤镜Fish lens图像扭曲效果示例
2018/01/29 Python
Python打开文件,将list、numpy数组内容写入txt文件中的方法
2018/10/26 Python
python+pyqt5编写md5生成器
2019/03/18 Python
PyQt4编程之让状态栏显示信息的方法
2019/06/18 Python
python opencv对图像进行旋转且不裁剪图片的实现方法
2019/07/09 Python
解决pycharm上的jupyter notebook端口被占用问题
2019/12/17 Python
python学习将数据写入文件并保存方法
2020/06/07 Python
Python logging模块handlers用法详解
2020/08/14 Python
python 三种方法提取pdf中的图片
2021/02/07 Python
移动端Html5页面生成图片解决方案
2018/08/07 HTML / CSS
高品质和独特的产品世界:Creations and Collections
2018/01/07 全球购物
Charlotte Tilbury澳大利亚官网:英国美妆品牌
2018/10/05 全球购物
Gretna Green中文官网:苏格兰格林小镇
2019/10/16 全球购物
营销总经理岗位职责
2014/02/02 职场文书
小学综合实践活动总结
2014/07/07 职场文书
仰望星空观后感
2015/06/10 职场文书
2016年秋季运动会通讯稿
2015/11/25 职场文书
Python实现查询剪贴板自动匹配信息的思路详解
2021/07/09 Python