Yii2.0 小技巧

返回一维数组配合验证规则验证数据正确性,如分类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')],
// 这样就可以过滤不正确的分类数据了!

友好时间表示方法

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

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

座机和手机号码必须填写一个:

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

关联查询


//客户表Model:CustomerModel 
//订单表Model:OrdersModel
//国家表Model:CountrysModel
//首先要建立表与表之间的关系 
//在CustomerModel中添加与订单的关系
      
Class CustomerModel extends \yii\db\ActiveRecord
{
    ...
    
    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()

批量查询

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


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

#验证某个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);
   }
}

点击下载,如下载安卓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");
}

关于事务:

// 优雅的写法

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;
}

对于表单提交过来的数据不是最终保存到数据库里的格式时,如时间戳等,

可以通过自定义rules或者重组表单数据来实现:(还有其他方法也可以实现)

backend/modules/test/models/Test.php

    public function validateCountry($attribute, $params)
    {
        $this->$attribute = 'new '.$this->$attribute;//这里可以重新设置name的值
        //也可以使用自定义验证规则
        //if (!in_array($this->$attribute, ['USA', 'Web'])) {
            //$this->addError($attribute, 'The country must be either "USA" or "Web".');
        //}
    }
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            ['name', 'validateCountry'],
            [['name'], 'required'],
            [['percent', 'listorder', 'datetime', 'status', 'created_by', 'updated_by'], 'integer'],
            [['name'], 'string', 'max' => 300],
            [['remark', 'hearttrait', 'common', 'nacs', 'attack'], 'string', 'max' => 500]
        ];
    }

用behaviors来实现一些字段的数据的自动化填充

backend/modules/test/models/Test.php

    public function behaviors()
    {
        return [
            [
                'class' => 'yii\behaviors\BlameableBehavior',
                'createdByAttribute' => 'created_by',//create时,created_by字段的值会自动填充为当前操作用户的ID:Yii::$app->user->identity->id;
                'updatedByAttribute' => 'updated_by',
            ],
            'timestamp' => [
                'class' => 'yii\behaviors\TimestampBehavior',
                'attributes' => [
                    //insert数据库前datetime的值会自动填充为当前的时间戳
                    BaseActiveRecord::EVENT_BEFORE_INSERT => ['datetime'],
                    //BaseActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
                ],
            ],
        ];
    } 

#对于字段值重写,只需定义好Model里的attributeFormats()方法来实现字段格式化输出。(非Yii2方法) 如下:

访问index方法时,datetime字段会被格式为”Y-m-d H:i:s”格式,

访问xls方法时,datetime字段会被格式为”Y年m月d日”格式,

匿名函数中的$value表示字段原始值,$data表示select所列出的所有字段值

backend/modules/test/models/Test.php

    /**
     * 字段格式化
    
     */
    public function attributeFormats()
    {
         return [
            'datetime'=>[//字段名
                [
                    'action'=> ['index'],
                    'data'=> function($value, $data){
                        return date("Y-m-d H:i:s", $value); 
                    }
                ],
                [
                    'action'=> ['xls'],
                    'data'=> function($value, $data){
                        return date("Y年m月d日", $value); 
                    }
                ],
            ]
        ];
    }

针对特定 action关闭CSRF验证

public function beforeAction($action)
{            
    if ($action->id == 'yiilib-method') {
        $this->enableCsrfValidation = false;
    }

    return parent::beforeAction($action);
}

其他

//当前域名 
echo Yii::app()->request->hostInfo;
//当前URL
echo Yii::app()->request->getUrl();
//返回首页 
echo Yii::app()->user->returnUrl;
 
//提交后 防止重复提交
Ccontroler->refresh();
 
//上一页url  很有用哦
Yii::app()->request->urlReferrer;
 
//获取ip地址 
Yii::app()->request->userHostAddress; 

//yii execute后获取insert id 
$id = Yii::app()->db->getLastInsertID(); 

