自学PHP大半年多了断断续续地但是最终还是坚定了我的想法将PHP继续下去所以写这个PHP的博客是为了找个稳定的 PHP工作不求工资多高但求一收留之地我能看懂大部分英语文档人不蠢爱学习有兴趣地可以联系下!有诚意的来吧!qq:
我会分次发布所有关键代码和文档说明博客后台所有的样式均套用博客园!
说明
不完全采用MVC架构但是理念就是这样的因为还不能写出很稳定的MVC架构
几乎不采用JQUERY AJAX 因为不是特别熟悉运用起来还不自如留言本可以用AJAX没问题
有几个公用类其他代码均手写有不足地地方请多多指出非常感谢
欢迎批评与指导但是请给出你的理由
言归正传先看数据库架构
这 些表的引擎都是MYISAM 利于存取(黄色钥匙表示的是 primary key 蓝色菱形的表示非空字段 白色菱形表示的 null 字段) 图中的链接仅表示他们之间有一种潜在关系无法在操作时关联因为搜索引擎是 MyISAM 所以需要联合查询 以及多表操作
我会挑最重要的 post category 个表中的特别字段来详细说明其他说重要的
post:
post_id
category_id varchar() 这个是用来索引博文的分类 这里的category_id 也是字符串类型所以可以为每一个博文设置多个分类
type varchar() 这个字段是用来区分 随笔(post)文章(article)和日记(diary)的 同时也是能够 设置为 postDraft articleDraft
visiable 博文是否可见
其他常用字段如 标题内容创建时间最后改动时间浏览次数评论次数标签允许评论以及些保留字段
category
parent count_child_number count_parent_number 用于以后扩展
type 可以分别设置相册博文日记的分类
其他常用字段如 名称描述创建时间可见性
comment:
address 用户IP
user_agent 用户浏览器类型
其它字段略
服务器架构
PHP + MYSQL + APACHE + Windows NT ARISTPC build (Windows Home Basic Edition) i (本地)
博客架构
后台目录
后台目录说明
assert 存放各种资源 jscssimage
class 存放我们的类 常用类如 数据库操作类分页类和我们的大部分model
extention 存放些扩展 如 mce 的富编辑器
config 存放我们的 配置信息
templates 存放所有的模版(没有采用 smarty)
upload 存放的是相片和其他文件
admin 根目录下会有一些类似的控制器 如 indexphp postphp articlephp photophp
我们先看看 admin/config/configphp
复制代码 代码如下:
<?php
ini_set( "display_errors"
true );
date_default_timezone_set( "Asia/Shanghai" );
// root and direcotry separate
define(
DS
DIRECTORY_SEPARATOR);
define(
ROOT
dirname(dirname(__FILE__)));
// database information
// need hash
define( "DB_USERNAME"
"****" );
define( "DB_PASSWORD"
*****
);
define( "DB_NAME"
"blog" );
// important directory
define( "CLASS_PATH"
"classes" );
define( "TEMPLATE_PATH"
"templates" );
// user imformation
define( "ADMIN_USERNAME"
"admin" );
define( "ADMIN_PASSWORD"
$
a$
$wim
kpwHhAKa
MBSsGUMGOYfjkU
xvRKd
Fxwal
wj
dqFboCVSFawim
kpwHhAKa
MBSsGUMGO
);
// hash and verified the password
function hasher($info
$encdata = false){
$strength = "
";
//if encrypted data is passed
check it against input ($info)
if ($encdata) {
if (substr($encdata
) == crypt($info
"$
a$"
$strength
"$"
substr($encdata
))) {
return true;
}else {
return false;
}
} else {
//make a salt and hash it with input
and add salt to end
$salt = "";
for ($i =
; $i <
; $i++) {
$salt
= substr("
/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
"
mt_rand(
)
);
}
//return
char string (
char hash &
char salt)
return crypt($info
"$
a$"
$strength
"$"
$salt)
$salt;
}
}
function __autoload($className) {
if (file_exists(ROOT
DS
classes
DS
strtolower($className)
class
php
)) {
require_once(ROOT
DS
classes
DS
strtolower($className)
class
php
);
} else {
/* Error Generation Code Here */
}
}
这里我们定义了一些基本常量和几个函数
__autoload() 函数加载 admin/class/ 中的所有类
用 hasher() 函数加密了一个 位的 不可逆密码 登录过程就是用configphp 中的常量和 hasher( ) 函数来进行验证
来看我们的 admin/indexphp 后台控制器 这个控制器主页 显示一些博客的相关数据
复制代码 代码如下:
<?php
require_once( "config/config
php" );
session_start( );
$action = isset( $_GET[
action
] ) ? $_GET[
action
] : "";
$username = isset( $_SESSION[
username
] ) ? $_SESSION[
username
] : "";
if ( $action != "login" && $action != "logout" && !$username ) {
login();
exit;
}
switch( $action ){
case "login" :
login( ) ;
break;
case "logout";
logout( );
break;
default :
admin( );
break;
}
function login( ){
$results[
pageTitle
] = "Login Form";
// handle login
if( isset( $_POST[
login
] ) ){
// we simple verify it from constant variable
// if we need to verify the user from database
do this later
// $user = new User ;
// $user
>isValidateUser( $name
$password );
if ( $_POST[
username
] == ADMIN_USERNAME && $_POST[
password
] == hasher($_POST[
password
]
ADMIN_PASSWORD ) ){
// register a session data
$_SESSION[
username
] = ADMIN_USERNAME ;
// location to admin page
header( "Location: index
php");
} else {
// Login failed: display an error message to the user
$results[
errorMessage
] = "Incorrect username or password
Please try again
";
require( TEMPLATE_PATH
"/loginForm
php" );
}
} else {
require( TEMPLATE_PATH
"/loginForm
php" );
}
}
function admin( ){
$results[
pageTitle
] = "Administrator Page";
require( TEMPLATE_PATH
"/admin
php" );
}
function logout( ){
$results[
pageTitle
] = "Login Page";
unset( $_SESSION[
username
] );
header( "Location: index
php ");
}
这个设计模式是从一个老外那里学的!
原理就是
首先我们加载我们的configphp 初始化session变量获得 $action 这个重要变量的值
然后我们判断 $action 和 $username 的值 如果用户没有登录以及用户名为空返回登录页面;
如 果用户正确输入了用户名和密码则注册一个session 变量 $username然后跳转到主页面 indexphp 这时我们会调用默认的 $action admin( ) 这个函数会加载一个模版adminphp里面有个数组变量 $results[pageTitle]以及我们的后台博客样式框架
如果用户输入错了则给出提示信息
这个设计理念的核心就是 give {action} then {do something}
我们会在后面的代码中反复看到
这个就是博客后台的框架样式从博客园copy 来的采用表格布局的兼容的可自定义其他样式的简单的实用的可扩展的完美后台框架
这个样式在其他的浏览器中表现同样兼容写这篇博文的时候我已完成了部分功能 下一篇实现随笔文章日记 以及他们分类的CRUD废话不多说了上一篇有个核心概念就是 give action do something !
这篇我就用代码来解释这个概念是啥意思先看我的 postclassphp 这个文件是我们的数据层处理类
简单介绍一下这个model 类它继承了一个数据库基类来做crud 等常用操作 每次初始化时就会初始化一个数据库对象 $db 我们就用这个对象来操作我们的数据
对于数据操作有个重要方法 storePostFormValues( ) storeDiaryFormValues( )它们个方法是数据流的开始
还 有个方法很有意思addChildNumber( ) reduceChildNumber( ) 它们负责在插入或删除文档时的 一个暗箱操作因为我的文档可以用多个分类所以在操作文档的时候要考虑到一个问题就是 category 表中有个字段 记录了该分类下的 文档数量所以要动态地改变这些数目的值
下面配合 postphp 控制器我们就可以开始我们数据的流程了(我的控制器还不是一个类所以无法生成API文档因为这还不是真正地MVC架构)所以在MVC之前这个也能更利于的理解MVC到底是神马东东以及你自己如何去应用写出自己的MVC
以下的情形都是假设
$action = "天上掉下个女朋友给我吧!"; 让我们传入这个控制器看会发生神马事情
复制代码 代码如下:
require_once( "config/config
php" );
session_start( );
$action = isset( $_GET[
action
] ) ? $_GET[
action
] : "";
$username = isset( $_SESSION[
username
] ) ? $_SESSION[
username
] : "";
if( !$username )
{
header("Location: indexphp?action=login");
exit;
}
这里我们有个重要流程控制语句 switch 这个单词是 开关的意思 所以当上面那个 $action = "天上掉下个女朋友给我吧!"; 传入 switch 时只有种可能一种是开一种是关这里有点双关地意思有些同学可能看出来了嘿嘿!
言归正传看看我们的 switch 是如何开关这些 $action 很明显 天上不会掉个女朋友给我因为控制器里没有这个开关所以只能还是说说代码的事
复制代码 代码如下:
switch( $action )
{
case "newPost" :
newPost( );
break;
case "delete" :
delete( ) ;
break;
case "updatePost":
updatePost( );
break;
case "IsDraft":
listDraft( );
break;
case "logout" :
logout( );
break;
case "isPost":
listPost( );
break;
case "diffentCategoryPost":
diffentCategoryPost( );
break;
case "unCategory":
unCategory( );
break;
default :
listPost( );
break;
}
每个switch都应该定义默认的 开关这样当没有女朋友的时候可以确保我们还有基友
如何传入 action 呢?
来看这样 一个url也就是我们的后台框架的导航 postphp?action=isPost 这个是一个标准的action 我们每个url 其实都是由这些action组成的也可以加入其他的一些参数到我们的url 中 这样我们可以在控制器定义的方法中 GET (得到这些变量的值)然后我们可以多些控制
好了当这个url 到达我们的控制器后我们接收判断然后打开一个 isPost 的开关这样我们就可以调用后面的方法了想想 开关灯开关电脑开关就是我们经常做的事
这里我们只是换了一个地方
ok 来看看这个开关的下面的方法
复制代码 代码如下:
function listPost( )
{
$results = array( );
$results[
pageTitle
] = "Post List" ;
$results[
path
] = "<a >随笔</a>";
// set the message
if ( isset( $_GET[
error
] ) )
{
if ( $_GET[
error
] == "InsertedFailed" ) $results[
errorMessage
] = "文档添加失败";
if ( $_GET[
error
] == "postDeleteFailed" ) $results[
errorMessage
] = "文档删除失败";
}
if ( isset( $_GET[
status
] ) )
{
if ( $_GET[
status
] == "changesSaved" ) $results[
statusMessage
] = "文档保存了!";
if ( $_GET[
status
] == "Deleted" ) $results[
statusMessage
] = "文档删除了!";
if ( $_GET[
status
] == "Inserted" ) $results[
statusMessage
] = "你添加了新的文档!";
if ( $_GET[
status
] == "SaveToDraft" ) $results[
statusMessage
] = "文档保存到了草稿箱!";
}
// 文档的分类浏览
$db = MySQL::getInstance( );
$pagination = new Pagination;
$cat = new Category;
$results[
categories
] = $cat
>getCategoryList("post");
$pagination
>countSQL = "select * from post where type =
post
" ;
$db
>Query( $pagination
>countSQL );
$pagination
>totalRecords = $db
>RowCount( );
$records = $db
>HasRecords( $pagination
>rebuiltSQL( ) );
if( $records )
{
$results[
posts
] = $db
>QueryArray( $pagination
>rebuiltSQL( ) );
require_once(TEMPLATE_PATH
"/post/post_list
php");
}
else
{
require_once(TEMPLATE_PATH
"/post/post_list
php");
}
}
我们定义了一个数组$results = array( ); 这个数组的作用明显它将保存我们从 model 中获取的任何数据也可以保存从url上 GET 的特殊参数然后将在我们下面require_once(*****) 包含的模版中显示出来 路径定义在了 path 变量中
同时我们会接收个提示参数
error 表示操作出现错误任何人都在所难免包括电脑谁都会犯错关键是去承认电脑做的很好他们勇于承认错误
status; 表示状态就是成功的操作
$pagination = new Pagination; 这个类是我们的分页类我们传入一个 总的数量给它然后它自己会算出总页数每跳转一个页面相当于刷新了一次所以大家的做法就是在构造器里 GET(获取)url上的page 的值让我们知道是当前那一页了同时我们重新生成了查询的语句后面加上一条限制的语句类似 limit $start(起始的id) $offset(长度); 原理就是从这个id起往后给我 条记录我的设定就是 条你也可以更灵活
$cat = new Category;这个类后面会详细说也是非常重要的分类model这里我们就是简单获取 这个类型下的所有分类显示在侧边栏我已经完成了有图有真相!
这样 我们的 $results 数组中就储存了我们页面所需的所有数据 好的来看看我们的模版是怎么输出的
复制代码 代码如下:
<!DOCTYPE HTML PUBLIC "
//W
C//DTD HTML
Transitional//EN" "
<html>
<head>
<title>
博客后台管理</title>
<meta http
equiv="Content
Type" content="text/html; charset=utf
"/>
<link rel="stylesheet" type="text/css" href="assert/css/blog
css" />
</head>
<body id="Posts">
<table id="BodyTable" border="
" cellpadding="
" cellspacing="
" width="
%">
<tr>
<td id="Header" colspan="
"><div id="SiteNav"></div>
<div id="BlogTitle">
Arist
s Blog
</div>
<div id="Site Title">
<b><blockquote>Hinging there
everything will be fine
</blockquote></b>
</div>
</td>
</tr>
<tr>
<td>
<div id="LeftNavHeader">操作</div>
</td>
<td class="NavHeaderRow">
<ul id="TopNav">
<li><a href="post
php?action=IsPost">随笔</a></li>
<li><a href="article
php?action=IsArticle">文章</a></li>
<li><a href="diary
php?action=IsDiary">日记</a></li>
<li><a href="comment
php?action=IsComment">评论</a></li>
<li><a href="photo
php?action=IsPhoto">相片</a></li& gt;
</ul>
<div id="SubNav">
当前位置:<?php if( isset( $results[
path
] )) echo $results[
path
]; ?>
</div>
</td>
</tr>
<tr>
<td class="NavLeftCell">
<div class="left_nav">
<ul id="LinksActions">
<li><a href="article
php?action=newArticle">» 添加新文章</a></li>
</ul>
</div>
<div id="CategoriesHeader" class="LeftNavHeader">
分类
</div>
<div class="left_nav">
<ul id="LinksCategories">
<li><a href="category
php?action=ListCat&type=article">[编辑分类]</a></li>
<li><a href="article
php?action=IsArticle">[所有分类]</a></li>
<li><a href="article
php?action=unCategory">[未分类]</a></li>
<?php
if( isset( $results[
categories
] ) && ! empty( $results[
categories
] ) ){
foreach( $results[
categories
] as $category ){
echo <<<EOB
<li><a href="article
php?action=diffentCategoryArticle&catID={$category[
category_id
]}">{$category[
name
]}({$category[
count_child_number
]})</a></li>
EOB;
}
}
?>
</ul>
</div>
</td>
<td id="Body">
<div id="Main">
<div id="Editor_Messages">
<!
显示提示信息
>
<?php
if( isset( $results[
statusMessage
] )){echo $results[
statusMessage
];}
if( isset( $results[
errorMessage
] )){echo $results[
errorMessage
];}
?>
</div>
<div id="Editor_Results">
<div id="Editor_Results_Header" class="CollapsibleHeader">
<span id="Editor_Results_headerTitle">文章(主要用于转载
发布原创博文要通过“随笔”)</span>
</div>
<div id="Editor_Results_Contents">
<?php
if( isset( $results[
posts
] )){
echo <<<EOB
<table id="Listing" class="Listing" cellspacing="
" cellpadding="
" border="
" style=
width:
%;
>
<tr style="text
align: center">
<th valign="bottom">
标题
</th>
<th width="
" style="text
align: center">
发布<br />
状态
</th>
<th valign="bottom" width="
" style="text
align: center">
评论
</th>
<th width="
" style="text
align: center">
页面<br />
浏览
</th>
<th valign="bottom" width="
" style="text
align: center">
操作
</th>
<th valign="bottom" width="
" style="text
align: center">
操作
</th>
</tr>
EOB;
foreach( $results[
posts
] as $post ){
$time = date("Y
m
d H:i:s"
$post[
create_time
]);
if( $post[
status
] == "
" ){
$post[
status
] = "发布";
} else {
$post[
status
] = "<b>未发布</b>";
}
echo <<<EOB
<tr id="entry_{$post[
post_id
]}" class="Alt">
<td style="text
align: left">{$post[
title
]} ({$time})</td>
<td style="text
align: center">{$post[
status
]}</td>
<td style="text
align: center">{$post[
view_count
]}</td>
<td style="text
align: center">{$post[
comment_count
]}</td>
<td style="text
align: center"><a href="article
php?action=editArticle&postID={$post[
post_id
]}">编 辑</a></td>
<td style="text
align: center"><a href="JavaScript:if(confirm(
从数据库中删除这篇文档?
)==true) {window
location=
article
php?action=delete&postID= {$post[
post_id
]}
;}">删除</a></td>
</tr>
EOB;
}
echo "</table>";
if( isset( $pagination) ){$pagination
>createLinks( ) ;}
} else {
echo "当前无内容!";
}
?>
</div>
</div>
<span id="currentPostId" style="display:none;"></span>
</div>
</td>
</tr>
</table>
<div id="blog_top_nav_block">
<div id="site_nav">
</div>
<div id="login_area">
<span id="span_userinfo"><b><?php echo $_SESSION[
username
]; ?> </b> <a href="?action=logout">logout</a></span>
</div>
<div class="clear"></div>
</div>
<table id="Footer" border="
" cellpadding="
" cellspacing="
" width="
%">
<tr>
<td colspan="
">
<div>
© <?php echo date("Y"
time( ) ); ?> Arist
</div>
</td>
</tr>
</table>
</body>
</html>
以上只是显示数据人人都会啊
我们怎么操作这些数据呢?
操作就像是一种控制能力 学生时代踢足球我对球场有一种很强的控制能力大学足球比赛拿了次冠军次亚军次季军大四没去中学更是无数荣誉
我的位置是中卫在足球场上这个位置你得有统观全局的能力也得有很强的个人能力还有指挥能力扯的远了现在天天坐在电脑前这些东西也早就没了
就剩下些经验之谈不过其中滋味你也须也体验过
我这个博客有个缺点每次你对数据库进行一次读写操作你得刷新啊!我知道这对服务器的负载很大但是我觉得如果一个新技术你没有完全吃透盲目运用只会适得其反
所以暂时我还是牺牲服务器的响应时间内存消耗来获得一种相对的稳定!
所以我对全局还不是很了解还有很多未知地领域没有涉入如深入ajax深入phpc 不多说了
好了看看怎么对数据进行CRUD 吧!
DELETE 删除
先看这个指令 postphp?action=delete&postID=
当我们确认要删除时这里有个注意的地方我们能先要对该文档所属的分类下的 count_child_number 这个字段进行 一个减 的操作
为什么? 因为我也开始犯了个逻辑错误删除后我才调用这个方法还记得嘛!reduceChildNumber( ) 有趣地地方就是这里让我受益匪浅!也让我调试了N久!
所以当你的语法都没错的时候可能是你的逻辑错了!或是方法错了!这就是我的注释! 请看
复制代码 代码如下:
$post = new Post;
$filter[
post_id
] = isset( $_GET[
postID
] ) ? ( int )$_GET[
postID
] : "";
// !important 在数据删除之前 先将该分类下的文章数量减
// 否则你不知道删除那个分类下的文章数量
// 我犯了个逻辑错误 先删除了 文档
然后查该文档的分类ID
永远也查不到
因为已经不存在了
$post
>reduceChildNumber( "category"
( int ) $_GET[
postID
] );
$result = $post
>delete("post"
$filter );
这里我们只要初始化我们文章头顶的那个 model 就可以轻松调用 delete() 方法
CREATE 插入
先看这个指令 postphp?action=newPost
说实话我很久没有插入了呵呵! 看控制方法
复制代码 代码如下:
function newPost( )
{
$results[
action
] = "newPost" ;
$results[
pageTitle
] = " Add New post" ;
$results[
newPost
] = "true";
$results[
path
] = "<a >随笔</a>» <span>添加随笔</span>" ;
$post = new Post;
$cat = new Category;
$results[
categories
] = $cat
>getCategoryList( "post");
// 新建文档
if( isset( $_POST[
saveChanged
] ))
{
$post
> storePostFormValues( $_POST );
$result = $post
>insertPost( );
if( $result )
{
$post
>addChildNumber( "category"
$_POST[
category
] );
header("Location: post
php?action=isPost&status=Inserted");
}
else
{
header("Location: post
php?action=isPost&error=InsertedFailed");
}
// 保存到草稿箱
} else if( isset( $_POST[
saveDraft
]) )
{
$post = new Post;
$post
> storePostFormValues( $_POST );
$post
>saveDraft( );
header("Location: post
php?action=isPost&status=postSaveToDraft");
// cancel
} else if( isset( $_POST[
cancel
] ))
{
header("Location: post
php?action=isPost");
}
else
{
require_once(TEMPLATE_PATH
"/post/post_edit
php");
}
}
我们使用一个模版来同时进行文档的插入和更新关键就是 isset( )当我们调用控制器的 newPost 方法时我们并没有往模版中传入文档
所 以在模版中我们用 isset() 来做判断时我们获得了空值是个好东西这样我们不会输出任何内容到模版中去这样我们等待用户提交表单在这里我为了省事暂时没有对表单进行过 滤不过我留了个后门以后来更新 好的假设我们的表单被提交了(也被你基本的过滤了)
我们调用 post model中 的 storePostFormValues( ) storeDiaryFormValues( ); 记得嘛这个方法把所有的表单内容放入一个数组在做了基本的类型检查之后
到这里已经成功一半了下面就是 insert***() 这就是mysql 万能数据库操作类的好处它能帮你处理各种表单各种类型当然你如果要求更细更多你可以继承它扩展
它的方法或新建方法 到这里离完成还有一步addChildNumber( ) 当你为你的文档选择分类时同时也要在相应的分类表中的 count_child_number中加
如果用户选择将文档放入草稿箱的话只需插入一个 type = PostDraft 的文档记录
UPDATE 更新
先看这个指令 postphp?action=updatePost&postID=
更新首先就要获得这个文档的数据postID 同样是 GET方法得到 这样我们就可以初始化表单中的 value 值了 isset( ) 在这里起了关键作用不是嘛?
后面的部分大同小异 storePostFormValues( ) storeDiaryFormValues( ); 然后你调用 post model update***( )
看代码
复制代码 代码如下:
function updatePost( )
{
$results[
action
] = "updatePost";
$results[
pageTitle
] = "Edit post";
$post = new Post;
$cat = new Category;
$results[
categories
] = $cat
>getCategoryList("post");
if( isset( $_POST[
saveChanged
] ))
{
// do update
$post
>storePostFormValues( $_POST );
$post
>updatePost( );
header("Location: post
php?action=isPost&status=changesSaved") ;
} else if( isset( $_POST[
cancel
] ) )
{
header("Location: post
php?action=isPost&status=Cancel");
}else
{
// get the post
$postID = isset( $_GET[
postID
] ) ? $_GET[
postID
] : " ";
$results[
post
] = $post
>getPostByID( $postID );
require_once(TEMPLATE_PATH
"/post/post_edit
php");
}
}
到这里就差不多了我们实现了几乎所有的基本操作
几点说明这些action 有的是导航有的是生成的大部分是固定的自己看着用吧 下篇说说 分类的事!还有就是这篇博客写完后会放在一个网站上
你如果想要源码学习的话我会提供下载谢谢你花这么长时间听我唠叨 看到这句的人祝你们 昨天 快乐嘿嘿