YII2项目常用技能知识总结

20次阅读

共计 7586 个字符,预计需要花费 19 分钟才能阅读完成。

1、不通过日志获取 AR 执行的原生 SQL 语句和打印变量数据

$query = User::find() ->select(['username'])->where(['id'=>[1,2,3,4])
// get the AR raw sql in YII2
$commandQuery = clone $query;
echo $commandQuery->createCommand()->getRawSql();$users = $query->all();

打印变量数据可以这样写:

// 引用命名空间
use yii\helpers\VarDumper;
// 使用
VarDumper::dump($var);
// 使用 2  第二个参数是数组的深度  第三个参数是是否显示代码高亮(默认不显示)VarDumper::dump($var, 10 ,true);

2、从数据库二维数组中返回一维数组并配合 rules 验证规则实现分类数据过滤。

普通返回表记录的二维数组

Member::find()->select('userid')->asArray()->all();
Array
([0] => Array
        ([userid] => 1
        )

    [1] => Array
        ([userid] => 2
        )

    [2] => Array
        ([userid] => 3
        )

)

返回字段的一维数组

Member::find()->select('userid')->asArray()->column();
或者:\yii\helpers\ArrayHelper::getColumn(Member::find()->all(), 'userid')
Array
([0] => 1
    [1] => 2
    [2] => 3
)

返回一维数组配合验证规则验证数据正确性,如分类 catid 正确分为只有 1 -4,但是在 devTools 打开修改 catid 为 5,提交同样会到数据库,此时 rules 验证规则如下:

['catid', 'in', 'range' => category::find()->select('id')->asArray()->column()],

当然,这个也可以通过下面这样子写,一样的:

['catid', 'in', 'range' => \yii\helpers\ArrayHelper::getColumn(category::find()->all(), 'catid')],

这样就可以过滤不正确的分类数据了!

3、友好时间表示方法

之前一直使用自定义的友好时间函数。几天前发现万能的 YII 已经提供了友好时间访问,代码如下:

Yii::$app->formatter->asRelativeTime('1447565922'); // 2 小时前

4、使用不同的响应类型或者自定义响应类型

有效的格式:

FORMAT_RAW

FORMAT_HTML

FORMAT_JSON

FORMAT_JSONP

FORMAT_XML

JSON 响应

public function actionIndex()
{
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    $items = ['some', 'array', 'of', 'data' => ['associative', 'array']];
    return $items;
}
返回:{
    "0": "some",
    "1": "array",
    "2": "of",
    "data": ["associative", "array"]
}

自定义响应格式

让我们创建一个定制的响应格式。例子做点有趣和疯狂的事我返回 PHP 数组。首先,我们需要格式化程序本身。创建 components/PhpArrayFormatter.php:

<?php
namespace app\components;
use yii\helpers\VarDumper;
use yii\web\ResponseFormatterInterface;
class PhpArrayFormatter implements ResponseFormatterInterface
{public function format($response)
    {$response->getHeaders()->set('Content-Type', 'text/php; charset=UTF-8');
        if ($response->data !== null) {$response->content = "<?php\nreturn" . VarDumper::export($response->data) . ";\n";
        }
    }
}
组件配置:return [
    // ...
    'components' => [
        // ...
        'response' => [
            'formatters' => ['php' => 'app\components\PhpArrayFormatter',],
        ],
    ],
];
现在是准备使用。在 controllers/SiteController 创建一个新的方法 actionTest:

public function actionTest()
{
    Yii::$app->response->format = 'php';
    return ['hello' => 'world!',];
}
返回如下:<?php
return ['hello' => 'world!',];

5、AR 入库前时间通过在模型重写 behaviors 方法实现优雅入库方式。

如下:

public function behaviors()
{
    return [
        'timestamp' => ['class' => TimestampBehavior::className(),
            'attributes' => [
                ActiveRecord::EVENT_BEFORE_INSERT => 'creation_time',
                ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
            ],
            'value' => function() { return date('U'); // unix timestamp },
        ],
    ];
}

6、除配置组件记录不同级别日志外,也可以自定义在某个地方记录 LOG 日志

use yii\log\Logger;
\Yii::getLogger()->log('User has been created', Logger::LEVEL_INFO);

7、ActiveForm 类不让生成 label 标签

// 方法一,通过 ActiveForm 类
$form->field($model, '字段名')->passwordInput(['maxlength' => true])->label(false) ?>
// 方法二,通过 HTML 类
Html::activeInput($type,$model,'字段名')
Yii2 给必填项加星,样式如下:div.required label:after {
    content: "*";
    color: red;
}

8、Yii2 获取接口传过来的 JSON 数据:

接收 get 和 post 的数据很容易,那么接收 json 数据呢?!没关系,看这里:

Yii::$app->request->rawBody;
9、座机和手机号码必须填写一个:

public function rules()
{
    return [[['telephone', 'mobile'], function ($attribute, $param) {// 至少要一个
            if (empty($this->telephone) && empty($this->mobile)) {$this->addError($attribute, 'telephone/mobile 至少要填一个');
            }
        }, 'skipOnEmpty' => false],
    ];
}

10、where 多条件查询示例:

//and 复杂示例:$time = time();
Member::find()->where(['and', ['userid' => 1, 'company' =>'测试公司'], ['>', 'addtime', $time]]);
//SELECT * FROM `member` WHERE ((`userid`=1) AND (`company`='测试公司')) AND (`addtime` > 1447587486)
//and 和 or 组合示例:$query = Member::find()->where(['and', ['>','userid',2], ['or', ['company' => '深圳市新民家具有限公司'], ['address' => '深圳']]]);
//SELECT * FROM `member` WHERE (`userid` > 2) AND ((`company`='深圳市新民家具有限公司') OR (`address`='深圳'))

11、关于事务:

优雅的写法

Yii::$app->db->transaction(function() {$order = new Order($customer);
    $order->save();});

这相当于下列冗长的代码:


$transaction = Yii::$app->db->beginTransaction();
try {$order = new Order($customer);
    $order->save();
    $transaction->commit();} catch (\Exception $e) {$transaction->rollBack();
    throw $e;
}

12、rest 风格 API 获取客户端提交的 get 和 post 的数组

// post
Yii::$app->request->bodyParams
// get
Yii::$app->request->queryParams;

13、一个控制器调用其他控制器 action 的方法:

方法一:

是经典的重写 actions 方法

public function actions()
    {
        return [
            'error' => ['class' => 'yii\web\ErrorAction',],
            'captcha' => [
                'class' => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
            ],
        ];
    }

actions 继承 yiibaseActions 类,并重写父类的 run 方法。

方法二:

site 控制器如下,访问 MemberController 控制器下面的 index 方法。class SiteController extends Controller
{public function actionIndex(){Yii::$app->runAction('member/index', ['param'=>'123']);
    }
}
MemberController 控制器如下:class MemberController extends Controller
{public function actionIndex($param = '456'){echo "second Controller".$param;}
}
访问:http://www.yii.dev/site/index.html 

输出:second Controller123

14、点击下载,如下载安卓 APK 文件。

public function actionDownload(){return \Yii::$app->response->setDownloadHeaders("http://xxx.com/apk/com.trade.activity.3.0.8.apk");
    //return \Yii::$app->response->sendFile("./com.trade.activity.3.0.8.apk");
}

15、YII 模块 IP 白名单设置,增加安全性

$config['modules']['gii'] = [
     'class' => 'yii\gii\Module',
     'allowedIPs' => ['127.0.0.1', '::1','10.10.1.*'], 
];
$config['modules']['debug'] = [
    'class' => 'yii\debug\Module',
    'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.33.1'],
];

16、防止 SQL 和 Script 注入

use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
echo Html::encode($view_hello_str) // 可以原样显示 <script></script> 代码  
echo HtmlPurifier::process($view_hello_str)  // 可以过滤掉 <script></script> 代码 

17、验证某个 ID 值是否存在

// 之前一直用 $model->findOne($id);exists() 方法,资源节约,有没有?!public function validateAttribute($model, $attribute)
{
   $value = $model->$attribute;
   if (!Status::find()->where(['id' => $value])->exists()) {$model->addError($attribute, $this->message);
   }
}

18、批量查询

如查询并循环 10000 条数据。一次性拿 1 万条内存会有压力,通过批量查询,每次拿 1000 条,那么内存始终只有 1000 条的占有量。

foreach(Member::find()->batch(1000) as $value){
    //do something
    //print_r(count($value));
}

19、关于 CSRF 验证

方法一:关闭 Csrf,除非必要,否则不推荐

public function init(){

$this->enableCsrfValidation = false;

}
方法二:普通提交,form 表单中加入隐藏域

<input name=”_csrf” type=”hidden” id=”_csrf” value=”<?= Yii::$app->request->csrfToken ?>”>
方法三:ajax 异步提交,加入_csrf 字段

var csrfToken = $(‘meta[name=”csrf-token”]’).attr(“content”);
$.ajax({
type: ‘POST’,
url: url,
data: {_csrf:csrfToken},
success: success,
dataType: dataType
});
20、YII 命令行生成数据库文件

自动列出可用的 migrate 文件

php yii migrate
从 vendor/callmez/wechat/migrations 目录下生成数据表

php yii migrate –migrationPath=@callmez/wechat/migrations
从当前应用 /migrations/db1 下初始化数据到 db1 表

php yii migrate –migrationPath=@app/migrations/db1 –db=db1
21. 关联查询

// 客户表 Model:CustomerModel
// 订单表 Model:OrdersModel
// 国家表 Model:CountrysModel
// 首先要建立表与表之间的关系
// 在 CustomerModel 中添加与订单的关系

Class CustomerModel extends yiidbActiveRecord
{

...

public function getOrders()
{
    // 客户和订单是一对多的关系所以用 hasMany
    // 此处 OrdersModel 在 CustomerModel 顶部别忘了加对应的命名空间
    //id 对应的是 OrdersModel 的 id 字段,order_id 对应 CustomerModel 的 order_id 字段
    return $this->hasMany(OrdersModel::className(), ['id'=>'order_id']);
}
 
public function getCountry()
{
    // 客户和国家是一对一的关系所以用 hasOne
    return $this->hasOne(CountrysModel::className(), ['id'=>'Country_id']);
}
....

}

// 查询客户与他们的订单和国家
CustomerModel::find()->with(‘orders’, ‘country’)->all();

// 查询客户与他们的订单和订单的发货地址
CustomerModel::find()->with(‘orders.address’)->all();

// 查询客户与他们的国家和状态为 1 的订单
CustomerModel::find()->with([

'orders' => function ($query) {$query->andWhere('status = 1');
    },
    'country',

])->all();
22、yii2 中关闭 debug 后 return $this->redirect($url);不能跳转,服务器报 500 错误。

问题分析:

1. 必须 return 才能让 $this->redirect($url); 立马跳转, 而不执行后续代码;

2.redirect() 中指定了响应的 http status code,默认是 302;

3. 当执行 $this->redirect($url) 时,不管是否在后面加 return false、return true 都没有用,还是继续执行完代码。使用 header(“Location:$url”);exit; 可以解决此问题,但是,这不是 yii2 的逻辑,并不完美。

解决办法:

【本文由 php_sir 的博客 http://blog.sina.com.cn/phpsi…,未经授权禁止转载】

1. 在正常情况下,使用 return $this->redirect($url);

2. 在解决方案 1 不生效时,用 $this->redirect($url);Yii::$app->response->send();

3. 在解决方案 2 不生效时,用 $this->redirect($url);Yii::$app->end();

总结:

用 Yii::$app->end();、Yii::$app->response->send(); 不管在 actionXXX 还是 init 方法都能终止代码,而 return 只能在 action 终止代码,是因为在 init() 里仅仅是代码的执行,return 只是代码返回。

正文完
 0