【Yii】学习笔记

发布于 2016-04-26  649 次阅读


QQ截图20160426055619.jpg
学习所用版本:yii2.0,基本应用程序模板。
所用视频教程:【Yii2不得不说的 - xx篇】 http://pan.baidu.com/s/1bpC0tx9 密码: 2333
文档:http://www.yiichina.com/doc

此处使用HelloController控制器和hello视图目录
../basic/controllers/HelloController.php
../basic/views/hello
浏览器访问:http://127.0.0.1/../basic/web/index.php?r=hello/index
../index.php?r=视图目录/视图文件 视图目录要对应控制器(HelloController控制器-->hello视图目录/xxxx)

<?php
namespace app\controllers;
use yii\web\Controller;
class HelloController extends Controller{
    public function actionIndex(){
        //如无特殊说明,以下代码都默认放在此处
    }
}

Request

echo 'HELLO WORLD!<br>';
        $request = \Yii::$app->request;
        echo $request->get('id',0); //第二个参数表示如果没有get到这个参数
        echo '<br>';
        echo $request->post('name','no name');//第二个参数表示如果没有post到这个参数
        echo '<br>';
        if($request->isGet){// isPost...
            echo '这是Get方式';
        }
        echo '<br>';
        echo $request->userIP.'<br>'.$request->userAgent;

QQ截图20160426033338.jpg
Response

$response = \Yii::$app->response;
$response->statusCode = '404';//设置状态码为404

//添加头信息,设置为不把消息缓存在浏览器里

$response->headers->add('pragma','no-cache');

//设置为缓存5秒钟

$response->headers->set('pragma','max-age=5');

//删除

    $response->headers->remove('pragma');
}

//跳转
//跳转到别的页面

$response->headers->add('location','http://laji.blog');

//yii框架直接包装了个跳转页面的函数,第二个参数是返回的状态码

$this->redirect('http://laji.blog',302);

//文件下载

$response->headers->add('content-disposition','attachment;filename="1T.bt"');

//同样包装了个函数,如果文件不存在会报错

$response->sendFile('./robots.txt');

Session

$session = \Yii::$app->session;
    $session->open();
    if($session->isActive){//判断session是否已开启
        echo 'session已开启';
    }

//把session当成【对象】来使用

$session->set('user','张三');

//session被保存在哪?查看php.ini配置文件,搜索session.save_path即可找到session文件保存的路径

//取出session数据

echo $session->get('user');
 

//删除
$session->remove('user');

//把session当成【数组】来使用

$session['user'] = '张三';
echo $session['user'];

//删除

unset($session['user']);

ArrayAccess接口(interface)所产生的类都可以把它当成数组来使用

$session['user'] = '张三';
echo $session['user'];

//在chrome浏览器生成了的session,到其他浏览器是获取不到的,
//因为会自动记录识别并返回给相应的浏览器,根据浏览器cookies里的PHPSESSID来识别。

Cookies
记得在代码顶部添加use yiiwebCookie;
//response

$cookies_res = \Yii::$app->response->cookies;
$cookies_data = array('name'=>'username','value'=>'zhangsan');

//记得选web下的那个cookie,最上面要引用yiiwebCookie

$cookies_res->add(new Cookie($cookies_data));
        //cookie的值会经过加密
        //如果name一样,值会被覆盖掉。

//删除

$cookies_res->remove('username');

//request


$cookies_req = \Yii::$app->request->cookies;
echo $cookies_req->getValue('username','木有');//如果没有对应的cookie,将返回第二个参数,不填默认返回空

Views
要先在HelloController下定义$layout

<?php
namespace app\controllers;
use yii\web\Controller;
class HelloController extends Controller{
        ///////
        public $layout = 'common';//选择使用的layout布局
        ///////
    public function actionIndex(){
        xxxxxxxx
    }
}

然后hello视图目录下新建index.php

<?php
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;//或者在下边直接写成yii\helpers\HtmlPurifier::process(****)
?>
<h1>HELLO <?=$view_username;?>!</h1>
<h1><?=$view_arr1['id'];?></h1>
<h1><?=$view_arr1['email'];?></h1>
<h1><?=$view_arr2[0];?> <?=$view_arr2[1];?></h1>
<!--<h1><?=$view_post_str;?></h1>-->
<h1>转义text输出:<?=Html::encode($view_post_str);?></h1>
<h1>直接过滤掉:<?=HtmlPurifier::process($view_post_str);?></h1>
<?php include('test1.php'); ?>
<?php echo $this->render('test2',array('view_test2'=>$view_test2)); ?><!--HelloController传来的view_test2-->
<?php echo $this->render('test3',array('view_test3'=>'test3')); ?>
<?php $this->beginBlock('block1'); ?>
<h1>此处的布局文件的原内容被视图文件里的block1覆盖(重写)掉了!</h1>
<?php $this->endBlock('block1'); ?>

