初学ThinkPHP,搭建一个网上商城

断断续续花了7天勉勉强强的把ThinkPHP视频教程看完了,现在尝试着用框架来搭建一个网上商城网站。
运行环境如下:

Apache Version :  2.4.9   
PHP Version :     5.5.12
MySQL Version :   5.6.17
ThinkPHP Version :3.2.3

下载ThinkPHP完毕,解压到站点目录,开始配置入口 index.php 文件

// 检测PHP环境
if(version_compare(PHP_VERSION,'5.3.0','<'))  die('require PHP > 5.3.0 !');
// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
define('APP_DEBUG',True);
// 定义应用目录
define('APP_PATH','./NutStore/');
// 定义自动生成的安全文件
define('DIR_SECURE_FILENAME', 'default.html');
define('DIR_SECURE_CONTENT', 'Dir!');
// 引入ThinkPHP入口文件
require './ThinkPHP/ThinkPHP.php';

修改了自动生成的文件为default.html,方便区分index,安全文件里面内容为“Dir!”,但是发现服务器不认default.html,那么修改下Apache配置文件在末尾加上default.html

<IfModule dir_module>
    DirectoryIndex index.php index.php3 index.html index.htm default.html
</IfModule>

既然来了,顺便修改下服务器支持路径重写。
加载路径重写模块,去掉#的注释符

LoadModule rewrite_module modules/mod_rewrite.so

授权

<Directory "F:/wamp2/bin/apache/apache2.4.9/cgi-bin">
	AllowOverride None #改为All
	Options None
	Require all granted
</Directory>

在配置文件中新增字段

'URL_MODEL' =>  2,

在入口文件新增.htaccess文件(花生的这个版本的ThinkPHP集成了),内容为

<IfModule mod_rewrite.c>
  Options +FollowSymlinks
  RewriteEngine On

  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

重启下Apache,应该就可以了,下面的路径完整路径是这样的

站点/index.php/home/Index/index

这时去掉index.php应该也是可以的了

站点/home/Index/index

只要执行了入口文件,那么就会生成一个NutStore应用,里面有个默认的Home模块,模块里有个Index控制器,控制器里有个index()方法,花生想要再建立一个模块,复制一份Home,改名为Admin,并且修改Controller里面的命名空间。
下面为站点NutStoreAdminControllerIndexController.class.php

<?php
namespace AdminController;
use ThinkController;
class IndexController extends Controller {
    public function index(){
        echo 'AdminIndexController -> index()';
    }
    public function test(){
        echo 'AdminIndexController -> test()';
    }
}

那么在URL中 站点/Admin/Index/test ,相当于执行了NutStore应用下的Admin模块下的Index控制器下的test方法,会打印出AdminIndexController -> test()

这里花生还要新建一个入口文件,复制index.php入口文件命名为admin.php,在引入ThinkPHP入口文件之前添加如下代码,来将入口文件绑定到Admin模块。

$_GET['m']='Admin';

m参数表示模块,c参数表示控制器,a参数表示操作,这些都是在入口文件可以绑定的,根据需要有爱自绑吧。

接下来配置数据库信息。

因为在本地调试用本地的数据库,而远程肯定用远程仓库,而在本地测试时肯定是开启调试模式的,开启调试模式会自动加载 站点ThinkPHPConfdebug.php 配置文件,而无论是否开启调试模式都会加载 站点ThinkPHPConfconvention.php 配置文件,那么我们就把远程的数据库信息写在convention.php里面,本地的卸载debug.php里面,因为debug.php仅会在开启调试模式下生效,且优先级大于convention.php,那么就没有问题了

我们这里使用pdo数据库配置,下面是debug.php新增的字段,顺便把调试工具打开

'DB_TYPE'				=>	'pdo',	//数据库类型
'DB_USER'				=>	'root',	//用户名
'DB_PWD'				=>	'', 	//密码
'DB_PREFIX'				=>	'think_', //数据库表前缀
'DB_DSN'				=>	'mysql:host=localhost;dbname=think;charset=UTF8',
//开启调试工具
'SHOW_PAGE_TRACE' =>true,

调试过程

使用过程中发现了一些bug,下面尝试修复下

1.Think\Model::getTableName()方法不会读取由Think\Model::table()方法创建的数据表,因此修改下判断规则

public function getTableName() {
	if(empty($this->trueTableName)) {
		$tableName  = !empty($this->tablePrefix) ? $this->tablePrefix : '';
		if(!empty($this->tableName)) {
			$tableName .= $this->tableName;
		}else if(!empty($this->options['table'])){
			$tableName = $this->options['table'];
		}else{
			$tableName .= parse_name($this->name);
		}
		$this->trueTableName    =   strtolower($tableName);
	}
	return (!empty($this->dbName)?$this->dbName.'.':'').$this->trueTableName;
}

2.如果调用过Think\Model::add()方法,会自动调用Think\Model::getTableName()方法来生成Think\Model::trueTableName属性,这会导致如果之后调用Think\Model::table()会完全不起作用,因此将Think\Model::table()方法开头加入一行代码清掉trueTableName值

public function table($table) {
	$this->trueTableName=null;
	$prefix =   $this->tablePrefix;
	if(is_array($table)) {
		$this->options['table'] =   $table;
	}elseif(!empty($table)) {
		//将__TABLE_NAME__替换成带前缀的表名
		$table  = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $table);
		$this->options['table'] =   $table;
	}
	return $this;
}

3.发现使用/ThinkPHP/Library/Think/Verify.class.php生成的验证码第一个字符总是有很大的边距,查看源代码发现ThinkPHP使用的是左边距来缩进,而第一个字符不需要左边距,它也缩进了,因此我们修改一下类里面entry方法

