yii2框架实现引导安装功能

最近有在学习yii框架,想着做一个小型的cms。
一个cms最开始的动作当然时安装引导,参考lulucms的代码,学习实现的结果如下。

第一步 安装介绍 (install.php?r=install/step_one)

 安装介绍

第二步 环境检查 (install.php?r=install/step_two)

环境检查

第三步 数据库配置 (install.php?r=install/step_three)

第四步 安装数据库表 (install.php?r=install/step_processing)

安装数据库表

第五步 结果显示 (install.php?r=install/finish)

这里写图片描述

其它 已经安装提示 (install.php?r=install/stop)

这里写图片描述

实现思路:

1.安装模块独立出来,命名为install,目录结构和正常的backend应用差不多.

这里写图片描述

2.前台的入口文件判断是否已经安装,通过文件锁进行标识,如果没有安装过则执行安装应用的入口文件install.php,已经安装则正常进入FrontApplication应用

3.进入了应用分几步依次进行,先进行环境判断,再进行数据库信息填写,最后安装数据库文件,如果安装成功,则创建一个安装锁文件install.lock,代表已经安装过程序.


记录笔记

1.yii2如何创建一个install应用?

如果使用yii advanced高级模板,以backend应用的入口文件举栗子,代码是长这样子的

<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');

$config = yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/../../common/config/main.php'),
    require(__DIR__ . '/../../common/config/main-local.php'),
    require(__DIR__ . '/../config/main.php'),
    require(__DIR__ . '/../config/main-local.php')
);

(new yii\web\Application($config))->run();

显然,应用是通过配置文件进行初始化的,因为自带的backend的入口文件位于 backend/web/index.php,所以它的配置文件位置就是/../config/main.php

打开backend的主配置文件main.php 可以看到如下代码

<?php
$params = array_merge(
    require(__DIR__ . '/../../common/config/params.php'),
    require(__DIR__ . '/../../common/config/params-local.php'),
    require(__DIR__ . '/params.php'),
    require(__DIR__ . '/params-local.php')
);

return [
    'id' => 'app-backend',
    'basePath' => dirname(__DIR__),
    'controllerNamespace' => 'backend\controllers',
    'bootstrap' => ['log'],
    'modules' => [],
    'components' => [
        'request' => [
            'csrfParam' => '_csrf-backend',
        ],
        'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
        ],
        'session' => [
            // this is the name of the session cookie used for login on the backend
            'name' => 'advanced-backend',
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],
        /*
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
            ],
        ],
        */
    ],
    'params' => $params,
];

其中返回的数组中
‘id’ => ‘app-backend’ //作用是指定全局唯一的应用id号,
‘basePath’ => dirname(__DIR__) //指定backend应用的目录位置
‘controllerNamespace’ => ‘backend\controllers’ //指定控制器命名空间
‘bootstrap’ => [‘log’] //指定随应用启动而启用的组件
‘modules’ => [], //指定应用的子模块 可理解为应用中的应用
‘components’ => …. //往应用注册组件
‘params’ => $parms //用户本地的一些配置参数

到这里答案就很明显了,假设现在需要自定义一个新的独立的安装引导应用,也就是需要在根目录中建立一个文件夹 命名为install,在install下新建一个main.php,保存应用的关键配置信息
这里写图片描述

main.php 配置信息如下

<?php

return [
    'id' => 'app-install',//指定模块名称
    'language' => 'zh-CN',
    'basePath' => dirname(__DIR__), //指定模块路径
    'bootstrap' => ['log'],
    'controllerNamespace' => 'install\controllers',//指定命名空间
    'defaultRoute' => 'install/index', //默认路由
    'components' => [
        //错误处理
        'errorHandler' => [
            'errorAction' => 'install/index',
        ],
    ],

];

同时,公共配置 根目录/data/config/main.php 代码如下

<?php
return [
    //第三方库加载路径
    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
    //运行缓存文件路径
    'runtimePath' => dirname(dirname(__DIR__)) . '/data/runtime',

    //组件配置
    'components' => [
        //缓存组件
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        //session组件
        'session' => [
            // this is the name of the session cookie used for login on the frontend
            'name' => 'advanced-frontend',
        ],
        //日志组件
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        //错误处理组件
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],
        //资源管理组件
        'assetManager' => [
            'basePath' => '@webroot/frontend/assets',
            'baseUrl'=>'@web/frontend/assets',
            'bundles' => [
                // you can override AssetBundle configs here
             ],
            'linkAssets' => true,
            // ...
        ],
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => '6_NpujNa4RDqhQsB9IfERWwD4F9GWbls',
        ],
    ],
];

入口文件 install.php存放在根目录下

<?php
use yii\web\Application;
use source\libs\Common;

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/common/config/bootstrap.php');
require (__DIR__. '/source/config/overwrite.php');

$config = yii\helpers\ArrayHelper::merge(
    //公共组件
    require(__DIR__ . '/data/config/main.php'),
    //模块独立组件
    require(__DIR__ . '/install/config/main.php')
);


(new Application($config))->run();

可以看到,除了添加公共配置文件外,还添加了应用主配置文作为应用最终的配置信息。

在上方包含的bootstrap.php文件中,它的作用是起别名,因为yii框架自身的类加载机制依赖别名,所以在bootstrap.php中需要给新增的install应用添加别名,指定install的目录路径,bootstrap最终代码如下

<?php
Yii::setAlias('@common', dirname(__DIR__));
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');

Yii::setAlias('@source', dirname(dirname(__DIR__)).'/source');
Yii::setAlias("@install",dirname(dirname(__DIR__)).'/install');
Yii::setAlias("@data",dirname(dirname(__DIR__)).'/data');

