• yii 学习笔记 基本操作


    一、入门

    二、创建控制器

    三、接收参数

    四、分配视图

    五、compact方法

    六、xxs攻击

    七、模版继承

    八、模型创建及数据读取

    九、防止sql注入

    一十、where单表查询

    一十一、节省内存

    一十二、模型添加记录

    一十三、模型修改记录

    一十四、模型删除记录

    一十五、一对多查询

    一十六、一对多查询性能优化

    一十七、一对一查询

    入门

    前置知识

    php yii是一个php框架,要确保熟悉类与对象及命名空间

    安装

    通过归档文件安装

    1.从 yiiframework.com 下载归档文件
    2.将下载的文件解压缩到 Web 访问的文件夹中
    3.将 basic/web 设置为文档根目录(document root)

    运行

    应用结构


    每个应用都有一个入口脚本web/index.php,这是整个应用中唯一可以访问的php脚本,入口脚本接受一个web请求并创建应用实例去处理它
    应用在它的组件辅助下解析请求,并分派请求至mvc元素
    视图使用小部件去创建复杂和动态的用户界面

    请求生命周期

    1.用户向入口脚本web/index.php发起请求
    2.入口脚本加载应用配置并创建一个应用实例去处理请求
    3.应用通过请求组件解析请求的路由
    4.应用创建一个控制器实例去处理请求
    5.控制器创建一个动作实例并针对操作执行过滤器
    6.如果任何一个过滤器返回失败,则动作取消
    7.如果所有过滤器都通过,动作将被执行
    8.动作会加载一个数据模型,或许是来自数据库
    9.动作会渲染一个视图,把数据模型提供给它
    10.渲染结果返回给响应组件
    11.响应组件发送渲染结果给用户浏览器

    说声hello

    如何创建一个动作去响应请求
    如何创建一个视图去构造响应内容
    以及一个应用如何分派请求给动作

    创建动作

    操作必须声明在控制器中,为了简单起见,直接在SiteController控制器里声明say操作。这个控制器是由文件controllers/SiteController.php定义的

    <?php
    namespace appcontrollerss;
    
    use yiiwebController;
    
    class SiteController extends Controller
    {
        //  ...现存的代码...
        // 为了hello,需要创建一个say操作,
        // 从请求中接收message参数并显示给最终用户
        // 如果请求没有message参数,操作将显示默认参数Hello
        // say操作被定义为actionSay方法
        // Yii使用action前缀区别普通方法和操作
        // action前缀后面的名称被映射为操作的id
        public function actionSay($message = 'Hello')
        {
            return $this->render('say', ['message' => $message]);
        }
    }
    

    给操作命名时,要先理解yii处理操作id。操作id总是被以小写处理。如果一个操作id由多个单词组成,单词之间将由连字符连接(如create-comment)。操作id映射为方法名时移除了连字符,将第一个单词首字母大写,并加上action前缀。如操作id create-comment相当于方法名actionCreateComment

    在操作方法中,render()被用来渲染一个名为say的视图文件。message参数也被传入视图,这样就可以在里面使用。操作方法会返回渲染结果。结果会被应用接收并显示给最终用户的浏览器

    当操作中调用了render()方法时,它将会按views/控制器ID/视图名.php路径加载php文件

    创建视图

    views/site/say.php
    message参数被输出之前被html-encoded方法处理过
    因为参数中可能隐含的恶意js代码会导致跨站脚本xss攻击

    <?php
    use yiihelpersHtml;
    ?>
    <?= Html::encode($message) ?>
    

    say视图脚本输出的内容将会响应结果返回给应用。应用将依次输出结果给最终用户

    试运行

    http://hostname/index.php?r=site/say&message=Hello+World

    会输出hello world的页面

    新页面和其它页面使用同样的头部和尾部是因为render()方法会自动把say视图执行的结果嵌入称为布局的文件中,本例中是views/layouts/main.php

    上面url参数r代表路由,指向特定操作的独立id。路由格式是控制器id/操作id。应用接受请求的时候会检查参数,使用控制器id去确定哪个控制器被用来处理请求。然后相应控制器将使用操作id去确定哪个操作方法将被用来做具体工作。上述例子中,路由site/say将被解析至SiteController控制器和基本的say操作

    使用表单

    创建一个让用户提交数据的表单页
    为了实现这个目标,需要创建一个操作和两个视图外,还需要创建一个模型

    创建模型

    models/EntryForm.php

    namespace appmodels;
    use Yii;
    use yiiaseModel;
    class EntryForm extends Model
    {
        public $name;
        public $email;
        public function rules()
        {
            return [
                [['name', 'email'], 'required'],
                ['email', 'email'],
            ];
        }
    }
    

    上面声明的验证规则表示
    name和email值必填
    email的值必须满足email规则验证

    $model = new EntryForm();
    $model->name = 'qiang';
    $model->email = 'bad';
    //使用validate方法触发数据验证
    if ($model->validate()) {
        //成功
    } else {
        //失败
        //如果有数据验证失败,将把hasErrors属性设true,想知道具体发生什么错误就调用getErrors
    }
    

    创建动作

    controllersSiteController

    class SiteController extends Controller
    {
        public function actionEntry()
        {
            //用户提交表单后,
            //操作将会渲染一个名为entry-confirm的视图去确认用户输入的数据,
            //如果没填表单就提交,或数据验证不通过,entry视图将会渲染输出
            $model = new EntryForm;
            if ($model->load(Yii::$app->request->post())&& $model->validate()) {
                //验证$model收到的数据
                return $this->render('entry-confirm', ['model' => $model]);
            } else {
                //无论是初始化显示还是数据验证错误
                return $this->render('entry', ['model' => $model]);
            }
        }
    }
    

    创建视图

    site/entry-confirm.php
    entry-confirm视图简单地显示提交的name和email数据

    <?php
    use yiihelpersHtml;
    ?>
    <p>You have entered the following information:</p>
    
    <ul>
        <li><label>Name</label>: <?= Html::encode($model->name) ?></li>
        <li><label>Email</label>: <?= Html::encode($model->email) ?></li>
    </ul>
    

    entry视图显示一个HTML表单
    site/entry.php
    使用了小部件ActiveForm生成html表单
    先用begin()和end()分别用来渲染表单的开始和关闭标签
    在这两个方法之间使用了field()方法创建输入框
    最后使用yiihelpersHtml::submitButton()方法生成提交按钮

    <?php
    use yiihelpersHtml;
    use yiiwidgetsActiveForm;
    ?>
    <?php $form = ActiveForm::begin(); ?>
    
        <?= $form->field($model, 'name') ?>
    
        <?= $form->field($model, 'email') ?>
    
        <div class="form-group">
            <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
        </div>
    
    <?php ActiveForm::end(); ?>
    

    用浏览器访问看是否工作

    效果说明

    没输入正确的信息时不需要刷新页面就能给出错误提示
    其实数据首先由客户端js脚本验证,然后才会提交给服务器通过php验证
    警告:客户端验证是提高用户体验手段,无论是否启用,服务端验证都是必须的

    输入框的文字标签是field()方法生成的,内容就是模型中该数据的属性名,例如模型中name属性生成的标签就是Name,当然这些都是可以自定义的

    <?= $form->field($model, 'name') -> label('自定义 Name')?>
    <?= $form->field($model, 'email') -> label('自定义 email')?>
    

    使用数据库

    创建一个从数据表country中读取数据并显示

    准备数据库

    创建一个名为yii2basic的数据库
    然后在数据库中创建一个名为 country 的表并插入简单的数据。可以执行下面的语句:

    CREATE TABLE `country` (
      `code` CHAR(2) NOT NULL PRIMARY KEY,
      `name` CHAR(52) NOT NULL,
      `population` INT(11) NOT NULL DEFAULT '0'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    INSERT INTO `country` VALUES ('AU','Australia',18886000);
    INSERT INTO `country` VALUES ('BR','Brazil',170115000);
    INSERT INTO `country` VALUES ('CA','Canada',1147000);
    INSERT INTO `country` VALUES ('CN','China',1277558000);
    INSERT INTO `country` VALUES ('DE','Germany',82164700);
    INSERT INTO `country` VALUES ('FR','France',59225700);
    INSERT INTO `country` VALUES ('GB','United Kingdom',59623400);
    INSERT INTO `country` VALUES ('IN','India',1013662000);
    INSERT INTO `country` VALUES ('RU','Russia',146934000);
    INSERT INTO `country` VALUES ('US','United States',278357000);
    

    配置数据库连接

    config/db.php

    <?php
    
    return [
        'class' => 'yiidbConnection',
        'dsn' => 'mysql:host=localhost;dbname=yii2basic',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
    ];
    

    上面的数据库连接可以在应用中通过Yii::$app->db表达式访问

    创建控制器

    新建一个控制器

    controllersHomeController.php

    namespace appcontrollers;
    use yiiaseController;
    class HomeController extends Controller
    {
        public function actionIndex() {
            //
        }
    }
    

    测试工具

    工具函数

    helper/function.php

    function p($var) {
        echo '<pre>';
        print_r($var);
        echo '</pre>';
    }
    function dd($var) {
        echo '<pre>';
        var_dump($var);
        echo '</pre>';
        //加了die阻止后续的操作,不会渲染公共的页头和页尾
        die;
    }
    

    引入

    webindex.php

    require(__DIR__.'/../helper/function.php');
    

    使用

    controllersHomeController.php

    namespace appcontroller;
    use yiiaseController;
    class HomeController extends Controller
    {
        public function actionIndex() 
        {
            $data = [
                'name' => 'houdunwang',
                'age' => 7
            ];
            dd($data);
        }
    }
    

    查看输出

    使用print_r

    使用var_dump

    接收参数

    controllersHomeController.php

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            $request = Yii::$app->request;
            // 接收get参数,默认值1
            echo $request->get('id', 1);
            // 也可以通过$request->isPost判断是什么方式请求的
    
        }
        
        public function actionHome()
        {
            $request = Yii::$app->request;
            // 接收post参数
            echo $request->post('username');
        }
    
    }
    

    分配视图

    controllersHomeController.php

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            $request = Yii::$app->request;
            $data = $request->userIP;
            //render 输出父模板的内容,将渲染的内容,嵌入父模板。
            //renderPartial 则不输出父模板的内容。只对本次渲染的局部内容进行输出
            //视图名和方法名一般保持一致
            return $this->renderPartial('index', ['data' => $data]);
        }
    }
    

    viewshomeindex.php

    <h1>hello</h1>
    <?php echo $data;?>
    

    输出

    多个参数时建议使用如下的方式写
    controllersHomeController.php

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            $request = Yii::$app->request;
            $data = [
                'username' => 'houdun',
                'ip' => $request->userIP,
                'arr' => [
                    'age' => 7,
                    'class' => 5, 
                ] 
            ];
            //注意这里renderPartial的第二个参数
            return $this->renderPartial('index', $data);
        }
    }
    

    viewshomeindex.php

    <h1>hello</h1>
    <h2><?php echo $username;?></h2>
    <h2><?php echo $ip;?></h2>
    <h2><?php echo $arr['age'];?></h2>
    <h2><?php echo $arr['class'];?></h2>
    

    输出

    用下面的方法写可以得到相同的结果

    compact方法

    class HomeController extends Controller
    {
        public function actionIndex()
        {
    
            $request = Yii::$app->request;
            $username = 'houdun';
            $ip = $request->userIP;
            $arr = [
                'age' => 7,
                'class' => 5,
            ];
            return $this->renderPartial('index', compact('username', 'ip', 'arr'));
    
        }
    }
    

    xxs攻击

    class HomeController extends Controller
    {
    
        public function actionIndex()
        {
            //这里模拟用户输入了一个script元素
            $str = "<script>alert('haha');</script>";
            return $this->renderPartial('index', ['str' => $str]);
        }
    }
    

    viewshomeindex.php

    <h1>hello</h1>
    <h2><?php echo $str;?></h2>
    

    出现了恶意弹窗,用以下的方法解决
    conn

    防止xss攻击

    <?php
    use yiihelpersHtml;
    use yiihelpersHtmlPurifier;
    ?>
    <!--//转码过滤,将html标签转成字符串-->
    <?= Html::encode($str);?>
    
    <!--//完全过滤,忽略html标签-->
    <?= HtmlPurifier::process($str);?>
    

    查看输出

    模板继承

    class HomeController extends Controller
    {
        //定义父模版
        public $layout = 'home';
        public function actionAbout()
        {
            return $this->render('about');
        }
    }
    

    views/home/about.php

    <h1>hello about</h1>
    

    views/layouts/home.php

    <html>
    <body>
        <h1>hello home</h1>
        <?=$content; ?>
    </body>
    </html>
    

    查看输出

    模版相互调用

    views/home/index.php

    <h1>hello index</h1>
    <?php echo $this->render('about');?>
    

    查看输出

    模型创建及数据读取

    新建数据库

    新建article数据表

    id int类型
    title varchar类型

    加入一些记录

    修改配置

    config/db.php

    return [
        'class' => 'yiidbConnection',
        'dsn' => 'mysql:host=localhost;dbname=yii',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
    ];
    

    新建模型

    models/Article

    <?php
    namespace appmodels;
    use yiidbActiveRecord;
    
    class Article extends ActiveRecord
    {
        //
    }
    

    使用

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            $sql = "select * from article where id=1";
            $r = Article::findBySql($sql)->all();
            dd($r);
        }
    }
    

    查看返回

    防止sql注入

    使用客户端传递参数进行查询

    class HomeController extends Controller 
    {
        public function actionIndex()
        {
            $request = Yii::$app->request;
            $id = $request->get('id');
            $sql = "select * from article where id=".$id;
            $r = Article::findBySql($sql) -> all();
            dd($r);
            return $this->render('index');
        }
    }
    

    上面这种方式十分危险,因为客户端可能传入恶意的sql语句

    防止加料

    使用替换占位符的方式传递查询参数

    class HomeController extends Controller 
    {
        public function actionIndex()
        {
            $request = Yii::$app->request;
            $id = $request->get('id');
            $sql = "select * from article where id=:id";
            $r = Article::findBySql($sql, [':id' => $id]) -> all();
            dd($r);
            return $this->render('index');
        }
    }
    

    浏览器输入http://hostname/index.php?r=home/index&id=3

    where单表查询

    class HomeController extends Controller 
    {
        public function actionIndex()
        {
            //条件查询
            // $data = Article::find()->where(['id' => 5])->all();
            //指定范围
            // $data = Article::find()->where(['>', 'id', 3])-> all();
            //在2和5之间的
            // $data = Article::find()->where(['between', 'id', 2, 5])-> all();
            // 标题含有巴菲特
            // $data = Article::find()->where(['like', 'title', '巴菲特'])-> all();
            //只查询一条
            // $data = Article::find()->where(['id' => 5])-> one();
            //更快的方式
            // $data = Article::findOne(5);
            $data = Article::findAll([3, 4, 5]);
            dd($data);
            return $this->render('index');
        }
    }
    

    节省内存

    class HomeController extends Controller 
    {
        public function actionIndex()
        {
            // find不加参数表示查询所有
            // 转成数组,节省内存
            $data = Article::find()->asArray()->all();
            dd($data);
            return $this->render('index');
        }
    }
    

    查看

    如果取的记录量大,一次性返回非常占内存

    class HomeController extends Controller 
    {
        public function actionIndex()
        {
            // 分多次取数据
            foreach (Article::find()->batch(2) as $article) {
                //看下每次输出几条
                // echo count($article);
                $data[] = $article;
            }
            dd($data);
            return $this->render('index');
        }
    }
    

    查看返回

    此时返回了长度为3的二维数组,说明总共5条数据被分成3批返回

    模型添加记录

    在Article表中添加整型字段num

    class HomeController extends Controller 
    {
        public function actionIndex()
        {
            $article = new Article();
            $article -> title = '这是新的一条记录';
            $article -> num = 10;
            // insert 方法可以进行插入,返回一个布尔值表示成功或失败
            // $data = $article -> insert();
            // seve 可以插入,也可以更新,返回一个布尔值表示成功或失败
            $data = $article -> save();
    
            //返回id属性方便进行后继操作
            $id = $article->attributes['id'];
            dd($id);
        }
    }
    

    查看

    模型修改记录

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            $article = Article::findOne(6);
            $article -> title = '哈哈哈哈哈';
            // update 更新操作,返回一个布尔值表示成功或失败
            $data = $article->update();
            // 同样的活save也能做
            // $data = $article->save();
            dd($data);
        }
    }
    

    如果num代表查看次数,我们想每次访问时将其加1怎么操作

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            //updateAllCounters 参数
            //1.要进行递增的字段和递增数
            //2.更新的条件
            //返回本次更新的记录条数
            $data = Article::updateAllCounters(['num' => 1], ['id' => 6]);
            dd($data);
        }
    }
    

    模型删除记录

    class HomeController extends Controller
    {
        public function actionIndex()
        {
            // 删除单条数据
            // $article = Article::findOne(17);
            // $article = Article::find()->where(['id' => 6 ])->one();
            // $data = $article -> delete();
    
            // all方法返回一个数组
            // $article = Article::find()->where(['id' => 6 ])->all();
            // $data = $article[0]->delete();
    
            // 另一种方式
            // 等号换成大小于号扩大查询范围
            // 也可以写成id >:id And num<:num 使用多条件
            $data = Article::deleteAll('id=:id', [':id' => 6]);
            dd($data);
        }
    }
    

    一对多查询

    新建分类表category

    为article 表新增一个表示分类的cate_id字段

    新建分类模型

    models/Category.php

    class Category extends ActiveRecord
    {
        //
    }
    

    取分类下的文章

    controllers/HomeController

    class HomeController
    {
        public function actionAbout()
        {
            // 不推荐的方法
            // 取到第一条分类信息
            // 取分类下文章
            // $category = Category::findOne(1);
            // $articles = Article::find()->where(['cate_id'=> $category->attributes['id']])->all();
    
            // 推荐方法
            // className方法返回类的命名空间
            // hasMany 参数
            // 1.关联模型命名空间
            // 2.关联条件['关联模型外键' => '当前模型主键']
            $category = Category::findOne(1);
            $articles = $category->hasMany(Article::className(), ['cate_id'=> 'id'])->all();
            dd($articles);
        }
    }
    

    一对多查询性能优化

    将关联封装成模型的一个方法可以避免写重复的代码

    controllers/HomeController

    class HomeController
    {
        public function actionAbout()
        {
            $category = Category::findOne(2);
            $articles = $category->getArticles();
            dd($articles);
        }
    }
    

    models/Article.php

    class Category extends ActiveRecord
    {
        public function getArticles()
        {
            $articles = $this->hasMany(Article::className(), ['cate_id'=> 'id'])->asArray()->all();
            return $articles;
        }
    }
    

    另一种取法

    controllers/HomeController

    class HomeController
    {
        public function actionAbout()
        {
            $category = Category::findOne(2);
            //注意这里使用的是模型属性,会调用模型的__get()找到对应的关联方法
            $articles = $category->articles;
            return $this->render('about');
        }
    }
    
    class Category extends ActiveRecord
    {
        public function getArticles()
        {
            // 用属性调用时all方法会被隐式调用,不需要写在get方法中
            $articles = $this->hasMany(Article::className(), ['cate_id'=> 'id'])->asArray();
            return $articles;
        }
    }
    

    一对一查询

    controllers/HomeController

    class HomeController
    {
        public function actionAbout()
        {
            $article = Article::findOne(1);
            $category = $article->category;
            dd($category);
        }
    }
    

    models/Article

    class Article
    {
        public function getCategory()
        {
            $category = $this->hasOne(Category::className(), ['id' => 'cate_id']);
            return $category;
        }
    }
    

    取多文章并带上对应的分类属性

    controllers/HomeController

    class HomeController
    {
        public function actionAbout()
        {
            //错误示例
            // $articles = Article::find()->all();
            // foreach ($articles as $article){
            //     $category[] = $article->category;
            // }
            // dd($category);
    
            // 正确示范
            $articles = Article::find()->with('category')->asArray()->all();
    
            dd($articles);
        }
    }
    
  • 相关阅读:
    深入Spring Security魔幻山谷-获取认证机制核心原理讲解
    灵魂拷问:你真的理解System.out.println()打印原理吗?
    解决分布式React前端在开发环境的跨域问题
    “我以为”与“实际上”
    前端:如何让background背景图片进行CSS自适应
    VSCode如何设置Vue前端的debug调试
    【JDBC】总结
    【JDBC第9章】Apache-DBUtils实现CRUD操作
    【JDBC第8章】数据库连接池
    【JDBC第7章】DAO及相关实现类
  • 原文地址:https://www.cnblogs.com/Qyhg/p/14245822.html
Copyright © 2020-2023  润新知