博客
通过一个例子简单说一下Yii2 模型中的场景(scenario)应用,现在在 文章表里面有 title image content 三个的字段,当我创建一个文章 的时候,我想三个字段全部是必填项,但是你修改的时候,title content 两个字段是必填的, iamge 可以不填写。正常的情况下, [['title', 'content', 'image'], 'required',], 但是我们更改的时候 只需要 [['title', 'content'], 'required'], 就可以了,但是少了 image 字段,我们的表单就无法提交,这种问题怎么办啊?? 场景可以帮你解决这种问题,下面是一个简单的场景实例。 1.首先我们在 model 里面定义一下场景 类名必须是 scenarios() public function scenarios() { return [ 'create' => ['title', 'image', 'content'], 'update' => ['title', 'content'], ]; } 2.好的,如上所示,场景的基本设置我们就已经完成一部分了,下面我们设置 rules() ,调用场景我们用 on 关键字 [['title', 'content'], 'required', 'on' => ['create', 'update']], [['image'], 'required', 'on' => 'create'], [ ['image'], 'image', 'enableClientValidation' => true, 'maxSize' => 1024, 'message' => '您上传的文件过大', 'on' => ['create', 'update'] ], on 指定的就是场景,一个场景用字符串,多个场景用数组 3.好的,model 里面我们就设置完毕了 现在开始调用吧 Controller里面 $model = $this->findModel($id); $model->setScenario('update'); //或者 $model->scenario = 'update'; 都可以 上面的意思就是 调用 update 场景。
1天前 喜欢(0) 浏览(19) 评论(0)
博客
在处理一个文章内容时,需要在指定标签后面插入 指定内容 1.查找到指定位置(例如 我要在第三个标签<\p>后插入广告代码): /** * 选择到标签指定位置后 字节长度 * $find 标签名字 * $number 第几个标签位置 * $body 被查找的内容 * @param unknown $number * @param unknown $find * @param unknown $body * @return number * @author kangjy */ public function position($number,$find,$body) { //获取标签长度 $findLen = strlen($find); //初始化位置 $position =0; for ($number=1; $number<=3; $number++) { $position = stripos($body, $find, $position); //var_dump('第'.$number.'个 位置'.$position); $position += $findLen; // var_dump('第'.$number.'个 位置加寻找词的长度'); } return $position ; } 2.插入指定字符串代码 /** * 指定位置插入字符串 * @param $str 原字符串 * @param $i 插入位置 * @param $substr 插入字符串 * @return string 处理后的字符串 * @author kangjy */ public function insertToStr($str, $i,$substr){ //指定插入位置前的字符串 $startstr=""; for($j=0; $j<$i; $j++){ $startstr .= $str[$j]; } //指定插入位置后的字符串 $laststr=""; for ($j=$i; $j<strlen($str); $j++){ $laststr .= $str[$j]; } //将插入位置前,要插入的,插入位置后三个字符串拼接起来 $str = $startstr . $substr . $laststr; //返回结果 return $str; } 这样就可以了
3天前 喜欢(0) 浏览(24) 评论(0)
博客
yii2是一个快速开发的框架,其中gii扩展不得不说是一个很大的助力,通过gii自动生成代码,把一些通用的代码交给程序去生成,很大程度上减少开发者的时间成本。但gii也有一些弊端,那就是生成的代码是yii2自带的模板,每次生成的代码并不是我们想要的,因而每次都要去对应的做调整。 那么如何才能让程序生成我们想要的代码呢?没错,yii2的gii扩展是支持自定义模板的,通过自定义模板我们就可以让gii生成我们想要的代码,又一次节约了去修改生成模板的时间成本。 配置 gii生成器所用到的模板文件位于目录 vendor\yiisoft\yii2-gii\generators\crud\default,我们既然要自定义模板,最好是在原有的模板基础下做调整。 1.复制一份模板,拷贝default目录,放在任意位置,此处我们放置在根目录 /backend/giitpl/crud 中。 2.修改模板(本文为教程不做细说,此处自行修改即可) 3.打开配置文件 /backend/config/main-local.php 修改$config['modules']['gii']的配置(如下): $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', 'allowedIPs' => ['127.0.0.1', '::1'], 'generators' => [ 'crud' => [ //生成器名称 'class' => 'yii\gii\generators\crud\Generator', 'templates' => [ //设置我们自己的模板 //模板名 => 模板路径 'myCrud' => '@backend/giitpl/crud/default', ] ] ], ]; 4.通过gii生成代码,打开gii界面,使用crud generator生成代码(注:此处配置要修改code template模板) 选择我们自定义的模板,然后点击生成,那么gii自定义的模板代码就生成好了。 总结 通过自定义gii模板,我们能够更加灵活快速的完成需要的功能,把许多相似度高,应用场景多,功能类似(比如:列表页,详情页等)的代码做成gii的模板,通过程序去生成。还是那句话yii2就是一个快速开发的框架。
8天前 喜欢(0) 浏览(57) 评论(0)
博客
Gii 这个扩展无疑是 yii2 快速开发的一大助力,通过使用gii生成代码很大程序上节约了开发的时间成本,那么如何使用gii这个组件呢?本文为你们简单介绍一下yii2中gii的一些常用功能。 生成模型(Model) 在数据库新建一张test的测试表 CREATE TABLE `test` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) DEFAULT NULL COMMENT '名称', `desc` varchar(255) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.打开gii的界面 /index.php?r=gii或者 /gii(url美化之后) 2.创建Model 首先,点击Model generator下面的start按钮,进入model生成界面,输入数据表名test,输入model class(一般会自动生成一个,也可以自定义model class名称) 然后,点击下方的preview,会列出要生成的模型 最后,点击generate生成model文件,如下提示测生成成功 生成CRUD 同样,点击CRUD Generator,去创建CRUD也就是增删改查的操作界面及功能,输入对应的参数(注:view path 不填,即生成在默认的位置) 然后,点击preview,查看要生成的文件列表(注:如果已经存在对应的控制会显示下方diff,可以选择覆盖或者保留) 最后点击Generate生成相应的CRUD文件,然后就可以查看对应的页面了,如此简单就完成了一个数据表的增删改成
11天前 喜欢(0) 浏览(94) 评论(0)
Wiki
1.视图页面直接加载 $css = <<<CSS p {text-indent:5em;} div{width: 500px;} CSS; $this->registerCss($css,['type'=>'text/css'],'test'); $js = <<<JS // console.log('sss'); JS; $this->registerJs($js); 视图页面直接加载css,js文件 $this->registerJsFile('@web/assets/js/site.js'); $this->registerCssFile('@web/assets/css/site.css');
12天前 喜欢(0) 浏览(92) 评论(0)
Wiki
yii2 Carousel是一个基于JavaScript的图片轮播组件,使用Carousel可以快速的在网站任意位置放置一个图片轮播的效果 先看下效果: 代码如下: <?php echo Carousel::widget([ 'items' => [ // 只有图片的格式 '<img src="http://www.yii-china.com/statics/images/b_1.jpg"/>', // 与上面的效果一致 ['content' => '<img src="http://www.yii-china.com/statics/images/b_1.jpg"/>'], // 包含图片和字幕的格式 [ 'content' => '<img src="http://www.yii-china.com/statics/images/b_1.jpg"/>', 'caption' => '<h4>This is title</h4><p>This is the caption text</p>', //'options' => [...], //配置对应的样式 ], ] ]); ?> 在轮播幻灯片列表。每个数组元素的可配属性 [ // 必要的,轮播的内容(HTML),比如一个图像标签 'content' => '<img src="http://www.yii-china.com/statics/images/b_1.jpg'/> // 可选的,该轮播标题(HTML) 'caption' => '<h4>This is title</h4><p>This is the caption text</p>', // 可选的,轮播样式 'options' => [], ]
15天前 喜欢(0) 浏览(122) 评论(0)
Wiki
以高级版为例:打开配置文件 /frontend/config/main.php 添加以下配置: return [ ··· 'defaultRoute' => 'topic/index', ··· ]
17天前 喜欢(0) 浏览(112) 评论(0)
Wiki
事务在程序应用中使用非常广泛,现在提供一个 Yii2 数据库事务使用的简单示例,让大家了解在 Yii2 中如何使用数据库事务 $transaction = Yii::$app->db->beginTransaction(); try { $connection->createCommand($sql1)->execute(); //如果执行失败则抛出错误 if(status == false) throw new \Exception('这里是错误原因'); $connection->createCommand($sql2)->execute(); //只有执行了commit(),对于上面数据库的操作才会真正执行 $transaction->commit(); }catch (Exception $e) { //获取抛出的错误 $error = $e->getMessage(); //操作回滚 $transaction->rollBack(); }
22天前 喜欢(0) 浏览(142) 评论(0)
博客
package main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" "time" ) func main() { var ( config clientv3.Config client *clientv3.Client lease clientv3.Lease leaseResp *clientv3.LeaseGrantResponse leaseId clientv3.LeaseID leaseRespChan <-chan *clientv3.LeaseKeepAliveResponse err error ) //客户端配置 config = clientv3.Config{ Endpoints: []string{"127.0.0.1:2379"}, DialTimeout: 5 * time.Second, } //建立连接 if client, err = clientv3.New(config); err != nil { fmt.Println(err) return } //上锁(创建租约,自动续租) lease = clientv3.NewLease(client) //设置一个ctx取消自动续租 ctx,cancleFunc := context.WithCancel(context.TODO()) //设置10秒租约(过期时间) if leaseResp,err = lease.Grant(context.TODO(),10);err != nil { fmt.Println(err) return } //拿到租约id leaseId = leaseResp.ID //自动续租(不停地往管道中扔租约信息) if leaseRespChan,err =lease.KeepAlive(ctx,leaseId);err != nil { fmt.Println(err) } //启动一个协程去监听 go listenLeaseChan(leaseRespChan) //业务处理 kv := clientv3.NewKV(client) //创建事务 txn := kv.Txn(context.TODO()) txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job9"),"=",0)). Then(clientv3.OpPut("/cron/lock/job9","xxx",clientv3.WithLease(leaseId))). Else(clientv3.OpGet("/cron/lock/job9"))//否则抢锁失败 //提交事务 if txtResp,err :=txn.Commit();err != nil { fmt.Println(err) return } else { //判断是否抢锁 if !txtResp.Succeeded { fmt.Println("锁被占用:",string(txtResp.Responses[0].GetResponseRange().Kvs[0].Value)) return } } fmt.Println("处理任务") //释放锁(停止续租,终止租约) defer cancleFunc()//函数退出取消自动续租 defer lease.Revoke(context.TODO(),leaseId) //终止租约(去掉过期时间) time.Sleep(10 * time.Second) } func listenLeaseChan(leaseRespChan <-chan *clientv3.LeaseKeepAliveResponse) { var ( leaseKeepResp *clientv3.LeaseKeepAliveResponse ) for { select { case leaseKeepResp = <-leaseRespChan: if leaseKeepResp == nil { fmt.Println("租约失效了") goto END } else { fmt.Println(leaseKeepResp.ID) } } } END: }
23天前 喜欢(0) 浏览(134) 评论(0)
博客
少比比直接代码(你可以理解为给key设置过期时间,但是比redis要强大的是它可以自动续租) package main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/mvcc/mvccpb" "time" ) func main() { var ( config clientv3.Config client *clientv3.Client err error kv clientv3.KV keepResp *clientv3.LeaseKeepAliveResponse keepRespChan <-chan *clientv3.LeaseKeepAliveResponse ) //创建租约 lease := clientv3.NewLease(client) //设置10秒租约(过期时间为10秒) if leaseRes,err := lease.Grant(context.TODO(),10);err != nil { fmt.Println(err) return } else { //得到租约id leaseId := leaseRes.ID //定义一个上下文使得租约5秒过期 ctx,_:= context.WithTimeout(context.TODO(),5*time.Second) //自动续租(底层会每次讲租约信息扔到 <-chan *clientv3.LeaseKeepAliveResponse 这个管道中) if keepRespChan,err = lease.KeepAlive(ctx,leaseId);err != nil { fmt.Println(err) return } //启动一个新的协程来select这个管道 go func() { for { select { case keepResp = <- keepRespChan: if keepResp == nil { fmt.Println("租约失效了") goto END//失效跳出循环 } else { //每秒收到一次应答 fmt.Println("收到租约应答",keepResp.ID) } } } END: }() //得到操作键值对的kv kv = clientv3.NewKV(client) //进行写操作 if putResp,err = kv.Put(context.TODO(),"/cron/lock/job1","",clientv3.WithLease(leaseId)/*高速etcd这个key对应的租约*/);err != nil { fmt.Println(err) return } else { fmt.Println("写入成功",putResp.Header.Revision/*这东西你可以理解为每次操作的id*/) } } //监听这个key的租约是否过期 for { if getResp,err = kv.Get(context.TODO(),"/cron/lock/job1");err != nil { fmt.Println(err) return } if getResp.Count == 0 { fmt.Println("kv过期了") break } fmt.Println("kv没过期",getResp.Kvs) time.Sleep(2 * time.Second) }
23天前 喜欢(0) 浏览(130) 评论(0)
博客
etcd做注册服务使用 类似java生态的zookeeper,最近在学学习 1、下载etcd包 //直接下载即可(因为包比较大有翻墙,可以直接去 https://golangtc.com/download/package 下载) go get go.etcd.io/etcd/clientv3 2、使用记录 packge main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" //"log/syslog" "time" ) func main() { var ( config clientv3.Config client *clientv3.Client err error kv clientv3.KV putResp *clientv3.PutResponse getResp *clientv3.GetResponse delResp *clientv3.DeleteResponse keepResp *clientv3.LeaseKeepAliveResponse keepRespChan <-chan *clientv3.LeaseKeepAliveResponse ) } //客户端配置 config = clientv3.Config{ Endpoints:[]string{"127.0.0.1:2379"}, DialTimeout:5 * time.Second, } //建立连接 if client,err = clientv3.New(config);err != nil { fmt.Println(err) return } //得到操作etcd键值对的kv kv = clientv3.NewKV(client) //写入etcd if putResp,err = kv.Put(context.TODO(),"/cron/jobs/job2","....",clientv3.WithPrevKV()/*可选参数,得到上次操作的值*/);err != nil { fmt.Println(err) } else { fmt.Println(putResp.Header.Revision) if putResp.PrevKv != nil { fmt.Println(string(putResp.PrevKv.Value)) } } //读取某个key的value值 getResp,err = kv.Get(context.TODO(),"/cron/jobs/job1"/*,clientv3.WithCountOnly()可选参数,得到数量*/) if err != nil { fmt.Println(err) return } else { fmt.Println(getResp.Kvs[0].Value/*得到的是一个切片*/) } //读取前缀为XXX的所有的key的value(需要加上参数clientv3.WithPrefix()) if getResp,err = kv.Get(context.TODO(),"/cron/jobs/",clientv3.WithPrefix());err != nil { fmt.Println(err) return } else { fmt.Println(getResp.Kvs) } //删除操作 if delResp,err = kv.Delete(context.TODO(),"/cron/jobs/job2",clientv3.WithPrevKV()/*得到删除之前的值*/);err != nil { fmt.Println(err) return } else { if len(delResp.PrevKvs) != 0 { fmt.Println(delResp.PrevKvs) } }
23天前 喜欢(0) 浏览(136) 评论(0)
Wiki
在views/layouts/main.php 中添加下面的代码: //views/layouts/main.php $this->registerMetaTag(['name' => 'keywords', 'content' => 'yii, yii2, Yii中文网, yii教程, yii实战教程']); !isset($this->metaTags['description']) && $this->registerMetaTag(["name" => "description", "content" => 'Yii中文网,零基础学习yii2框架,提供丰富的yii2视频教程和应用实例,以及各种实用扩展推荐,我们旨在学术的互相学习探讨,共同进步!']); 解释一下为什么在 description 加 !isset($this->metaTags['description']),因为不同的页面可能需要设置不同的 description 信息。此处如此设置是在没有 description 的设置页面,默认使用上述的 description 的内容。 举个例子: 我想在文章详情页的描述是文章内容简介,我们就可以在具体的文章详情页添加如下代码: //views/topic/detail.php $this->registerMetaTag(["name" => "description", "content" => '某某文章的简介内容']);
25天前 喜欢(0) 浏览(112) 评论(0)
Wiki
如下图所示,在 Yii2.0 的框架中如何实现顶部下拉导航呢? 实现过程中主要用到 NavBar 与 Nav 两个 Html 组件: 在 NavBar 组件 [[begin()]] 和 [[end()]] 调用之间包含的任何内容都被认为是 NavBar 的内容。可以使用诸如 [[NaV]] 或 [[\yii\widgets\Menu]] 这样的小部件来构建这样的内容。 举个例子: use yii\bootstrap\NavBar; use yii\bootstrap\Nav; NavBar::begin([ 'brandLabel' => 'Yii 中文网', 'brandUrl' => Yii::$app->homeUrl, 'options' => [ 'class' => 'navbar-top navbar-fixed-top', //对应的样式 ], ]); echo Nav::widget([ 'items' => [ ['label' => '社区', 'url' => ['/topic/index']], ['label' => '分类', 'options'=>['class'=>'ycn-banner'], 'items'=>[ ['label' => '<i class="fa fa-wikipedia-w top-tag"></i> Wiki', 'url' => ['topic/wiki'],'encode' => false], ['label' => '<i class="fa fa-question-circle top-tag"></i> 问答', 'url' => ['topic/question'],'encode' => false], ['label' => '<i class="fa fa-book top-tag"></i> 博客', 'url' => ['topic/blog'],'encode' => false], ] ], ], 'options' => ['class' => 'navbar-nav'], ]); NavBar::end(); Tip:想要 label 中的内容支持 Html(例如:图标) 就需要添加配置 encode=>false
26天前 喜欢(0) 浏览(129) 评论(0)
博客
教程有两种,一种是别人家的教程和撩妹的教程。撩妹家的教程只是看看就好了。至于运用得看你们项目经理脾气好不好。 做了一个很是杀马特的网站 http://foxhome.top/ , SiteController actionCategory($video_type = VideoType::All) 按照美化教程生成了以下http://foxhome.top/site/sategory.html?video_type=999 对比别人家的视频网站发现一般都是 /category/999.html ,/category/122.html,/category/122.html 之类的。瞬间感觉拉低了一个档次。善于搬砖的撩妹最后折腾成了 http://foxhome.top/category/1000.html http://foxhome.top/category/1001.html http://foxhome.top/category/1002.html 详情页页弄成了 http://foxhome.top/videoinfo/3.html 。 下面是重点 对config下的 web.php进行以下修改 列如'category/<video_type:\d+>.html' => 'site/category' 中 'category/<video_type:\d+>.html' 指的是我们在浏览器中的请求<video_type:\d+>表示参数video_type,并且是数字。 当我们请求/category/1000.html 相当于将video_type转由 site/category?video_type=1000 路由去处理而category/<video_type:\d+>.html中 category和.html你想怎么写就怎么写主要是把参数video_type传递给后面的控制器即可。 如果只是想稍微修饰以下url 也可以这样子操作'/signup'=>'/user/signup' 即请求/signup相当于就是在请求/user/signup,/signup可以瞎写,但是后面的一定需要是【/控制器名称/动作】
29天前 喜欢(0) 浏览(121) 评论(0)
博客
YII2为我们提供了很多widget用来快速构建前端HTML代码,但是很多属性我们并不知道。经过一阵子瞎摸索找到了以下技巧。 YII2大部分widget都是构建了Bootstrap类标签,所以widget分为普通widget和Bootstrap widget,Bootstrap widget位于yii\bootstrap下,而一般常用的widgets位于yii\widgets或者通过yii\helpers\Html构建; 我们很多时候需要定义一个HTML元素的属性,style,id,或者class。例如 yii\helpers\Html::submitButton($content = 'Submit', $options = []); 作者为方便开发者如果需要改变widget的属性时定义options参数即可,比如我需要定义一个html标签的class则只需要定义 Html::submitButton('登录', ['class' => 'btn btn-primary', 'name' => 'login-button']),['属性'=>'属性值']; 很惊奇的发现一个widget只要有options参数则都是一样,列如定义 class ['class' => 'btn btn-primary',...], 定义html的ID ['id' => 'mView',...] 如果需要强大而优美的Bootstrap widget则只需要研究以下Bootstrap V3下的样式即可参考文档 https://v3.bootcss.com/ 以上代码生成了三个Bootstrap风格的按钮,以为YII2集成了Bootstrap css所以只要按钮中有Bootstrap的类样式就会被应用上去,至于需要哪些样式则只需要研究 https://v3.bootcss.com/css/ 可以了。 另外需要强调的是YII中有很多组合控件比如ActiveForm中的$form->field,这类控件会有一个div,再套一个input或者别的元素form->field 中的options和别处不同form->field中的options参数并不是直接定义div中的属性form->field对应的是 ActiveForm::begin中的fieldConfig变量,不同的是ActiveForm::begin是定义全部的子条目,而form->field是定义当前的一个条目。所以form->field定义外套的div的各种属性是 [...,'options'=>['class' => 'input-group'],..]因为form->field不仅能定义div元素的各种标签属性options还能定义template等。我们很多时候的需求是既要定义组合表达的div还要定义子HTML元素的各种属性 passwordInput和普通的Input区别就是多了一个type="password"属性,我们通过了上面的代码给这个input添加了一个style属性并且属性值是width: 25px; 至于options能定义什么得HTML支持什么属性,就算不支持也能加上去,至于效果嘛得看浏览器得眼色了。不管什么view一定得先找到它的options能在哪里传
29天前 喜欢(0) 浏览(123) 评论(0)
Wiki
适用场景:在完成某些操作后,需要在页面进行相应的提示,比如文章保存成功,消息发送失败等 在控制器里面这样写,可以根据实际情况选择相应的类型: 错误消息: \Yii::$app->getSession()->setFlash('error', '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('error', ['Error 1', 'Error 2']); 演示:
1月前 喜欢(0) 浏览(185) 评论(0)
Wiki
我们经常会比如博客文章,上一篇/下一篇 这样类似的需求,那么 Yii2 如何实现这样的需求呢? 以获取下一篇记录为例: public function actionView($id) { //下一篇内容获取 $res = Article::find()->where(['>', 'id', $id])->one(); if($res){ $next['url'] = yii\helpers\Url::to(['post/view','id'=>$res->id]); $next['title'] = $res->title; }else{ $next['url'] = '#'; $next['title'] = '没有文章了'; } return $this->render('view', [ 'model' => $this->findModel($id), 'next' => $next ]); } 在对应的页面,渲染获取下一篇的数据。 <div> 下一篇:<a href="<?=$next['url']?>"><?=$next['title']?></a> </div>
1月前 喜欢(0) 浏览(142) 评论(0)
Wiki
下面是快速打印 SQL 的方法: $query = new Books::find()->where('author=2'); echo $query->createCommand()->sql; 或者用下面的方式也可以: $query->createCommand()->getRawSql()
1月前 喜欢(0) 浏览(136) 评论(0)
Wiki
Gridview 如何对关联模型的数据做显示、排序、过滤? 您会发现对显示相关模型数据的GridView列进行排序和筛选实现起来很棘手。 正如您所知道的,如果最近一直在使用Yii2,那么有一种新的搜索数据的建议方法,即使用从主要实体模型扩展的对象,并将可搜索属性标记为“.”。那么,我们如何在GridView小部件上排序和过滤相关数据呢? 让我们想象一下下面的关系:一个叫做 “tour” 的模型: /** * @return \yii\db\ActiveQuery */ public function getCountry() { return $this->hasOne(Country::className(), ['id' => 'country_id']); } /** * @return \yii\db\ActiveQuery */ public function getCity() { return $this->hasOne(City::className(), ['id' => 'city_id']); } 我们希望在GridView上显示国家的名称和城市的名称。为了做到这一点,我们对 “tourSearch” 做了以下调整: class TourSearch extends Tour // extends from Tour see? { // add the public attributes that will be used to store the data to be search public $city; public $country; // now set the rules to make those attributes safe public function rules() { return [ // ... more stuff here [['city', 'country'], 'safe'], // ... more stuff here ]; } // ... model continues here 然后,我们配置一下 gredview,以便显示相关数据: // ... more grid configuration here 'columns' => [ // ... more columns configuration here [ 'attribute' => 'city', 'value' => 'city.name' ], [ 'attribute' => 'country', 'value' => 'country.name' ], 如上所述,我们将能够显示数据,但如何排序或筛选?让我们举例说明,这一次让我们关注 “TearSearch” 类的 “search” 方法: public function search($params) { // create ActiveQuery $query = Tour::find(); // Important: lets join the query with our previously mentioned relations // I do not make any other configuration like aliases or whatever, feel free // to investigate that your self $query->joinWith(['city', 'country']); $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); // Important: here is how we set up the sorting // The key is the attribute name on our "TourSearch" instance $dataProvider->sort->attributes['city'] = [ // The tables are the ones our relation are configured to // in my case they are prefixed with "tbl_" 'asc' => ['tbl_city.name' => SORT_ASC], 'desc' => ['tbl_city.name' => SORT_DESC], ]; // Lets do the same with country now $dataProvider->sort->attributes['country'] = [ 'asc' => ['tbl_country.name' => SORT_ASC], 'desc' => ['tbl_country.name' => SORT_DESC], ]; // No search? Then return data Provider if (!($this->load($params) && $this->validate())) { return $dataProvider; } // We have to do some search... Lets do some magic $query->andFilterWhere([ //... other searched attributes here ]) // Here we search the attributes of our relations using our previously configured // ones in "TourSearch" ->andFilterWhere(['like', 'tbl_city.name', $this->city]) ->andFilterWhere(['like', 'tbl_country.name', $this->country]); return $dataProvider; } 就是这样了,希望本教程能帮助你找到方向。
1月前 喜欢(0) 浏览(139) 评论(0)
社区公告
[公告] Yii中文网为优化用户体验进行大版本升级,老版网站会维持一段时间,可以点击顶部"旧版"链接访问旧版网站。
沟通交流

:492175201(技术1群)

:183620600(技术2群)

:291010569(技术3群)