新建test1.php

<h2><?=$view_test1;?></h2>

新建test2.php

<h3><?=$view_test2;?></h3>

新建test3.php

<h4><?=$view_test3;?></h4>

//actionIndex()内

$username = '张三';
$arr1 = array('id'=>233,'email'=>'[email protected]');
$arr2 = array(1,2,3);
$post_str = '666666666<script>alert("用户传来了script脚本攻击")</script>';
$test1 = 'test1';
$test2 = 'test2';
//yii会把字符串里的js脚本真的当做js来执行了,所以避免这个情况,在视图文件里用Html::encode()来转义一下。
//也可以使用HtmlPurifier::process()来把脚本代码过滤掉
//把变量传递给视图
//创建一个数组
$data = array();
//把需要传递给视图的数据放到数组当中
$data['view_username'] = $username;
$data['view_arr1'] = $arr1;
$data['view_arr2'] = $arr2;
$data['view_post_str'] = $post_str;
$data['view_test1'] = $test1;
$data['view_test2'] = $test2;
return $this->renderPartial('index',$data);//不用加.php后缀,第二个参数传递数据给视图
//注意:yii的视图文件规定放到views目录下对应的控制器的目录里
//如:HelloController对应views目录下的hello目录
//布局文件,即把重复的内容放到layouts目录当中
return $this->render('index',$data);//视图文件会保存到布局文件的$content这个变量当中

QQ截图20160426042321.jpg
SQL
首先在config目录下的db.php里配置好数据库
然后在models目录下新建Test.php 文件名要对应表名

<?php
namespace app\models;
use \yii\db\ActiveRecord;
class Test extends ActiveRecord{
    public function rules(){//传入数据库的规则
        return [
            ['id','integer'], //是否整型
            ['title','string','length'=>[1,15]]
        ];
    }
}

最后要在HelloController最上面添加use appmodelsTest;
查询数据
//直接使用sql语句

$sql = 'select * from test where id=:id';//使用了占位符,用户传来的id会被当做一个整体,避免sql注入。
$results = Test::findBySql($sql,array(':id'=>1))->all();
print_r($results);

//使用yii框架封装好的

$results = Test::find()->where(['id'=>1])->all(); //等于
$results = Test::find()->where(['>','id',0])->all(); //大于
$results = Test::find()->where(['between','id',0,2])->all(); //之间 1 <= X <= 2
$results = Test::find()->where(['like','title','标题'])->all(); //模糊查询

//把查询结果由对象转化成为数组,降低内存使用

$results = Test::find()->where(['between','id',0,2])->asArray()->all(); //之间 1 <= X <= 2

//批量查询

$i = 0;
foreach(Test::find()->batch(2) as $tests){//存放到$test里
    //因为批量查询是分批去读取数据的,不会只读取一次,耗资源,所以用batch来指定每次读取的条数
    //如果数据表里只有2条数据,就只读取一次就结束了。
    $i++;
}
echo '读取了'.$i.'次数据库<br>';
print_r($tests);

删除数据

$results = Test::find()->where(['id'=>3])->all();

//一次只能删一条....

$results[0]->delete();

//批量删除

Test::deleteAll('id>:id',array(':id'=>1));//删除id>1的数据

增加数据

$test = new Test();
$test->id = 123;
$test->title = 'title';
//首先得去验证数据的合法性(models/Test.php rules)
$test->validate();
if($test->hasErrors()){//如果有数据校验发生了错误
    echo 'data is error!';
    die;
}
$test->save();

修改数据

$test = Test::find()->where(['id'=>666])->one();
$test->title = '我是666';
$test->validate();
if($test->hasErrors()){//如果有数据校验发生了错误
    echo 'data is error!';
    die;
}
$test->save();

关联查询
先在models里新建Customer.php和Order.php,因为文件名是对应表名的所以在数据库新建相对应的表和添加数据
并在HelloController顶部添加:

use app\models\Customer;
use app\models\Order;

//Customer.php