//yii获取get,post过来的数据 
Yii::app()->request->getParam('id');

//Yii->user(当前用户)相关  常用方法是
class Controller extends CController 
{ 
    public $user = null; 
  $this->user = Yii:app()->user; 
} 
  this->user->isGuest; 
  this->user->id; 
  this->user->name;

徽章气泡

视图中:

<li><span class="badge">2(值)</span></li>

URL

假设我们当前页面的访问地址是: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()

表单验证,两个参数中至少需要一个:

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.'],

排序 有标记的置顶 在***search类里

思路: 先归类同一天 其次有标记的置顶 最后 按时间戳排序

表单提交失败获取save()错误信息调试代码

echo array_values($model->getFirstErrors())[0];exit;
var_dump($model->getErrors());die;

Behavior

// 新建一个Behavior

use Yii;
use yii\base\Behavior;
use yii\web\Controller;

class NoCsrf extends Behavior
{
    public $actions = [];
    public $controller;
    public function events()
    {
        return [Controller::EVENT_BEFORE_ACTION => 'beforeAction'];
    }
    public function beforeAction($event)
    {
        $action = $event->action->id;
        if(in_array($action, $this->actions)){
            $this->controller->enableCsrfValidation = false;
        }
    }
}

// 然后在Controller中添加Behavior

public function behaviors()
{
    return [
        'csrf' => [
            'class' => NoCsrf::className(),
            'controller' => $this,
            'actions' => [
                'action-name'
            ]
        ]
    ];
}

感谢一个网友提供的思路

$dataProvider = new ActiveDataProvider([
    'query' => $query,
    'pagination'=>[
        'pagesize'=>40,
    ],
    'sort'=>[
        'defaultOrder'=>[
            'hot' => SORT_DESC,
            'match_start_time' => SORT_DESC,
        ],
        'attributes' => [
            'match_start_time' => [
                'desc' => ['FROM_UNIXTIME(`match_start_time`,"%Y%m%d")' => SORT_DESC, 'hot' => SORT_DESC, 'match_start_time' => SORT_DESC],
                'asc'  => ['FROM_UNIXTIME(`match_start_time`,"%Y%m%d")' => SORT_ASC, 'hot' => SORT_DESC, 'match_start_time' => SORT_ASC]
            ],
            'hot' => [
                'desc' => ['FROM_UNIXTIME(`match_start_time`,"%Y%m%d")' => SORT_DESC, 'hot' => SORT_DESC, 'match_start_time' => SORT_DESC],
                'asc'  => ['FROM_UNIXTIME(`match_start_time`,"%Y%m%d")' => SORT_ASC, 'hot' => SORT_DESC, 'match_start_time' => SORT_ASC]
            ],
        ],

    ]
]);

返回上一页

return $this->redirect(Yii::$app->request->referrer);

新增/修改 自动更改时间(重写beforeSave()方法)

public function beforeSave($insert)
{
    if(parent::beforeSave($insert))
    {
        if($insert)
        {
            $this->created_at = time();
            $this->updated_at = time();
        }
        else
        {
            $this->updated_at = time();
        }
        return true;
    }
    else
    {
        return false;
    }

}

查看版本:

cd到项目目录 终端输入 ./yii

修改默认控制器:

1、在/vendor/yiisoft/yii2/web/Application.php

    // 不建议修改源码
    public $defaultRoute = 'site';

2、可以在 frontend/config/mian.php return[]下添加一行

    // 推荐方式
    'defaultRoute' =>'index', 这样默认控制器就改好了

用户密码加密:

//加密
$hash = Yii::$app->getSecurity()->generatePasswordHash('123456']);

//验证
Yii::$app->getSecurity()->validatePassword('123456', $hash);//返回true或false

输出sql语句:

