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

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

[code]
Apache Version : 2.4.9
PHP Version : 5.5.12
MySQL Version : 5.6.17
ThinkPHP Version :3.2.3
[/code]

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

[code]
// 检测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';
[/code]

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

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

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

[code]
LoadModule rewrite_module modules/mod_rewrite.so
[/code]

授权

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

在配置文件中新增字段

[code]
'URL_MODEL' => 2,
[/code]

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

[code]
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On

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

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

[code]
站点/index.php/home/Index/index
[/code]

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

[code]
站点/home/Index/index
[/code]

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

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

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

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

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

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

接下来配置数据库信息。

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

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

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

调试过程

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

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

[code]
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;
}
[/code]

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

[code]
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;
}
[/code]

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

[code]
$codeNX = -$this->fontSize*1.0;
[/code]

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

改造过程

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

QQ截图20151224153813

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

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

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

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

[code]
// 系统默认的特殊变量替换
$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,// 站点公共目录+当前控制器名,

);
[/code]

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

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

QQ截图20151224200230

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

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

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

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

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

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

[code]
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);
}
[/code]

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

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

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

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

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

改吧。。。

[code]
//修改文件:.\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;
}
[/code]

  • 浏览:3224
  • 评论:0

发表新的回复