<?php
namespace app\models;
use \yii\db\ActiveRecord;
class Customer extends ActiveRecord{
    //获取顾客订单信息
    public function getOrders(){
        $orders = $this->hasMany(Order::className(),['customer_id'=>'user_id'])->asArray();
        //因为控制器调用未定义的XXXX方法,系统会去调用_get(),并转向getXXXX()方法,并在最后自动补上all(),所以这里删掉->all()
        return $orders;
    }
}

//Order.php

<?php
namespace app\models;
use \yii\db\ActiveRecord;
class Order extends ActiveRecord{
    //根据订单查询顾客
    public function getCustomer(){
        return $this->hasOne(Customer::className(),['user_id'=>'customer_id'])->asArray();
        //因为控制器调用未定义的XXXX方法,系统会去调用_get(),并转向getXXXX()方法,并在最后自动补上one(),所以这里删掉->one()
    }
}

//根据顾客查询他的订单的信息

$customer = Customer::find()->where(['username'=>'zhangsan'])->one();
                              //Order::className()会自动获取app\models\Order
$orders = $customer->hasMany(Order::className(),['customer_id'=>'user_id'])->asArray()->all();
//customer_id是属于订单表里面的,user_id是属于顾客表的,注意先后顺序,根据第一个参数对应的表
//最好把上面这句代码封装起来-->models/Customer.php getOrders()
//$orders = $customer->getOrders();
//上面这句代码可以写成下面这条
$orders = $customer->orders;
//我们并没有在Customes里定义orders方法,为什么还会获取到数据呢?
//因为yii检测到没有XXXxx方法时,会去调用_get(),
//_get()于是会去调用getXXXX()方法,并在最后补上一个all()方法
//获取到数据后返回到调用的变量当中
print_r($orders);

//根据订单查询顾客

$order = Order::find()->where(['order_id'=>1])->one();
$customer = $order->customer;
print_r($customer);

//关联查询结果缓存

$customer = Customer::find()->where(['username'=>'zhangsan'])->one();
$orders = $customer->orders;//首先会select * from order where customer_id = ...
//如果再执行一次的话会直接调用$orders而不去先执行select * from...了,导致更新了数据却使用了之前的缓存
//所以可以先unset掉再执行
unset($customer->orders);
$orders = $customer->orders;

//关联查询的多次查询

$customers = Customer::find()->all();//select * from customer
foreach($customers as $customer){
    $orders = $customer->orders;//select * from order where customer_id = ....
    //假如有100个客户那么,就会执行100次
}
//那么一共执行101次sql语句了,所以~
//使用with()方法
$customers = Customer::find()->with('orders')->all();
foreach($customers as $customer){
    $orders = $customer->orders;
}

类的映射表机制

\yii::$classMap['app\models\Order'] = '../models/Order.php';
$order = new Order();//系统会根据所给的路径去找

组件的延迟加载

$session = \yii::$app->session;
// \yii::$app会在使用的时候去调用到__get()这个方法,需要用到的时候再去加载组件

数据缓存的增删改查
//获取缓存组件

$cache = \yii::$app->cache;

//往缓存当中写入数据

$cache->add('key1','helle world~111111');
    //如果前面已经有的key,后面继续add给这个key是会不起作用的,也不会报错~
    $cache->add('key1','helle world~yoooooooo');
$cache->add('key2','helle world~222222');

//修改数据

$cache->set('key1','helle world~niconiconi');

//删除数据

$cache->delete('key1');

//清空数据

$cache->flush();

//读取缓存

$data = $cache->get('key1');
var_dump($data);

//缓存的有效期设置

$cache->add('key3','helle world~333333',15);//第三个参数是缓存的有效期,单位为秒。
$cache->set('key3','helle world~123456',5);//第三个参数是缓存的有效期,单位为秒。
$data = $cache->get('key3');
var_dump($data);

数据缓存依赖关系
文件依赖
首先在web目录下新建一个hw.txt,随便填点什么。

$cache = \yii::$app->cache;
$dependency = new \yii\caching\FileDependency(['fileName'=>'hw.txt']);
$cache->add('file_key','hello world~FileDependency',6666,$dependency);
//当hw.txt这个文件遭到修改(发生了变化)时,该缓存会自动失效
//而且把文件改回原来的样子也获取不到原来的缓存了
$data = $cache->get('file_key');
var_dump($data);

1.jpg
hw.txt文件被修改后:
11.jpg
表达式依赖