假设已经给install建立好了默认的控制器文件,那么此时访问index.php,不出意外的话就会跳转到index.php?r=install/index,如果成功,就说明应用成功的跑起来了。

2.如何不改动yii框架源码的情况下重载yii的类

场景:install模块检查用户系统环境时需要使用到yii2的FileHelper类,来检测文件夹的可读可写,需要重载此类,而又不想直接改Yii的源代码。

解决方法: 通过Yii::$classMap 改变类与路径的映射关系,在入口文件中引入以下文件,命名为overwrite.php

<?php
$alias = '@source/helpers/FileHelper.php';
Yii::$classMap['yii\helpers\FileHelper'] = $alias ;

原理: 因为在Yii2源码 yii2\BaseYii中,自动加载机制如下


public static function autoload($className)
{
    //先判断映射中是否存在
    if (isset(static::$classMap[$className])) {
        $classFile = static::$classMap[$className];
        if ($classFile[0] === '@') {
            $classFile = static::getAlias($classFile);
        }
    //判断是否指定命名空间
    } elseif (strpos($className, '\\') !== false) {
        $classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', false);
        if ($classFile === false || !is_file($classFile)) {
            return;
        }
    } else {
        return;
    }

    include($classFile);
    //抛出异常
    if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
        throw new UnknownClassException("Unable to find '$className' in file: $classFile. Namespace missing?");
    }
}

3.关于PHP函数的收获

a.获取运行PHP当前系统
  PHP_OS,是常量

b.获取php.ini的配置信息 如获取最大上传空间限制
  get_cfg_var('upload_max_filesize')

c.获取磁盘剩余空间
  disk_free_space()函数

d.判断是否可写
  is_writable(filename)

e.获取PHP版本号
  PHP_VERSION 常量

f.判断某个类是否存在
  class_exists()

g.判断某个扩展是否加载
  extension_loaded()

h.把变量打印处理 其返回的表示是合法的PHP代码
var_export($var)
用在用户填写了数据库配置信息,然后把数组变为合法的php代码,把这些代码写入文件中.
4.如何绑定事件 ?

在填写完数据库信息后,点击下一步,先渲染出界面,同时动态的输出安装过程的信息,而不是程序执行到最后一步才渲染页面,这样等待的时间长。这就使用到yii2的绑定.

InstallController继承的控制器是BaseController,而BaseController继承的是yii\web\Controller;

在BaseController中的init初始化方法中,进行对EVENT_AFTER_SEND事件的绑定,EVENT_AFTER_SEND 是指在动作完成后才会被触发的动作。
Response是yii2框架中的HTTP响应组件,

 public function init()
    {
        parent::init(); 
        //绑定yii EVENT_AFTER_SEND事件
        Yii::$app->response->on(Response::EVENT_AFTER_SEND,[$this,'afterResponse']);
    }

所有的子类继承BaseController后,通过建立afterResponse方法就能在方法执行后触发一些自定义的行为动作.在InstallController中

 //绑定了EVENT_AFTER_SEND事件
    public function afterResponse()
    {  
    //如果当前的请求方法为processing 
       if (Yii::$app->requestedAction->id == 'processing') {
       //那么这个方法执行后 就执行下面的_installing方法
            $this->_installing();
       }
    }
5.yii2如何检测数据库是否连接成功 ?
//$this->InstallForm是表单模型
$config = [
            'dsn'=>"mysql:host={$this->InstallForm->dbHost};dbname={$this->InstallForm->dbName}",
            'username' => $this->InstallForm->dbUser,
            'password' => $this->InstallForm->dbPassword
        ];

        $db = new Connection($config);
        try {
            $db->open();
            $result = $db->isActive? ['status'=>true,'msg'=>'数据库连接成功']:['status'=>false,'msg'=>'数据库连接失败'];
        }catch (Exception $e) {
            $db->close();
            $result = ['status'=>false,'msg'=>$this->getDbError($e->getMessage(),[
                'dbHost'=>$this->InstallForm->dbHost,
                'dbName'=>$this->InstallForm->dbName
            ])];
        }

6.如何使用yii\db\Connection执行sql语句

//Connection为yii\db\Connection
try
       {
           $db = new Connection($dbConfig);
           Lychee::getApp()->set('db',$db);

           $db->createCommand("USE {$this->InstallForm->dbName}")->execute();
           $db->createCommand("SET NAMES 'utf8'")->execute();

           self::_showLog("准备初始化数据库",true);
           return $db;
       }
       catch(\Exception $e)
       {
           var_dump($e->getMessage());
           $error = self::getDbError($e->getMessage(),[
               'dbHost' => $this->InstallForm->dbHost,
               'dbName' => $this->InstallForm->dbName
           ]);

           self::_showLog($error,false);
           return false;
       }

7.如何利用缓冲动态输出安装过程

 $str = "安装过程数据";
 echo $str;
 ob_flush();
 flush();

8.如果已经安装过 如何防止再进入页面进行安装

//使用beforeAction进行检查 $action->id为当前的动作名称
public function beforeAction($action)
    {
        if ($action->id == 'stop' || $action->id == 'finish') {
            return parent::beforeAction($action);
        }
        if (file_exists(Yii::getAlias('@data/install.lock')))   {
            return $this->redirect(['stop']);
        }
        return parent::beforeAction($action);
    }

9.如何使用数据库操作事务

//任何一个语句失败将会回滚
 $db = Yii::$app->get('db');
 $transaction =  $db->beginTransaction();
 try {
       $db->createCommand($sql)->execute();
       $transaction->commit();
       return true;
  }catch(\Exception $e) {
      $transaction->rollBack();
      echo $e->getMessage();
      $this->_showLog("建立数据库表时出错",false);
      return false;
  }

9.如果还有的话 再来补充

长风破浪会有时,直挂云帆济沧海

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值