自學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 有的是導航有的是生成的大部分是固定的自己看著用吧 下篇說說 分類的事!還有就是這篇博客寫完後會放在一個網站上
你如果想要源碼學習的話我會提供下載謝謝你花這麼長時間聽我唠叨 看到這句的人祝你們 昨天 快樂嘿嘿
From:http://tw.wingwit.com/Article/program/PHP/201311/21062.html