$cache = \yii::$app->cache;
$dependency = new \yii\caching\ExpressionDependency(
    ['expression'=>'\yii::$app->request->get("name")']
            //这个表达式是在浏览器的地址栏传递过来的name参数
            //如:http://127.0.0.1/xxxxxx/index&name=zhangsan
            //我们添加了这个缓存,传递的是zhangsan的name(添加完记得注释掉add语句)
            //如果我们把zhangsan改成其它,会获取不到原来的缓存,
            //但把name的值改回zhangsan又获取到原来的缓存了~
);
$cache->add('expression_key','hello world~ExpressionDependency',6666,$dependency);
$data = $cache->get('expression_key');
var_dump($data);

QQ截图20160426042321.jpg
name参数被修改后:
QQ截图20160426052443.jpg
DB依赖

$cache = \yii::$app->cache;
$dependency = new \yii\caching\DbDependency(
    ['sql'=>'SELECT count(*) FROM yii.order']
        //当order表发生变化时,会获取不到原来的缓存,
        //但如果把order表恢复成原来的,又获取到原来的缓存了
);
$cache->add('db_key','hello world~DbDependency',6666,$dependency);
$data = $cache->get('db_key');
var_dump($data);

3.jpg
当sql查出来的结果和第一次的不一样后:
33.jpg

片段缓存
//跳转到cache1.php(开启片段缓存)

return $this->renderPartial('cache1');

../hello/cache1.php

<?php
    if($this->beginCache('cache_div')) { //
?>
<div id="cache_div">
    <h2>这里待会会被缓存,缓存后,修改此处代码依旧显示原来的代码,不会实时更新</h2>
</div>
<?php
    $this->endCache();
} ?>
<div id="no_cache_div">
    <h2>这里不会被缓存,修改此处代码会实时更新</h2>
</div>

//跳转到cache2.php(设置缓存时间)

return $this->renderPartial('cache2');

../hello/cache2.php

<?php
    //缓存时间
    $duration = 15;
    echo '<h3>缓存时间:'.$duration.'秒</h3>'
?>
<?php
    if($this->beginCache('cache_div',['duration'=>$duration])) { //
?>
<div id="cache_div">
    <h2>这里待会会被缓存,缓存后,在缓存时间内修改此处代码依旧显示原来的代码,不会实时更新123</h2>
</div>
<?php
    $this->endCache();
} ?>
<div id="no_cache_div">
    <h2>这里不会被缓存,修改此处代码会实时更新123</h2>
</div>

//跳转到cache3.php(缓存依赖)

return $this->renderPartial('cache3');

../hello/cache3.php

<?php
    //缓存依赖
    echo '<h3>缓存依赖</h3>';
    echo '<h3>此处使用文件依赖</h3>';
    $dependency = [
        'class'=>'yii\caching\FileDependency',
        'fileName'=>'hw.txt'
    ];
    echo '<h3>依赖文件:'.$dependency['fileName'].'</h3>';
?>
<?php
    if($this->beginCache('cache_div',['dependency'=>$dependency])) { //
?>
<div id="cache_div">
    <h2>这里待会会被缓存,缓存后,在依赖文件被修改前修改此处代码依旧显示原来的代码,不会实时更新</h2>
</div>
<?php
    $this->endCache();
} ?>
<div id="no_cache_div">
    <h2>这里不会被缓存,修改此处代码会实时更新</h2>
</div>

//跳转到cache4.php(缓存开关)

return $this->renderPartial('cache4');

../hello/cache4.php

<?php
//缓存开关
$enabled = true;
echo '<p>缓存开关:</p>';
var_dump($enabled);
?>
<?php
    if($this->beginCache('cache_div',['enabled'=>$enabled])) { //
?>
<div id="cache_div">
    <h2>缓存开关为true,这里待会会被缓存,缓存后,修改此处代码依旧显示原来的代码,不会实时更新</h2>
</div>
<?php
    $this->endCache();
} ?>
<div id="no_cache_div">
    <h2>这里不会被缓存,修改此处代码会实时更新</h2>
</div>

//跳转到cache5.php(嵌套缓存)

return $this->renderPartial('cache5');

../hello/cache5.php