$query = Order::find()->where(['order_id'=>[1,2]])->select(['id']);
$commandQuery = clone $query;
echo $commandQuery->createCommand()->getRawSql();
exit;

监测性能:

\YII::beginProfile('profile1');
echo "hello world~";
sleep(1);
\YII::endProfile('profile1');

然后到Debug中的Profiling查看

Yii2的Alert组件(弹窗提示信息)

该组件提供

五种类型提示:

error,danger,success,info,warning

总的样式有四种:

alert-danger,alert-success,alert-info,alert-warning

// 单条消息:
\Yii::$app->getSession()->setFlash('error', 'This is the message');

\Yii::$app->getSession()->setFlash('danger', 'This is the message');

\Yii::$app->getSession()->setFlash('success', 'This is the message');

\Yii::$app->getSession()->setFlash('info', 'This is the message');

\Yii::$app->getSession()->setFlash('warning', 'This is the message');

// 多条消息:
\Yii::$app->getSession()->setFlash('error', ['Error 1', 'Error 2']);

eg:

控制层:

public function actionDelete($id)
{
    $model = new Attributelist();
    $child = $model->find()->where(['pid'=>$id])->asArray()->all();

    // ---------------若有子类 则不能删除-----------------------
    if (empty($child)){
        $this->findModel($id)->delete();
    }
    else{
        \Yii::$app->getSession()->setFlash('error', '该删除项有子类,不能删除!');
    }
    return $this->redirect(['index']);
}

视图层:

先引入 Alert:use yii\bootstrap\Alert;

<div>
	<?php
	    if( Yii::$app->getSession()->hasFlash('error') ) {
	    echo Alert::widget([
	        'options' => [
	            'class' => 'alert-danger',
	        ],
	        'body' => Yii::$app->getSession()->getFlash('error'),
	    ]);
	}
	?>
</div>

若出现两个提示框,去掉widget后面的参数

中国话/Yii::t()使用方法

1.在gii中 创建 Model Generator 的时候,记得 打勾 Enable I18N(省得后面自己写)

2.因为是 前后台共用 的翻译语言,so 在common目录下创建messages/zh_CN/app.php文件

完整目录是 common/messages/zh_CN/app.php

app.php文件内容:

/**
 * app.php
 * 翻译文件
 */
return [
    'ID' => 'id',
    'Name' => '名字',
    'Create' => '创建',
    'Delete' => '删除',
    // ...
];

修改common/config/main.php

components中添加以下

'language' => 'zh-CN',
'i18n' => [
    'translations' => [
        'app*' => [
            'class' => 'yii\i18n\PhpMessageSource',
            'basePath' => '@common/messages/',
            // 'sourceLanguage' => 'en',
            'fileMap' => [
                'app' => 'app.php',
                'app/error' => 'error.php',
            ],
        ],
        'model*' => [
            'class' => 'yii\i18n\PhpMessageSource',
            'basePath' => '@common/messages',
            'fileMap' => [
                'model' => 'model.php'
            ]
        ]
    ],
],

使用:

public function attributeLabels()
{
    return [
        'id' => 'ID',
        'name' => Yii::t('app','Name'),
        'mobile' => Yii::t('app','Mobile'),
        // ...
    ];
}

参考更多: -http://www.yiifans.com/yii2/guide/tutorial-i18n.html

系统定义的路径