$codeNX = -$this->fontSize*1.0;

这样第一个字符就只有一点点的缩进了

改造过程

发现ThinkPHP的图片和CSS等文件都默认的应该放在./Public/文件夹,感觉很不科学,因此花生自己在./Nutjs/Home/下建立了一个Public文件夹,里面存放着各个控制器要使用的资源文件

QQ截图20151224153813

花生还希望能够有一个模板常量可以直接指向这个文件夹,因此在.\Nutjs\Common\Conf\config.php文件中定义了一个常量

'TMPL_PARSE_STRING' =>array(
	'__public__'    =>  __ROOT__.str_replace('.','', APP_PATH).'Public/'.CONTROLLER_NAME,
)

后来发现在配置文件中定义的话,有很多常量都不认,比如CONTROLLER_NAME,花生希望这个小写的__self__可以直接指向花生新建的资源文件夹下的当前控制器。

好吧,换个地方定义,定义在.\ThinkPHP\Library\Behavior\ContentReplaceBehavior.class.php里面就好了

// 系统默认的特殊变量替换
$replace =  array(
	'__ROOT__'      =>  __ROOT__,       // 当前网站地址
	'__APP__'       =>  __APP__,        // 当前应用地址
	'__MODULE__'    =>  __MODULE__,
	'__ACTION__'    =>  __ACTION__,     // 当前操作地址
	'__SELF__'      =>  htmlentities(__SELF__),       // 当前页面地址
	'__CONTROLLER__'=>  __CONTROLLER__,
	'__URL__'       =>  __CONTROLLER__,
	'__PUBLIC__'    =>  __ROOT__.'/Public',// 站点公共目录
	//自己定义的模板常量
        '__public__'    =>  __ROOT__.str_replace('.','', APP_PATH).BIND_MODULE.'Public/',// 站点公共目录,
        '__self__'      =>  __ROOT__.str_replace('.','', APP_PATH).BIND_MODULE.'/Public/'.CONTROLLER_NAME,// 站点公共目录+当前控制器名,

);

定义好了路径,发现ZendStudio会对模板文件报错,说没有这个CSS/JS文件,这个简直不能忍啊,必须要屏蔽报错!

在一位高手的指点下,找到了屏蔽报错的位置

QQ截图20151224200230

发现ThinkPHP的模板中内置标签不支持__XXX__这种常量,花生之前定义的失效的,失效的原因有2

  • 之前定义的__XXX__是针对浏览器加载的,而include标签是PHP代码,路径不通用
  • ThinkPHP的include标签检查替换在解析__XXX__常量之前

那么就这么做,首先还是在.\ThinkPHP\Library\Behavior\ContentReplaceBehavior.class.php增加

'__INCLUDE__'     => APP_PATH.BIND_MODULE.'/Public/Include/'.CONTROLLER_NAME,//公共引用文件夹

注意这个常量和前面的几个不一样,是./开头的PHP路径

然后修改.\ThinkPHP\Library\Think\Template.class.php文件里面的函数如下

protected function compiler($tmplContent) {
	// 模版编译过滤标签,替换各种__XXX__常量
	//这里在开始解析一遍,末尾再解析一遍,可以确保include等特殊标签页支持__XXX__常量
	//var_dump($tmplContent);
	Hook::listen('template_filter',$tmplContent);
	//模板解析
	$tmplContent =  $this->parse($tmplContent);
	// 还原被替换的Literal标签
	$tmplContent =  preg_replace_callback('/<!--###literal(\d+)###-->/is', array($this, 'restoreLiteral'), $tmplContent);
	// 添加安全代码
	$tmplContent =  '<?php if (!defined(\'THINK_PATH\')) exit();?>'.$tmplContent;
	// 优化生成的php代码
	$tmplContent = str_replace('?><?php','',$tmplContent);
	// 模版编译过滤标签,替换各种__XXX__常量
	Hook::listen('template_filter',$tmplContent);
	return strip_whitespace($tmplContent);
}

问题就解决了,不过也带来了而额外的性能消耗

又发现如果控制器调用create方法后,提交的数据没有被临时的存储,调用起来比较麻烦,比如在数据表模型类中无法方便的取得,因此修改

//修改文件:.\ThinkPHP\Library\Think\Model.class.php
    /**
     * 通过create提交的未经过滤的数据
     * @var submit
     * @access public
     * */
    public $submit=array();

     public function create($data='',$type='') {
     	//PeA:首先将传来的数据放到一个字段中
     	$this->submit=$data;
     	//......

又发现ThinkPHP在执行create的时候会强制执行所有的自动验证$_validate,哪怕是提前用field()方法指定都不行,这个明显不科学!

改吧。。。

//修改文件:.\ThinkPHP\Library\Think\Model.class.php
//autoValidation方法
        //...
        foreach($_validate as $key=>$val) {
                //PeA:让自动验证读取$this->options['field']
                if(!empty($this->options['field']) && !in_array($val[0],         $this->options['field'])) continue;
                // 验证因子定义格式
                // array(field,rule,message,condition,type,when,params)
                // 判断是否需要执行验证
                if(empty($val[5]) || ( $val[5]== self::MODEL_BOTH && $type < 3 ) || $val[5]== $type ) {
        //...
//create方法也要改
        //PeA:让自动验证读取$this->options['field']
        $this->options['field']=$fields;
        // 数据自动验证
        if(!$this->autoValidation($data,$type)) return false;
        //PeA:释放配置
        unset($this->options['field']);

        // 表单令牌验证
        if(!$this->autoCheckToken($data)) {
            $this->error = L('_TOKEN_ERROR_');
            return false;
        }
  • 浏览:1678
  • 评论:0

发表新的回复