<?php
    if($this->beginCache('cache_div',['duration'=>20])) { //
?>
<div id="cache_outer_div">
    <h2>这里是外层,待会会被缓存</h2>
        <?php
        if($this->beginCache('cache_inner_div',['duration'=>1])) { //
        ?>
        <div id="cache_inner_div">
            <h3>这里是内层,待会会被缓存</h3>
        </div>
            <?php
            $this->endCache();
        } ?>
</div>
<?php
$this->endCache();
} ?>
<h1>得出结论:</h1>
<h4>缓存后,在外层的缓存时间未结束前,内部缓存时间会被无视。</h4>
<h4>因为,当外层缓的存换层时间未结束前,系统会直接把已经缓存好的整个外层所包含的代码直接读取出来,</h4>
<h4>当外层的缓存时间结束后,再考虑内层的缓存时间,若内层的缓存时间已经过期,则缓存结束,反之,继续缓存着。</h4>
<div id="no_cache_div">
    <h2>这里不会被缓存,修改此处代码会实时更新</h2>
</div>

(整个)页面缓存

<?php
namespace app\controllers;
use yii\web\Controller;
class HelloController extends Controller{
    public function actionIndex(){
        echo '请修改此处测试是否会被缓存actionIndex';
    }
    public function behaviors(){//这个函数会先于其它函数执行
    //(整个)页面缓存
    //当有一个请求到actionIndex方法时,会先到behaviors,behaviors会告诉yii框,
    //架要使用PageCache缓存,yii得知后,到Cache缓存里查看,如果有值得话会直接使用缓存里的值,
    //actionIndex里的代码/操作就不会被执行了。
        return [
            [
                'class'=>'yii\filters\PageCache', //使用PageCache缓存
                'duration'=>30, //缓存时间
                //不使用only时,默认所有页面都缓存
                'only'=>['index'],//此处写进数组的页面才会被缓存
                                  //例如test没被写进去,actionTest不会被缓存
                'dependency'=>[ //依赖缓存
                        //此处使用文件依赖的类(表达式依赖、DB依赖以此类推。
                    'class'=>'yii\caching\FileDependency',//依赖的类
                    'fileName'=>'hw.txt'
                ]
            ]
        ];
    }
    public function actionTest(){//(整个)页面缓存
            echo '请修改此处测试是否会被缓存actionTest';
    }
}

http缓存

<?php
namespace app\controllers;
use yii\web\Controller;
class HelloController extends Controller{
    public function actionIndex(){
            //浏览器首次访问网页时,会发送请求到服务器,
            //服务器会将已生成的last-modified(最后修改时间)和etag(特征?!)随着数据/内容响应给浏览器,
            //并告诉浏览器接收到数据后要将内容缓存起来,那么浏览器是如何知道服务器要它将内容缓存起来的呢?
            //具体是在响应的头部信息(Response Headers)里的Cache-Control,
            //浏览器刷新/下次访问时,再次发送请求到服务器,同时会把If-Modified-Since(Last-Modified)和
            //If-None-Match(Etag)发送给服务器,服务器将浏览器发来的数据与服务器的进行对比,
            //如果相同,表示服务器内容没有变化,为避免浪费资源,让浏览器使用之前的浏览器缓存,
            //如果不同,表示服务器内容发生了变化,则进行上面第2~5行的步骤。
            $content = file_get_contents('hw.txt');
            return $this->renderPartial('cache6',['new'=>$content]);
    }
    public function behaviors(){//这个函数会先于其它函数执行
    //http缓存(见actionIndex函数的http缓存部分)
            return [
                [
                'class'=>'yii\filters\HttpCache',
                'lastModified'=>function(){
                    return  filemtime('hw.txt'); //最后修改时间,返回时间戳给浏览器
                                                 //(浏览器会自动转换为格林时间,统一标准格林时间
                },//比如return 1432817566(2015-05-28 【20】:52:46)--转换-->2015-05-28 【12】:52:46
                'etagSeed'=>function(){
                    $fp = fopen('hw.txt','r');
                    $file_one_line = fgets($fp);
                    fclose($fp);
                    $file_size = filesize('hw.txt');
                    $md5 = md5('hw.txt');
                    $etag = $file_one_line.'|filesize:'.(string)$file_size.'|md5:'.$md5;
                    //return一些该文件的特征,比如文件的内容、文件大小、md5之类的,此处代码还不严谨。
                    return $etag;
                }
                ]
            ];
    }
}

../hello/cache6.php

<div>
    <div><?=$new;?></div>
</div>

gii模型生成器 http://../basic/web/index.php?r=gii
可以直接配置并生成各种模型


❤动漫 | 音乐 | 游戏 萝莉赛高! 过膝袜赛高!