'@yii' =>
      '@yii/swiftmailer' => string 'C:\wamp\www\advanced\vendor/yiisoft/yii2-swiftmailer' (length=52)
      '@yii/gii' => string 'C:\wamp\www\advanced\vendor/yiisoft/yii2-gii' (length=44)
      '@yii/faker' => string 'C:\wamp\www\advanced\vendor/yiisoft/yii2-faker' (length=46)
      '@yii/debug' => string 'C:\wamp\www\advanced\vendor/yiisoft/yii2-debug' (length=46)
      '@yii/codeception' => string 'C:\wamp\www\advanced\vendor/yiisoft/yii2-codeception' (length=52)
      '@yii/bootstrap' => string 'C:\wamp\www\advanced\vendor/yiisoft/yii2-bootstrap' (length=50)
      '@yii' => string 'C:\wamp\www\advanced\vendor\yiisoft\yii2' (length=40)
  '@app' => string 'C:\wamp\www\advanced\frontend' (length=29)
  '@vendor' => string 'C:\wamp\www\advanced/vendor' (length=27)
  '@bower' => string 'C:\wamp\www\advanced/vendor\bower' (length=33)
  '@npm' => string 'C:\wamp\www\advanced/vendor\npm' (length=31)
  '@runtime' => string 'C:\wamp\www\advanced\frontend\runtime' (length=37)
  '@webroot' => string 'C:/wamp/www/advanced/frontend/web' (length=33)
  '@web' => string '/advanced/frontend/web' (length=22)

其中最常用的别名有 @app

@vendor

@runtime

@webroot

@web

而且可以看到除了@web别名是url外,其它的都是物理路径

过滤器

public function behaviors()
{
    return [
        'access' => [
            'class' => \yii\filters\AccessControl::className(),// 访问行为
            'only'  => ['*'],// 对所在的控制器的哪些方法做验证
            // 'except'=> [], // 除了哪一个方法
            'rules' => [
                [
                    'allow'  => false,
                    'actions'=> [],// 方法名 针对哪些方法不允许访问
                    'roles'  => ['?'],// ?代表 未登陆的 用户
                ],
                [
                    'allow'  => true,
                    'actions'=> [],// 方法名 针对哪些方法允许访问
                    'roles'  => ['@'],// @代表 登陆的 用户
                ],
            ],
        ],
    ];
}

新增一个按钮

[
    'class' => 'yii\grid\ActionColumn',
    'header' => '操作',
    'options' => ['width' => '100px;'],
    'template' => '{view} {update} {area}',
    'buttons' => [
        'area' => function ($url, $model) {
            return Html::a('<span class="glyphicon glyphicon-list"></span>', $url, [
                'title' => Yii::t('app', 'Area'),
            ]);
        }
    ],
    'urlCreator' => function ($action, $model, $key, $index) {
        if ($action === 'view') {
            return ['view', 'id' => $model->id];
        } else if ($action === 'update') {
            return ['update', 'id' => $model->id];
        } else if ($action === 'area') {
            return ['area/index', 'group_id' => $model->id];
        }
    }
]

根据状态动态显示按钮

[
    'class' => 'yii\grid\ActionColumn',
    'header' => '操作',
    'template' => '{export} {use}',
    'buttons' => [
        'export' => function ($url, $model) {
            if($model->status==0){
                return Html::a('导出 Excel', $url, [
                    'class' => 'btn btn-success',
                ]);
            }
            return null;
        },
        'use' => function ($url, $model) {
            if($model->status==2){
                return Html::a('投入使用', $url, [
                    'class' => 'btn btn-info',
                    'data' => [
                        'confirm' => "确认要投入使用吗?",
                        'method' => 'post',
                    ],
                ]);
            }
            return null;
        },
    ],
],

复选框列

要添加复选框列到[[yii\grid\GridView]],如下添加它到[[yii\grid\GridView::$columns|columns]]配置:

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        // ...
        [
            'class' => 'yii\grid\CheckboxColumn',
            // 在此配置其他属性
        ],
    ],

被选中的行可调用以下 JavaScript 代码获取:

var keys = $('#grid').yiiGridView('getSelectedRows');

// keys 是键名关联到选中行的数组

建立权限数据库

配置common/config/main.php 加入以下代码.

        'authManager' => [
            // 'class' => 'yii\rbac\PhpManager', // 使用文件存储
            'class' => 'yii\rbac\DbManager',
            'itemTable' => '',// 指定表前缀
            'itemChildTable' => '',// 指定表前缀
            'assignmentTable' => '',// 指定表前缀
            'ruleTable' => '',// 指定表前缀
        ],

