张弦子:醉清风

愿您每天都有好心情

yii框架
用yii开发个人网站之搭建前后台-上
用yii开发个人网站之搭建前后台-中
用yii开发个人网站之搭建前后台-下

用yii开发个人网站之搭建前后台-中

一 . 为网站后台安装adminLTE界面模板以及yii2-admin,实现基于角色的RBAC权限控制功能。先看一下这两个功能完成后的效果图,如下:

可参考 https://www.kancloud.cn/curder/yii/247759https://www.kancloud.cn/curder/yii/247760 


二 . 用yii2-admin新增一个后台菜单,实现这个菜单的数据的增删查改,为前台的数据展示铺路。

这里以前台的导航菜单做例子,步骤先后顺序不能错:

1.在数据库新建一张表,具体做法是:在sqlYOG的“询问”页面输入以下代码:

CREATE TABLE `nav_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '导航菜单表的自增编号',
  `name` varchar(10) NOT NULL COMMENT '菜单名称',
  `English_name` varchar(30) DEFAULT NULL COMMENT '英文名称(将作为url后缀)',
  `describe` text COMMENT '菜单描述',
  `pid` int(11) DEFAULT '0' COMMENT '上级菜单',
  `order` smallint(1) NOT NULL COMMENT '菜单排序:整数,数值越小,排序越靠左。',
  `status` smallint(1) NOT NULL COMMENT '菜单状态:1代表有效,2代表无效。',
  `creater` int(11) DEFAULT NULL COMMENT '创建者',
  `created_at` int(11) DEFAULT NULL COMMENT '创建时',
  `updater` int(11) DEFAULT NULL COMMENT '修改者',
  `updated_at` int(11) DEFAULT NULL COMMENT '修改时',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=24 DEFAULT CHARSET=utf8

注意要在上面的nav_menu前加上表前缀。
全部选中,按F8或F9等待运行完成,点击数据库名或数据表名,按F5刷新就能看到多了一张nav_menu(前台导航菜单)表了


2.在浏览器输入admin.project1.com/gii ,也就是在后台地址后面加上/gii,就能看到gii页面了,gii是yii2里可以自动生成多种代码的工具,其中的“g"就是generator(生成器)的首字母。如果你打开admin.project1.com/gii报错或没有显示gii工具,请检查配置文件里是否配置了以下代码:

$config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
    ];
gii界面默认是全英文的,鼠标右键点击”翻成中文“即可。怎么用gii呢?

点击”模型生成器“下的”开始“按钮,跳转到模型生成器页面。Table Name的输入框填刚才新建的表名wyt1_nav_menu,Model Class Name填NavMenu(规则是去掉表前缀,首字母变成大写,去掉下划线,把下划线右边的首字母变成大写)Namespace处切记要填backend\models(因为要生成的模型文件要放在后台backend下),不勾选”使用表前缀“的话,生成的模型文件(在E:\www\project1\backend\models\NavMenu.php的表名是wyt1_nav_menu,而勾选了”使用表前缀“后生成的模型文件则是{{%nav_menu}} ,见下图:

  

其实两者都差不多。


3.接下来利用模型类名生成一些增删查改(crud)文件:

点击CRUD Gnerator按钮,进入CRUD Gnerator页面,Model Class填backend\models\NavMenu(注意是反斜杠),Search Model Class填backend\models\search\NavMenuSearch注意是反斜杠,如果models文件夹下没有search文件夹,就要手动新建一个search文件夹),Controller Class填backend\controllers\NavMenuController,View Path填绝对路径E:/www/project1/backend/views/nav-menu(注意是正斜杠,gii会自动生成nav-menu文件夹)或相对路径@backend/views/nav-menu ,如果Model Class下报错:Class 'backend\models\NavMenu' does not exist or has syntax error. 是因为刚才生成的模型文件里没有指定是前端还是后台的命名空间,见下图:

把模型文件的第三行代码namespace app\models;改为namespace backend\models;再来操作就可以操作成功了!成功的话,gii会帮我们自动生成8个文件,其中一个是控制器文件NavMenuController.php,在项目根目录下的backend/controllers文件夹下、一个是搜索功能文件NavMenuSearch.php,在项目根目录下的backend/search文件夹下,其余的6个文件都在项目根目录下的backend/views/nav-menu文件夹下,加上前面生成的模型文件NavMenu.php,一共是9个文件,这9个文件基本上就能满足我们在后台对“前台导航菜单”这个功能的对“数据的增删查改”操作界面的需求了。

4.进入后台admin.project1.com,点击“权限控制”下的“路由列表”,可以看到原本空空如也的界面多了6个视图文件,见下图:


把这6个文件全部选中,点击绿色的按钮,左边的这6个文件就跑到右边去了。点击“菜单列表”的“新增”按钮,名称填“导航菜单”(名称随意,叫“前台菜单”也行),父级名称留空不填,路由输入框输入正斜杠/就会有下拉列表让我们选,选/nav-menu/index,“数据”处填{"icon": "bars", "visible": true},最后点击“新增”按钮就可以看到在后台左侧菜单里多了一个菜单项“导航菜单”了。现在就可以点击这个菜单来新增一条数据了。在新增一条数据前,可以把英文改成中文。

在新增一条数据前,先做一个小功能:由于以后要新增的数据表基本上都有creater(数据创建者)、created_at(创建这条数据时的时间)、updater(数据修改者)、updated_at(修改这条数据时的时间),所以有必要写一个公共方法,以后新增的全部模型文件都继承这个方法就行了。


如上图:这个模型类继承了\common\models\BackendActiveRecord模型类,所以我们要在根目录下的commoon/models下新建一个叫BackendActiveRecord.php的模型文件,它的代码如下:

<?php

namespace common\models;

use Yii;
use yii\db\ActiveRecord;
use backend\models\TableUpdatedAt;

/**
 * ContactForm is the model behind the contact form.
 */
class BackendActiveRecord extends ActiveRecord
{
    public function getUserIP()
    {
        $ip = Yii::$app->request->userIP;
        return ip2long(ip2long($ip)?$ip:'0.0.0.1'); // 转换失败的全用0.0.0.1ip代替
    }

    public function beforeSave($insert)
    {
        if (parent::beforeSave($insert)) {
            
            if($insert) {// 只有在新建记录时执行

                if ($this->hasAttribute('status')) { // 如果模型有 status 字段
                    $this->status = 1;//状态值:1有效
                }
                if ($this->hasAttribute('created_at')) { // 如果模型有 createate 字段
                    empty($this->created_at)?$this->created_at = time():''; 
                }
                if ($this->hasAttribute('updated_at')) { // 如果模型有 updateDate 字段
                    empty($this->updated_at)?$this->updated_at = time():'';
                }
                if ($this->hasAttribute('created_ip')) { // 如果模型有 createIp 字段
                    $this->created_ip = $this->getUserIP();
                }
                if ($this->hasAttribute('creater') && !Yii::$app->user->isGuest) { // 如果模型有 creater 字段
                    $this->creater = Yii::$app->user->id;
                }
                if ($this->hasAttribute('updater') && !Yii::$app->user->isGuest) { // 如果模型有 updater 字段
                    $this->updater = Yii::$app->user->id;
                }
            } else { // 只有在更新记录时执行
                if ($this->hasAttribute('updated_at')) { // 如果模型有 updateDate 字段
                    !empty($this->updated_at)?$this->updated_at = time():'';
                } 
                if ($this->hasAttribute('updated_ip')) { // 如果模型有 updateIp 字段
                    $this->updated_ip = $this->getUserIP();
                };
                if ($this->hasAttribute('updater') && !Yii::$app->user->isGuest) { // 如果模型有 creater 字段
                    $this->updater = Yii::$app->user->id;
                };
            }
            
            $this->updateTableTime();
            
            return true;
        } else {
            return false;
        }
    }
    
    public function beforeDelete()
    {
        $this->updateTableTime();
        return parent::beforeDelete();
    }
    
    public function updateTableTime($table = '')
    {
        if(!$table){
            $table = $this->tableName();
        }
        //保存每个表最后的修改时间
        $TableUpdatedAt = TableUpdatedAt::find()->where(['table'=>$table])->one();
        if(!$TableUpdatedAt){
            $TableUpdatedAt = new TableUpdatedAt();
            $TableUpdatedAt->table = $this->tableName();
        }
        $TableUpdatedAt->updated_at = time();
        $TableUpdatedAt->save();
    }
    
    //获取 Model 错误信息中的 第一条,无错误时 返回 null
    public function getModelError() {
        $errors = $this->getErrors();    //得到所有的错误信息
        if(!is_array($errors)) return '';
        $firstError = array_shift($errors);
        if(!is_array($firstError)) return '';
        return array_shift($firstError);
    }
}

在第7行代码里可以看出,引用了TableUpdatedAt表,这张表的作用是记录对数据库全部表(除了比TableUpdatedAt表更早建立的一些表之外)的操作(增删改),所以我们要新建一张表,表名叫 table_updated_at(注意要加上表前缀),建表的sql语句如下:

CREATE TABLE `表前缀_table_updated_at` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '表最后更改(包括增改删)时间',
  `table` varchar(255) DEFAULT NULL COMMENT '表名',
  `updated_at` int(11) DEFAULT NULL COMMENT '最后此表更改时间',
  PRIMARY KEY (`id`),
  KEY `idx_table` (`table`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8

把视图文件夹里的表单提交视图文件(E:\www\project1\backend\views\nav-menu\_form.php)里的creater、created_at、updater、updated_at四个表单代码删掉,因为在新增一条数据时,上面继承的模型类会帮我们自动填充这四字段的数据。

把这个_form.php文件的代码<?php $form = ActiveForm::begin():?>改为:

<?php $form = ActiveForm::begin([
        'options'=>['class'=>'form-horizontal'],
        'fieldConfig' => [
            'template' => "
                <div class='col-xs-3 col-sm-2 text-right'>{label}</div>
                <div class='col-xs-9 col-sm-7'>{input}</div>
                <div class='col-xs-12 col-xs-offset-3 col-sm-3 col-sm-offset-0'>{error}</div>",
        ]        
        
    ]); ?>
把底下的代码

<div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
</div>
改为:

<div class="form-group">
        <div class="col-xs-3 col-sm-2"></div>
        <div class="col-xs-9 col-sm-7">
            <?= Html::submitButton($model->isNewRecord ? '新增' : '修改', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
        </div>
</div>

这样,全部表单的宽度就不会占据界面的差不多100%了,好看多了:如下图:


“上级”和“排序”这两个字段不应该是文本输入框,因为我们不知道要输入什么内容好,应该是下拉列表,让我们选择其一。

“上级”其实就是要新增的这个菜单的老爸,比如:我的个人网站前台导航菜单“技术交流”是它下属的“yii框架”、“thinkPHP框架”等儿子们的共同老爸,所以“上级”的数据来源还是要从这张表的全部条数据的字段“名称”里找,但是一开始,这张表连一条数据都没有,怎么办呢?我们可以加上一个不存在于数据表里的四个字“顶级菜单”,“上级”里选择这个“顶级菜单”,说明它没有老爸,它本身就是老爸,他本身就是最高级别的顶级菜单。或者不叫“顶级菜单”,叫“没有上级”也行。方法如下:

在控制器文件NavMenuController.php文件里新增:use backend\models\NavMenu;

在actionCreate方法里新增$navMenu = NavMenu::find()->where(['status'=>1])->all();意思就是从数据表nav_menu里查询status等于1(状态等于1,就是状态有效)的全部数据出来并赋值给$navMen,还要在render下加上'navMenu'=>$navMenu,表示把这个变量传到视图里。同理,在actionUpdate方法也要这么写。因为数据不但在新增时会让我们选择上级和状态,在修改时也会让我们选择,当然,你可以保持原数据不修改。这样,视图文件夹nav-menu下的create.php(新增)和update.php(修改)文件就获取了从控制器传来的变量$navMenu,由于代码精简、优雅的yii2框架把新增数据和修改数据这两个常用功能合并到_form.php文件里了(我不得不佩服yii框架的开发者薛强博士,为yii注入了很多比php其它框架更先进的功能),所以create.php和update.php这两个文件代码非常少,主要作用都是把表单提交(就是数据新增、修改)功能扔给_form文件来做(通过render渲染_form.php视图并把变量$model传过去)。

在_form.php文件里引用use yii\helpers\ArrayHelper;

把<?=$form->field($model,'pid')->textInput() ?>改成:

<?=$form->field($model,'pid')->dropDownList(ArrayHelper::map($navMenu,'id','name'),['prompt'=>'顶级菜单']) ?>

“上级”的下拉列表完成了,现在来完成“状态”的下拉列表:

在/common/models/BackendActiveRecord.php文件新增一个公共静态方法,代码如下:

public static function status()
{
        $status = [
            1 => '有效',
            2 => '无效',
        ];
        return $status;
}
在视图文件_form.php里引用use common\models\BackendActiveRecord;

把代码<?= $form->field($model, 'status')->textInput() ?>改成:<?= $form->field($model, 'status')->dropDownList(BackendActiveRecord::status()) ?>

大功告成,看看效果:




三. url美化。

在没启用url美化之前,yii2默认的url地址栏是如“http://admin.project1.com/index.php?r=admin%2Froute%2Findex”这般长且难看的,其中“admin.project1.com”是这个网站后台的二级域名,也就是主网址,正斜杠后面的“index.php”代表入口文件名,问号代表可以接上参数,“r”代表route(路由),“r=”后面的值就是路由的内容了,由模块module(例如这里的admin模块)、控制器controller(例如这里的route),方法也叫动作action(例如这里的index函数)三部份组成,这也是包括php的其它框架例如thinkPHP等通用的设计思想,一个控制器里可以有多个方法,一个模块里可以有多个控制器,yii2把这三者之间用“%2F”连接起来,新手可能不知道这个“%2F”是什么,其实很简单,它就是正斜杠“/”,只不过在这里会被显示成“%2F”,“admin%2Froute%2Findex”其实就是“admin/route/index”,现在我们想要的效果是把这么长、还很难看的url变成“http://admin.project1.com/admin/route/index”,也就是去掉index.php文件名、去掉“?r=”,把全部“%2F”变成正斜杠“/”

只需在根目录下的common/config下的main.php或main-local.php文件的components里加上代码如下即可:

'urlManager' => [
            'enablePrettyUrl' => true,//开启url美化
            'enableStrictParsing' => false,  //不启用严格解析
            'showScriptName' => false,   //隐藏index.php
            'rules' => [
                '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
                '<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
            ],
        ],
还要在backend/web下新增一个 .htaccess文件(注意:这个文件是没有文件名的,只有一点和后缀名),文件的内容很简单:

Options +FollowSymLinks
IndexIgnore  */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond  %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
如果你无法新建这样的一个没有文件名的文件,可以在百度上下载。


四. 解决一些小问题。

1.我们从yii官网下载的新版的高级版本其实有一些小问题,其中一个小毛病就是框架文件夹(就是整个项目)自带的jQuery文件版本太高了(3.*以上),导致出现这两个小问题:1.当我们在父级名称输入框输入一个正斜杠时,yii2默认会有“自动完成”功能,但是位置偏到离谱,跑到左上角去了。还导致了第2点报错。

问题见:https://www.yiichina.com/question/3307

解决办法就是在网上下载2.*版本的jQuery文件,然后替换掉系统自带的jQuey文件,打开E:\www\project1\vendor\bower-asset\jquery\dist下的jquery.js文件,注意不是它隔壁的jquery.min.js文件,可以在第2行看到这个文件的版本,我这里的是v3.4.1,我把整个文件替换成v2.1.1版本的文件,上述两个小问题马上迎刃而解。如下图:

有可能还会出现一个小问题,后台F12可能会报错,提示找不到ui-bg_flat_75_ffffff_40x100.png图片,其实这个就是上面自动完成区域的白色背景图,非常小的一块图片,在https://www.jsclasses.org/browse/file/3412.html下载,放到合适的位置就可以了。例如我的是放在E:\www\project1\backend\web\assets\6334a636\images下


2.如果没有把E:\www\project1\backend\config下的main.php或main-local.php文件里的

'modules' => [
        'admin' => [
            'class' => 'mdm\admin\Module',
            //'layout' => 'left-menu',//yii2-admin的导航菜单
        ]
    ],
的 'layout' => 'left-menu', 删掉或注释掉,就会出现让人抓狂的问题,如下图:

点击左侧菜单,跳转到的页面是如下图这样的:


可以看到,已经没有左侧和顶部这两个公共页面 了,只要把 'layout' => 'left-menu', 删掉或注释掉就行(话说回来,系统本来就没这行代码,是我们后来加上的),好了,效果就是如下图:


3. yii2框架从官网下载下来后,里面基本上都是英文,通过gii生成的文件,里面也是纯英文,可以在\common\config\main.php或main-local.php文件的return [] 的里面加上一行代码'language' => 'zh-CN', 来设置成中文,注意别把这句代码放在components里面。如果这样做之后,还是实现不了中文化,可以分别在前台的\frontend\config\main.php或main-local.php文件和\backend\config\main.php或main-local.php文件的return [] 里加上代码'language' => 'zh-CN', 来设置成中文,注意别把这句代码放在components里面。视图详情页(例如E:\www\project1\backend\views\video\view.php)文件里的Are you sure you want to delete this item?(confirm的值),有时候在设置了中文化的情况下,还是英文,就要直接把它改成中文了,另外,视图列表页(例如E:\www\project1\backend\views\video\index.php)文件里的点击删除按钮后的确认文字Are you sure you want to delete this item?在E:\www\project1\vendor\yiisoft\yii2\grid\ActionColumn.php文件的第148行处,无需把这句英文改成中文(yii2框架里的全部核心代码最好别改!)语言文件在E:\www\project1\vendor\yiisoft\yii2\messages\zh-CN的yii.php文件里,例如里面的这句'Are you sure you want to delete this item?' => '您确定要删除此项吗?',就是把英文翻译成简体中文了,还有繁体中文和世界多国文字,如果没有你想要的异国文字,你可以上网下载,放在\vendor\yiisoft\yii2\messages\新建文件夹,例如english,下,把语言文件重命名为yii.php即可。

现在可以愉快地玩耍了 n_n 。