Yii中巧用
afterSave
使用 ar中的save方法后被调用,常用来做操作日志,或者其他状态变更后的后续处理
方法:afterSave($insert, $changedAttributes)
参数:bool $insert 是否是添加, 添加时为true,修改时为false
array $changedAttributes 变更的字段,保存的时变更前的字段的值,获取当前变更后的值用$this->字段名,获取
下面的例子,就是通过字段的状态的变更,来判断用户进行的操作,添加对应的操作日志。如:
public function afterSave($insert, $changedAttributes)
{
$action = '';
if($insert){
if($this->getStatus() == 1) $action = "添加意向单位/【待跟进】";
if($this->getStatus() == 3) $action = "添加协议单位/【已签约】";
}elseif(
key_exists('agreement_status', $changedAttributes)
&& $changedAttributes['agreement_status'] != $this->agreement_status
&& $changedAttributes['agreement_status'] != @static::$statusEnum[$this->agreement_status]
){
if ($this->getStatus() == 1) $action = "转为后续跟进/【待跟进】";
if ($this->getStatus() == 2) $action = "开始跟进单位/【跟进中】";
if ($this->getStatus() == 3) $action = "签约合同/【已签约】";
if ($this->getStatus() == 5) {
if ($changedAttributes['agreement_status'] == 3 || $changedAttributes['agreement_status'] == '已签约') {
$action = "废弃合同/【已作废】";
} else {
$action = "废弃意向单位/【已作废】";
}
}
}
if($action){
//增加操作记录
/** @var OperationLogService $operationLogService */
$operationLogService = \Yii::createObject([
'class' => OperationLogService::class
]);
$operationLogService->add($this->agreement_id, $action, '无', true);
}
parent::afterSave($insert, $changedAttributes);
}
afterFind
当创建AR对象并用查询结果填充时,将调用此方法。
有时候我们判断数据是否过期,由于定时脚本刚好未执行,导致读取出来的数据未及时更新,此时我们就可以通过此方法实时的判断,根据数据的实际情况,对数据实时进行处理。
下面的例子,就是读取数据的时候,如果数据过期了,则我们此时就把该数据的状态更改成过期状态,同时将读取的数据的模型的对应的字段也同步更改
public function afterFind()
{
parent::afterFind();
//检查合同过期时间
if($this->is_auto_expire
&& date('Y-m-d',strtotime($this->contract_limited_end)) < date('Y-m-d')
&& in_array($this->agreement_status, [3,'已签约'])
){
$this->agreement_status = '已过期';
//1.修改已查询结果集中的返回值
//我们在开始获取查询结果集时,状态还是未过期状态,读取出来的值为未过期的,但是实际是已过期的,我们要将已读取的模型的状态的设置为过期(返回给前端),这样就达到了实时变更字段的值
$this->setOldAttribute('agreement_status', '已过期');
// 2.同时更新数据库中字段的状态
self::updateAll(['agreement_status'=>'已过期'],['agreement_id'=>$this->agreement_id]);
}
}
表单验证,两个参数中至少需要一个
public function rules()
{
return [
[['card_id', 'card_code'],
function ($attribute, $param) {
//两个参数中至少需要一个
if (empty($this->card_code) && empty($this->card_id)) {
$this->addError($attribute, 'card_id/card_code至少要填一个');
}
},
'skipOnEmpty' => false],
];
}
表单验证,去除首尾空格:
public function rules()
{
return [[title', 'content'],'trim']];
}
校验 user_id 在User表中是否存在,并自定义错误信息。
public function rules()
{
return [
...
[['user_id'], 'exist',
'targetClass' => User::className(),
'targetAttribute' => 'id',
'message' => '此{attribute}不存在。'
],
...
];
}
Model 里面 rules 联合唯一规则
[['store_id', 'member_name'], 'unique', 'targetAttribute' => ['store_id', 'member_name'], 'message' => 'The combination of Store ID and Member Name has already been taken.'] !
嵌套查询,groupBy 分组之后排序功能
$subQuery = new Query();
$subQuery->from(PostComment::tableName())->where(['status' => PostComment::STATUS_ACTIVE])->orderBy(['created_at' => SORT_DESC]);
$comment = PostComment::find()->from(['tmpA' => $subQuery])
->groupBy('post_id')
->all();
生成的语句是
SELECT * FROM (SELECT * FROM `post_comment` WHERE `status`=1 ORDER BY `created_at` DESC) `tmpA` GROUP BY `post_id`
总结:这里是用到的子查询。Yii2中,from 子句中除了用表名外,还可以用AR对象作为子查询。
避免子句被识别成字段,或者字符串,而是要被识别成原生的SQL
使用 new Expression()
如:order by null 排序,如果我们YII2中使用子句->orderBy('null'), 这里的 null 就会别识别成字段,而非NULL,此时我们可以用 ->orderBy(new Expression('null')) 即可。
- 加where条件:
$query->andWhere(new \yii\db\Expression('c.type = b.type'));
- 如果要用 find_in_set 需要使用到 Expression 表达式:
User::find()
->where(new Expression('FIND_IN_SET(:status, status)'))
->addParams([':status' => 1])
->all();
LIKE 查询 单边加%
['like', 'name', 'tester'] 会生成 name LIKE '%tester%'。
['like', 'name', '%tester', false] => name LIKE '%tester'
$query = User::find()
->where(['LIKE', 'name', $id.'%', false]);
SQL 随机抽取十名幸运用户
$query = new Query;
$query->select('ID, City,State,StudentName')
->from('student')
->where(['IsActive' => 1])
->andWhere(['not', ['State' => null]])
->orderBy(['rand()' => SORT_DESC])
->limit(10);
where条件中两字段相加或相减
$query->andWhere(['<', '`updated_at` + `duration`', time()])->all();
搜索的时候添加条件筛选
$dataProvider = $searchModel->search(Yii::$app->request->queryParams); $dataProvider->query->andWhere(['pid' => 0]);
yii2 给mysql数据库表添加字段后,立即使用这个字段时会出现未定义的情况(Getting unknown property)
原因:yii 对数据表结构进行了缓存。
方法1. 清理掉runtime下的cache缓存之后也可以正常使用这个字段。
方法2. 修改完表字段后执行
# 清理指定表结构缓存数据 Yii::$app->db->getSchema()->refreshTableSchema($tableName); 或 # 清理所有表结构缓存数据 Yii::$app->db->getSchema()->refresh();
建议将以上代码添加到修改数据表结构的migration中。
字段去重的三种方法
static::find()
->where([
'user_id' => $user_id,
])
->groupBy('uuid')
->all();
--------------------------------
static::find()
->select(['uuid'])
->where([
'user_id' => $user_id,
])
->distinct()
->count();
-----------------------------------
static::find()->where([
'user_id' => $user_id,
])->count('distinct uuid');
事务
Yii::$app->db->transaction(function() {
$order = new Order($customer);
$order->save();
$order->addItems($items);
});
// 这相当于下列冗长的代码:
$transaction = Yii::$app->db->beginTransaction();
try {
$order = new Order($customer);
$order->save();
$order->addItems($items);
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollBack();
throw $e;
}
执行SQL查询并缓存结果
$styleId = Yii::$app->request->get('style');
$collection = Yii::$app->db->cache(function($db) use($styleId){
return Collection::findOne(['style_id'=>$styleId]);
}, self::SECONDS_IN_MINITUE * 10);
批量插入数据
第一种方法
$model = new User();
foreach($data as $attributes)
{
$_model = clone $model;
$_model->setAttributes($attributes);
$_model->save();
}
第二种方法
$model = new User();
foreach($data as $attributes)
{
$model->isNewRecord = true;
$model->setAttributes($attributes);
$model->save() && $model->id = 0;
}
URL
URL地址:在WWW上,每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL(Uniform Resource Locator,统一资源定位符),它是WWW的统一资源定位标志,就是指网络地址。
URL由三部分组成:资源类型、存放资源的主机域名、资源文件名。格式:protocol(协议) :// hostname(主机地址,可以是域名)[:port](协议端口) / path(路径:目录+文件) / [?query](查询字符串或者说是参数)#fragment(锚点)
假设我们当前页面的访问地址是:http://localhost/public/index.php?r=news&id=1
- 获取url中的host信息:http://localhost
Yii::$app->request->getHostInfo()
- 获取url中的路径信息(不包含host和参数):
Yii::$app->request->getPathInfo()
- 获取不包含host信息的url(含参数): /public/index.php?r=news&id=1
Yii::$app->request->url 或者 Yii::$app->request->requestUri
- 获取完整url(含host以及参数):
Yii::$app->request->getHostInfo() . Yii::app()->request->url
- 只想获取url中的参数部分: r=news&id=1
Yii::$app->getRequest()->queryString
- 获取某个参数的值,比如id
Yii::$app->getRequest()->getQuery('id'); //get parameter 'id'
- 获取(除域名外的)首页地址 /public/index.php
Yii::$app->user->returnUrl;
- 获取Referer
Yii::$app->request->headers['Referer'] 或者 Yii::$app->getRequest()->getReferrer()
写 log 日志
use yii\log\Logger;
\Yii::getLogger()->log('User has been created', Logger::LEVEL_INFO);
Yii2 获取接口传过来的 JSON 数据
\Yii::$app->request->rawBody;
防止 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>代码
restful 获取 GET 和 POST 过来的数据(得到结果是数组)
// post Yii::$app->request->bodyParams // get Yii::$app->request->queryParams;
Yii2 生成url的两种方式实例
Html::a("链接1", \yii\helpers\Url::toRoute(['product/view', 'id' => 42]);
Html::a("链接2", Yii::$app->urlManager->createUrl(['mysql/chart', 'id' => 43,'time_interval' => '1800', 'end'=>'0']));
一个控制器调用其他控制器action的方法
Yii::$app->runAction('new_controller/view', $params);
// 或者
return (new SecondController('second', Yii::$app->module))->runAction('index', $data);
注意:runAction函数的第二个参数是绑定的是控制器的actionParams参数,可以通过 $this->actionParams (在控制器中时),或 Yii::$app->controller->actionParams 获取 。runAction 第二个参数如果传的时数组,那么第一个参数对应的控制器方法的参数一定要是数组,如
function actionView(array $params){//接受参数是数组类型}
点击下载文件 action
public function actionDownload($id)
{
$model = $this->findModel($id);
if ($model) {
// do something
}
return \Yii::$app->response->setDownloadHeaders($model->downurl);
}
发送邮件
注意,使用邮件发送前发送邮件的邮箱必须开启 POP3/SMTP/IMAP 服务,请在邮箱账号设置中自行开启
- config/config.php中的components配置
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
//'viewPath' => '@common/mail',
// 默认把所有邮件发送到一个文件里,若需要发送真邮件,你需要把userFileTransport设置为false,并且添加邮件的配置
'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => 'admin@gmail.com',
'password' => 'password12345678',
'port' => 587,//or 25/587
'encryption' => 'tls',//tls or ssl
],
'messageConfig'=>[
'charset' => 'UTF-8',
'from'=>[ 'xxx@126.com' => '发件人名称']
],
],
- 发送
$mail= Yii::$app->mailer->compose()
->setFrom(['admin@gmail.com' => Yii::$app->name])
->setTo('<target_email@qq.com>')
->setSubject('邮件标题')
->setHtmlBody('邮件内容'); //->setTextBody('test body') 文本发送
if($mail->send()) {
echo '发送成功';
} else {
echo '发送失败';
}
修改登陆状态超时时间(到期后自动退出登陆) config/web.php中的components
user’ => [ ‘class’=>’yii\web\User’, ‘identityClass’ => ‘common\models\User’, ‘loginUrl’=>[‘/user/sign-in/login’], ‘authTimeout’ => 1800,//登陆有效时间 ‘as afterLogin’ => ‘common\behaviors\LoginTimestampBehavior’ ],
修改返回的数据格式(详见Response::FORMAT_XXXX)
$result = array('code' => $code, 'msg' => $msg, 'data' => $data);
$callback = Yii::$app->request->get('callback',null);
$format = $callback ? Response::FORMAT_JSONP : Response::FORMAT_JSON;
Yii::$app->response->format = $format;
if($callback){
return array(
'callback' => $callback,
'data' => $result
);
}
return $result;
# 获取字段列
modelAR::getTableSchema()->getColumn("字段名")
如:获取枚举值, static::getTableSchema()->getColumn("system_type")->enumValues;
浙公网安备 33010602011771号