cd到项目的跟目录,执行以下代码.

./yii migrate --migrationPath=@yii/rbac/migrations/

显示关联字段

举个例子,我有个文章表Article,表里有个作者id author_id字段 这个字段跟作者表Author的主键id相关联

给Article模型添加如下方法

public function getAuthor() { return $this->hasOne(Author::className(), [‘id’=>’author_id’]); } 给columns数组添加一个成员author.name

添加 第一页和最后一页

<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    // 添加pager
    'pager' => [
        'firstPageLabel' => '第一页',
        'lastPageLabel' => '最后一页',
    ],
    // ...

规定视图 每页显示多少条数据

public function search($params)
{
    $query = BlogForm::find();

    // add conditions that should always apply here

    $dataProvider = new ActiveDataProvider([
        'query' => $query,
        'pagination' => ['pageSize'=>3],// 每页显示多少条数据
        'sort'=>[
            'defaultOrder'=>['created_at'=>SORT_DESC],// 默认排序
        ],
    ]);



    //...
}

防止 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>代码

打印数据:

// 引用命名空间
use yii\helpers\VarDumper;

// 使用
VarDumper::dump($var);

//  使用2  第二个参数是数组的深度  第三个参数是是否显示代码高亮(默认不显示)
VarDumper::dump($var, 10 ,true);die;

表单验证,只要需要一个参数:

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],
    ];
}

校验 point_template_id 在 PointTemplate 是否存在

public function rules()
{
    return [
        ...
        [['point_template_id'], 'exist',
            'targetClass' => PointTemplate::className(),
            'targetAttribute' => 'id',
            'message' => '此{attribute}不存在。'
        ],
        ...
    ];
}

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

Yii::$app->runAction('new_controller/new_action', $params);
// 或者
return (new SecondController('second', Yii::$app->module))->runAction('index', $data);

模态框表单

https://segmentfault.com/a/1190000005172345

添加按钮:

echo Html::a('添加', ['create'], ['class' => 'btn btn-success','data-toggle'=>'modal','data-target'=>'#ajax']);

在index视图添加如下代码 来显示模态弹出框:

<div class="modal fade" id="ajax" role="basic" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-body">

            </div>
        </div>
    </div>
</div>

修改控制器方法

return $this->renderAjax('create', [
    'model' => $model, // 验证规则
]);

框架自带打印数据(可高亮)

\yii\helpers\VarDumper::dump($data);

// 第二个参数是数组的深度  第三个参数是是否显示代码高亮(默认不显示)
\yii\helpers\VarDumper::dump($data, 10 ,true);die;

防止SQL注入或者XSS攻击

echo yii\helpers\Html::encode($view_hello_str) // 可以原样显示<script></script>代码,但不解析

echo yii\helpers\HtmlPurifier::process($view_hello_str)  // 可以过滤掉<script></script>代码

使用mPDF 生成中文pdf文

//open /mPDF/config.php
$this->autoLangToFont = true; // mPDF 6.0 (similar to old useLang)
require dirname ( dirname ( __DIR__ ) ).'/vendor/mpdf/vendor/autoload.php';

$mpdf = new \mPDF(
        'utf-8',    // mode - default ''
        'A4-L',  // format - A4, for example, default ''
         0,     // font size - default 0
         '',    // default font family
         10,    // margin_left
         10,    // margin right
         10,     // margin top
         0,    // margin bottom
         9,     // margin header
         9     // margin footer
        );
$mpdf->autoScriptToLang = true;

// add html to pdf
$mpdf->WriteHTML("大家好这里是YiiLib.com");

// Output the generated PDF to Browser
$mpdf->Output();

//save pdf as yiilib.pdf
$mpdf->Output('/var/tmp/yiilib.pdf');

未完,待续~~




导航